diff --git a/.asf.yaml b/.asf.yaml deleted file mode 100644 index 99596cfd6..000000000 --- a/.asf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -notifications: - commits: commits@rocketmq.apache.org - issues: commits@rocketmq.apache.org - pullrequests: commits@rocketmq.apache.org - jobs: commits@rocketmq.apache.org - discussions: dev@rocketmq.apache.org diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 000000000..8c28fe2ba --- /dev/null +++ b/.bazelignore @@ -0,0 +1,2 @@ +_build +cmake-build-debug \ No newline at end of file diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 000000000..dce4c4711 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,138 @@ +# Bazel doesn't need more than 200MB of memory for local build based on memory profiling: +# https://docs.bazel.build/versions/master/skylark/performance.html#memory-profiling +# The default JVM max heapsize is 1/4 of physical memory up to 32GB which could be large +# enough to consume all memory constrained by cgroup in large host. +# Limiting JVM heapsize here to let it do GC more when approaching the limit to +# leave room for compiler/linker. +# The number 2G is chosen heuristically to both support large VM and small VM with RBE. +# Startup options cannot be selected via config. +startup --host_jvm_args=-Xmx2g + +run --color=yes + +build --color=yes + +build --host_force_python=PY3 +build --host_javabase=@bazel_tools//tools/jdk:remote_jdk11 +build --javabase=@bazel_tools//tools/jdk:remote_jdk11 + +# https://docs.bazel.build/versions/main/command-line-reference.html#flag--enable_platform_specific_config +# If true, Bazel picks up host-OS-specific config lines from bazelrc files. For example, if the host OS is Linux and +# you run bazel build, Bazel picks up lines starting with build:linux. Supported OS identifiers are linux, macos, +# windows, freebsd, and openbsd. Enabling this flag is equivalent to using --config=linux on Linux, +# --config=windows on Windows, etc. +build --enable_platform_specific_config + +build:windows --cxxopt=/std:c++14 + +build:macos --cxxopt=-std=c++11 +build:macos --strip=never +build:macos --copt -g + +# Enable position independent code (this is the default on macOS and Windows) +# (Workaround for https://github.com/bazelbuild/rules_foreign_cc/issues/421) +build:linux --copt=-fPIC +build:linux --cxxopt=-std=c++11 +build:linux --strip=never + +# We already have absl in the build, define absl=1 to tell googletest to use absl for backtrace. +build --define absl=1 +build --define SPDLOG_FMT_EXTERNAL=1 + +# Pass PATH, CC, CXX and LLVM_CONFIG variables from the environment. +build --action_env=CC +build --action_env=CXX +build --action_env=LD_LIBRARY_PATH +build --action_env=LLVM_CONFIG +build --action_env=PATH + +build --copt=-maes + +# Common flags for sanitizers +build:sanitizer --define tcmalloc=disabled +build:sanitizer --linkopt -ldl +build:sanitizer --build_tag_filters=-no_san +build:sanitizer --test_tag_filters=-no_san + +# Common flags for Clang +build:clang --action_env=BAZEL_COMPILER=clang +build:clang --linkopt=-fuse-ld=lld + + +# Address sanitizer +# To use it: bazel build --config=asan +build:asan --config=sanitizer +build:asan --compiler clang +build:asan --strip=never +build:asan --copt -fsanitize=address +# ASAN needs -O1 to get reasonable performance. +build:asan --copt -O1 +build:asan --copt -fno-optimize-sibling-calls +build:asan --copt -g +build:asan --copt -fno-omit-frame-pointer +build:asan --linkopt -fsanitize=address +build:asan --copt -DADDRESS_SANITIZER=1 +build:asan --copt -D__SANITIZE_ADDRESS__ + +# Clang ASAN/UBSAN +build:clang-asan --config=asan +build:clang-asan --linkopt -fuse-ld=lld + +build:macos-asan --config=asan +# Workaround, see https://github.com/bazelbuild/bazel/issues/6932 +build:macos-asan --copt -Wno-macro-redefined +build:macos-asan --copt -D_FORTIFY_SOURCE=0 +# Workaround, see https://github.com/bazelbuild/bazel/issues/4341 +build:macos-asan --copt -DGRPC_BAZEL_BUILD +# Dynamic link cause issues like: `dyld: malformed mach-o: load commands size (59272) > 32768` +build:macos-asan --dynamic_mode=off + +# Thread sanitizer +# bazel build --config=tsan +build:tsan --config=sanitizer +build:tsan --compiler clang +build:tsan --strip=never +build:tsan --copt -fsanitize=thread +build:tsan --copt -DTHREAD_SANITIZER +build:tsan --copt -DDYNAMIC_ANNOTATIONS_ENABLED=1 +build:tsan --copt -DDYNAMIC_ANNOTATIONS_EXTERNAL_IMPL=1 +build:tsan --copt -O1 +build:tsan --copt -fno-omit-frame-pointer +build:tsan --linkopt -fsanitize=thread + +# --config msan: Memory sanitizer +build:msan --compiler clang +build:msan --strip=never +build:msan --copt -fsanitize=memory +build:msan --copt -DADDRESS_SANITIZER +build:msan --copt -O1 +build:msan --copt -fno-omit-frame-pointer +build:msan --linkopt -fsanitize=memory + +# --config ubsan: Undefined Behavior Sanitizer +build:ubsan --compiler clang +build:ubsan --strip=never +build:ubsan --copt -fsanitize=undefined +build:ubsan --copt -O1 +build:ubsan --copt -fno-omit-frame-pointer +build:ubsan --linkopt -fsanitize=undefined +build:ubsan --linkopt -lubsan + +# Use remote cache, --config=remote_cache See https://docs.bazel.build/versions/main/guide.html#--config +build:remote_cache --remote_cache=grpc://11.122.50.85:9092 +build:remote_cache --experimental_remote_downloader=grpc://11.122.50.85:9092 +test:remote_cache --remote_cache=grpc://11.122.50.85:9090 +test:remote_cache --remote_upload_local_results=true + +# Coverage options +coverage --config=coverage +# 1.5x original timeout + 300s for trace merger in all categories +build:coverage --test_timeout=390,750,1500,5700 +build:coverage --define=dynamic_link_tests=true +build:coverage --combined_report=lcov +build:coverage --strategy=TestRunner=sandboxed,local +build:coverage --strategy=CoverageReport=sandboxed,local +build:coverage --collect_code_coverage +build:coverage --instrumentation_filter="//src/main/cpp[/:],//api[/:],-//src/main/cpp/base/mocks[/:],-//src/main/cpp/client/mocks[/:],-//src/main/cpp/rocketmq/mocks[/:]" + +test --test_output=errors \ No newline at end of file diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 000000000..6aba2b245 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +4.2.0 diff --git a/.clang-format b/.clang-format index 7904cdf16..6345fe113 100644 --- a/.clang-format +++ b/.clang-format @@ -1,3 +1,24 @@ -BasedOnStyle: Chromium +--- +Language: Cpp +AccessModifierOffset: -2 ColumnLimit: 120 -TabWidth: 2 +DerivePointerAlignment: false +PointerAlignment: Left +SortIncludes: true +IndentWidth: 2 +NamespaceIndentation: Inner +AlwaysBreakTemplateDeclarations: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +IndentCaseBlocks: false +IndentCaseLabels: true +... + +--- +Language: Proto +ColumnLimit: 120 +SpacesInContainerLiterals: false +AllowShortFunctionsOnASingleLine: None +ReflowComments: false +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..693858657 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,81 @@ +Checks: '-clang-analyzer-core.NonNullParamChecker, + -clang-analyzer-optin.cplusplus.UninitializedObject, + abseil-duration-*, + abseil-faster-strsplit-delimiter, + abseil-no-namespace, + abseil-redundant-strcat-calls, + abseil-str-cat-append, + abseil-string-find-startswith, + abseil-upgrade-duration-conversions, + bugprone-assert-side-effect, + bugprone-unused-raii, + bugprone-use-after-move, + clang-analyzer-core.DivideZero, + misc-unused-using-decls, + modernize-deprecated-headers, + modernize-loop-convert, + modernize-make-shared, + modernize-make-unique, + modernize-return-braced-init-list, + modernize-use-default-member-init, + modernize-use-equals-default, + modernize-use-nullptr, + modernize-use-override, + modernize-use-using, + performance-faster-string-find, + performance-for-range-copy, + performance-inefficient-algorithm, + performance-inefficient-vector-operation, + performance-noexcept-move-constructor, + performance-move-constructor-init, + performance-type-promotion-in-math-fn, + performance-unnecessary-copy-initialization, + readability-braces-around-statements, + readability-container-size-empty, + readability-identifier-naming, + readability-redundant-control-flow, + readability-redundant-member-init, + readability-redundant-smartptr-get, + readability-redundant-string-cstr' + +WarningsAsErrors: '*' + +CheckOptions: + - key: bugprone-assert-side-effect.AssertMacros + value: 'ASSERT' + + - key: bugprone-dangling-handle.HandleClasses + value: 'std::basic_string_view;std::experimental::basic_string_view;absl::string_view' + + - key: modernize-use-auto.MinTypeNameLength + value: '10' + + - key: readability-identifier-naming.ClassCase + value: 'CamelCase' + + - key: readability-identifier-naming.EnumCase + value: 'CamelCase' + + - key: readability-identifier-naming.EnumConstantCase + value: 'CamelCase' + + - key: readability-identifier-naming.ParameterCase + value: 'lower_case' + + - key: readability-identifier-naming.PrivateMemberCase + value: 'lower_case' + + - key: readability-identifier-naming.PrivateMemberSuffix + value: '_' + + - key: readability-identifier-naming.StructCase + value: 'CamelCase' + + - key: readability-identifier-naming.TypeAliasCase + value: 'CamelCase' + + - key: readability-identifier-naming.UnionCase + value: 'CamelCase' + + - key: readability-identifier-naming.FunctionCase + value: 'camelBack' diff --git a/.github/ISSUE_TEMPLATE/issue_template.md b/.github/ISSUE_TEMPLATE/issue_template.md deleted file mode 100644 index f3ad25a46..000000000 --- a/.github/ISSUE_TEMPLATE/issue_template.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -name: ISSUE_TEMPLATE -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -The issue tracker is **ONLY** used for the CPP/C client (feature request of RocketMQ need to follow [RIP process](https://github.com/apache/rocketmq/wiki/RocketMQ-Improvement-Proposal)). Keep in mind, please check whether there is an existing same report before your raise a new one. - -Alternately (especially if your communication is not a bug report), you can send mail to our [mailing lists](http://rocketmq.apache.org/about/contact/). We welcome any friendly suggestions, bug fixes, collaboration, and other improvements. - -Please ensure that your bug report is clear and that it is complete. Otherwise, we may be unable to understand it or to reproduce it, either of which would prevent us from fixing the bug. We strongly recommend the report(bug report or feature request) could include some hints as to the following: - -**BUG REPORT** - -1. Please describe the issue you observed: - -- What did you do (The steps to reproduce)? - -- What did you expect to see? - -- What did you see instead? - -2. Please tell us about your environment: - - - What is your OS? - - - What is your client version? - - - What is your RocketMQ version? - -3. Other information (e.g. detailed explanation, logs, related issues, suggestions on how to fix, etc): - -**FEATURE REQUEST** - -1. Please describe the feature you are requesting. - -2. Provide any additional detail on your proposed use case for this feature. - -2. Indicate the importance of this issue to you (blocker, must-have, should-have, nice-to-have). Are you currently using any workarounds to address this issue? - -4. If there are some sub-tasks using -[] for each subtask and create a corresponding issue to map to the sub task: - -- [sub-task1-issue-number](example_sub_issue1_link_here): sub-task1 description here, -- [sub-task2-issue-number](example_sub_issue2_link_here): sub-task2 description here, -- ... diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..72c1725fd --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,38 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the master branch + push: + branches: [ main ] + pull_request: + branches: [ main ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + - name: Setup Bazel + run: | + sudo apt-get -qq install npm + sudo npm install -g @bazel/bazelisk + - name: Use Bazel + if: matrix.os != 'windows' + run: bazel -h + - name: Compile All Targets + run: bazel build //... + - name: Run Unit Tests + run: bazel test --test_output=errors //src/test/cpp/ut/... diff --git a/.gitignore b/.gitignore index 013ac4aa0..d754e5239 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,18 @@ -.idea -cmake-build-debug/ -bin -build -libs/signature/lib -tmp_* -Testing +bazel-bin +bazel-out +bazel-rocketmq-cpp +bazel-testlogs +cmake-build-debug +.clwb +compile_commands.json .vscode +logs +_build +build +cmake-build-debug-coverage +.idea .cache -compile_commands.json +.DS_Store +external +bazel-apache_rocketmq_client_cpp +bazel-rocketmq-client-cpp diff --git a/.travis.yml b/.travis.yml index 2d7869994..d2440c443 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,60 +1,173 @@ language: cpp -sudo: required -dist: trusty -compiler: gcc - -addons: - apt: - packages: lcov - -install: - #- sudo apt-get update - #- sudo apt-get install -y git gcc-4.8 g++-4.8 autoconf cmake libtool wget unzip libbz2-dev zlib1g-dev - - echo 'MAVEN_OPTS="$MAVEN_OPTS -Xmx1024m -XX:MaxPermSize=512m -XX:+BytecodeVerificationLocal"' >> ~/.mavenrc - - cat ~/.mavenrc - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then export JAVA_HOME=$(/usr/libexec/java_home); fi - - if [ "$TRAVIS_OS_NAME" == "linux" ]; then jdk_switcher use "$CUSTOM_JDK"; fi - -before_script: - - wget https://archive.apache.org/dist/rocketmq/4.3.2/rocketmq-all-4.3.2-bin-release.zip - - unzip rocketmq-all-4.3.2-bin-release.zip - - cd rocketmq-all-4.3.2-bin-release - - perl -i -pe's/-Xms8g -Xmx8g -Xmn4g/-Xms2g -Xmx2g -Xmn1g/g' bin/runbroker.sh - - nohup sh bin/mqnamesrv & - - nohup sh bin/mqbroker -n localhost:9876 & - - sleep 10 - - ./bin/mqadmin updateTopic -b '127.0.0.1:10911' –n '127.0.0.1:9876' -t test - - ./bin/mqadmin updateSubGroup -b '127.0.0.1:10911' –n '127.0.0.1:9876' -g testGroup - - cd .. - -script: - - ./build.sh test codecov noVerbose - -after_success: - # Create lcov report - # capture coverage info - - lcov --directory . --capture --output-file coverage.info - # filter out system and extra files. - # To also not include test code in coverage add them with full path to the patterns: '*/tests/*' - - lcov --remove coverage.info '/usr/*' '/home/travis/build/*/rocketmq-client-cpp/bin/*' '/home/travis/build/*/rocketmq-client-cpp/libs/*' --output-file coverage.info - # output coverage data for debugging (optional) - - lcov --list coverage.info - # Uploading to CodeCov - # '-f' specifies file(s) to use and disables manual coverage gathering and file search which has already been done above - - bash <(curl -s https://codecov.io/bash) -f coverage.info || echo "Codecov did not collect coverage reports" - -matrix: +dist: focal +jobs: include: - os: linux - env: CUSTOM_JDK="oraclejdk8" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - g++-4.9 + - cmake + env: + - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - g++-5 + - cmake + env: + - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" + + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - g++-6 + - cmake + env: + - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" + + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - g++-7 + - cmake + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" + # Clang on Linux + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.6 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-3.6 + - cmake + env: + - MATRIX_EVAL="CC=clang-3.6 && CXX=clang++-3.6" + + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.7 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-3.7 + - cmake + env: + - MATRIX_EVAL="CC=clang-3.7 && CXX=clang++-3.7" + + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-3.8 + - cmake + env: + - MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" + + # works on Trusty + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-3.9 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-3.9 + - cmake + env: + - MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9" - # This is the job to check code format. + # works on Trusty - os: linux - dist: trusty - env: LINT=1 PYTHON=2.7 - before_install: - #- sudo apt-get update -qq - #- sudo apt-get install -qq clang-format-3.8 - install: [] - script: - - sudo sh .travis/check-git-clang-format.sh + addons: + apt: + sources: + - llvm-toolchain-trusty-4.0 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-4.0 + - cmake + env: + - MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0" + + # works on Trusty + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-5.0 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-5.0 + - cmake + env: + - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" + # GCC on Mac +# - os: osx +# osx_image: xcode8 +# env: +# - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" +# +# - os: osx +# osx_image: xcode8 +# env: +# - MATRIX_EVAL="brew install gcc5 && CC=gcc-5 && CXX=g++-5" +# +# - os: osx +# osx_image: xcode8 +# env: +# - MATRIX_EVAL="brew install gcc6 && CC=gcc-6 && CXX=g++-6" +# +# - os: osx +# osx_image: xcode8 +# env: +# - MATRIX_EVAL="brew install gcc && CC=gcc-7 && CXX=g++-7" +before_install: + - eval "${MATRIX_EVAL}" + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew update ; + brew install cmake ; + fi + +script: + - ci/build.sh diff --git a/.travis/check-git-clang-format.sh b/.travis/check-git-clang-format.sh deleted file mode 100644 index b8d3d1ffa..000000000 --- a/.travis/check-git-clang-format.sh +++ /dev/null @@ -1,32 +0,0 @@ -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#!/bin/bash - - if [ "$TRAVIS_PULL_REQUEST" = "true" ]; then - base_commit="$TRAVIS_BRANCH" -else - base_commit="HEAD^" -fi - - output="$(sudo python .travis/git-clang-format --binary clang-format-3.8 --commit $base_commit --diff)" - - if [ "$output" = "no modified files to format" ] || [ "$output" = "clang-format did not modify any files" ]; then - echo "clang-format passed." - exit 0 -else - echo "clang-format failed." - echo "$output" - exit 1 -fi diff --git a/.travis/git-clang-format b/.travis/git-clang-format deleted file mode 100644 index c41be9509..000000000 --- a/.travis/git-clang-format +++ /dev/null @@ -1,490 +0,0 @@ -#!/usr/bin/env python -# -#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -#===------------------------------------------------------------------------===# - -r""" -clang-format git integration -============================ - -This file provides a clang-format integration for git. Put it somewhere in your -path and ensure that it is executable. Then, "git clang-format" will invoke -clang-format on the changes in current files or a specific commit. - -For further details, run: -git clang-format -h - -Requires Python 2.7 -""" - -import argparse -import collections -import contextlib -import errno -import os -import re -import subprocess -import sys - -usage = 'git clang-format [OPTIONS] [] [--] [...]' - -desc = ''' -Run clang-format on all lines that differ between the working directory -and , which defaults to HEAD. Changes are only applied to the working -directory. - -The following git-config settings set the default of the corresponding option: - clangFormat.binary - clangFormat.commit - clangFormat.extension - clangFormat.style -''' - -# Name of the temporary index file in which save the output of clang-format. -# This file is created within the .git directory. -temp_index_basename = 'clang-format-index' - - -Range = collections.namedtuple('Range', 'start, count') - - -def main(): - config = load_git_config() - - # In order to keep '--' yet allow options after positionals, we need to - # check for '--' ourselves. (Setting nargs='*' throws away the '--', while - # nargs=argparse.REMAINDER disallows options after positionals.) - argv = sys.argv[1:] - try: - idx = argv.index('--') - except ValueError: - dash_dash = [] - else: - dash_dash = argv[idx:] - argv = argv[:idx] - - default_extensions = ','.join([ - # From clang/lib/Frontend/FrontendOptions.cpp, all lower case - 'c', 'h', # C - 'm', # ObjC - 'mm', # ObjC++ - 'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp', # C++ - # Other languages that clang-format supports - 'proto', 'protodevel', # Protocol Buffers - 'js', # JavaScript - 'ts', # TypeScript - ]) - - p = argparse.ArgumentParser( - usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter, - description=desc) - p.add_argument('--binary', - default=config.get('clangformat.binary', 'clang-format'), - help='path to clang-format'), - p.add_argument('--commit', - default=config.get('clangformat.commit', 'HEAD'), - help='default commit to use if none is specified'), - p.add_argument('--diff', action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", - help='print a diff instead of applying the changes') - p.add_argument('--extensions', - default=config.get('clangformat.extensions', - default_extensions), - help=('comma-separated list of file extensions to format, ' - 'excluding the period and case-insensitive')), - p.add_argument('--exclude', help='Exclude files matching this regex.') - p.add_argument('-f', '--force', action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", - help='allow changes to unstaged files') - p.add_argument('-p', '--patch', action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fstore_true", - help='select hunks interactively') - p.add_argument('-q', '--quiet', action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fcount", default=0, - help='print less information') - p.add_argument('--style', - default=config.get('clangformat.style', None), - help='passed to clang-format'), - p.add_argument('-v', '--verbose', action="iframe.php?url=https%3A%2F%2Fgithub.com%2Fcount", default=0, - help='print extra information') - # We gather all the remaining positional arguments into 'args' since we need - # to use some heuristics to determine whether or not was present. - # However, to print pretty messages, we make use of metavar and help. - p.add_argument('args', nargs='*', metavar='', - help='revision from which to compute the diff') - p.add_argument('ignored', nargs='*', metavar='...', - help='if specified, only consider differences in these files') - opts = p.parse_args(argv) - - opts.verbose -= opts.quiet - del opts.quiet - - commit, files = interpret_args(opts.args, dash_dash, opts.commit) - changed_lines = compute_diff_and_extract_lines(commit, files) - if opts.verbose >= 1: - ignored_files = set(changed_lines) - filter_by_extension(changed_lines, opts.extensions.lower().split(',')) - if opts.exclude: - for filename in changed_lines.keys(): - if re.match(opts.exclude, filename): - del changed_lines[filename] - if opts.verbose >= 1: - ignored_files.difference_update(changed_lines) - if ignored_files: - print 'Ignoring changes in the following files:' - for filename in ignored_files: - print ' ', filename - if changed_lines: - print 'Running clang-format on the following files:' - for filename in changed_lines: - print ' ', filename - if not changed_lines: - print 'no modified files to format' - return - # The computed diff outputs absolute paths, so we must cd before accessing - # those files. - cd_to_toplevel() - old_tree = create_tree_from_workdir(changed_lines) - new_tree = run_clang_format_and_save_to_tree(changed_lines, - binary=opts.binary, - style=opts.style) - if opts.verbose >= 1: - print 'old tree:', old_tree - print 'new tree:', new_tree - if old_tree == new_tree: - if opts.verbose >= 0: - print 'clang-format did not modify any files' - elif opts.diff: - print_diff(old_tree, new_tree) - else: - changed_files = apply_changes(old_tree, new_tree, force=opts.force, - patch_mode=opts.patch) - if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1: - print 'changed files:' - for filename in changed_files: - print ' ', filename - - -def load_git_config(non_string_options=None): - """Return the git configuration as a dictionary. - - All options are assumed to be strings unless in `non_string_options`, in which - is a dictionary mapping option name (in lower case) to either "--bool" or - "--int".""" - if non_string_options is None: - non_string_options = {} - out = {} - for entry in run('git', 'config', '--list', '--null').split('\0'): - if entry: - name, value = entry.split('\n', 1) - if name in non_string_options: - value = run('git', 'config', non_string_options[name], name) - out[name] = value - return out - - -def interpret_args(args, dash_dash, default_commit): - """Interpret `args` as "[commit] [--] [files...]" and return (commit, files). - - It is assumed that "--" and everything that follows has been removed from - args and placed in `dash_dash`. - - If "--" is present (i.e., `dash_dash` is non-empty), the argument to its - left (if present) is taken as commit. Otherwise, the first argument is - checked if it is a commit or a file. If commit is not given, - `default_commit` is used.""" - if dash_dash: - if len(args) == 0: - commit = default_commit - elif len(args) > 1: - die('at most one commit allowed; %d given' % len(args)) - else: - commit = args[0] - object_type = get_object_type(commit) - if object_type not in ('commit', 'tag'): - if object_type is None: - die("'%s' is not a commit" % commit) - else: - die("'%s' is a %s, but a commit was expected" % (commit, object_type)) - files = dash_dash[1:] - elif args: - if disambiguate_revision(args[0]): - commit = args[0] - files = args[1:] - else: - commit = default_commit - files = args - else: - commit = default_commit - files = [] - return commit, files - - -def disambiguate_revision(value): - """Returns True if `value` is a revision, False if it is a file, or dies.""" - # If `value` is ambiguous (neither a commit nor a file), the following - # command will die with an appropriate error message. - run('git', 'rev-parse', value, verbose=False) - object_type = get_object_type(value) - if object_type is None: - return False - if object_type in ('commit', 'tag'): - return True - die('`%s` is a %s, but a commit or filename was expected' % - (value, object_type)) - - -def get_object_type(value): - """Returns a string description of an object's type, or None if it is not - a valid git object.""" - cmd = ['git', 'cat-file', '-t', value] - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - if p.returncode != 0: - return None - return stdout.strip() - - -def compute_diff_and_extract_lines(commit, files): - """Calls compute_diff() followed by extract_lines().""" - diff_process = compute_diff(commit, files) - changed_lines = extract_lines(diff_process.stdout) - diff_process.stdout.close() - diff_process.wait() - if diff_process.returncode != 0: - # Assume error was already printed to stderr. - sys.exit(2) - return changed_lines - - -def compute_diff(commit, files): - """Return a subprocess object producing the diff from `commit`. - - The return value's `stdin` file object will produce a patch with the - differences between the working directory and `commit`, filtered on `files` - (if non-empty). Zero context lines are used in the patch.""" - cmd = ['git', 'diff-index', '-p', '-U0', commit, '--'] - cmd.extend(files) - p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - p.stdin.close() - return p - - -def extract_lines(patch_file): - """Extract the changed lines in `patch_file`. - - The return value is a dictionary mapping filename to a list of (start_line, - line_count) pairs. - - The input must have been produced with ``-U0``, meaning unidiff format with - zero lines of context. The return value is a dict mapping filename to a - list of line `Range`s.""" - matches = {} - for line in patch_file: - match = re.search(r'^\+\+\+\ [^/]+/(.*)', line) - if match: - filename = match.group(1).rstrip('\r\n') - match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line) - if match: - start_line = int(match.group(1)) - line_count = 1 - if match.group(3): - line_count = int(match.group(3)) - if line_count > 0: - matches.setdefault(filename, []).append(Range(start_line, line_count)) - return matches - - -def filter_by_extension(dictionary, allowed_extensions): - """Delete every key in `dictionary` that doesn't have an allowed extension. - - `allowed_extensions` must be a collection of lowercase file extensions, - excluding the period.""" - allowed_extensions = frozenset(allowed_extensions) - for filename in dictionary.keys(): - base_ext = filename.rsplit('.', 1) - if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions: - del dictionary[filename] - - -def cd_to_toplevel(): - """Change to the top level of the git repository.""" - toplevel = run('git', 'rev-parse', '--show-toplevel') - os.chdir(toplevel) - - -def create_tree_from_workdir(filenames): - """Create a new git tree with the given files from the working directory. - - Returns the object ID (SHA-1) of the created tree.""" - return create_tree(filenames, '--stdin') - - -def run_clang_format_and_save_to_tree(changed_lines, binary='clang-format', - style=None): - """Run clang-format on each file and save the result to a git tree. - - Returns the object ID (SHA-1) of the created tree.""" - def index_info_generator(): - for filename, line_ranges in changed_lines.iteritems(): - mode = oct(os.stat(filename).st_mode) - blob_id = clang_format_to_blob(filename, line_ranges, binary=binary, - style=style) - yield '%s %s\t%s' % (mode, blob_id, filename) - return create_tree(index_info_generator(), '--index-info') - - -def create_tree(input_lines, mode): - """Create a tree object from the given input. - - If mode is '--stdin', it must be a list of filenames. If mode is - '--index-info' is must be a list of values suitable for "git update-index - --index-info", such as " ". Any other mode - is invalid.""" - assert mode in ('--stdin', '--index-info') - cmd = ['git', 'update-index', '--add', '-z', mode] - with temporary_index_file(): - p = subprocess.Popen(cmd, stdin=subprocess.PIPE) - for line in input_lines: - p.stdin.write('%s\0' % line) - p.stdin.close() - if p.wait() != 0: - die('`%s` failed' % ' '.join(cmd)) - tree_id = run('git', 'write-tree') - return tree_id - - -def clang_format_to_blob(filename, line_ranges, binary='clang-format', - style=None): - """Run clang-format on the given file and save the result to a git blob. - - Returns the object ID (SHA-1) of the created blob.""" - clang_format_cmd = [binary, filename] - if style: - clang_format_cmd.extend(['-style='+style]) - clang_format_cmd.extend([ - '-lines=%s:%s' % (start_line, start_line+line_count-1) - for start_line, line_count in line_ranges]) - try: - clang_format = subprocess.Popen(clang_format_cmd, stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - except OSError as e: - if e.errno == errno.ENOENT: - die('cannot find executable "%s"' % binary) - else: - raise - clang_format.stdin.close() - hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin'] - hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout, - stdout=subprocess.PIPE) - clang_format.stdout.close() - stdout = hash_object.communicate()[0] - if hash_object.returncode != 0: - die('`%s` failed' % ' '.join(hash_object_cmd)) - if clang_format.wait() != 0: - die('`%s` failed' % ' '.join(clang_format_cmd)) - return stdout.rstrip('\r\n') - - -@contextlib.contextmanager -def temporary_index_file(tree=None): - """Context manager for setting GIT_INDEX_FILE to a temporary file and deleting - the file afterward.""" - index_path = create_temporary_index(tree) - old_index_path = os.environ.get('GIT_INDEX_FILE') - os.environ['GIT_INDEX_FILE'] = index_path - try: - yield - finally: - if old_index_path is None: - del os.environ['GIT_INDEX_FILE'] - else: - os.environ['GIT_INDEX_FILE'] = old_index_path - os.remove(index_path) - - -def create_temporary_index(tree=None): - """Create a temporary index file and return the created file's path. - - If `tree` is not None, use that as the tree to read in. Otherwise, an - empty index is created.""" - gitdir = run('git', 'rev-parse', '--git-dir') - path = os.path.join(gitdir, temp_index_basename) - if tree is None: - tree = '--empty' - run('git', 'read-tree', '--index-output='+path, tree) - return path - - -def print_diff(old_tree, new_tree): - """Print the diff between the two trees to stdout.""" - # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output - # is expected to be viewed by the user, and only the former does nice things - # like color and pagination. - subprocess.check_call(['git', 'diff', old_tree, new_tree, '--']) - - -def apply_changes(old_tree, new_tree, force=False, patch_mode=False): - """Apply the changes in `new_tree` to the working directory. - - Bails if there are local changes in those files and not `force`. If - `patch_mode`, runs `git checkout --patch` to select hunks interactively.""" - changed_files = run('git', 'diff-tree', '-r', '-z', '--name-only', old_tree, - new_tree).rstrip('\0').split('\0') - if not force: - unstaged_files = run('git', 'diff-files', '--name-status', *changed_files) - if unstaged_files: - print >>sys.stderr, ('The following files would be modified but ' - 'have unstaged changes:') - print >>sys.stderr, unstaged_files - print >>sys.stderr, 'Please commit, stage, or stash them first.' - sys.exit(2) - if patch_mode: - # In patch mode, we could just as well create an index from the new tree - # and checkout from that, but then the user will be presented with a - # message saying "Discard ... from worktree". Instead, we use the old - # tree as the index and checkout from new_tree, which gives the slightly - # better message, "Apply ... to index and worktree". This is not quite - # right, since it won't be applied to the user's index, but oh well. - with temporary_index_file(old_tree): - subprocess.check_call(['git', 'checkout', '--patch', new_tree]) - index_tree = old_tree - else: - with temporary_index_file(new_tree): - run('git', 'checkout-index', '-a', '-f') - return changed_files - - -def run(*args, **kwargs): - stdin = kwargs.pop('stdin', '') - verbose = kwargs.pop('verbose', True) - strip = kwargs.pop('strip', True) - for name in kwargs: - raise TypeError("run() got an unexpected keyword argument '%s'" % name) - p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stdin=subprocess.PIPE) - stdout, stderr = p.communicate(input=stdin) - if p.returncode == 0: - if stderr: - if verbose: - print >>sys.stderr, '`%s` printed to stderr:' % ' '.join(args) - print >>sys.stderr, stderr.rstrip() - if strip: - stdout = stdout.rstrip('\r\n') - return stdout - if verbose: - print >>sys.stderr, '`%s` returned %s' % (' '.join(args), p.returncode) - if stderr: - print >>sys.stderr, stderr.rstrip() - sys.exit(2) - - -def die(message): - print >>sys.stderr, 'error:', message - sys.exit(2) - - -if __name__ == '__main__': - main() diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 000000000..28e0699af --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,16 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100755 new mode 100644 index baa227d0d..f37577c49 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,284 +1,31 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +cmake_minimum_required(VERSION 3.19) +project(rocketmq) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) -cmake_minimum_required(VERSION 2.8) +set(gRPC_DEBUG ON) -if (APPLE) - set(CMAKE_MACOSX_RPATH 1) -endif (APPLE) +# Assume gRPC is installed $HOME/grpc +list(APPEND CMAKE_PREFIX_PATH $ENV{HOME}/grpc) -# CMake complains if we don't have this. -if (COMMAND cmake_policy) - cmake_policy(SET CMP0003 NEW) -endif () +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +find_package(protobuf CONFIG REQUIRED) +find_package(gRPC CONFIG REQUIRED) +find_package(absl REQUIRED) +find_package(OpenSSL REQUIRED) -# We're escaping quotes in the Windows version number, because -# for some reason CMake won't do it at config version 2.4.7 -# It seems that this restores the newer behaviour where define -# args are not auto-escaped. -if (COMMAND cmake_policy) - cmake_policy(SET CMP0005 NEW) -endif () +add_subdirectory(proto) -# First, declare project (important for prerequisite checks). -project(rocketmq-client-cpp) -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release") -endif () -set(CMAKE_CONFIGURATION_TYPES "Release") -set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON) -set(CMAKE_VERBOSE_MAKEFILE 1) -option(BUILD_ROCKETMQ_STATIC "build rocketmq-client static library" ON) -option(BUILD_ROCKETMQ_SHARED "build rocketmq-client shared library" ON) +add_library(api INTERFACE) +target_include_directories(api INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/api) -option(OPENSSL_USE_STATIC_LIBS "only find openssl static libs" ON) # only find static libs -if (WIN32) - find_package(OpenSSL 1.1.1 REQUIRED COMPONENTS) - if (OPENSSL_FOUND) - include_directories(${OPENSSL_INCLUDE_DIR}) - message(STATUS "** OpenSSL Include dir: ${OPENSSL_INCLUDE_DIR}") - message(STATUS "** OpenSSL Libraries: ${OPENSSL_LIBRARIES}") - endif () -else () - #find_package(OpenSSL 1.1.1 REQUIRED COMPONENTS) - set(OPENSSL_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/bin/include) - set(OPENSSL_LIBRARIES_DIR ${PROJECT_SOURCE_DIR}/bin/lib) - set(OPENSSL_LIBRARIES ${OPENSSL_LIBRARIES_DIR}/libssl.a;${OPENSSL_LIBRARIES_DIR}/libcrypto.a) - include_directories(${OPENSSL_INCLUDE_DIR}) -endif () -message(STATUS "** OpenSSL_INCLUDE_DIR: ${OPENSSL_INCLUDE_DIR}") -message(STATUS "** OpenSSL_LIBRARIES: ${OPENSSL_LIBRARIES}") +add_subdirectory(third_party) -#Find dependency -option(Boost_USE_STATIC_LIBS "only find boost static libs" ON) # only find static libs -set(Boost_USE_MULTITHREADED ON) -set(Boost_USE_STATIC_RUNTIME ON) -if (WIN32) - find_package(Boost 1.56 REQUIRED COMPONENTS atomic thread system chrono date_time - log log_setup regex serialization filesystem locale iostreams zlib) - if (Boost_FOUND) - message(STATUS "** Boost Include dir: ${Boost_INCLUDE_DIR}") - message(STATUS "** Boost Libraries dir: ${Boost_LIBRARY_DIRS}") - message(STATUS "** Boost Libraries: ${Boost_LIBRARIES}") - include_directories(${Boost_INCLUDE_DIRS}) - endif () -else () - #find_package(Boost 1.56 REQUIRED COMPONENTS atomic thread system chrono date_time log log_setup regex serialization filesystem locale iostreams) - set(Boost_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/bin/include) - set(Boost_LIBRARY_DIRS ${PROJECT_SOURCE_DIR}/bin/lib) - set(Boost_LIBRARIES ${Boost_LIBRARY_DIRS}/libboost_atomic.a;${Boost_LIBRARY_DIRS}/libboost_thread.a;${Boost_LIBRARY_DIRS}/libboost_system.a; - ${Boost_LIBRARY_DIRS}/libboost_chrono.a;${Boost_LIBRARY_DIRS}/libboost_date_time.a;${Boost_LIBRARY_DIRS}/libboost_log.a; - ${Boost_LIBRARY_DIRS}/libboost_log_setup.a;${Boost_LIBRARY_DIRS}/libboost_regex.a;${Boost_LIBRARY_DIRS}/libboost_serialization.a; - ${Boost_LIBRARY_DIRS}/libboost_filesystem.a;${Boost_LIBRARY_DIRS}/libboost_locale.a;${Boost_LIBRARY_DIRS}/libboost_iostreams.a) - include_directories(${Boost_INCLUDE_DIR}) -endif () - -message(STATUS "** Boost_INCLUDE_DIR: ${Boost_INCLUDE_DIR}") -message(STATUS "** Boost_LIBRARIES: ${Boost_LIBRARIES}") - -option(Libevent_USE_STATIC_LIBS "only find libevent static libs" ON) # only find static libs -if (WIN32) - find_package(Libevent 2.0.22 REQUIRED COMPONENTS) - if (LIBEVENT_FOUND) - include_directories(${LIBEVENT_INCLUDE_DIRS}) - message(STATUS "** libevent Include dir: ${LIBEVENT_INCLUDE_DIRS}") - message(STATUS "** libevent Libraries: ${LIBEVENT_LIBRARIES}") - endif () -else () - #find_package(Libevent 2.0.22 REQUIRED COMPONENTS) - set(LIBEVENT_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/bin/include) - set(LIBEVENT_LIBRARIES_DIR ${PROJECT_SOURCE_DIR}/bin/lib) - set(LIBEVENT_LIBRARIES ${LIBEVENT_LIBRARIES_DIR}/libevent.a;${LIBEVENT_LIBRARIES_DIR}/libevent_core.a;${LIBEVENT_LIBRARIES_DIR}/libevent_extra.a; - ${LIBEVENT_LIBRARIES_DIR}/libevent_pthreads.a;${LIBEVENT_LIBRARIES_DIR}/libevent_openssl.a) - include_directories(${LIBEVENT_INCLUDE_DIRS}) -endif () - -message(STATUS "** LIBEVENT_INCLUDE_DIR: ${LIBEVENT_INCLUDE_DIRS}") -message(STATUS "** LIBEVENT_LIBRARIES: ${LIBEVENT_LIBRARIES}") - -option(JSONCPP_USE_STATIC_LIBS "only find jsoncpp static libs" ON) # only find static libs -if (WIN32) - find_package(Jsoncpp 0.10.6) - if (JSONCPP_FOUND) - include_directories(${JSONCPP_INCLUDE_DIRS}) - endif () -else () - set(JSONCPP_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/bin/include/jsoncpp) - set(JSONCPP_LIBRARIES_DIR ${PROJECT_SOURCE_DIR}/bin/lib) - set(JSONCPP_LIBRARIES ${JSONCPP_LIBRARIES_DIR}/libjsoncpp.a) - include_directories(${JSONCPP_INCLUDE_DIRS}) -endif () - -message(STATUS "** JSONCPP_INCLUDE_DIRS: ${JSONCPP_INCLUDE_DIRS}") -message(STATUS "** JSONCPP_LIBRARIES: ${JSONCPP_LIBRARIES}") - -# put binaries in a different dir to make them easier to find. -set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) -set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) - -# for unix, put debug files in a separate bin "debug" dir. -# release bin files should stay in the root of the bin dir. -# if (CMAKE_GENERATOR STREQUAL "Unix Makefiles") -# if (CMAKE_BUILD_TYPE STREQUAL Debug) -# set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/debug) -# set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/debug) -# endif() -# endif() - -IF (WIN32) - add_definitions(-DWIN32 -DROCKETMQCLIENT_EXPORTS) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") -ELSE () - set(C_FLAGS - #-g - -Wall - -Wno-deprecated - -fPIC - -fno-strict-aliasing - ) - set(CXX_FLAGS - #-g - -Wall - -Wno-deprecated - -fPIC - -fno-strict-aliasing - -std=c++11 - -Wno-unused-local-typedef - -Wno-expansion-to-defined - # -finline-limit=1000 - # -Wextra - # -pedantic - # -pedantic-errors - # -D_FILE_OFFSET_BITS=64 - # -DVALGRIND - # -DCHECK_PTHREAD_RETURN_VALUE - # -Werror - # -Wconversion - # -Wno-unused-parameter - # -Wunused-but-set-variable - # -Wold-style-cast - # -Woverloaded-virtual - # -Wpointer-arith - # -Wshadow - # -Wwrite-strings - # -Wdeprecated-declarations - # -march=native - # -MMD - # -std=c++0x - # -rdynamic - ) - - if (CMAKE_BUILD_BITS EQUAL 32) - list(APPEND CXX_FLAGS "-m32") - else () #not-condition - list(APPEND CXX_FLAGS "-m64") - endif () - - string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CXX_FLAGS}") - string(REPLACE ";" " " CMAKE_C_FLAGS "${C_FLAGS}") - - option(ENABLE_ASAN "Enable asan reporting" OFF) - if (ENABLE_ASAN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -static-libasan") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -static-libasan") - message(STATUS "** ENABLE_ASAN: ${ENABLE_ASAN} Enable asan reporting") - endif () - - option(ENABLE_LSAN "Enable lsan reporting" OFF) - if (ENABLE_LSAN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=leak -fno-omit-frame-pointer -static-liblsan") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=leak -fno-omit-frame-pointer -static-liblsan") - message(STATUS "** ENABLE_LSAN: ${ENABLE_LSAN} Enable lsan reporting") - endif () - - set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -DDEBUG") - set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") - set(CMAKE_C_FLAGS_DEBUG "-g -O0 -DDEBUG") - set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") - - - # Declare deplibs, so we can use list in linker later. There's probably - # a more elegant way of doing this; with SCons, when you check for the - # lib, it is automatically passed to the linker. - set(deplibs) - - # For some reason, the check_function_exists macro doesn't detect - # the inet_aton on some pure Unix platforms (e.g. sunos5). So we - # need to do a more detailed check and also include some extra deplibs. - list(APPEND deplibs dl) - list(APPEND deplibs pthread) - if (NOT APPLE) - list(APPEND deplibs rt) - endif () - list(APPEND deplibs z) - - option(CODE_COVERAGE "Enable coverage reporting" OFF) - if (CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - # Add required flags (GCC & LLVM/Clang) - # Code Coverage Configuration - add_library(coverage_config INTERFACE) - target_compile_options(coverage_config INTERFACE - -O0 # no optimization - -g # generate debug info - --coverage # sets all required flags - ) - if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - target_link_options(coverage_config INTERFACE --coverage) - else () - target_link_libraries(coverage_config INTERFACE --coverage) - endif () - list(APPEND deplibs coverage_config) - endif (CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - # add include dir for bsd (posix uses /usr/include/) - set(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH}:/usr/local/include") -ENDIF () - -# For config.h, set some static values; it may be a good idea to make -# these values dynamic for non-standard UNIX compilers. -set(ACCEPT_TYPE_ARG3 socklen_t) -set(HAVE_CXX_BOOL 1) -set(HAVE_CXX_CASTS 1) -set(HAVE_CXX_EXCEPTIONS 1) -set(HAVE_CXX_MUTABLE 1) -set(HAVE_CXX_STDLIB 1) -set(HAVE_PTHREAD_SIGNAL 1) -set(SELECT_TYPE_ARG1 int) -set(SELECT_TYPE_ARG234 "(fd_set *)") -set(SELECT_TYPE_ARG5 "(struct timeval *)") -set(STDC_HEADERS 1) -set(TIME_WITH_SYS_TIME 1) -set(HAVE_SOCKLEN_T 1) - -# For config.h, save the results based on a template (config.h.in). -# configure_file(res/config.h.in ${root_dir}/config.h) - -# add_definitions(-DSYSAPI_UNIX=1 -DHAVE_CONFIG_H) - -add_subdirectory(libs) -add_subdirectory(project) -add_subdirectory(example) - - -option(RUN_UNIT_TEST "RUN_UNIT_TEST" OFF) - -if (RUN_UNIT_TEST) - message(STATUS "** RUN_UNIT_TEST: ${RUN_UNIT_TEST} Do execution testing") - enable_testing() - add_subdirectory(test) -endif () +add_subdirectory(src/main/cpp) +option(BUILD_EXAMPLES "Build example programs or not" ON) +if (BUILD_EXAMPLES) + message("Would build examples") + find_package(ZLIB REQUIRED) + add_subdirectory(examples) +endif () \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 7ba226e8a..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,2 +0,0 @@ -## How to Contribute - Contributions are warmly welcome! Be it trivial cleanup, major new feature or other suggestion. Read this [how to contribute](http://rocketmq.apache.org/docs/how-to-contribute/) guide for more details. diff --git a/LICENSE b/LICENSE index 360cc7426..261eeb9e9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -15,7 +15,7 @@ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, - "control" means (properties) the power, direct or indirect, to cause the + "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. @@ -199,65 +199,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - - ------ - This product has a bundle disruptor--, which is available under BSD License. - The source code of disruptor-- can be found at https://github.com/fsaintjacques/disruptor--. - - Copyright (c) 2011, François Saint-Jacques - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the disruptor-- nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - ------- - This product has a bundle of big_endian.h and big_endian.cpp, which is available under BSD License. - The source code of them can be found at https://github.com/chromium/chromium/tree/master/base. - - Copyright 2015 The Chromium Authors. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 85b0032cd..000000000 --- a/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ -Apache RocketMQ -Copyright 2016-2024 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 7f27427ed..000000000 --- a/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,19 +0,0 @@ -## What is the purpose of the change - -XXXXX - -## Brief changelog - -XX - -## Verifying this change - -XXXX - -Follow this checklist to help us incorporate your contribution quickly and easily. Notice, `it would be helpful if you could finish the following 5 checklist(the last one is not necessary)before request the community to review your PR`. - -- [x] Make sure there is a [Github issue](https://github.com/apache/rocketmq/issues) filed for the change (usually before you start working on it). Trivial changes like typos do not require a Github issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue. -- [x] Format the pull request title like `[ISSUE #123] Fix UnknownException when host config not exist`. Each commit in the pull request should have a meaningful subject line and body. -- [x] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. -- [x] Write necessary unit-test(over 80% coverage) to verify your logic correction, more mock a little better when a cross-module dependency exists. -- [ ] If this contribution is large, please file an [Apache Individual Contributor License Agreement](http://www.apache.org/licenses/#clas). diff --git a/README.md b/README.md index 6bc2222fa..9b3463ace 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,158 @@ -# RocketMQ Client CPP -[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) -[![Build Status](https://app.travis-ci.com/apache/rocketmq-client-cpp.svg?branch=master)](https://app.travis-ci.com/apache/rocketmq-client-cpp) -[![CodeCov](https://codecov.io/gh/apache/rocketmq-client-cpp/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/rocketmq-client-cpp) -[![GitHub release](https://img.shields.io/github/downloads/apache/rocketmq-client-cpp/total)](https://github.com/apache/rocketmq-client-cpp/releases) -![Twitter Follow](https://img.shields.io/twitter/follow/ApacheRocketMQ?style=social) - -RocketMQ-Client-CPP is the C/C++ client of Apache RocketMQ, a distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability. - -## Features - -- produce messages, including normal and delayed messages, synchronously or asynchronously. -- consume messages, in cluster or broadcast model, concurrently or orderly -- c and c++ style API. -- cross-platform, all features are supported on Windows, Linux and Mac OS. -- automatically rebalanced, both in producing and consuming process. -- reliability, any downtime broker or name server has no impact on the client. - -## Build and Install - -### Linux and Mac OS - -**note**: make sure the following compile tools or libraries have been installed before running the build script **build.sh**. - -- compile tools: - - gcc-c++ 4.8.2: c++ compiler while need support C++11 - - cmake 2.8.0: build jsoncpp require it - - automake 1.11.1: build libevent require it - - autoconf 2.65: build libevent require it - - libtool 2.2.6: build libevent require it - -- libraries: - - bzip2-devel 1.0.6: boost depend it - - zlib-devel - -The **build.sh** script will automatically download and build the dependency libraries including libevent, json and boost. It will save libraries under rocketmq-client-cpp folder, and then build both static and shared libraries for rocketmq-client. If the dependent libraries are built failed, you could try to build it manually with sources [libevent 2.0.22](https://github.com/libevent/libevent/archive/release-2.0.22-stable.zip "lib event 2.0.22"), [jsoncpp 0.10.7](https://github.com/open-source-parsers/jsoncpp/archive/0.10.7.zip "jsoncpp 0.10.7"), [boost 1.58.0](http://sourceforge.net/projects/boost/files/boost/1.58.0/boost_1_58_0.tar.gz "boost 1.58.0") - -If your host is not available to internet to download the three library source files, you can copy the three library source files (release-2.0.22-stable.zip 0.10.7.zip and boost_1_58_0.tar.gz) to rocketmq-client-cpp root dir, then the build.sh will automatically use the three library source files to build rocketmq-client-cpp: - - sh build.sh - -Finally, both librocketmq.a and librocketmq.so are saved in rocketmq-client-cpp/bin. when using them to build application or library, besides rocketmq you should also link with following libraries -lpthread -lz -ldl -lrt. Here is an example: - - g++ -o consumer_example consumer_example.cpp -lrocketmq -lpthread -lz -ldl -lrt - -### Windows -**note**: make sure the following compile tools or libraries have been installed before running the build script **win32_build.bat**: - -- compile tools: - - vs2015: libevent,jsoncpp,zlib,boost rocket-client require it - - git: download source code - -The build script will automatically download dependent libraries including libevent json and boost to build shared library: - - win32_build.bat - - -If your host is not available to internet to download the four library source files by build script, you can copy the four library source files - -[zlib-1.2.3-src](https://codeload.github.com/jsj020122/zlib-1.2.3-src/zip/master "zlib-1.2.3-src") Extract to $(rocketmq-client-cpp root dir)/thirdparty/zlib-1.2.3-src - -[libevent-release-2.0.22](https://codeload.github.com/jsj020122/libevent-release-2.0.22/zip/master "libevent-release-2.0.22") Extract to $(rocketmq-client-cpp root dir)/thirdparty/libevent-release-2.0.22 - -[boost_1_58_0](https://codeload.github.com/jsj020122/boost_1_58_0/zip/master "boost_1_58_0") Extract to $(rocketmq-client-cpp root dir)/thirdparty/boost_1_58_0 - -[jsoncpp-0.10.6](https://codeload.github.com/jsj020122/jsoncpp-0.10.6/zip/master "jsoncpp-0.10.6") Extract to $(rocketmq-client-cpp root dir)/thirdparty/jsoncpp-0.10.6 - -And then run following command to build x86 rocketmq-client: - - win32_build.bat build - -to build x64 rocketmq-client: - - win32_build.bat build64 - - +[![CI](https://github.com/apache/rocketmq-client-cpp/actions/workflows/main.yml/badge.svg)](https://github.com/lizhanhui/rocketmq-client-cpp/actions/workflows/main.yml) +### Introduction +Apache RocketMQ supports two styles of APIs to acknowledge messages once they are successfully processed. + +1. Selective Acknowledgement + For each logical message queue(aka, topic partition), SDK manages offsets locally and periodically syncs committed offset to brokers in charge. +2. Per Message Acknowledgement + On consumption of each message, SDK acknowledge it to the broker instantly. Broker is responsible of managing consuming progress. + +Either of them is widely adopted by products. Per message acknowledgement simplifies SDK implementation while selective approach is more performant considering that fewer RPCs are required. + +### Transport Layer + +This SDK is built on top of [gRPC](https://grpc.io/). [Protocol Buffers](https://developers.google.com/protocol-buffers) is used to serialize application messages. + +### Type Hierarchy +Classes of this project are designed to be interface oriented. +![Basic class hierarchy](docs/assets/BasicMode.png) +This paradigm makes dependency injection possible. DI is especially helpful when writing unit tests. + +### Core Concepts +![Class Diagram](docs/assets/class_diagram.png) + +### Code Style +Generally, we follow [Google C++ Code Style](https://google.github.io/styleguide/cppguide.html). A few exceptions are made to maintain API compatibility. +1. C++ exception is only allowed in the outer wrapper classes, for example, DefaultMQProducer, DefaultMQConsumer. +2. C++ --std=c++11 is preferred. We intend to maintain the same compiler compatibility matrix to [those of gRPC](https://github.com/grpc/grpc/blob/master/BUILDING.md) +3. Smart pointers are preferred where it makes sense. Use raw pointers only when it is really necessary. + +### Dependency Management +Considering SDK built on top of gRPC, ensure it is really necessary before introducing a third-party library. Check [gRPC deps](https://github.com/grpc/grpc/blob/master/bazel/grpc_deps.bzl) and [gRPC extra deps](https://github.com/grpc/grpc/blob/master/bazel/grpc_extra_deps.bzl) first! + +When introducing a third-party dependency or raising version of a dependency, make sure it is back-off friendly. For example, +``` +if "com_google_googletest" not in native.existing_rules(): + http_archive( + name = "com_google_googletest", + sha256 = "b4870bf121ff7795ba20d20bcdd8627b8e088f2d1dab299a031c1034eddc93d5", + strip_prefix = "googletest-release-1.11.0", + urls = [ + "https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz", + ], + ) +``` + +### How To Build + +#### Build with Bazel + +[Google Bazel](https://bazel.build/) is the primary build tool we supported, Please follow [bazel installation guide](https://docs.bazel.build/versions/main/install.html). + +1. Build + From the repository root, + ``` + bazel build //... + ``` +2. Run Unit Tests + From the repository root, + ``` + bazel test //src/test/cpp/ut/... + ``` + +#### Build with CMake + + 1. Make sure you have installed a modern CMake 3.13+ and C++ compilation toolchain that at least supports C++11; + + 2. Following [gRPC installation instructions](https://grpc.io/docs/languages/cpp/quickstart/) to install grpc. + gRPC [v1.48.0](https://shutian.oss-cn-hangzhou.aliyuncs.com/cdn/grpc/grpc_v1.48.0.tar.gz) with its third-party dependencies may be downloaded. + + Note: + * Remember to `export MY_INSTALL_DIR=$HOME/grpc` as our primary CMakeLists.txt hints + + ```cmake + list(APPEND CMAKE_PREFIX_PATH $ENV{HOME}/grpc) + ``` + If your grpc is installed somewhere else yet non-standard, please adjust accordingly. + + * When configure grpc, use your pre-installed system package if possible; + ```shell + cmake -DCMAKE_INSTALL_PREFIX=$HOME/grpc -DgRPC_SSL_PROVIDER=package -DgRPC_ZLIB_PROVIDER=package + ``` + A few more options are involved. Check CMakeLists.txt of grpc + ```cmake + # Providers for third-party dependencies (gRPC_*_PROVIDER properties): + # "module": build the dependency using sources from git submodule (under third_party) + # "package": use cmake's find_package functionality to locate a pre-installed dependency + + set(gRPC_ZLIB_PROVIDER "module" CACHE STRING "Provider of zlib library") + set_property(CACHE gRPC_ZLIB_PROVIDER PROPERTY STRINGS "module" "package") + + set(gRPC_CARES_PROVIDER "module" CACHE STRING "Provider of c-ares library") + set_property(CACHE gRPC_CARES_PROVIDER PROPERTY STRINGS "module" "package") + + set(gRPC_RE2_PROVIDER "module" CACHE STRING "Provider of re2 library") + set_property(CACHE gRPC_RE2_PROVIDER PROPERTY STRINGS "module" "package") + + set(gRPC_SSL_PROVIDER "module" CACHE STRING "Provider of ssl library") + set_property(CACHE gRPC_SSL_PROVIDER PROPERTY STRINGS "module" "package") + + set(gRPC_PROTOBUF_PROVIDER "module" CACHE STRING "Provider of protobuf library") + set_property(CACHE gRPC_PROTOBUF_PROVIDER PROPERTY STRINGS "module" "package") + + set(gRPC_PROTOBUF_PACKAGE_TYPE "" CACHE STRING "Algorithm for searching protobuf package") + set_property(CACHE gRPC_PROTOBUF_PACKAGE_TYPE PROPERTY STRINGS "CONFIG" "MODULE") + + if(gRPC_BUILD_TESTS) + set(gRPC_BENCHMARK_PROVIDER "module" CACHE STRING "Provider of benchmark library") + set_property(CACHE gRPC_BENCHMARK_PROVIDER PROPERTY STRINGS "module" "package") + else() + set(gRPC_BENCHMARK_PROVIDER "none") + endif() + + set(gRPC_ABSL_PROVIDER "module" CACHE STRING "Provider of absl library") + set_property(CACHE gRPC_ABSL_PROVIDER PROPERTY STRINGS "module" "package") + ``` + + 3. OpenSSL development package is also required. + + 4. Run the following commands to build from ${YOUR_GIT_REPOSITORY}/cpp directory + ```shell + mkdir build && cd build + cmake -DOPENSSL_ROOT_DIR=/usr/local/Cellar/openssl@1.1/1.1.1q .. + make -j $(nproc) + ``` + 5. Static archive and dynamic linked libraries are found in the build directory. + +### IDE +[Visual Studio Code](https://code.visualstudio.com/) + [Clangd](https://clangd.llvm.org/) is the recommended development toolset. +1. VSCode + Clangd + + [Clangd](https://clangd.llvm.org/) is a really nice code completion tool. Clangd requires compile_commands.json to work properly. + To generate the file, we need clone another repository along with the current one. + + ``` + git clone git@github.com:grailbio/bazel-compilation-database.git + ``` + From current repository root, + + ``` + ../bazel-compilation-database/generate.sh + ``` + Once the script completes, you should have compile_commands.json file in the repository root directory. + + LLVM project has an extension for [clangd](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd). Please install it from the extension market. + + The following configuration entries should be appended to your VSC settings file. + ```text + "C_Cpp.intelliSenseEngine": "Disabled", + "C_Cpp.autocomplete": "Disabled", // So you don't get autocomplete from both extensions. + "C_Cpp.errorSquiggles": "Disabled", // So you don't get error squiggles from both extensions (clangd's seem to be more reliable anyway). + "clangd.path": "/Users/lizhanhui/usr/clangd_12.0.0/bin/clangd", + "clangd.arguments": ["-log=verbose", "-pretty", "--background-index"], + "clangd.onConfigChanged": "restart", + ``` + +2. CLion + Bazel Plugin + + Bazel also has a plugin for CLion. diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 000000000..86484441d --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,17 @@ +workspace(name = "org_apache_rocketmq") +load("//bazel:rocketmq_deps.bzl", "rocketmq_deps") +rocketmq_deps() + +load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps", "grpc_test_only_deps") +grpc_deps() +grpc_test_only_deps() +load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps") +grpc_extra_deps() + +load("@rules_proto_grpc//:repositories.bzl", "rules_proto_grpc_toolchains", "rules_proto_grpc_repos") +rules_proto_grpc_toolchains() +rules_proto_grpc_repos() + +load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") +rules_proto_dependencies() +rules_proto_toolchains() \ No newline at end of file diff --git a/Win32/rocketmq-client-cpp.sln b/Win32/rocketmq-client-cpp.sln deleted file mode 100644 index a0858b171..000000000 --- a/Win32/rocketmq-client-cpp.sln +++ /dev/null @@ -1,188 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rocketmq-client-cpp", "rocketmq-client-cpp.vcxproj", "{DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AsyncProducer", "AsyncProducer.vcxproj", "{AC059D17-1D30-4D83-AE00-5FFFE3ED50A9}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AsyncPushConsumer", "AsyncPushConsumer.vcxproj", "{19D42952-0418-4162-92EA-6B601B11847C}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OrderlyPushConsumer", "OrderlyPushConsumer.vcxproj", "{F1657573-16CB-440E-9CAD-E6333141377D}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "COrderlyAsyncProducer", "COrderlyAsyncProducer.vcxproj", "{F1657573-16CB-440E-9CAD-E6333141387D}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OrderProducer", "OrderProducer.vcxproj", "{7BB3595B-CF36-4F12-8F12-897342C643CD}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Producer", "Producer.vcxproj", "{52063BCB-8547-463D-AADA-A086B5339167}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PullConsumer", "PullConsumer.vcxproj", "{B167E18F-71BE-4873-A806-6B340C5838EF}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PushConsumeMessage", "PushConsumeMessage.vcxproj", "{00DA7C52-1EB6-46B5-9D2A-177E6A81F598}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PushConsumer", "PushConsumer.vcxproj", "{07CA501C-90B7-45A5-BC89-886ACB3B30CC}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SendDelayMsg", "SendDelayMsg.vcxproj", "{0B156CC7-A991-4554-A29D-8CD0ED982773}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SyncProducer", "SyncProducer.vcxproj", "{C281AB41-79DE-49DD-AEEF-F300C0580D18}" - ProjectSection(ProjectDependencies) = postProject - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "example", "example", "{9B81E85B-7438-4B55-8BE0-798B4A7C8775}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{097B656C-BF7C-445B-9C2B-57292DF0BD92}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13}.Debug|x64.ActiveCfg = Debug|x64 - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13}.Debug|x64.Build.0 = Debug|x64 - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13}.Debug|x86.ActiveCfg = Debug|Win32 - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13}.Debug|x86.Build.0 = Debug|Win32 - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13}.Release|x64.ActiveCfg = Release|x64 - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13}.Release|x64.Build.0 = Release|x64 - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13}.Release|x86.ActiveCfg = Release|Win32 - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13}.Release|x86.Build.0 = Release|Win32 - {AC059D17-1D30-4D83-AE00-5FFFE3ED50A9}.Debug|x64.ActiveCfg = Debug|x64 - {AC059D17-1D30-4D83-AE00-5FFFE3ED50A9}.Debug|x64.Build.0 = Debug|x64 - {AC059D17-1D30-4D83-AE00-5FFFE3ED50A9}.Debug|x86.ActiveCfg = Debug|Win32 - {AC059D17-1D30-4D83-AE00-5FFFE3ED50A9}.Debug|x86.Build.0 = Debug|Win32 - {AC059D17-1D30-4D83-AE00-5FFFE3ED50A9}.Release|x64.ActiveCfg = Release|x64 - {AC059D17-1D30-4D83-AE00-5FFFE3ED50A9}.Release|x64.Build.0 = Release|x64 - {AC059D17-1D30-4D83-AE00-5FFFE3ED50A9}.Release|x86.ActiveCfg = Release|Win32 - {AC059D17-1D30-4D83-AE00-5FFFE3ED50A9}.Release|x86.Build.0 = Release|Win32 - {19D42952-0418-4162-92EA-6B601B11847C}.Debug|x64.ActiveCfg = Debug|x64 - {19D42952-0418-4162-92EA-6B601B11847C}.Debug|x64.Build.0 = Debug|x64 - {19D42952-0418-4162-92EA-6B601B11847C}.Debug|x86.ActiveCfg = Debug|Win32 - {19D42952-0418-4162-92EA-6B601B11847C}.Debug|x86.Build.0 = Debug|Win32 - {19D42952-0418-4162-92EA-6B601B11847C}.Release|x64.ActiveCfg = Release|x64 - {19D42952-0418-4162-92EA-6B601B11847C}.Release|x64.Build.0 = Release|x64 - {19D42952-0418-4162-92EA-6B601B11847C}.Release|x86.ActiveCfg = Release|Win32 - {19D42952-0418-4162-92EA-6B601B11847C}.Release|x86.Build.0 = Release|Win32 - {F1657573-16CB-440E-9CAD-E6333141377D}.Debug|x64.ActiveCfg = Debug|x64 - {F1657573-16CB-440E-9CAD-E6333141377D}.Debug|x64.Build.0 = Debug|x64 - {F1657573-16CB-440E-9CAD-E6333141377D}.Debug|x86.ActiveCfg = Debug|Win32 - {F1657573-16CB-440E-9CAD-E6333141377D}.Debug|x86.Build.0 = Debug|Win32 - {F1657573-16CB-440E-9CAD-E6333141377D}.Release|x64.ActiveCfg = Release|x64 - {F1657573-16CB-440E-9CAD-E6333141377D}.Release|x64.Build.0 = Release|x64 - {F1657573-16CB-440E-9CAD-E6333141377D}.Release|x86.ActiveCfg = Release|Win32 - {F1657573-16CB-440E-9CAD-E6333141377D}.Release|x86.Build.0 = Release|Win32 - {F1657573-16CB-440E-9CAD-E6333141387D}.Debug|x64.ActiveCfg = Debug|x64 - {F1657573-16CB-440E-9CAD-E6333141387D}.Debug|x64.Build.0 = Debug|x64 - {F1657573-16CB-440E-9CAD-E6333141387D}.Debug|x86.ActiveCfg = Debug|Win32 - {F1657573-16CB-440E-9CAD-E6333141387D}.Debug|x86.Build.0 = Debug|Win32 - {F1657573-16CB-440E-9CAD-E6333141387D}.Release|x64.ActiveCfg = Release|x64 - {F1657573-16CB-440E-9CAD-E6333141387D}.Release|x64.Build.0 = Release|x64 - {F1657573-16CB-440E-9CAD-E6333141387D}.Release|x86.ActiveCfg = Release|Win32 - {F1657573-16CB-440E-9CAD-E6333141387D}.Release|x86.Build.0 = Release|Win32 - {7BB3595B-CF36-4F12-8F12-897342C643CD}.Debug|x64.ActiveCfg = Debug|x64 - {7BB3595B-CF36-4F12-8F12-897342C643CD}.Debug|x64.Build.0 = Debug|x64 - {7BB3595B-CF36-4F12-8F12-897342C643CD}.Debug|x86.ActiveCfg = Debug|Win32 - {7BB3595B-CF36-4F12-8F12-897342C643CD}.Debug|x86.Build.0 = Debug|Win32 - {7BB3595B-CF36-4F12-8F12-897342C643CD}.Release|x64.ActiveCfg = Release|x64 - {7BB3595B-CF36-4F12-8F12-897342C643CD}.Release|x64.Build.0 = Release|x64 - {7BB3595B-CF36-4F12-8F12-897342C643CD}.Release|x86.ActiveCfg = Release|Win32 - {7BB3595B-CF36-4F12-8F12-897342C643CD}.Release|x86.Build.0 = Release|Win32 - {52063BCB-8547-463D-AADA-A086B5339167}.Debug|x64.ActiveCfg = Debug|x64 - {52063BCB-8547-463D-AADA-A086B5339167}.Debug|x64.Build.0 = Debug|x64 - {52063BCB-8547-463D-AADA-A086B5339167}.Debug|x86.ActiveCfg = Debug|Win32 - {52063BCB-8547-463D-AADA-A086B5339167}.Debug|x86.Build.0 = Debug|Win32 - {52063BCB-8547-463D-AADA-A086B5339167}.Release|x64.ActiveCfg = Release|x64 - {52063BCB-8547-463D-AADA-A086B5339167}.Release|x64.Build.0 = Release|x64 - {52063BCB-8547-463D-AADA-A086B5339167}.Release|x86.ActiveCfg = Release|Win32 - {52063BCB-8547-463D-AADA-A086B5339167}.Release|x86.Build.0 = Release|Win32 - {B167E18F-71BE-4873-A806-6B340C5838EF}.Debug|x64.ActiveCfg = Debug|x64 - {B167E18F-71BE-4873-A806-6B340C5838EF}.Debug|x64.Build.0 = Debug|x64 - {B167E18F-71BE-4873-A806-6B340C5838EF}.Debug|x86.ActiveCfg = Debug|Win32 - {B167E18F-71BE-4873-A806-6B340C5838EF}.Debug|x86.Build.0 = Debug|Win32 - {B167E18F-71BE-4873-A806-6B340C5838EF}.Release|x64.ActiveCfg = Release|x64 - {B167E18F-71BE-4873-A806-6B340C5838EF}.Release|x64.Build.0 = Release|x64 - {B167E18F-71BE-4873-A806-6B340C5838EF}.Release|x86.ActiveCfg = Release|Win32 - {B167E18F-71BE-4873-A806-6B340C5838EF}.Release|x86.Build.0 = Release|Win32 - {00DA7C52-1EB6-46B5-9D2A-177E6A81F598}.Debug|x64.ActiveCfg = Debug|x64 - {00DA7C52-1EB6-46B5-9D2A-177E6A81F598}.Debug|x64.Build.0 = Debug|x64 - {00DA7C52-1EB6-46B5-9D2A-177E6A81F598}.Debug|x86.ActiveCfg = Debug|Win32 - {00DA7C52-1EB6-46B5-9D2A-177E6A81F598}.Debug|x86.Build.0 = Debug|Win32 - {00DA7C52-1EB6-46B5-9D2A-177E6A81F598}.Release|x64.ActiveCfg = Release|x64 - {00DA7C52-1EB6-46B5-9D2A-177E6A81F598}.Release|x64.Build.0 = Release|x64 - {00DA7C52-1EB6-46B5-9D2A-177E6A81F598}.Release|x86.ActiveCfg = Release|Win32 - {00DA7C52-1EB6-46B5-9D2A-177E6A81F598}.Release|x86.Build.0 = Release|Win32 - {07CA501C-90B7-45A5-BC89-886ACB3B30CC}.Debug|x64.ActiveCfg = Debug|x64 - {07CA501C-90B7-45A5-BC89-886ACB3B30CC}.Debug|x64.Build.0 = Debug|x64 - {07CA501C-90B7-45A5-BC89-886ACB3B30CC}.Debug|x86.ActiveCfg = Debug|Win32 - {07CA501C-90B7-45A5-BC89-886ACB3B30CC}.Debug|x86.Build.0 = Debug|Win32 - {07CA501C-90B7-45A5-BC89-886ACB3B30CC}.Release|x64.ActiveCfg = Release|x64 - {07CA501C-90B7-45A5-BC89-886ACB3B30CC}.Release|x64.Build.0 = Release|x64 - {07CA501C-90B7-45A5-BC89-886ACB3B30CC}.Release|x86.ActiveCfg = Release|Win32 - {07CA501C-90B7-45A5-BC89-886ACB3B30CC}.Release|x86.Build.0 = Release|Win32 - {0B156CC7-A991-4554-A29D-8CD0ED982773}.Debug|x64.ActiveCfg = Debug|x64 - {0B156CC7-A991-4554-A29D-8CD0ED982773}.Debug|x64.Build.0 = Debug|x64 - {0B156CC7-A991-4554-A29D-8CD0ED982773}.Debug|x86.ActiveCfg = Debug|Win32 - {0B156CC7-A991-4554-A29D-8CD0ED982773}.Debug|x86.Build.0 = Debug|Win32 - {0B156CC7-A991-4554-A29D-8CD0ED982773}.Release|x64.ActiveCfg = Release|x64 - {0B156CC7-A991-4554-A29D-8CD0ED982773}.Release|x64.Build.0 = Release|x64 - {0B156CC7-A991-4554-A29D-8CD0ED982773}.Release|x86.ActiveCfg = Release|Win32 - {0B156CC7-A991-4554-A29D-8CD0ED982773}.Release|x86.Build.0 = Release|Win32 - {C281AB41-79DE-49DD-AEEF-F300C0580D18}.Debug|x64.ActiveCfg = Debug|x64 - {C281AB41-79DE-49DD-AEEF-F300C0580D18}.Debug|x64.Build.0 = Debug|x64 - {C281AB41-79DE-49DD-AEEF-F300C0580D18}.Debug|x86.ActiveCfg = Debug|Win32 - {C281AB41-79DE-49DD-AEEF-F300C0580D18}.Debug|x86.Build.0 = Debug|Win32 - {C281AB41-79DE-49DD-AEEF-F300C0580D18}.Release|x64.ActiveCfg = Release|x64 - {C281AB41-79DE-49DD-AEEF-F300C0580D18}.Release|x64.Build.0 = Release|x64 - {C281AB41-79DE-49DD-AEEF-F300C0580D18}.Release|x86.ActiveCfg = Release|Win32 - {C281AB41-79DE-49DD-AEEF-F300C0580D18}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} = {097B656C-BF7C-445B-9C2B-57292DF0BD92} - {AC059D17-1D30-4D83-AE00-5FFFE3ED50A9} = {9B81E85B-7438-4B55-8BE0-798B4A7C8775} - {19D42952-0418-4162-92EA-6B601B11847C} = {9B81E85B-7438-4B55-8BE0-798B4A7C8775} - {F1657573-16CB-440E-9CAD-E6333141377D} = {9B81E85B-7438-4B55-8BE0-798B4A7C8775} - {7BB3595B-CF36-4F12-8F12-897342C643CD} = {9B81E85B-7438-4B55-8BE0-798B4A7C8775} - {52063BCB-8547-463D-AADA-A086B5339167} = {9B81E85B-7438-4B55-8BE0-798B4A7C8775} - {B167E18F-71BE-4873-A806-6B340C5838EF} = {9B81E85B-7438-4B55-8BE0-798B4A7C8775} - {00DA7C52-1EB6-46B5-9D2A-177E6A81F598} = {9B81E85B-7438-4B55-8BE0-798B4A7C8775} - {07CA501C-90B7-45A5-BC89-886ACB3B30CC} = {9B81E85B-7438-4B55-8BE0-798B4A7C8775} - {0B156CC7-A991-4554-A29D-8CD0ED982773} = {9B81E85B-7438-4B55-8BE0-798B4A7C8775} - {C281AB41-79DE-49DD-AEEF-F300C0580D18} = {9B81E85B-7438-4B55-8BE0-798B4A7C8775} - EndGlobalSection -EndGlobal diff --git a/Win32/rocketmq-client-cpp.vcxproj b/Win32/rocketmq-client-cpp.vcxproj deleted file mode 100644 index 720aab464..000000000 --- a/Win32/rocketmq-client-cpp.vcxproj +++ /dev/null @@ -1,308 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - {DFF2BED1-4A9E-4B0B-B1EC-F01D70457E13} - Win32Proj - 8.1 - - - - DynamicLibrary - true - v140 - - - DynamicLibrary - false - v140_xp - - - DynamicLibrary - true - v140 - - - DynamicLibrary - false - v140 - - - - - - - - - - - - - - - - - - - - - true - $(WindowsSdk_71A_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\thirdparty\boost_1_58_0;$(SolutionDir)..\include;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\include;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\WIN32-Code\;$(SolutionDir)..\src\common;$(SolutionDir)..\src\consumer;$(SolutionDir)..\src\log;$(SolutionDir)..\src\message;$(SolutionDir)..\src\producer;$(SolutionDir)..\src\protocol;$(SolutionDir)..\src\thread;$(SolutionDir)..\src\transport;$(SolutionDir)..\thirdparty\jsoncpp-0.10.6\;$(SolutionDir)..\src;$(SolutionDir)..\libs\signature\include;$(VC_IncludePath) - $(WindowsSdk_71A_LibraryPath_x86);$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(NETFXKitsDir)Lib\um\x86;$(SolutionDir)thirdparty..\boost_1_58_0\stage\lib - - - true - $(WindowsSdk_71A_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\thirdparty\boost_1_58_0;$(SolutionDir)..\include;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\include;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\WIN32-Code\;$(SolutionDir)..\src\common;$(SolutionDir)..\src\consumer;$(SolutionDir)..\src\log;$(SolutionDir)..\src\message;$(SolutionDir)..\src\producer;$(SolutionDir)..\src\protocol;$(SolutionDir)..\src\thread;$(SolutionDir)..\src\transport;$(SolutionDir)..\thirdparty\jsoncpp-0.10.6\;$(SolutionDir)..\src;$(SolutionDir)..\libs\signature\include;$(VC_IncludePath) - $(WindowsSdk_71A_LibraryPath_x86);$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(NETFXKitsDir)Lib\um\x86;$(SolutionDir)..\thirdparty\boost_1_58_0\stage\lib - $(Configuration)\$(ProjectName) - - - true - $(WindowsSdk_71A_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\thirdparty\boost_1_58_0;$(SolutionDir)..\include;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\include;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\WIN32-Code\;$(SolutionDir)..\src\common;$(SolutionDir)..\src\consumer;$(SolutionDir)..\src\log;$(SolutionDir)..\src\message;$(SolutionDir)..\src\producer;$(SolutionDir)..\src\protocol;$(SolutionDir)..\src\thread;$(SolutionDir)..\src\transport;$(SolutionDir)..\thirdparty\jsoncpp-0.10.6\;$(SolutionDir)..\src;$(SolutionDir)..\libs\signature\include;$(VC_IncludePath) - $(WindowsSdk_71A_LibraryPath_x64);$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64;$(SolutionDir)..\thirdparty\boost_1_58_0\stage\lib - $(Configuration)\$(ProjectName) - - - true - $(WindowsSdk_71A_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\thirdparty\boost_1_58_0;$(SolutionDir)..\include;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\include;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\WIN32-Code\;$(SolutionDir)..\src\common;$(SolutionDir)..\src\consumer;$(SolutionDir)..\src\log;$(SolutionDir)..\src\message;$(SolutionDir)..\src\producer;$(SolutionDir)..\src\protocol;$(SolutionDir)..\src\thread;$(SolutionDir)..\src\transport;$(SolutionDir)..\thirdparty\jsoncpp-0.10.6\;$(SolutionDir)..\src;$(SolutionDir)..\libs\signature\include;$(VC_IncludePath) - $(WindowsSdk_71A_LibraryPath_x64);$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64;$(SolutionDir)..\thirdparty\boost_1_58_0\stage\lib - $(Configuration)\$(ProjectName) - - - - WIN32;_DEBUG;ROCKETMQCLIENT_EXPORTS;_WIN32_WINNT=0x501;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - Level3 - ProgramDatabase - Disabled - /MDd %(AdditionalOptions) - - - MachineX86 - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\$(Configuration)\libevent.lib;$(SolutionDir)..\thirdparty\jsoncpp-0.10.6\$(Configuration)\jsoncpp_lib_static.lib;%(AdditionalDependencies) - /NODEFAULTLIB:library %(AdditionalOptions) - - - - - WIN32;NDEBUG;ROCKETMQCLIENT_EXPORTS;_WIN32_WINNT=0x501;%(PreprocessorDefinitions) - MultiThreadedDLL - Level3 - ProgramDatabase - - - MachineX86 - true - Console - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\$(Configuration)\libevent.lib;$(SolutionDir)..\thirdparty\jsoncpp-0.10.6\$(Configuration)\jsoncpp_lib_static.lib;%(AdditionalDependencies) - - - - - WIN32;NDEBUG;ROCKETMQCLIENT_EXPORTS;_WIN32_WINNT=0x501;%(PreprocessorDefinitions) - MultiThreadedDLL - Level3 - ProgramDatabase - /FS %(AdditionalOptions) - - - MachineX64 - true - Console - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\$(Platform)\$(Configuration)\libevent.lib;$(SolutionDir)..\thirdparty\jsoncpp-0.10.6\$(Platform)\$(Configuration)\jsoncpp_lib_static.lib;%(AdditionalDependencies) - - - - - - - WIN32;_DEBUG;ROCKETMQCLIENT_EXPORTS;_WIN32_WINNT=0x501;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - Level3 - ProgramDatabase - Disabled - /FS /MDd %(AdditionalOptions) - - - MachineX64 - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;$(SolutionDir)..\thirdparty\libevent-release-2.0.22\$(Configuration)\libevent.lib;$(SolutionDir)..\thirdparty\jsoncpp-0.10.6\$(Configuration)\jsoncpp_lib_static.lib;%(AdditionalDependencies) - /NODEFAULTLIB:library %(AdditionalOptions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Win32/rocketmq-client-cpp.vcxproj.filters b/Win32/rocketmq-client-cpp.vcxproj.filters deleted file mode 100644 index b92019c3f..000000000 --- a/Win32/rocketmq-client-cpp.vcxproj.filters +++ /dev/null @@ -1,450 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav - - - {41638677-648d-470b-a0dd-27ffbb643445} - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/api/BUILD.bazel b/api/BUILD.bazel new file mode 100644 index 000000000..b7eb594a7 --- /dev/null +++ b/api/BUILD.bazel @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "rocketmq_interface", + hdrs = glob(["rocketmq/*.h"]), + strip_include_prefix = "//api", + visibility = ["//visibility:public"], + deps = [ + "@com_google_absl//absl/strings", + ], +) \ No newline at end of file diff --git a/api/ons/Action.h b/api/ons/Action.h new file mode 100644 index 000000000..0b5efa6bd --- /dev/null +++ b/api/ons/Action.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +// consuming result +enum class Action : std::int8_t +{ + // consume success, application could continue to consume next message + CommitMessage = 0, + + // consume fail, server will deliver this message later, application could + // continue to consume next message + ReconsumeLater = 1, +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ConsumeContext.h b/api/ons/ConsumeContext.h new file mode 100644 index 000000000..caf7cc9b1 --- /dev/null +++ b/api/ons/ConsumeContext.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API ConsumeContext { +public: + virtual ~ConsumeContext() = default; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ConsumeOrderContext.h b/api/ons/ConsumeOrderContext.h new file mode 100644 index 000000000..707da2117 --- /dev/null +++ b/api/ons/ConsumeOrderContext.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API ConsumeOrderContext { +public: + virtual ~ConsumeOrderContext() = default; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/LocalTransactionChecker.h b/api/ons/LocalTransactionChecker.h new file mode 100644 index 000000000..3c24a3689 --- /dev/null +++ b/api/ons/LocalTransactionChecker.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Message.h" +#include "TransactionStatus.h" + +ONS_NAMESPACE_BEGIN +class LocalTransactionChecker { +public: + virtual ~LocalTransactionChecker() = default; + + virtual TransactionStatus check(const Message& msg) noexcept = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/LocalTransactionExecuter.h b/api/ons/LocalTransactionExecuter.h new file mode 100644 index 000000000..642aacf7b --- /dev/null +++ b/api/ons/LocalTransactionExecuter.h @@ -0,0 +1,2 @@ +#pragma once +#include "LocalTransactionExecutor.h" \ No newline at end of file diff --git a/api/ons/LocalTransactionExecutor.h b/api/ons/LocalTransactionExecutor.h new file mode 100644 index 000000000..8d3af4eac --- /dev/null +++ b/api/ons/LocalTransactionExecutor.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Message.h" +#include "TransactionStatus.h" + +ONS_NAMESPACE_BEGIN +class LocalTransactionExecutor { +public: + virtual ~LocalTransactionExecutor() = default; + + virtual TransactionStatus execute(const Message& msg) noexcept = 0; +}; + +// Keep API compatible +using LocalTransactionExecuter = LocalTransactionExecutor; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/Message.h b/api/ons/Message.h new file mode 100755 index 000000000..fbbc760fa --- /dev/null +++ b/api/ons/Message.h @@ -0,0 +1,114 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN +class SystemPropKey { +public: + SystemPropKey() = default; + + ~SystemPropKey() = default; + + static const char* TAG; + static const char* KEY_SEPARATOR; + static const char* MSGID; + static const char* RECONSUMETIMES; + static const char* STARTDELIVERTIME; +}; + +class ONSUtil; + +class ONSCLIENT_API Message { +public: + Message() = default; + + Message(const std::string& topic, const std::string& body); + + Message(const std::string& topic, const std::string& tag, const std::string& body); + + Message(const std::string& topic, const std::string& tag, const std::string& key, const std::string& body); + + virtual ~Message() = default; + + // Developers may attach application specific key-value pairs to message, + // which will be accessible at consuming stage. + void putUserProperty(const std::string& key, const std::string& value); + + // Used to acquire the key-value pair as was put before the message is sent. + std::string getUserProperty(const std::string& key) const; + + // To put key-value pairs in batch. + void setUserProperties(const std::map& user_properties); + + // Acquire a copy all application specific key-value pairs. + std::map getUserProperties() const; + + std::string getTopic() const; + void setTopic(const std::string& topic); + + std::string getTag() const; + void setTag(const std::string& tags); + + std::vector getKeys() const; + void attachKey(const std::string& key); + + std::string getMsgID() const; + void setMsgID(const std::string& message_id); + + std::chrono::system_clock::time_point getStartDeliverTime() const; + + void setStartDeliverTime(std::chrono::system_clock::time_point delivery_timepoint); + + std::string getBody() const; + void setBody(const std::string& body); + + std::int32_t getReconsumeTimes() const; + + std::chrono::system_clock::time_point getStoreTimestamp() const; + + std::chrono::system_clock::time_point getBornTimestamp() const { + return born_timestamp_; + } + + std::string toUserString() const; + + std::int64_t getQueueOffset() const; + +protected: + void setStoreTimestamp(std::chrono::system_clock::time_point store_timestamp); + + void setBornTimestamp(std::chrono::system_clock::time_point born_timestamp) { + born_timestamp_ = born_timestamp; + } + + void setQueueOffset(std::int64_t offset); + + void setReconsumeTimes(std::int32_t reconsume_times); + + std::string toString() const; + + friend class ONSUtil; + +private: + std::string topic_; + std::string tag_; + std::string body_; + std::string message_id_; + std::chrono::system_clock::time_point store_timestamp_{std::chrono::system_clock::now()}; + std::chrono::system_clock::time_point born_timestamp_{std::chrono::system_clock::now()}; + std::chrono::system_clock::time_point delivery_timestamp_{std::chrono::system_clock::now()}; + std::int64_t queue_offset_{0}; + std::map user_properties_; + std::vector keys_; + std::int32_t reconsume_times_{0}; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/MessageListener.h b/api/ons/MessageListener.h new file mode 100644 index 000000000..6e95754c7 --- /dev/null +++ b/api/ons/MessageListener.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Action.h" +#include "ConsumeContext.h" +#include "Message.h" + +ONS_NAMESPACE_BEGIN +class ONSCLIENT_API MessageListener { +public: + virtual ~MessageListener() = default; + + virtual Action consume(const Message& message, ConsumeContext& context) noexcept = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/MessageModel.h b/api/ons/MessageModel.h new file mode 100644 index 000000000..1d636af60 --- /dev/null +++ b/api/ons/MessageModel.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +enum class MessageModel : std::uint8_t +{ + CLUSTERING = 0, + BROADCASTING = 1, +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/MessageOrderListener.h b/api/ons/MessageOrderListener.h new file mode 100644 index 000000000..030022762 --- /dev/null +++ b/api/ons/MessageOrderListener.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ConsumeOrderContext.h" +#include "Message.h" +#include "OrderAction.h" + +ONS_NAMESPACE_BEGIN + +class MessageOrderListener { +public: + virtual ~MessageOrderListener() = default; + + virtual OrderAction consume(const Message& message, const ConsumeOrderContext& context) noexcept = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/MessageQueueONS.h b/api/ons/MessageQueueONS.h new file mode 100755 index 000000000..bf96ffd74 --- /dev/null +++ b/api/ons/MessageQueueONS.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +class MessageQueueONS { +public: + MessageQueueONS() = default; + + MessageQueueONS(std::string topic, std::string broker_name, int queue_id) + : topic_(std::move(topic)), broker_name_(std::move(broker_name)), queue_id_(queue_id) { + } + + std::string getTopic() const; + void setTopic(const std::string& topic); + + std::string getBrokerName() const; + void setBrokerName(const std::string& broker_name); + + int getQueueId() const; + void setQueueId(int queue_id); + + bool operator==(const MessageQueueONS& mq) const; + bool operator<(const MessageQueueONS& mq) const; + int compareTo(const MessageQueueONS& mq) const; + +private: + std::string topic_; + std::string broker_name_; + int queue_id_{-1}; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/MessageQueueSelectorONS.h b/api/ons/MessageQueueSelectorONS.h new file mode 100644 index 000000000..7de556ffc --- /dev/null +++ b/api/ons/MessageQueueSelectorONS.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Message.h" +#include "MessageQueueONS.h" + +ONS_NAMESPACE_BEGIN + +class MessageQueueSelectorONS { +public: + virtual ~MessageQueueSelectorONS() = default; + + virtual MessageQueueONS select(const std::vector& mqs, const Message& msg, void* arg) = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ONSCallback.h b/api/ons/ONSCallback.h new file mode 100644 index 000000000..ad18e66a5 --- /dev/null +++ b/api/ons/ONSCallback.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ONSClientException.h" +#include "SendResultONS.h" + +ONS_NAMESPACE_BEGIN + +class SendCallbackONS { +public: + virtual ~SendCallbackONS() = default; + + virtual void onSuccess(SendResultONS& send_result) = 0; + + virtual void onException(ONSClientException& e) = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ONSChannel.h b/api/ons/ONSChannel.h new file mode 100644 index 000000000..84689cfa4 --- /dev/null +++ b/api/ons/ONSChannel.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +enum class ONSChannel : uint8_t +{ + CLOUD = 0, + ALIYUN = 1, + ALL = 2, + LOCAL = 3, + INNER = 4, +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ONSClient.h b/api/ons/ONSClient.h new file mode 100644 index 000000000..1078c09d3 --- /dev/null +++ b/api/ons/ONSClient.h @@ -0,0 +1,30 @@ +#pragma once + +#ifdef WIN32 +#ifdef ONSCLIENT_EXPORTS + +#ifndef SWIG +#define ONSCLIENT_API __declspec(dllexport) +#else +#define ONSCLIENT_API +#endif + +#else +#define ONSCLIENT_API __declspec(dllimport) +#endif +#else +#define ONSCLIENT_API +#endif + +#ifndef ONS_NAMESPACE +#define ONS_NAMESPACE ons +#endif + + +#ifndef ONS_NAMESPACE_BEGIN +#define ONS_NAMESPACE_BEGIN namespace ONS_NAMESPACE { +#endif + +#ifndef ONS_NAMESPACE_END +#define ONS_NAMESPACE_END } +#endif \ No newline at end of file diff --git a/api/ons/ONSClientException.h b/api/ons/ONSClientException.h new file mode 100644 index 000000000..c0df6744d --- /dev/null +++ b/api/ons/ONSClientException.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "ONSClient.h" +#include "ONSErrorCode.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API ONSClientException : public std::exception { +public: + ONSClientException() = delete; + ONSClientException(const ONSClientException& e) noexcept; + ~ONSClientException() noexcept override = default; + explicit ONSClientException(std::string msg, int code = OTHER_ERROR) noexcept; + + const char* GetMsg() const noexcept; + const char* what() const noexcept override; + int GetError() const noexcept; + +private: + std::string msg_; + int error_{}; +}; + +#define THROW_ONS_EXCEPTION(e, msg, code) throw e(msg, code) + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ONSEnvironmentVariableFactoryProperty.h b/api/ons/ONSEnvironmentVariableFactoryProperty.h new file mode 100644 index 000000000..6506b5ddf --- /dev/null +++ b/api/ons/ONSEnvironmentVariableFactoryProperty.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ONSFactoryProperty.h" + +ONS_NAMESPACE_BEGIN + +class ONSEnvironmentVariableFactoryProperty : public ONSFactoryProperty { +public: + ONSEnvironmentVariableFactoryProperty(); + + ~ONSEnvironmentVariableFactoryProperty() override = default; + +private: + void parseEnvironmentVariables(); +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ONSErrorCode.h b/api/ons/ONSErrorCode.h new file mode 100644 index 000000000..2bd934d59 --- /dev/null +++ b/api/ons/ONSErrorCode.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +constexpr int SEND_CALLBACK_IS_EMPTY = -1; +constexpr int MESSAGE_LISTENER_IS_EMPTY = -2; +constexpr int MESSAGE_SELECTOR_QUEUE_EMPTY = -3; +constexpr int CONSUME_MESSAGE_LISTENER_IS_NULL = -4; +constexpr int OTHER_ERROR = -999; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ONSFactory.h b/api/ons/ONSFactory.h new file mode 100755 index 000000000..ef46e0bd4 --- /dev/null +++ b/api/ons/ONSFactory.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ONSFactoryAPI.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API ONSFactory { +public: + ONSFactory() = delete; + + virtual ~ONSFactory() = default; + + static ONSFactoryAPI* getInstance(); +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ONSFactoryAPI.h b/api/ons/ONSFactoryAPI.h new file mode 100644 index 000000000..2179e8159 --- /dev/null +++ b/api/ons/ONSFactoryAPI.h @@ -0,0 +1,36 @@ +#pragma once + +#include "LocalTransactionChecker.h" +#include "MessageModel.h" +#include "ONSChannel.h" +#include "ONSClient.h" +#include "ONSClientException.h" +#include "ONSFactoryProperty.h" +#include "OrderConsumer.h" +#include "OrderProducer.h" +#include "Producer.h" +#include "PullConsumer.h" +#include "PushConsumer.h" +#include "TransactionProducer.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API ONSFactoryAPI { +public: + virtual ~ONSFactoryAPI() = default; + + virtual Producer* createProducer(ONSFactoryProperty& factory_properties) = 0; + + virtual OrderProducer* createOrderProducer(ONSFactoryProperty& factory_properties) = 0; + + virtual OrderConsumer* createOrderConsumer(ONSFactoryProperty& factory_properties) = 0; + + virtual TransactionProducer* createTransactionProducer(ONSFactoryProperty& factory_properties, + LocalTransactionChecker* checker) = 0; + + virtual PullConsumer* createPullConsumer(ONSFactoryProperty& factory_properties) = 0; + + virtual PushConsumer* createPushConsumer(ONSFactoryProperty& factory_properties) = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ONSFactoryProperty.h b/api/ons/ONSFactoryProperty.h new file mode 100644 index 000000000..1bc8e9d1c --- /dev/null +++ b/api/ons/ONSFactoryProperty.h @@ -0,0 +1,152 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "MessageModel.h" +#include "ONSChannel.h" +#include "ONSClient.h" +#include "Trace.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API ONSFactoryProperty { +public: + ONSFactoryProperty(bool set_defaults = true); + + virtual ~ONSFactoryProperty() = default; + + std::string getLogPath() const; + + ONSFactoryProperty& setSendMsgTimeout(int value); + ONSFactoryProperty& setSendMsgTimeout(std::chrono::milliseconds timeout); + std::chrono::milliseconds getSendMsgTimeout() const; + + ONSFactoryProperty& setSendMsgRetryTimes(int value); + ONSFactoryProperty& setMaxMsgCacheSize(int size); + + ONSFactoryProperty& withTraceFeature(Trace trace_flag); + ONSFactoryProperty& setOnsTraceSwitch(bool should_trace); + + void setOnsChannel(ONSChannel ons_channel); + + void setFactoryProperty(const std::string& key, const std::string& value) { + validate(key, value); + property_map_.insert({key, value}); + } + + std::map getFactoryProperties() const; + std::string getProducerId() const; + std::string getConsumerId() const; + std::string getGroupId() const; + + std::string getMessageModel() const; + ONSFactoryProperty& setMessageModel(MessageModel message_model); + + /** + * @brief When processing First-In-First-Out messages, immediate retry on + * consuming failure may not be desired. Instead, developers prefer to + * introduce period of time as cool-down. + * + * @param duration Period of time to cool down. + */ + void setSuspendDuration(std::chrono::milliseconds duration); + std::chrono::milliseconds getSuspendTimeMillis() const; + + int getSendMsgRetryTimes() const; + int getConsumeThreadNums() const; + int getMaxMsgCacheSize() const; + int getMaxMsgCacheSizeInMiB() const; + ONSChannel getOnsChannel() const; + std::string getChannel() const; + std::string getNameSrvAddr() const; + std::string getNameSrvDomain() const; + std::string getAccessKey() const; + std::string getSecretKey() const; + std::string getConsumerInstanceName() const; + bool getOnsTraceSwitch() const; + std::string getInstanceId() const; + + operator bool(); + + /** + * @brief Client-side throttling policy. Maximum number of messages + * consumption per topic allowed. + * + * @param topic Topic name + * @param qps Maximum number of messages per second allowed. Clients would + * enforce thottling if there are excessively many messages locally cached. + */ + void throttle(const std::string& topic, std::uint32_t qps) { + throttle_.insert({topic, qps}); + } + + const std::map throttle() const { + return throttle_; + } + + /** + * @brief Path of log files. If unspecified, defaults to ${HOME}/logs/rocketmq + */ + static const char* LogPath; + + // Deprecated, please use GroupId instead. + static const char* ProducerId; + + // Deprecated, please use GroupId instead. + static const char* ConsumerId; + + /** + * @brief Name of the load-balancing group. Take message consuming for + * example. If multiple consumer clients work collaborately to fulfill their + * design goals, they should have the same GroupId and MessageModel set + * CLUSTERING. + */ + static const char* GroupId; + + // Deprecated. + static const char* ONSAddr; + + static const char* AccessKey; + static const char* SecretKey; + + static const char* MessageModel; + static const char* BROADCASTING; + static const char* CLUSTERING; + static const char* SendMsgTimeoutMillis; + // Suspend time after failure of consuming order message. + static const char* SuspendTimeMillis; + static const char* NAMESRV_ADDR; + static const char* ConsumeThreadNums; + static const char* OnsChannel; + static const char* MaxMsgCacheSize; + static const char* MaxCachedMessageSizeInMiB; + static const char* OnsTraceSwitch; + static const char* SendMsgRetryTimes; + static const char* ConsumerInstanceName; + static const char* InstanceId; + static const char* DEFAULT_CHANNEL; + + static const std::string EMPTY_STRING; + +protected: + void setDefaults(); + +private: + std::map property_map_; + + std::map throttle_; + + std::string getProperty(const std::string& key) const; + + std::string getProperty(const std::string& key, std::string default_value) const; + + bool validate(const std::string& key, const std::string& value); + + void loadConfigFile(); +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/ONSPullStatus.h b/api/ons/ONSPullStatus.h new file mode 100644 index 000000000..de9770bda --- /dev/null +++ b/api/ons/ONSPullStatus.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +enum class ONSPullStatus : uint8_t +{ + ONS_FOUND = 0, + ONS_NO_NEW_MSG = 1, + ONS_NO_MATCHED_MSG = 2, + ONS_OFFSET_ILLEGAL = 3, + ONS_BROKER_TIMEOUT = 3, // indicate pull request timeout without receiving valid response +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/OffsetStore.h b/api/ons/OffsetStore.h new file mode 100644 index 000000000..c9a67620e --- /dev/null +++ b/api/ons/OffsetStore.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +/** + * @brief By default, consumers of broadcasting model skip existing records when at startup. Application developers may + * make use of this class to pick up from the previous progress. + */ +class OffsetStore { +public: + /** + * @brief Read offset of the given queue from persist source. + * + * @param queue + * @param offset + * @return true it manages to read offset from persistence layer; + * @return false if something wrong occurred; + */ + virtual bool readOffset(const std::string& queue, std::int64_t& offset) = 0; + + + /** + * @brief Save offset of the given queue; + * + * @param queue + * @param offset + */ + virtual void writeOffset(const std::string& queue, std::int64_t offset) = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/OrderAction.h b/api/ons/OrderAction.h new file mode 100644 index 000000000..c11e93fab --- /dev/null +++ b/api/ons/OrderAction.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +// order consuming result +enum class OrderAction +{ + // consume success, application could continue to consume next message + Success, + // consume fail, suspends the current queue + Suspend, +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/OrderConsumer.h b/api/ons/OrderConsumer.h new file mode 100644 index 000000000..72d315b7a --- /dev/null +++ b/api/ons/OrderConsumer.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "MessageOrderListener.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API OrderConsumer { +public: + virtual ~OrderConsumer() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual void subscribe(const std::string& topic, const std::string& expression) = 0; + + virtual void registerMessageListener(MessageOrderListener* listener) = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/OrderProducer.h b/api/ons/OrderProducer.h new file mode 100644 index 000000000..afc9b961b --- /dev/null +++ b/api/ons/OrderProducer.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Message.h" +#include "SendResultONS.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API OrderProducer { +public: + virtual ~OrderProducer() = default; + + /** + * @brief Start the instance, allowing it to allocate preparing resources, start internal threads to orchastrate + * internal components. + * + */ + virtual void start() = 0; + + /** + * @brief Shutdown the instance, which release all internally allocated resources. + * + */ + virtual void shutdown() = 0; + + /** + * @brief Send message pertaining to the specified message_group. Messages of the same message_group go to the same + * message queue. + * + * If errors encountered, ONSClientException would be raised. + * + * @param message + * @param message_group + * @return SendResultONS + */ + virtual SendResultONS send(Message& message, std::string message_group) noexcept(false) = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/Producer.h b/api/ons/Producer.h new file mode 100644 index 000000000..81c75f2ff --- /dev/null +++ b/api/ons/Producer.h @@ -0,0 +1,61 @@ +#pragma once + +#include + +#include "Message.h" +#include "MessageQueueONS.h" +#include "ONSCallback.h" +#include "ONSClientException.h" +#include "SendResultONS.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API Producer { +public: + virtual ~Producer() = default; + + // before sending msg, start function must be called to prepare and allocate + // necessary resources. + virtual void start() = 0; + + // shutdown must be called to release all internally allocated resources + // before quit. + virtual void shutdown() = 0; + + // retry at most 3 times internally. Sending can be regarded as success if no + // exception is raised. + + /** + * @brief Delivery message to brokers. By default, 3-times-retry policy are + * employed on sending failure. If SDK still failed after all retry quota are + * exchausted, ONSClientException will be raised and application developers + * are supposed to handle such situation. + * + * @param message + * @return ons::SendResultONS + * @throw ONSClientException + */ + virtual ons::SendResultONS send(Message& message) noexcept(false) = 0; + + /** + * @brief Similar to the previous send(Message&) function, with one difference: this function is noexcept. + * + * @param message + * @param ec + * @return ons::SendResultONS + */ + virtual ons::SendResultONS send(Message& message, std::error_code& ec) noexcept = 0; + + /** + * @brief Send message asynchronously. + * + * @param msg + * @param callback + */ + virtual void sendAsync(Message& message, ons::SendCallbackONS* callback) noexcept = 0; + + // one-way send + virtual void sendOneway(Message& message) noexcept = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/PullConsumer.h b/api/ons/PullConsumer.h new file mode 100755 index 000000000..49fce458e --- /dev/null +++ b/api/ons/PullConsumer.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include + +#include "MessageQueueONS.h" +#include "ONSClientException.h" +#include "PullResultONS.h" + +ONS_NAMESPACE_BEGIN + +class ONSFactoryProperty; + +class ONSCLIENT_API PullConsumer { +public: + virtual ~PullConsumer() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual void fetchSubscribeMessageQueues(const std::string& topic, std::vector& mqs) = 0; + + virtual PullResultONS pull(const MessageQueueONS& mq, const std::string& expression, std::int64_t offset, + std::int32_t batch_size) = 0; + + virtual std::int64_t searchOffset(const MessageQueueONS& mq, long long timestamp) = 0; + + virtual std::int64_t maxOffset(const MessageQueueONS& mq) = 0; + + virtual std::int64_t minOffset(const MessageQueueONS& mq) = 0; + + virtual void updateConsumeOffset(const MessageQueueONS& mq, std::int64_t offset) = 0; + + virtual void removeConsumeOffset(const MessageQueueONS& mq) = 0; + + virtual std::int64_t fetchConsumeOffset(const MessageQueueONS& mq, bool skip_cache) = 0; + + virtual void persistConsumerOffset4PullConsumer(const MessageQueueONS& mq) noexcept(false) = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/PullResultONS.h b/api/ons/PullResultONS.h new file mode 100644 index 000000000..0f8882c80 --- /dev/null +++ b/api/ons/PullResultONS.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "Message.h" +#include "ONSClient.h" +#include "ONSPullStatus.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API PullResultONS { +public: + PullResultONS(ONSPullStatus status) : pull_status_(status), next_begin_offset_(0), min_offset_(0), max_offset_(0) { + } + + PullResultONS(ONSPullStatus status, long long next_begin_offset, long long min_offset, long long max_offset) + : pull_status_(status), next_begin_offset_(next_begin_offset), min_offset_(min_offset), max_offset_(max_offset) { + } + + virtual ~PullResultONS() = default; + + ONSPullStatus pull_status_; + long long next_begin_offset_; + long long min_offset_; + long long max_offset_; + std::vector message_list_; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/PushConsumer.h b/api/ons/PushConsumer.h new file mode 100644 index 000000000..74293dd43 --- /dev/null +++ b/api/ons/PushConsumer.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "MessageListener.h" +#include "OffsetStore.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API PushConsumer { +public: + PushConsumer() = default; + + virtual ~PushConsumer() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual void subscribe(const std::string& topic, const std::string& filter_expression) = 0; + + virtual void registerMessageListener(MessageListener* listener) = 0; + + virtual void withOffsetStore(std::unique_ptr offset_store) = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/SendResultONS.h b/api/ons/SendResultONS.h new file mode 100644 index 000000000..2989188da --- /dev/null +++ b/api/ons/SendResultONS.h @@ -0,0 +1,39 @@ +#pragma once +#include + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +class SendCallbackONSWrapper; +class ONSSendCallback; +class ProducerImpl; +class OrderProducerImpl; +class TransactionProducerImpl; + +class ONSCLIENT_API SendResultONS { +public: + SendResultONS(); + + virtual ~SendResultONS(); + + const std::string& getMessageId() const; + + operator bool() { + return !message_id_.empty(); + } + +protected: + void setMessageId(const std::string& message_id); + +private: + std::string message_id_; + + friend class SendCallbackONSWrapper; + friend class ONSSendCallback; + friend class ProducerImpl; + friend class OrderProducerImpl; + friend class TransactionProducerImpl; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/Trace.h b/api/ons/Trace.h new file mode 100644 index 000000000..9d8d1c093 --- /dev/null +++ b/api/ons/Trace.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +enum class Trace : std::uint8_t +{ + ON = 0, + OFF = 1, +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/TransactionProducer.h b/api/ons/TransactionProducer.h new file mode 100644 index 000000000..35526198f --- /dev/null +++ b/api/ons/TransactionProducer.h @@ -0,0 +1,25 @@ +#pragma once + +#include "LocalTransactionExecuter.h" +#include "ONSClient.h" +#include "SendResultONS.h" + +ONS_NAMESPACE_BEGIN + +class ONSCLIENT_API TransactionProducer { +public: + virtual ~TransactionProducer() = default; + + // before send msg, start must be called to allocate resources. + virtual void start() = 0; + + // before exit ons, shutdown must be called to release all resources allocated + // by ons internally. + virtual void shutdown() = 0; + + // retry max 3 times if send failed. if no exception thrown, it sends + // success; + virtual SendResultONS send(Message& msg, LocalTransactionExecuter* executor) noexcept(false) = 0; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/ons/TransactionStatus.h b/api/ons/TransactionStatus.h new file mode 100644 index 000000000..f1a6de9b6 --- /dev/null +++ b/api/ons/TransactionStatus.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "ONSClient.h" + +ONS_NAMESPACE_BEGIN + +enum class TransactionStatus : std::uint8_t +{ + CommitTransaction = 0, + RollbackTransaction = 1, + Unknow = 2, +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/AdminServer.h b/api/rocketmq/AdminServer.h new file mode 100644 index 000000000..3876a2ca0 --- /dev/null +++ b/api/rocketmq/AdminServer.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "State.h" + +namespace rocketmq { +namespace admin { + + class AdminServer { + public: + virtual ~AdminServer() = default; + + virtual bool start() = 0; + + virtual bool stop() = 0; + + virtual int port() const = 0; + }; + + class AdminFacade { + public: + static AdminServer& getServer(); + }; + +} // namespace admin +} // namespace rocketmq \ No newline at end of file diff --git a/api/rocketmq/AsyncCallback.h b/api/rocketmq/AsyncCallback.h new file mode 100644 index 000000000..8004ba027 --- /dev/null +++ b/api/rocketmq/AsyncCallback.h @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "MQClientException.h" +#include "PullResult.h" +#include "SendResult.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class AsyncCallback { +public: + virtual ~AsyncCallback() = default; +}; + +class SendCallback : public AsyncCallback { +public: + ~SendCallback() override = default; + + virtual void onSuccess(SendResult& send_result) noexcept = 0; + + virtual void onFailure(const std::error_code& ec) noexcept = 0; +}; + +class PullCallback : public AsyncCallback { +public: + ~PullCallback() override = default; + + virtual void onSuccess(const PullResult& pull_result) noexcept = 0; + + virtual void onFailure(const std::error_code& ec) noexcept = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ConsumeType.h b/api/rocketmq/ConsumeType.h new file mode 100644 index 000000000..4cbf5a2fa --- /dev/null +++ b/api/rocketmq/ConsumeType.h @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum ConsumeType +{ + CONSUME_ACTIVELY, + CONSUME_PASSIVELY, +}; + +enum ConsumeFromWhere +{ + CONSUME_FROM_LAST_OFFSET, + CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRST, + CONSUME_FROM_MIN_OFFSET, + CONSUME_FROM_MAX_OFFSET, + CONSUME_FROM_FIRST_OFFSET, + CONSUME_FROM_TIMESTAMP, +}; + +enum ConsumeInitialMode +{ + MIN, + MAX, +}; + +enum QueryOffsetPolicy : uint8_t +{ + BEGINNING = 0, + END = 1, + TIME_POINT = 2, +}; + +struct OffsetQuery { + MQMessageQueue message_queue; + QueryOffsetPolicy policy; + std::chrono::system_clock::time_point time_point; +}; + +struct PullMessageQuery { + MQMessageQueue message_queue; + int64_t offset; + int32_t batch_size; + std::chrono::system_clock::duration await_time; + std::chrono::system_clock::duration timeout; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Credentials.h b/api/rocketmq/Credentials.h new file mode 100644 index 000000000..1bd5dbcfc --- /dev/null +++ b/api/rocketmq/Credentials.h @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Credentials { +public: + Credentials() : expiration_instant_(std::chrono::system_clock::time_point::max()) { + } + + Credentials(std::string access_key, std::string access_secret) + : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)), + expiration_instant_(std::chrono::system_clock::time_point::max()) { + } + + Credentials(std::string access_key, std::string access_secret, std::string session_token, + std::chrono::system_clock::time_point expiration) + : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)), + session_token_(std::move(session_token)), expiration_instant_(expiration) { + } + + bool operator==(const Credentials& rhs) const { + return access_key_ == rhs.access_key_ && access_secret_ == rhs.access_secret_ && + session_token_ == rhs.session_token_ && expiration_instant_ == rhs.expiration_instant_; + } + + bool operator!=(const Credentials& rhs) const { + return !(*this == rhs); + } + + bool empty() const { + return access_key_.empty() || access_secret_.empty(); + } + + bool expired() const { + return std::chrono::system_clock::now() > expiration_instant_; + } + + const std::string& accessKey() const { + return access_key_; + } + + const std::string& accessSecret() const { + return access_secret_; + } + + const std::string& sessionToken() const { + return session_token_; + } + + std::chrono::system_clock::time_point expirationInstant() const { + return expiration_instant_; + } + +private: + std::string access_key_; + std::string access_secret_; + std::string session_token_; + std::chrono::system_clock::time_point expiration_instant_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/CredentialsProvider.h b/api/rocketmq/CredentialsProvider.h new file mode 100644 index 000000000..595746f1c --- /dev/null +++ b/api/rocketmq/CredentialsProvider.h @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "Credentials.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class CredentialsProvider { +public: + virtual ~CredentialsProvider() = default; + + virtual Credentials getCredentials() = 0; +}; + +using CredentialsProviderPtr = std::shared_ptr; + +class StaticCredentialsProvider : public CredentialsProvider { +public: + StaticCredentialsProvider(std::string access_key, std::string access_secret); + + ~StaticCredentialsProvider() override = default; + + Credentials getCredentials() override; + +private: + std::string access_key_; + std::string access_secret_; +}; + +/** + * Read credentials from environment variables ROCKETMQ_ACCESS_KEY, ROCKETMQ_ACCESS_SECRET + */ +class EnvironmentVariablesCredentialsProvider : public CredentialsProvider { +public: + EnvironmentVariablesCredentialsProvider(); + + ~EnvironmentVariablesCredentialsProvider() override = default; + + Credentials getCredentials() override; + + static const char* ENVIRONMENT_ACCESS_KEY; + static const char* ENVIRONMENT_ACCESS_SECRET; + +private: + std::string access_key_; + std::string access_secret_; +}; + +/** + * Read credentials from configuration file: ~/rocketmq/credentials. By default, the client library would refresh every + * 10 seconds. + */ +class ConfigFileCredentialsProvider : public CredentialsProvider { +public: + ConfigFileCredentialsProvider(); + + ConfigFileCredentialsProvider(std::string config_file, std::chrono::milliseconds refresh_interval); + + Credentials getCredentials() override; + + /** + * For test purpose only. + * @return + */ + static const char* credentialFile() { + return CREDENTIAL_FILE_; + } + +private: + std::chrono::system_clock::duration refresh_interval_{std::chrono::seconds(10)}; + std::string access_key_; + std::string access_secret_; + static const char* CREDENTIAL_FILE_; + static const char* ACCESS_KEY_FIELD_NAME; + static const char* ACCESS_SECRET_FIELD_NAME; +}; + +class StsCredentialsProviderImpl; + +class StsCredentialsProvider : public CredentialsProvider { +public: + explicit StsCredentialsProvider(std::string ram_role_name); + + Credentials getCredentials() override; + +private: + std::unique_ptr impl_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h new file mode 100644 index 000000000..97306d538 --- /dev/null +++ b/api/rocketmq/DefaultMQProducer.h @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "AsyncCallback.h" +#include "CredentialsProvider.h" +#include "ErrorCode.h" +#include "LocalTransactionStateChecker.h" +#include "Logger.h" +#include "MQMessage.h" +#include "MQSelector.h" +#include "SendResult.h" + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * This class employs pointer-to-implementation paradigm to achieve the goal of stable ABI. + * Refer https://en.cppreference.com/w/cpp/language/pimpl for an explanation. + */ +class ProducerImpl; + +class DefaultMQProducer { +public: + explicit DefaultMQProducer(const std::string& group_name); + + ~DefaultMQProducer() = default; + + void start(); + + void shutdown(); + + /** + * Acquire previous send timeout in milliseconds. + * @return Send timeout in milliseconds + */ + std::chrono::milliseconds getSendMsgTimeout() const; + + /** + * Set default send message timeout in milliseconds. + * @param timeout_millis Timeout used when sending messages. + */ + void setSendMsgTimeout(std::chrono::milliseconds timeout); + + SendResult send(MQMessage& message, const std::string& message_group); + + /** + * Send message in synchronous manner. + * @param message Message to send. + * @param filter_active_broker Do NOT rely on this parameter. it has been deprecated. + */ + SendResult send(const MQMessage& message, bool filter_active_broker = true); + + SendResult send(const MQMessage& message, std::error_code& ec) noexcept; + + SendResult send(MQMessage& message, const MQMessageQueue& message_queue); + SendResult send(MQMessage& message, MessageQueueSelector* selector, void* arg); + + /** + * @brief This function is deprecated. + * + * @param message + * @param selector + * @param arg + * @param retry_times retry_times is ignored with respect to member retry_times setting. + * @param select_active_broker + * @return SendResult + */ + SendResult send(MQMessage& message, MessageQueueSelector* selector, void* arg, int retry_times, + bool select_active_broker = false); + + /** + * Send message in asynchronous manner. + * @param message Message to send. + * @param send_callback Callback to execute on completion of message sending. + * @param select_active_broker Do NOT rely on this parameter. it has been deprecated. + */ + void send(const MQMessage& message, SendCallback* send_callback, bool select_active_broker = false); + void send(MQMessage& message, const MQMessageQueue& message_queue, SendCallback* send_callback); + void send(MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* send_callback); + + /** + * send message in Oneway(The implementation is simply ignore the result of send message in synchronous). + * @param message Message to send. + * @param select_active_broker Do NOT rely on this parameter. it has been deprecated. + */ + void sendOneway(const MQMessage& message, bool select_active_broker = false); + void sendOneway(MQMessage& message, const MQMessageQueue& message_queue); + void sendOneway(MQMessage& message, MessageQueueSelector* selector, void* arg); + + void setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker); + + void setNamesrvAddr(const std::string& name_server_address_list); + + void setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint); + + void setGroupName(const std::string& group_name); + + void setInstanceName(const std::string& instance_name); + + void enableTracing(bool enabled); + + bool isTracingEnabled(); + + /** + * Number of attempts before claiming a send action as failure. By default, 3 attempts will be performed for sync and + * async send methods. + * + * @return + */ + int getMaxAttemptTimes() const; + + void setMaxAttemptTimes(int max_attempt_times); + + std::vector getTopicMessageQueueInfo(const std::string& topic); + + void setUnitName(std::string unit_name); + + const std::string& getUnitName(); + + uint32_t compressBodyThreshold() const; + void compressBodyThreshold(uint32_t threshold); + + void setResourceNamespace(const std::string& resource_namespace); + + void setCredentialsProvider(CredentialsProviderPtr credentials_provider); + + void setRegion(const std::string& region); + + TransactionPtr prepare(MQMessage& message); + +private: + std::shared_ptr impl_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/DefaultMQPullConsumer.h b/api/rocketmq/DefaultMQPullConsumer.h new file mode 100644 index 000000000..9bbfc6031 --- /dev/null +++ b/api/rocketmq/DefaultMQPullConsumer.h @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "AsyncCallback.h" +#include "ConsumeType.h" +#include "CredentialsProvider.h" +#include "MQMessageExt.h" +#include "MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PullConsumerImpl; + +class DefaultMQPullConsumer { +public: + explicit DefaultMQPullConsumer(const std::string& group_name); + + void start(); + + void shutdown(); + + std::future> queuesFor(const std::string& topic); + + std::future queryOffset(const OffsetQuery& query); + + bool pull(const PullMessageQuery& request, PullResult& pull_result); + + void pull(const PullMessageQuery& request, PullCallback* callback); + + void setResourceNamespace(const std::string& resource_namespace); + + void setNamesrvAddr(const std::string& name_srv); + + void setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint); + + void setCredentialsProvider(std::shared_ptr credentials_provider); + +private: + std::shared_ptr impl_; +}; + +ROCKETMQ_NAMESPACE_END diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h new file mode 100644 index 000000000..0693b8d3c --- /dev/null +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "AsyncCallback.h" +#include "ConsumeType.h" +#include "CredentialsProvider.h" +#include "ExpressionType.h" +#include "Logger.h" +#include "MQMessageQueue.h" +#include "MessageListener.h" +#include "rocketmq/Executor.h" +#include "rocketmq/MessageModel.h" +#include "rocketmq/OffsetStore.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumerImpl; + +class DefaultMQPushConsumer { +public: + explicit DefaultMQPushConsumer(const std::string& group_name); + + ~DefaultMQPushConsumer() = default; + + void start(); + + void shutdown(); + + void subscribe(const std::string& topic, const std::string& expression, + ExpressionType expression_type = ExpressionType::TAG); + + void setConsumeFromWhere(ConsumeFromWhere policy); + + void registerMessageListener(MessageListener* listener); + + void setNamesrvAddr(const std::string& name_srv); + + void setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint); + + void setGroupName(const std::string& group_name); + + void setConsumeThreadCount(int thread_count); + + void setInstanceName(const std::string& instance_name); + + int getProcessQueueTableSize(); + + void setUnitName(std::string unit_name); + + const std::string& getUnitName() const; + + void enableTracing(bool enabled); + + bool isTracingEnabled(); + + /** + * SDK of this version always uses asynchronous IO operation. As such, this + * function is no-op to keep backward compatibility. + */ + void setAsyncPull(bool); + + /** + * Maximum number of messages passed to each callback. + * @param batch_size Batch size + */ + void setConsumeMessageBatchMaxSize(int batch_size); + + /** + * Lifecycle of executor is managed by external application. Passed-in + * executor should remain valid after consumer start and before stopping. + * @param executor Executor pool used to invoke consume callback. + */ + void setCustomExecutor(const Executor& executor); + + /** + * This function sets maximum number of message that may be consumed per + * second. + * @param topic Topic to control + * @param threshold Threshold before throttling is enforced. + */ + void setThrottle(const std::string& topic, uint32_t threshold); + + /** + * Set abstract-resource-namespace, in which canonical name of topic, group + * remains unique. + * @param resource_namespace Abstract resource namespace. + */ + void setResourceNamespace(const std::string& resource_namespace); + + void setCredentialsProvider(CredentialsProviderPtr credentials_provider); + + void setMessageModel(MessageModel message_model); + + std::string groupName() const; + + void setOffsetStore(std::unique_ptr offset_store); + +private: + std::shared_ptr impl_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ErrorCategory.h b/api/rocketmq/ErrorCategory.h new file mode 100644 index 000000000..075266b86 --- /dev/null +++ b/api/rocketmq/ErrorCategory.h @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "ErrorCode.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ErrorCategory : public std::error_category { +public: + static const ErrorCategory& instance() { + static ErrorCategory instance; + return instance; + } + + const char* name() const noexcept override { + return "RocketMQ"; + } + + std::string message(int code) const override; + +private: + ErrorCategory() = default; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ErrorCode.h b/api/rocketmq/ErrorCode.h new file mode 100644 index 000000000..c3ebb7a39 --- /dev/null +++ b/api/rocketmq/ErrorCode.h @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "RocketMQ.h" + +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +enum class ErrorCode : int +{ + Success = 0, + + /** + * @brief Client state not as expected. Call Producer#start() first. + * + */ + IllegalState = 1, + + /** + * @brief Bad configuration. For example, negative max-attempt-times. + * + */ + BadConfiguration = 300, + + /** + * @brief The server cannot process the request due to apprent client-side + * error. For example, topic contains invalid character or is excessively + * long. + * + */ + BadRequest = 400, + + /** + * @brief Authentication failed. Possibly caused by invalid credentials. + * + */ + Unauthorized = 401, + + /** + * @brief Credentials are understood by server but authenticated user does not + * have privilege to perform the requested action. + * + */ + Forbidden = 403, + + /** + * @brief Topic not found, which should be created through console or + * administration API before hand. + * + */ + NotFound = 404, + + /** + * @brief Timeout when connecting, reading from or writing to brokers. + * + */ + RequestTimeout = 408, + + /** + * @brief Message body is too large. + * + */ + PayloadTooLarge = 413, + + /** + * @brief When trying to perform an action whose dependent procedure state is + * not right, this code will be used. + * 1. Acknowledge a message that is not previously received; + * 2. Commit/Rollback a transactional message that does not exist; + * 3. Commit an offset which is greater than maximum of partition; + */ + PreconditionRequired = 428, + + /** + * @brief Quota exchausted. The user has sent too many requests in a given + * amount of time. + * + */ + TooManyRequest = 429, + + /** + * @brief The server is unwilling to process the request because either an + * individual header field, or all the header fields collectively, are too + * large + * + */ + HeaderFieldsTooLarge = 431, + + /** + * @brief A server operator has received a legal demand to deny access to a + * resource or to a set of resources that includes the requested resource. + * + */ + UnavailableForLegalReasons = 451, + + /** + * @brief Server side interval error + * + */ + InternalServerError = 500, + + /** + * @brief The server either does not recognize the request method, or it lacks + * the ability to fulfil the request. + * + */ + NotImplemented = 501, + + /** + * @brief The server was acting as a gateway or proxy and received an invalid + * response from the upstream server. + * + */ + BadGateway = 502, + + /** + * @brief The server cannot handle the request (because it is overloaded or + * down for maintenance). Generally, this is a temporary state. + * + */ + ServiceUnavailable = 503, + + /** + * @brief The server was acting as a gateway or proxy and did not receive a + * timely response from the upstream server. + * + */ + GatewayTimeout = 504, + + /** + * @brief The server does not support the protocol version used in the + * request. + * + */ + ProtocolVersionNotSupported = 505, + + /** + * @brief The server is unable to store the representation needed to complete + * the request. + * + */ + InsufficientStorage = 507, +}; + +std::error_code make_error_code(ErrorCode code); + +ROCKETMQ_NAMESPACE_END + +namespace std { +template <> +struct is_error_code_enum : true_type {}; +} // namespace std \ No newline at end of file diff --git a/api/rocketmq/Executor.h b/api/rocketmq/Executor.h new file mode 100644 index 000000000..bbac1d0eb --- /dev/null +++ b/api/rocketmq/Executor.h @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +using Executor = std::function&)>; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ExpressionType.h b/api/rocketmq/ExpressionType.h new file mode 100644 index 000000000..6c2848fe5 --- /dev/null +++ b/api/rocketmq/ExpressionType.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum ExpressionType : int8_t +{ + TAG = 0, + SQL92 = 1 +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/LocalTransactionStateChecker.h b/api/rocketmq/LocalTransactionStateChecker.h new file mode 100644 index 000000000..59d788120 --- /dev/null +++ b/api/rocketmq/LocalTransactionStateChecker.h @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "MQMessageExt.h" +#include "Transaction.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class LocalTransactionStateChecker { +public: + virtual ~LocalTransactionStateChecker() = default; + + virtual TransactionState checkLocalTransactionState(const MQMessageExt& message) = 0; +}; + +using LocalTransactionStateCheckerPtr = std::unique_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Logger.h b/api/rocketmq/Logger.h new file mode 100644 index 000000000..7518d19f7 --- /dev/null +++ b/api/rocketmq/Logger.h @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "RocketMQ.h" + +#ifndef SPDLOG_ACTIVE_LEVEL +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE +#endif + +ROCKETMQ_NAMESPACE_BEGIN + +enum class Level : uint8_t +{ + Trace = 0, + Debug = 1, + Info = 2, + Warn = 3, + Error = 4 +}; + +class Logger { +public: + virtual ~Logger() = default; + + /** + * @brief Set log level for file sink. Its default log level is: Info. + * + * @param level + */ + virtual void setLevel(Level level) = 0; + + /** + * @brief Set log level for stdout, aka, console. Its default log level is: Warn. + * + * @param level + */ + virtual void setConsoleLevel(Level level) = 0; + + virtual void setFileSize(std::size_t file_size) = 0; + + virtual void setFileCount(std::size_t file_count) = 0; + + virtual void setPattern(std::string pattern) = 0; + + virtual void init() = 0; +}; + +Logger& getLogger(); + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQClientException.h b/api/rocketmq/MQClientException.h new file mode 100644 index 000000000..f74c45f3c --- /dev/null +++ b/api/rocketmq/MQClientException.h @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MQException : public std::exception { +public: + MQException(const std::string& msg, int error, const char* file, int line) noexcept + : m_error(error), m_line(line), m_file(file) { + try { + std::stringstream ss; + ss << "msg: " << msg << ",error:" << error << ",in file <" << file << "> line:" << line; + m_msg = ss.str(); + } catch (...) { + } + } + + MQException(const std::string& msg, int error, const char* file, const char* type, int line) noexcept + : m_error(error), m_line(line), m_file(file), m_type(type) { + try { + std::stringstream ss; + ss << "msg: " << msg << ",error:" << error << ",in file <" << file << "> line:" << line; + m_msg = ss.str(); + } catch (...) { + } + } + + ~MQException() noexcept override = default; + + const char* what() const noexcept override { + return m_msg.c_str(); + } + + int GetError() const noexcept { + return m_error; + } + + virtual const char* GetType() const noexcept { + return m_type.c_str(); + } + +protected: + int m_error; + int m_line; + std::string m_msg; + std::string m_file; + std::string m_type; +}; + +inline std::ostream& operator<<(std::ostream& os, const MQException& e) { + os << "Type: " << e.GetType() << " , " << e.what(); + return os; +} + +#define DEFINE_MQ_CLIENT_EXCEPTION(name) \ + class name : public MQException { \ + public: \ + name(const std::string& msg, int error, const char* file, int line) throw() \ + : MQException(msg, error, file, #name, line) { \ + } \ + virtual const char* GetType() const throw() { \ + return m_type.c_str(); \ + } \ + }; + +DEFINE_MQ_CLIENT_EXCEPTION(MQClientException) +DEFINE_MQ_CLIENT_EXCEPTION(MQBrokerException) +DEFINE_MQ_CLIENT_EXCEPTION(InterruptedException) +DEFINE_MQ_CLIENT_EXCEPTION(RemotingException) +DEFINE_MQ_CLIENT_EXCEPTION(UnknownHostException) + +#define THROW_MQ_EXCEPTION(e, msg, err) throw e(msg, err, __FILE__, __LINE__) +#define NEW_MQ_EXCEPTION(e, msg, err) e(msg, err, __FILE__, __LINE__) + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h new file mode 100644 index 000000000..e5d27fa2e --- /dev/null +++ b/api/rocketmq/MQMessage.h @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" + +#include "MQMessageQueue.h" +#include "MessageType.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageImpl; + +class MessageAccessor; + +class MQMessage { +public: + MQMessage(); + MQMessage(const std::string& topic, const std::string& body); + MQMessage(const std::string& topic, const std::string& tags, const std::string& body); + MQMessage(const std::string& topic, const std::string& tags, const std::string& keys, const std::string& body); + + virtual ~MQMessage(); + + MQMessage(const MQMessage& other); + MQMessage& operator=(const MQMessage& other); + + const std::string& getMsgId() const; + + void setProperty(const std::string& name, const std::string& value); + std::string getProperty(const std::string& name) const; + + const std::string& getTopic() const; + void setTopic(const std::string& topic); + void setTopic(const char* data, int len); + + std::string getTags() const; + void setTags(const std::string& tags); + + const std::vector& getKeys() const; + + /** + * @brief Add a unique key for the message + * TODO: a message may be associated with multiple keys. setKey, actually mean + * attach the given key to the message. Better rename it. + * @param key Unique key in perspective of bussiness logic. + */ + void setKey(const std::string& key); + void setKeys(const std::vector& keys); + + int getDelayTimeLevel() const; + void setDelayTimeLevel(int level); + + const std::string& traceContext() const; + void traceContext(const std::string& trace_context); + + std::string getBornHost() const; + + std::chrono::system_clock::time_point deliveryTimestamp() const; + + const std::string& getBody() const; + void setBody(const char* data, int len); + void setBody(const std::string& body); + + uint32_t bodyLength() const; + + const std::map& getProperties() const; + void setProperties(const std::map& properties); + + void messageType(MessageType message_type); + MessageType messageType() const; + + void bindMessageGroup(absl::string_view message_group); + + void bindMessageQueue(const MQMessageQueue& message_queue) { + message_queue_ = message_queue; + } + + const std::string& messageGroup() const; + + const MQMessageQueue& messageQueue() const { + return message_queue_; + } + +protected: + MessageImpl* impl_; + + friend class MessageAccessor; + +private: + MQMessageQueue message_queue_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessageExt.h b/api/rocketmq/MQMessageExt.h new file mode 100644 index 000000000..ed95484fc --- /dev/null +++ b/api/rocketmq/MQMessageExt.h @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "MQMessage.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageAccessor; + +class MQMessageExt : public MQMessage { +public: + MQMessageExt(); + + MQMessageExt(const MQMessageExt& other); + + MQMessageExt& operator=(const MQMessageExt& other); + + int32_t getQueueId() const; + + /** + * @return Milli-seconds since epoch in perspective of system_clock. + */ + int64_t getBornTimestamp() const; + + std::chrono::system_clock::time_point bornTimestamp() const; + + std::chrono::system_clock::time_point storeTimestamp() const; + int64_t getStoreTimestamp() const; + + std::string getStoreHost() const; + + int64_t getQueueOffset() const; + + int32_t getDeliveryAttempt() const; + + const std::string& receiptHandle() const; + + bool operator==(const MQMessageExt& other); + + friend class MessageAccessor; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessageQueue.h b/api/rocketmq/MQMessageQueue.h new file mode 100644 index 000000000..ec62b3d8f --- /dev/null +++ b/api/rocketmq/MQMessageQueue.h @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MQMessageQueue { +public: + MQMessageQueue() = default; + + MQMessageQueue(std::string topic, std::string broker_name, int queue_id) + : topic_(std::move(topic)), broker_name_(std::move(broker_name)), queue_id_(queue_id) { + } + + MQMessageQueue(const MQMessageQueue& other) { + this->operator=(other); + } + + MQMessageQueue(MQMessageQueue&& other) noexcept { + topic_ = std::move(other.topic_); + broker_name_ = std::move(other.broker_name_); + queue_id_ = other.queue_id_; + service_address_ = other.service_address_; + } + + MQMessageQueue& operator=(const MQMessageQueue& other) { + if (this == &other) { + return *this; + } + + topic_ = other.topic_; + broker_name_ = other.broker_name_; + queue_id_ = other.queue_id_; + service_address_ = other.service_address_; + return *this; + } + + const std::string& getTopic() const { + return topic_; + } + + void setTopic(const std::string& topic) { + topic_ = topic; + } + + const std::string& getBrokerName() const { + return broker_name_; + } + + void setBrokerName(const std::string& broker_name) { + broker_name_ = broker_name; + } + + int getQueueId() const { + return queue_id_; + } + + void setQueueId(int queue_id) { + queue_id_ = queue_id; + } + + bool operator!=(const MQMessageQueue& rhs) const { + return topic_ != rhs.topic_ || broker_name_ != rhs.broker_name_ || queue_id_ != rhs.queue_id_; + } + + bool operator==(const MQMessageQueue& mq) const { + return topic_ == mq.topic_ && broker_name_ == mq.broker_name_ && queue_id_ == mq.queue_id_; + } + + bool operator<(const MQMessageQueue& mq) const { + if (topic_ != mq.topic_) { + return topic_ < mq.topic_; + } + + if (broker_name_ != mq.broker_name_) { + return broker_name_ < mq.broker_name_; + } + + return queue_id_ < mq.queue_id_; + } + + operator bool() const { + return !topic_.empty() && !broker_name_.empty() && queue_id_ >= 0; + } + + int compareTo(const MQMessageQueue& mq) const { + if (this == &mq) { + return 0; + } + + if (this->operator<(mq)) { + return -1; + } + + if (mq.operator<(*this)) { + return 1; + } + + return 0; + } + + std::string simpleName() const { + return topic_ + "_" + broker_name_ + "_" + std::to_string(queue_id_); + } + + std::string toString() const { + std::stringstream ss; + ss << "MessageQueue [topic=" << topic_ << ", brokerName=" << broker_name_ << ", queueId=" << queue_id_ << "]"; + return ss.str(); + } + + template + friend H AbslHashValue(H h, const MQMessageQueue& mq) { + return H::combine(std::move(h), mq.topic_, mq.broker_name_, mq.queue_id_); + } + + const std::string& serviceAddress() const { + return service_address_; + } + + void serviceAddress(std::string service_address) { + service_address_ = std::move(service_address); + } + +private: + std::string topic_; + std::string broker_name_; + int queue_id_{0}; + + std::string service_address_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/include/MQSelector.h b/api/rocketmq/MQSelector.h similarity index 79% rename from include/MQSelector.h rename to api/rocketmq/MQSelector.h index 46e548c3e..0b31e55d8 100644 --- a/include/MQSelector.h +++ b/api/rocketmq/MQSelector.h @@ -1,30 +1,33 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef _MQSELECTOR_H_ -#define _MQSELECTOR_H_ -#include "MQMessage.h" -#include "MQMessageQueue.h" -#include "RocketMQClient.h" - -namespace rocketmq { -class ROCKETMQCLIENT_API MessageQueueSelector { - public: - virtual ~MessageQueueSelector() {} - virtual MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) = 0; -}; -} // namespace rocketmq -#endif +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "MQMessage.h" +#include "MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageQueueSelector { +public: + virtual ~MessageQueueSelector() = default; + + virtual MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MessageListener.h b/api/rocketmq/MessageListener.h new file mode 100644 index 000000000..5fdf59d2b --- /dev/null +++ b/api/rocketmq/MessageListener.h @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "MQMessageExt.h" +#include "MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class ConsumeMessageResult : uint8_t +{ + SUCCESS = 0, + FAILURE = 1 +}; + +enum class MessageListenerType : uint8_t +{ + STANDARD = 0, + FIFO = 1 +}; + +class MessageListener { +public: + virtual ~MessageListener() = default; + + virtual MessageListenerType listenerType() = 0; +}; + +class StandardMessageListener : public MessageListener { +public: + virtual ConsumeMessageResult consumeMessage(const std::vector& msgs) = 0; + + MessageListenerType listenerType() override { + return MessageListenerType::STANDARD; + } +}; + +class FifoMessageListener : public MessageListener { +public: + MessageListenerType listenerType() override { + return MessageListenerType::FIFO; + } + + virtual ConsumeMessageResult consumeMessage(const MQMessageExt& msgs) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MessageModel.h b/api/rocketmq/MessageModel.h new file mode 100644 index 000000000..2422d32b9 --- /dev/null +++ b/api/rocketmq/MessageModel.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "RocketMQ.h" + +#include + +ROCKETMQ_NAMESPACE_BEGIN + +enum class MessageModel : int8_t +{ + BROADCASTING, + CLUSTERING, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MessageType.h b/api/rocketmq/MessageType.h new file mode 100644 index 000000000..8d4ea261b --- /dev/null +++ b/api/rocketmq/MessageType.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class MessageType : int8_t +{ + NORMAL = 0, + FIFO = 1, + DELAY = 2, + TRANSACTION = 3, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/OffsetStore.h b/api/rocketmq/OffsetStore.h new file mode 100644 index 000000000..514d5da84 --- /dev/null +++ b/api/rocketmq/OffsetStore.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class OffsetStore { +public: + virtual ~OffsetStore() = default; + + virtual void load() = 0; + + virtual void updateOffset(const MQMessageQueue& message_queue, int64_t offset) = 0; + + virtual bool readOffset(const MQMessageQueue& message_queue, int64_t& offset) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/PullResult.h b/api/rocketmq/PullResult.h new file mode 100644 index 000000000..2e91ec4c8 --- /dev/null +++ b/api/rocketmq/PullResult.h @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "MQMessageExt.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PullResult { +public: + PullResult(int64_t min, int64_t max, int64_t next, std::vector messages) + : min_(min), max_(max), next_(next), messages_(std::move(messages)) { + } + + int64_t min() const { + return min_; + } + + int64_t max() const { + return max_; + } + + int64_t next() const { + return next_; + } + + const std::vector& messages() const { + return messages_; + } + +private: + int64_t min_; + int64_t max_; + int64_t next_; + std::vector messages_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/RocketMQ.h b/api/rocketmq/RocketMQ.h new file mode 100644 index 000000000..bc8e10ba8 --- /dev/null +++ b/api/rocketmq/RocketMQ.h @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#ifndef ROCKETMQ_NAMESPACE +#define ROCKETMQ_NAMESPACE rocketmq +#endif + +#define ROCKETMQ_NAMESPACE_BEGIN namespace ROCKETMQ_NAMESPACE { +#define ROCKETMQ_NAMESPACE_END } \ No newline at end of file diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h new file mode 100644 index 000000000..b491cc629 --- /dev/null +++ b/api/rocketmq/SendResult.h @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class SendStatus : int8_t +{ + SEND_OK, + + // Deprecated. Remove in the next release. + SEND_FLUSH_DISK_TIMEOUT, + + // Deprecated. Remove in the next release. + SEND_FLUSH_SLAVE_TIMEOUT, + + // Deprecated. Remove in the next release. + SEND_SLAVE_NOT_AVAILABLE +}; + +class SendResult { +public: + SendResult() = default; + + SendResult(const SendStatus& send_status, std::string msg_id, MQMessageQueue message_queue, long long queue_offset, + std::string region_id) + : send_status_(send_status), message_id_(std::move(msg_id)), message_queue_(std::move(message_queue)), + queue_offset_(queue_offset), region_id_(std::move(region_id)) { + } + + SendResult(const SendResult& other) { + this->operator=(other); + } + + SendResult& operator=(const SendResult& other) { + if (this == &other) { + return *this; + } + + send_status_ = other.send_status_; + message_id_ = other.message_id_; + message_queue_ = other.message_queue_; + queue_offset_ = other.queue_offset_; + transaction_id_ = other.transaction_id_; + region_id_ = other.region_id_; + return *this; + } + + const std::string& getMsgId() const { + return message_id_; + } + + void setMsgId(const std::string& msg_id) { + message_id_ = msg_id; + } + + const std::string& getRegionId() const { + return region_id_; + } + + void setRegionId(const std::string& region_id) { + region_id_ = region_id; + } + + SendStatus getSendStatus() const { + return send_status_; + } + + void setSendStatus(const SendStatus& send_status) { + send_status_ = send_status; + } + + MQMessageQueue& getMessageQueue() const { + return message_queue_; + } + + void setMessageQueue(const MQMessageQueue& message_queue) { + message_queue_ = message_queue; + } + + long long getQueueOffset() const { + return queue_offset_; + } + + void setQueueOffset(long long queue_offset) { + queue_offset_ = queue_offset; + } + + const std::string& getTransactionId() const { + return transaction_id_; + } + + void setTransactionId(const std::string& transaction_id) { + transaction_id_ = transaction_id; + } + + const std::string& traceContext() const { + return trace_context_; + } + + void traceContext(std::string trace_context) { + trace_context_ = std::move(trace_context); + } + + operator bool() { + return !message_id_.empty(); + } + +private: + SendStatus send_status_{SendStatus::SEND_OK}; + std::string message_id_; + mutable MQMessageQueue message_queue_; + long long queue_offset_{0}; + std::string transaction_id_; + std::string region_id_; + std::string trace_context_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/State.h b/api/rocketmq/State.h new file mode 100644 index 000000000..8c8ca529b --- /dev/null +++ b/api/rocketmq/State.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum State : uint8_t +{ + CREATED = 0, + STARTING = 1, + STARTED = 2, + STOPPING = 3, + STOPPED = 4 +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Transaction.h b/api/rocketmq/Transaction.h new file mode 100644 index 000000000..9a135a68f --- /dev/null +++ b/api/rocketmq/Transaction.h @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Transaction { +public: + Transaction() = default; + + virtual ~Transaction() = default; + + virtual bool commit() = 0; + + virtual bool rollback() = 0; + + virtual std::string messageId() const = 0; + + virtual std::string transactionId() const = 0; +}; + +using TransactionPtr = std::unique_ptr; + +enum class TransactionState : int8_t +{ + COMMIT = 0, + ROLLBACK = 1, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel new file mode 100644 index 000000000..28e0699af --- /dev/null +++ b/bazel/BUILD.bazel @@ -0,0 +1,16 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/bazel/curl.bzl b/bazel/curl.bzl new file mode 100644 index 000000000..b83d054b8 --- /dev/null +++ b/bazel/curl.bzl @@ -0,0 +1,201 @@ +# Copyright 2018, OpenCensus Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Compiler options for building libcurl. + +BASE_CURL_COPTS = [ + # Disable everything else except HTTP protocol. + "-DHTTP_ONLY=1", + "-DENABLE_IPV6=1", + "-DGETHOSTNAME_TYPE_ARG2=size_t", + "-DGETSERVBYPORT_R_ARGS=6", + "-DGETSERVBYPORT_R_BUFSIZE=4096", + "-DHAVE_ALARM=1", + "-DHAVE_ALLOCA_H=1", + "-DHAVE_ARPA_INET_H=1", + "-DHAVE_ARPA_TFTP_H=1", + "-DHAVE_ASSERT_H=1", + "-DHAVE_BASENAME=1", + "-DHAVE_BOOL_T=1", + "-DHAVE_CLOCK_GETTIME_MONOTONIC=1", + "-DHAVE_CONNECT=1", + "-DHAVE_DLFCN_H=1", + "-DHAVE_ENGINE_LOAD_BUILTIN_ENGINES=1", + "-DHAVE_ERRNO_H=1", + "-DHAVE_FCNTL=1", + "-DHAVE_FCNTL_H=1", + "-DHAVE_FCNTL_O_NONBLOCK=1", + "-DHAVE_FDOPEN=1", + "-DHAVE_FREEADDRINFO=1", + "-DHAVE_FREEIFADDRS=1", + "-DHAVE_FSETXATTR=1", + "-DHAVE_FSETXATTR_5=1", + "-DHAVE_FTRUNCATE=1", + "-DHAVE_GAI_STRERROR=1", + "-DHAVE_GETADDRINFO=1", + "-DHAVE_GETADDRINFO_THREADSAFE=1", + "-DHAVE_GETEUID=1", + "-DHAVE_GETHOSTBYADDR=1", + "-DHAVE_GETHOSTBYADDR_R=1", + "-DHAVE_GETHOSTBYADDR_R_8=1", + "-DHAVE_GETHOSTBYNAME=1", + "-DHAVE_GETHOSTBYNAME_R=1", + "-DHAVE_GETHOSTBYNAME_R_6=1", + "-DHAVE_GETHOSTNAME=1", + "-DHAVE_GETIFADDRS=1", + "-DHAVE_GETNAMEINFO=1", + "-DHAVE_GETPPID=1", + "-DHAVE_GETPWUID=1", + "-DHAVE_GETPWUID_R=1", + "-DHAVE_GETRLIMIT=1", + "-DHAVE_GETSERVBYPORT_R=1", + "-DHAVE_GETTIMEOFDAY=1", + "-DHAVE_GMTIME_R=1", + "-DHAVE_IFADDRS_H=1", + "-DHAVE_IF_NAMETOINDEX=1", + "-DHAVE_INET_NTOP=1", + "-DHAVE_INET_PTON=1", + "-DHAVE_INTTYPES_H=1", + "-DHAVE_IOCTL=1", + "-DHAVE_IOCTL_FIONBIO=1", + "-DHAVE_IOCTL_SIOCGIFADDR=1", + "-DHAVE_LIBGEN_H=1", + "-DHAVE_LL=1", + "-DHAVE_LOCALE_H=1", + "-DHAVE_LOCALTIME_R=1", + "-DHAVE_LONGLONG=1", + "-DHAVE_MALLOC_H=1", + "-DHAVE_MEMORY_H=1", + "-DHAVE_NETDB_H=1", + "-DHAVE_NETINET_IN_H=1", + "-DHAVE_NETINET_TCP_H=1", + "-DHAVE_NET_IF_H=1", + "-DHAVE_PIPE=1", + "-DHAVE_POLL=1", + "-DHAVE_POLL_FINE=1", + "-DHAVE_POLL_H=1", + "-DHAVE_POSIX_STRERROR_R=1", + "-DHAVE_PTHREAD_H=1", + "-DHAVE_PWD_H=1", + "-DHAVE_RECV=1", + "-DHAVE_SELECT=1", + "-DHAVE_SEND=1", + "-DHAVE_SETJMP_H=1", + "-DHAVE_SETLOCALE=1", + "-DHAVE_SETRLIMIT=1", + "-DHAVE_SETSOCKOPT=1", + "-DHAVE_SGTTY_H=1", + "-DHAVE_SIGACTION=1", + "-DHAVE_SIGINTERRUPT=1", + "-DHAVE_SIGNAL=1", + "-DHAVE_SIGNAL_H=1", + "-DHAVE_SIGSETJMP=1", + "-DHAVE_SIG_ATOMIC_T=1", + "-DHAVE_SOCKADDR_IN6_SIN6_SCOPE_ID=1", + "-DHAVE_SOCKET=1", + "-DHAVE_SOCKETPAIR=1", + "-DHAVE_STDBOOL_H=1", + "-DHAVE_STDINT_H=1", + "-DHAVE_STDIO_H=1", + "-DHAVE_STDLIB_H=1", + "-DHAVE_STRCASECMP=1", + "-DHAVE_STRDUP=1", + "-DHAVE_STRERROR_R=1", + "-DHAVE_STRINGS_H=1", + "-DHAVE_STRING_H=1", + "-DHAVE_STRNCASECMP=1", + "-DHAVE_STRSTR=1", + "-DHAVE_STRTOK_R=1", + "-DHAVE_STRTOLL=1", + "-DHAVE_STRUCT_SOCKADDR_STORAGE=1", + "-DHAVE_STRUCT_TIMEVAL=1", + "-DHAVE_SYS_IOCTL_H=1", + "-DHAVE_SYS_PARAM_H=1", + "-DHAVE_SYS_POLL_H=1", + "-DHAVE_SYS_RESOURCE_H=1", + "-DHAVE_SYS_SELECT_H=1", + "-DHAVE_SYS_SOCKET_H=1", + "-DHAVE_SYS_STAT_H=1", + "-DHAVE_SYS_TIME_H=1", + "-DHAVE_SYS_TYPES_H=1", + "-DHAVE_SYS_UIO_H=1", + "-DHAVE_SYS_UN_H=1", + "-DHAVE_SYS_WAIT_H=1", + "-DHAVE_SYS_XATTR_H=1", + "-DHAVE_TERMIOS_H=1", + "-DHAVE_TERMIO_H=1", + "-DHAVE_TIME_H=1", + "-DHAVE_UNISTD_H=1", + "-DHAVE_UTIME=1", + "-DHAVE_UTIMES=1", + "-DHAVE_UTIME_H=1", + "-DHAVE_VARIADIC_MACROS_C99=1", + "-DHAVE_VARIADIC_MACROS_GCC=1", + "-DHAVE_WRITABLE_ARGV=1", + "-DHAVE_WRITEV=1", + "-DRECV_TYPE_ARG1=int", + "-DRECV_TYPE_ARG2=void*", + "-DRECV_TYPE_ARG3=size_t", + "-DRECV_TYPE_ARG4=int", + "-DRECV_TYPE_RETV=ssize_t", + "-DRETSIGTYPE=void", + "-DSELECT_QUAL_ARG5=", + "-DSELECT_TYPE_ARG1=int", + "-DSELECT_TYPE_ARG234=fd_set*", + "-DSELECT_TYPE_RETV=int", + "-DSEND_QUAL_ARG2=const", + "-DSEND_TYPE_ARG1=int", + "-DSEND_TYPE_ARG2=void*", + "-DSEND_TYPE_ARG3=size_t", + "-DSEND_TYPE_ARG4=int", + "-DSEND_TYPE_RETV=ssize_t", + "-DSIZEOF_CURL_OFF_T=8", + "-DSIZEOF_INT=4", + "-DSIZEOF_LONG=8", + "-DSIZEOF_OFF_T=8", + "-DSIZEOF_SHORT=2", + "-DSIZEOF_SIZE_T=8", + "-DSIZEOF_TIME_T=8", + "-DSTDC_HEADERS=1", + "-DSTRERROR_R_TYPE_ARG3=size_t", + "-DTIME_WITH_SYS_TIME=1", + "-DUSE_THREADS_POSIX=1", + "-DUSE_UNIX_SOCKETS=1", + + # Extra defines needed by curl + "-DBUILDING_LIBCURL", + "-DCURL_HIDDEN_SYMBOLS", +] + +LINUX_CURL_COPTS = [ + "-DHAVE_LINUX_TCP_H=1", + "-DHAVE_MSG_NOSIGNAL=1", +] + +CURL_COPTS = select({ + "//:windows": [ + # Disable everything else except HTTP protocol. + "/DHTTP_ONLY=1", + "/DCURL_STATICLIB", + "/DWIN32", + "/DBUILDING_LIBCURL", + "/DUSE_WIN32_IDN", + "/DWANT_IDN_PROTOTYPES", + "/DUSE_IPV6", + "/DUSE_WINDOWS_SSPI", + "/DUSE_SCHANNEL", + ], + "//:osx": BASE_CURL_COPTS, + "//conditions:default": BASE_CURL_COPTS + LINUX_CURL_COPTS, +}) diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl new file mode 100644 index 000000000..d593bee28 --- /dev/null +++ b/bazel/rocketmq_deps.bzl @@ -0,0 +1,140 @@ +"""Load dependencies needed to compile and test the RocketMQ library as a 3rd-party consumer.""" +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +def rocketmq_deps(): + """Loads dependencies need to compile and test the RocketMQ library.""" + native.bind( + name = "opentelementry_api", + actual = "@com_github_opentelemetry//api:api", + ) + + if "com_google_googletest" not in native.existing_rules(): + http_archive( + name = "com_google_googletest", + sha256 = "b4870bf121ff7795ba20d20bcdd8627b8e088f2d1dab299a031c1034eddc93d5", + strip_prefix = "googletest-release-1.11.0", + urls = [ + "https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz", + ], + ) + + if "com_github_gulrak_filesystem" not in native.existing_rules(): + http_archive( + name = "com_github_gulrak_filesystem", + strip_prefix = "filesystem-1.5.0", + sha256 = "eb6f3b0739908ad839cde68885d70e7324db191b9fad63d9915beaa40444d9cb", + urls = [ + "https://github.com/gulrak/filesystem/archive/v1.5.0.tar.gz", + ], + build_file = "@org_apache_rocketmq//third_party:filesystem.BUILD", + ) + + if "com_github_gabime_spdlog" not in native.existing_rules(): + http_archive( + name = "com_github_gabime_spdlog", + strip_prefix = "spdlog-1.9.2", + sha256 = "6fff9215f5cb81760be4cc16d033526d1080427d236e86d70bb02994f85e3d38", + urls = [ + "https://github.com/gabime/spdlog/archive/refs/tags/v1.9.2.tar.gz", + ], + build_file = "@org_apache_rocketmq//third_party:spdlog.BUILD", + ) + + if "com_github_fmtlib_fmt" not in native.existing_rules(): + http_archive( + name = "com_github_fmtlib_fmt", + strip_prefix = "fmt-8.0.1", + sha256 = "b06ca3130158c625848f3fb7418f235155a4d389b2abc3a6245fb01cb0eb1e01", + urls = [ + "https://github.com/fmtlib/fmt/archive/refs/tags/8.0.1.tar.gz", + ], + build_file = "@org_apache_rocketmq//third_party:fmtlib.BUILD", + ) + + if "com_google_protobuf" not in native.existing_rules(): + http_archive( + name = "com_google_protobuf", + sha256 = "36f81e03a0702f8f935fffd5a486dac1c0fc6d4bae1cd02c7a32448ad6e63bcb", + strip_prefix = "protobuf-3.17.2", + urls = [ + "https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.17.2.tar.gz", + ], + ) + + if "rules_proto_grpc" not in native.existing_rules(): + http_archive( + name = "rules_proto_grpc", + sha256 = "7954abbb6898830cd10ac9714fbcacf092299fda00ed2baf781172f545120419", + strip_prefix = "rules_proto_grpc-3.1.1", + urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/archive/3.1.1.tar.gz"], + ) + + if "com_google_absl" not in native.existing_rules(): + http_archive( + name = "com_google_absl", + sha256 = "59b862f50e710277f8ede96f083a5bb8d7c9595376146838b9580be90374ee1f", + strip_prefix = "abseil-cpp-20210324.2", + urls = [ + "https://github.com/abseil/abseil-cpp/archive/refs/tags/20210324.2.tar.gz", + ], + ) + + if "com_github_grpc_grpc" not in native.existing_rules(): + http_archive( + name = "com_github_grpc_grpc", + strip_prefix = "grpc-1.39.0", + sha256 = "b16992aa1c949c10d5d5ce2a62f9d99fa7de77da2943e643fb66dcaf075826d6", + urls = ["https://github.com/grpc/grpc/archive/v1.39.0.tar.gz"], + ) + + if "io_opentelemetry_cpp" not in native.existing_rules(): + http_archive( + name = "io_opentelemetry_cpp", + sha256 = "24ba9b83f6cb8ba717ae30ebc570f5e8d0569008aee3c8b9a7ce6e4e1a5115b7", + strip_prefix = "opentelemetry-cpp-1.0.0-rc4", + urls = [ + "https://github.com/open-telemetry/opentelemetry-cpp/archive/refs/tags/v1.0.0-rc4.tar.gz", + ], + ) + + maybe( + http_archive, + name = "com_github_opentelemetry_proto", + build_file = "@io_opentelemetry_cpp//bazel:opentelemetry_proto.BUILD", + sha256 = "08f090570e0a112bfae276ba37e9c45bf724b64d902a7a001db33123b840ebd6", + strip_prefix = "opentelemetry-proto-0.6.0", + urls = [ + "https://github.com/open-telemetry/opentelemetry-proto/archive/v0.6.0.tar.gz", + ], + ) + + maybe( + http_archive, + name = "asio", + sha256 = "c864363205f78768c795ba14a9989200075e732f877ddef01a19237c2eccf44b", + build_file = "@org_apache_rocketmq//third_party:asio.BUILD", + strip_prefix = "asio-1.18.2", + urls = [ + "https://github.com/lizhanhui/asio/archive/refs/tags/v1.18.2.tar.gz", + ], + ) + + maybe( + http_archive, + name = "com_github_yhirose_cpp_httplib", + sha256 = "0ff62e28eb0f6e563178d44b77c94dddb8702141d83dd34b83cb046399c2b1d5", + build_file = "@org_apache_rocketmq//third_party:cpp_httplib.BUILD", + strip_prefix = "cpp-httplib-0.9.4", + urls = ["https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.9.4.tar.gz"], + ) + + maybe( + http_archive, + name = "com_google_googleapis", + sha256 = "e89f15d54b0ddab0cd41d18cb2299e5447db704e2b05ff141cb1769170671466", + urls = [ + "https://github.com/googleapis/googleapis/archive/af7fb72df59a814221b123a4d1acb3f6c3e6cc95.zip" + ], + strip_prefix = "googleapis-af7fb72df59a814221b123a4d1acb3f6c3e6cc95", + ) \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100755 index be500a6cd..000000000 --- a/build.sh +++ /dev/null @@ -1,532 +0,0 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -basepath=$( - cd $(dirname $0) - pwd -) -declare down_dir="${basepath}/tmp_down_dir" -declare build_dir="${basepath}/tmp_build_dir" -declare packet_dir="${basepath}/tmp_packet_dir" -declare install_lib_dir="${basepath}/bin" -declare static_package_dir="${basepath}/tmp_static_package_dir" -declare fname_openssl="openssl*.tar.gz" - -declare fname_libevent="libevent*.zip" -declare fname_jsoncpp="jsoncpp*.zip" -declare fname_boost="boost*.tar.gz" -declare fname_openssl_down="openssl-1.1.1d.tar.gz" -declare fname_libevent_down="release-2.1.11-stable.zip" -declare fname_jsoncpp_down="0.10.7.zip" -declare fname_boost_down="1.78.0/boost_1_78_0.tar.gz" - -PrintParams() { - echo "=========================================one key build help============================================" - echo "sh build.sh [no build libevent:noEvent] [no build json:noJson] [no build boost:noBoost] [ execution test:test]" - echo "usage: sh build.sh noJson noEvent noBoost test" - echo "=========================================one key build help============================================" - echo "" -} - -if test "$(uname)" = "Linux"; then - declare cpu_num=$(cat /proc/cpuinfo | grep "processor" | wc -l) -elif test "$(uname)" = "Darwin" ; then - declare cpu_num=$(sysctl -n machdep.cpu.thread_count) -fi - -declare need_build_openssl=1 -declare need_build_libevent=1 -declare need_build_jsoncpp=1 -declare need_build_boost=1 -declare enable_asan=0 -declare enable_lsan=0 -declare verbose=1 -declare codecov=0 -declare debug=0 -declare test=0 - -pasres_arguments() { - for var in "$@"; do - case "$var" in - noOpenSSL) - need_build_openssl=0 - ;; - noEvent) - need_build_libevent=0 - ;; - noJson) - need_build_jsoncpp=0 - ;; - noBoost) - need_build_boost=0 - ;; - asan) - enable_asan=1 - ;; - lsan) - enable_lsan=1 - ;; - noVerbose) - verbose=0 - ;; - codecov) - codecov=1 - ;; - debug) - debug=1 - ;; - test) - test=1 - ;; - esac - done - -} -pasres_arguments $@ - -PrintParams() { - echo "###########################################################################" - if [ $need_build_openssl -eq 0 ]; then - echo "no need build openssl lib" - else - echo "need build openssl lib" - fi - if [ $need_build_jsoncpp -eq 0 ]; then - echo "no need build jsoncpp lib" - else - echo "need build jsoncpp lib" - fi - if [ $need_build_libevent -eq 0 ]; then - echo "no need build libevent lib" - else - echo "need build libevent lib" - fi - if [ $need_build_boost -eq 0 ]; then - echo "no need build boost lib" - else - echo "need build boost lib" - fi - if [ $enable_asan -eq 1 ]; then - echo "enable asan reporting" - else - echo "disable asan reporting" - fi - if [ $enable_lsan -eq 1 ]; then - echo "enable lsan reporting" - else - echo "disable lsan reporting" - fi - if [ $verbose -eq 0 ]; then - echo "no need print detail logs" - else - echo "need print detail logs" - fi - if [ $codecov -eq 1 ]; then - echo "run unit tests with code coverage" - else - echo "run unit tests without code coverage" - fi - if [ $debug -eq 1 ]; then - echo "enable debug" - else - echo "disable debug" - fi - if [ $test -eq 1 ]; then - echo "build unit tests" - else - echo "without build unit tests" - fi - - echo "###########################################################################" - echo "" -} - -Prepare() { - if [ -e ${down_dir} ]; then - echo "${down_dir} exists" - #cd ${down_dir} - #ls |grep -v ${fname_libevent} |grep -v ${fname_jsoncpp} | grep -v ${fname_boost} |xargs rm -rf - else - mkdir -p ${down_dir} - fi - - cd ${basepath} - if [ -e ${fname_openssl} ]; then - mv -f ${basepath}/${fname_openssl} ${down_dir} - fi - - if [ -e ${fname_libevent} ]; then - mv -f ${basepath}/${fname_libevent} ${down_dir} - fi - - if [ -e ${fname_jsoncpp} ]; then - mv -f ${basepath}/${fname_jsoncpp} ${down_dir} - fi - - if [ -e ${fname_boost} ]; then - mv -f ${basepath}/${fname_boost} ${down_dir} - fi - - if [ -e ${build_dir} ]; then - echo "${build_dir} exists" - #rm -rf ${build_dir}/* - else - mkdir -p ${build_dir} - fi - - if [ -e ${packet_dir} ]; then - echo "${packet_dir} exists" - #rm -rf ${packet_dir}/* - else - mkdir -p ${packet_dir} - fi - - if [ -e ${install_lib_dir} ]; then - echo "${install_lib_dir} exists" - else - mkdir -p ${install_lib_dir} - fi -} - -BuildOpenSSL() { - if [ $need_build_openssl -eq 0 ]; then - echo "no need build openssl lib" - return 0 - fi - - cd ${down_dir} - if [ -e ${fname_openssl} ]; then - echo "${fname_openssl} exists" - else - wget https://www.openssl.org/source/old/1.1.1/${fname_openssl_down} -O ${fname_openssl_down} --no-check-certificate - fi - tar -zxvf ${fname_openssl} &> unzipopenssl.txt - if [ $? -ne 0 ]; then - exit 1 - fi - - openssl_dir=$(ls | grep ^openssl | grep .*[^gz]$) - cd ${openssl_dir} - if [ $? -ne 0 ]; then - exit 1 - fi - echo "build openssl static #####################" - if [ $verbose -eq 0 ]; then - ./config shared CFLAGS=-fPIC CPPFLAGS=-fPIC --prefix=${install_lib_dir} --openssldir=${install_lib_dir} &> opensslconfig.txt - else - ./config shared CFLAGS=-fPIC CPPFLAGS=-fPIC --prefix=${install_lib_dir} --openssldir=${install_lib_dir} - fi - if [ $? -ne 0 ]; then - exit 1 - fi - if [ $verbose -eq 0 ]; then - echo "build openssl without detail log." - make depend &> opensslbuild.txt - make -j $cpu_num &> opensslbuild.txt - else - make depend - make -j $cpu_num - fi - if [ $? -ne 0 ]; then - exit 1 - fi - make install - echo "build openssl success." -} - -BuildLibevent() { - if [ $need_build_libevent -eq 0 ]; then - echo "no need build libevent lib" - return 0 - fi - - cd ${down_dir} - if [ -e ${fname_libevent} ]; then - echo "${fname_libevent} exists" - else - wget https://github.com/libevent/libevent/archive/${fname_libevent_down} -O libevent-${fname_libevent_down} - fi - unzip -o ${fname_libevent} &> unziplibevent.txt - if [ $? -ne 0 ]; then - exit 1 - fi - - libevent_dir=$(ls | grep ^libevent | grep .*[^zip^txt]$) - cd ${libevent_dir} - if [ $? -ne 0 ]; then - exit 1 - fi - ./autogen.sh - if [ $? -ne 0 ]; then - exit 1 - fi - echo "build libevent static #####################" - if [ $verbose -eq 0 ]; then - ./configure --enable-static=yes --enable-shared=no CFLAGS="-fPIC -I${install_lib_dir}/include" CPPFLAGS="-fPIC -I${install_lib_dir}/include" LDFLAGS="-L${install_lib_dir}/lib" --prefix=${install_lib_dir} &> libeventconfig.txt - else - ./configure --enable-static=yes --enable-shared=no CFLAGS="-fPIC -I${install_lib_dir}/include" CPPFLAGS="-fPIC -I${install_lib_dir}/include" LDFLAGS="-L${install_lib_dir}/lib" --prefix=${install_lib_dir} - fi - if [ $? -ne 0 ]; then - exit 1 - fi - if [ $verbose -eq 0 ]; then - echo "build libevent without detail log." - make -j $cpu_num &> libeventbuild.txt - else - make -j $cpu_num - fi - if [ $? -ne 0 ]; then - exit 1 - fi - make install - echo "build linevent success." -} - -BuildJsonCPP() { - if [ $need_build_jsoncpp -eq 0 ]; then - echo "no need build jsoncpp lib" - return 0 - fi - - cd ${down_dir} - - if [ -e ${fname_jsoncpp} ]; then - echo "${fname_jsoncpp} exists" - else - wget https://github.com/open-source-parsers/jsoncpp/archive/${fname_jsoncpp_down} -O jsoncpp-${fname_jsoncpp_down} - fi - unzip -o ${fname_jsoncpp} &> unzipjsoncpp.txt - if [ $? -ne 0 ]; then - exit 1 - fi - jsoncpp_dir=$(ls | grep ^jsoncpp | grep .*[^zip]$) - cd ${jsoncpp_dir} - if [ $? -ne 0 ]; then - exit 1 - fi - mkdir build - cd build - echo "build jsoncpp static ######################" - if [ $verbose -eq 0 ]; then - echo "build jsoncpp without detail log." - cmake .. -DCMAKE_CXX_FLAGS=-fPIC -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${install_lib_dir} &> jsoncppbuild.txt - else - cmake .. -DCMAKE_CXX_FLAGS=-fPIC -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${install_lib_dir} - fi - if [ $? -ne 0 ]; then - exit 1 - fi - if [ $verbose -eq 0 ]; then - make -j $cpu_num &> jsoncppbuild.txt - else - make -j $cpu_num - fi - if [ $? -ne 0 ]; then - exit 1 - fi - make install - echo "build jsoncpp success." - if [ ! -f ${install_lib_dir}/lib/libjsoncpp.a ]; then - echo " ./bin/lib directory is not libjsoncpp.a" - cp ${install_lib_dir}/lib/x86_64-linux-gnu/libjsoncpp.a ${install_lib_dir}/lib/ - fi -} - -BuildBoost() { - if [ $need_build_boost -eq 0 ]; then - echo "no need build boost lib" - return 0 - fi - - cd ${down_dir} - if [ -e ${fname_boost} ]; then - echo "${fname_boost} exists" - else - wget http://sourceforge.net/projects/boost/files/boost/${fname_boost_down} --no-check-certificate - fi - tar -zxvf ${fname_boost} &> unzipboost.txt - boost_dir=$(ls | grep ^boost | grep .*[^gz]$) - cd ${boost_dir} - if [ $? -ne 0 ]; then - exit 1 - fi - ./bootstrap.sh - if [ $? -ne 0 ]; then - exit 1 - fi - echo "build boost static #####################" - pwd - if [ $verbose -eq 0 ]; then - echo "build boost without detail log." - ./b2 -j$cpu_num cflags=-fPIC cxxflags=-fPIC --with-atomic --with-thread --with-system --with-chrono --with-date_time --with-log --with-regex --with-serialization --with-filesystem --with-locale --with-iostreams threading=multi link=static release install --prefix=${install_lib_dir} &> boostbuild.txt - else - ./b2 -j$cpu_num cflags=-fPIC cxxflags=-fPIC --with-atomic --with-thread --with-system --with-chrono --with-date_time --with-log --with-regex --with-serialization --with-filesystem --with-locale --with-iostreams threading=multi link=static release install --prefix=${install_lib_dir} - fi - if [ $? -ne 0 ]; then - exit 1 - fi -} - -BuildRocketMQClient() { - cd ${build_dir} - echo "============start to build rocketmq client cpp.=========" - local ROCKETMQ_CMAKE_FLAG="" - if [ $test -eq 1 ]; then - if [ $codecov -eq 1 ]; then - ROCKETMQ_CMAKE_FLAG=$ROCKETMQ_CMAKE_FLAG" -DRUN_UNIT_TEST=ON -DCODE_COVERAGE=ON" - else - ROCKETMQ_CMAKE_FLAG=$ROCKETMQ_CMAKE_FLAG" -DRUN_UNIT_TEST=ON -DCODE_COVERAGE=OFF" - fi - else - ROCKETMQ_CMAKE_FLAG=$ROCKETMQ_CMAKE_FLAG" -DRUN_UNIT_TEST=OFF -DCODE_COVERAGE=OFF" - fi - if [ $enable_asan -eq 1 ]; then - ROCKETMQ_CMAKE_FLAG=$ROCKETMQ_CMAKE_FLAG" -DENABLE_ASAN=ON" - else - ROCKETMQ_CMAKE_FLAG=$ROCKETMQ_CMAKE_FLAG" -DENABLE_ASAN=OFF" - fi - if [ $enable_lsan -eq 1 ]; then - ROCKETMQ_CMAKE_FLAG=$ROCKETMQ_CMAKE_FLAG" -DENABLE_LSAN=ON" - else - ROCKETMQ_CMAKE_FLAG=$ROCKETMQ_CMAKE_FLAG" -DENABLE_LSAN=OFF" - fi - if [ $debug -eq 1 ]; then - ROCKETMQ_CMAKE_FLAG=$ROCKETMQ_CMAKE_FLAG" -DCMAKE_BUILD_TYPE=Debug" - else - ROCKETMQ_CMAKE_FLAG=$ROCKETMQ_CMAKE_FLAG" -DCMAKE_BUILD_TYPE=Release" - fi - cmake .. $ROCKETMQ_CMAKE_FLAG - if [ $verbose -eq 0 ]; then - echo "build rocketmq without detail log." - make -j $cpu_num &> buildclient.txt - else - make -j $cpu_num - fi - if [ $? -ne 0 ]; then - echo "build error....." - exit 1 - fi - #sudo make install - PackageRocketMQStatic -} - -BuildGoogleTest() { - if [ $test -eq 0 ]; then - echo "no need build google test lib" - return 0 - fi - if [ -f ./bin/lib/libgtest.a ]; then - echo "GTest already exists, no need build" - return 0 - fi - cd ${down_dir} - if [ -e release-1.8.1.tar.gz ]; then - echo "${fname_boost} exists" - else - wget https://github.com/abseil/googletest/archive/release-1.8.1.tar.gz - fi - if [ ! -d "googletest-release-1.8.1" ]; then - tar -zxvf release-1.8.1.tar.gz &> googletest.txt - fi - cd googletest-release-1.8.1 - mkdir -p build - cd build - echo "build googletest static #####################" - if [ $verbose -eq 0 ]; then - echo "build googletest without detail log." - cmake .. -DCMAKE_CXX_FLAGS=-fPIC -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${install_lib_dir} &> googletestbuild.txt - else - cmake .. -DCMAKE_CXX_FLAGS=-fPIC -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=${install_lib_dir} - fi - if [ $? -ne 0 ]; then - exit 1 - fi - if [ $verbose -eq 0 ]; then - make -j $cpu_num &> gtestbuild.txt - else - make -j $cpu_num - fi - if [ $? -ne 0 ]; then - exit 1 - fi - make install - - if [ ! -f ${install_lib_dir}/lib/libgtest.a ]; then - echo " ./bin/lib directory is not libgtest.a" - cp ${install_lib_dir}/lib64/lib* ${install_lib_dir}/lib - fi -} - -ExecutionTesting() { - if [ $test -eq 0 ]; then - echo "Build success without executing unit tests." - return 0 - fi - echo "############# unit test start ###########" - cd ${build_dir} - if [ $verbose -eq 0 ]; then - ctest - else - ctest -V - fi - if [ $? -ne 0 ]; then - echo "############# unit test failed ###########" - exit 1 - fi - echo "############# unit test finish ###########" -} - -PackageRocketMQStatic() { - echo "############# Start package static rocketmq library. #############" - if test "$(uname)" = "Linux"; then - #packet libevent,jsoncpp,boost,rocketmq,Signature to one librocketmq.a - cp -f ${basepath}/libs/signature/lib/libSignature.a ${install_lib_dir}/lib - ar -M <${basepath}/package_rocketmq.mri - cp -f librocketmq.a ${install_lib_dir} - elif test "$(uname)" = "Darwin" ; then - mkdir -p ${static_package_dir} - cd ${static_package_dir} - cp -f ${basepath}/libs/signature/lib/libSignature.a . - cp -f ${install_lib_dir}/lib/lib*.a . - cp -f ${install_lib_dir}/librocketmq.a . - echo "Md5 Hash RocketMQ Before:" - md5sum librocketmq.a - local dir=`ls *.a | grep -E 'gtest|gmock'` - for i in $dir - do - rm -rf $i - done - libtool -no_warning_for_no_symbols -static -o librocketmq.a *.a - echo "Md5 Hash RocketMQ After:" - md5sum librocketmq.a - echo "Try to copy $(pwd)/librocketmq.a to ${install_lib_dir}/" - cp -f librocketmq.a ${install_lib_dir}/ - cd ${basepath} - rm -rf ${static_package_dir} - fi - echo "############# Package static rocketmq library success.#############" -} - -PrintParams -Prepare -BuildOpenSSL -BuildLibevent -BuildJsonCPP -BuildBoost -BuildGoogleTest -BuildRocketMQClient -ExecutionTesting diff --git a/ci/build.sh b/ci/build.sh new file mode 100755 index 000000000..3b48b7d66 --- /dev/null +++ b/ci/build.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +CI_DIR=$(dirname "$0") +BASE_DIR=$(pwd $CI_DIR/..) + +cd $BASE_DIR +mkdir _build +cd _build +cmake .. +make -j $(nproc) +ctest \ No newline at end of file diff --git a/cmake/FindJsoncpp.cmake b/cmake/FindJsoncpp.cmake deleted file mode 100755 index 963211811..000000000 --- a/cmake/FindJsoncpp.cmake +++ /dev/null @@ -1,95 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Find jsoncpp -# -# Find the jsoncpp includes and library -# -# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH -# -# -*- cmake -*- -# - Find JSONCpp -# Find the JSONCpp includes and library -# This module defines -# JSONCPP_INCLUDE_DIRS, where to find json.h, etc. -# JSONCPP_LIBRARIES, the libraries needed to use jsoncpp. -# JSONCPP_FOUND, If false, do not try to use jsoncpp. -# also defined, but not for general use are -# JSONCPP_LIBRARIES, where to find the jsoncpp library. - -# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES -if( JSONCPP_USE_STATIC_LIBS ) - set(_jsoncpp_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES :${CMAKE_FIND_LIBRARY_SUFFIXES}) - if(WIN32) - list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .lib .a) - else() - set(CMAKE_FIND_LIBRARY_SUFFIXES .a) - endif() -else() - set(_jsoncpp_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES :${CMAKE_FIND_LIBRARY_SUFFIXES}) - if(WIN32) - list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .dll .so) - else() - set(CMAKE_FIND_LIBRARY_SUFFIXES .so) - endif() -endif() - -FIND_PATH(JSONCPP_INCLUDE_DIRS -NAMES - json.h - json/json.h -PATHS - ${CMAKE_SOURCE_DIR}/bin/include - C:/jsoncpp/include - ${CMAKE_SOURCE_DIR}/win32-deps/include - C:/jsoncpp-0.10.6/include -PATH_SUFFIXES jsoncpp -) - -find_library(JSONCPP_LIBRARIES - NAMES jsoncpp - PATHS ${CMAKE_SOURCE_DIR}/bin/lib C:/jsoncpp/lib ${CMAKE_SOURCE_DIR}/win32-deps/lib C:/jsoncpp-0.10.6/ -) -IF (JSONCPP_LIBRARIES AND JSONCPP_INCLUDE_DIRS) - SET(JSONCPP_LIBRARIES ${JSONCPP_LIBRARIES}) - SET(JSONCPP_FOUND "YES") -ELSE (JSONCPP_LIBRARIES AND JSONCPP_INCLUDE_DIRS) - SET(JSONCPP_FOUND "NO") -ENDIF (JSONCPP_LIBRARIES AND JSONCPP_INCLUDE_DIRS) - - -IF (JSONCPP_FOUND) - IF (NOT JSONCPP_FIND_QUIETLY) - MESSAGE(STATUS "Found JSONCpp: ${JSONCPP_LIBRARIES}") - ENDIF (NOT JSONCPP_FIND_QUIETLY) -ELSE (JSONCPP_FOUND) - IF (JSONCPP_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find JSONCPP library include: ${JSONCPP_INCLUDE_DIRS}, lib: ${JSONCPP_LIBRARIES}") - ENDIF (JSONCPP_FIND_REQUIRED) -ENDIF (JSONCPP_FOUND) - -# Deprecated declarations. -SET (NATIVE_JSONCPP_INCLUDE_PATH ${JSONCPP_INCLUDE_DIRS} ) -GET_FILENAME_COMPONENT (NATIVE_JSONCPP_LIB_PATH ${JSONCPP_LIBRARIES} PATH) - -MARK_AS_ADVANCED( - JSONCPP_LIBRARIES - JSONCPP_INCLUDE_DIRS - ) - -# Restore the original find library ordering -if( JSONCPP_USE_STATIC_LIBS ) - set(CMAKE_FIND_LIBRARY_SUFFIXES ${_jsoncpp_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) -endif() diff --git a/cmake/FindLibevent.cmake b/cmake/FindLibevent.cmake deleted file mode 100755 index c013d4981..000000000 --- a/cmake/FindLibevent.cmake +++ /dev/null @@ -1,152 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# - Try to find libevent -#.rst -# FindLibevent -# ------------ -# -# Find Libevent include directories and libraries. Invoke as:: -# -# find_package(Libevent -# [version] [EXACT] # Minimum or exact version -# [REQUIRED] # Fail if Libevent is not found -# [COMPONENT ...]) # Libraries to look for -# -# Valid components are one or more of:: libevent core extra pthreads openssl. -# Note that 'libevent' contains both core and extra. You must specify one of -# them for the other components. -# -# This module will define the following variables:: -# -# LIBEVENT_FOUND - True if headers and requested libraries were found -# LIBEVENT_INCLUDE_DIRS - Libevent include directories -# LIBEVENT_LIBRARIES - Libevent libraries to be linked -# LIBEVENT__FOUND - Component was found ( is uppercase) -# LIBEVENT__LIBRARY - Library to be linked for Libevent component . - -find_package(PkgConfig QUIET) -pkg_check_modules(PC_LIBEVENT QUIET libevent) - -# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES -if( Libevent_USE_STATIC_LIBS ) - set(_libevent_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES :${CMAKE_FIND_LIBRARY_SUFFIXES}) - if(WIN32) - list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .lib .a) - else() - set(CMAKE_FIND_LIBRARY_SUFFIXES .a) - endif() -else() - set(_libevent_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES :${CMAKE_FIND_LIBRARY_SUFFIXES}) - if(WIN32) - list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .dll .so) - else() - set(CMAKE_FIND_LIBRARY_SUFFIXES .so) - endif() -endif() -# Look for the Libevent 2.0 or 1.4 headers -find_path(LIBEVENT_INCLUDE_DIR - NAMES - WIN32-Code/event2/event-config.h - event2/event-config.h - event-config.h - PATHS ${CMAKE_SOURCE_DIR}/bin/include C:/libevent/include ${CMAKE_SOURCE_DIR}/win32-deps/include - HINTS - ${PC_LIBEVENT_INCLUDE_DIRS} -) - -# ------------------------------------------------------------------------ -# Prefix initialization -# ------------------------------------------------------------------------ -set(Libevent_LIB_PREFIX "") -set(LIBEVENT_EVENT_CONFIG_DIR ${LIBEVENT_INCLUDE_DIR}) -if(WIN32) - set(Libevent_LIB_PREFIX "lib") - set(LIBEVENT_EVENT_CONFIG_DIR "${LIBEVENT_INCLUDE_DIR}/../WIN32-Code/") -endif() - -if(LIBEVENT_INCLUDE_DIR) - set(_version_regex "^#define[ \t]+_EVENT_VERSION[ \t]+\"([^\"]+)\".*") - if(EXISTS "${LIBEVENT_EVENT_CONFIG_DIR}/event2/event-config.h") - # Libevent 2.0 - file(STRINGS "${LIBEVENT_EVENT_CONFIG_DIR}/event2/event-config.h" - LIBEVENT_VERSION REGEX "${_version_regex}") - else() - # Libevent 1.4 - if(EXISTS "${LIBEVENT_EVENT_CONFIG_DIR}/event-config.h") - file(STRINGS "${LIBEVENT_EVENT_CONFIG_DIR}/event-config.h" - LIBEVENT_VERSION REGEX "${_version_regex}") - endif() - endif() - string(REGEX REPLACE "${_version_regex}" "\\1" - LIBEVENT_VERSION "${LIBEVENT_VERSION}") - unset(_version_regex) -endif() - -set(_LIBEVENT_REQUIRED_VARS) -if(WIN32) - set(Libevent_FIND_COMPONENTS ${Libevent_LIB_PREFIX}event core extras) -else() - set(Libevent_FIND_COMPONENTS ${Libevent_LIB_PREFIX}event core extra pthreads) -endif() -message(status "** libevent components: ${Libevent_FIND_COMPONENTS}") -foreach(COMPONENT ${Libevent_FIND_COMPONENTS}) - set(_LIBEVENT_LIBNAME "${Libevent_LIB_PREFIX}event") - # Note: compare two variables to avoid a CMP0054 policy warning - if(COMPONENT STREQUAL _LIBEVENT_LIBNAME) - set(_LIBEVENT_LIBNAME "${Libevent_LIB_PREFIX}event") - else() - set(_LIBEVENT_LIBNAME "${Libevent_LIB_PREFIX}event_${COMPONENT}") - endif() - string(TOUPPER "${COMPONENT}" COMPONENT_UPPER) - message(status "** ${_LIBEVENT_LIBNAME}") - find_library(LIBEVENT_${COMPONENT_UPPER}_LIBRARY - NAMES ${_LIBEVENT_LIBNAME} - PATHS ${CMAKE_SOURCE_DIR}/bin/lib C:/libevent-2.0.22-stable C:/libevent-2.0.22-stable/lib C:/libevent/lib ${CMAKE_SOURCE_DIR}/win32-deps/lib - HINTS ${PC_LIBEVENT_LIBRARY_DIRS} - ) - if(LIBEVENT_${COMPONENT_UPPER}_LIBRARY) - set(Libevent_${COMPONENT}_FOUND 1) - endif() - list(APPEND _LIBEVENT_REQUIRED_VARS LIBEVENT_${COMPONENT_UPPER}_LIBRARY) -endforeach() -unset(_LIBEVENT_LIBNAME) - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set LIBEVENT_FOUND to TRUE -# if all listed variables are TRUE and the requested version matches. -find_package_handle_standard_args(Libevent REQUIRED_VARS - ${_LIBEVENT_REQUIRED_VARS} - LIBEVENT_INCLUDE_DIR - VERSION_VAR LIBEVENT_VERSION - HANDLE_COMPONENTS) - -if(LIBEVENT_FOUND) - set(LIBEVENT_INCLUDE_DIRS ${LIBEVENT_INCLUDE_DIR}) - set(LIBEVENT_LIBRARIES) - foreach(COMPONENT ${Libevent_FIND_COMPONENTS}) - string(TOUPPER "${COMPONENT}" COMPONENT_UPPER) - list(APPEND LIBEVENT_LIBRARIES ${LIBEVENT_${COMPONENT_UPPER}_LIBRARY}) - set(LIBEVENT_${COMPONENT_UPPER}_FOUND ${Libevent_${COMPONENT}_FOUND}) - endforeach() -endif() - -mark_as_advanced(LIBEVENT_INCLUDE_DIR ${_LIBEVENT_REQUIRED_VARS}) - -# Restore the original find library ordering -if( Libevent_USE_STATIC_LIBS ) - set(CMAKE_FIND_LIBRARY_SUFFIXES ${_libevent_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) -endif() -unset(_LIBEVENT_REQUIRED_VARS) diff --git a/cmake/FindProtobufWithTargets.cmake b/cmake/FindProtobufWithTargets.cmake new file mode 100644 index 000000000..e96bc6c7e --- /dev/null +++ b/cmake/FindProtobufWithTargets.cmake @@ -0,0 +1,203 @@ +# ~~~ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ~~~ + +#[=======================================================================[.rst: +FindProtobufWithTargets +------------------- + +A module to use ``Protobuf`` with less complications. + +Using ``find_package(Protobuf)`` should be simple, but it is not. + +CMake provides a ``FindProtobuf`` module. Unfortunately it does not generate +``protobuf::*`` targets until CMake-3.9, and ``protobuf::protoc`` does not +appear until CMake-3.10. + +The CMake-config files generated by ``protobuf`` always create these targets, +but on some Linux distributions (e.g. Fedora>=29, and openSUSE-Tumbleweed) there +are system packages for protobuf, but these packages are installed without the +CMake-config files. One must either use the ``FindProtobuf`` module, find the +libraries via ``pkg-config``, or find the libraries manually. + +When the CMake-config files are installed they produce the same targets as +recent versions of ``FindProtobuf``. However, they do not produce the +``Protobuf_LIBRARY``, ``Protobuf_INCLUDE_DIR``, etc. that are generated by the +module. Furthermore, the ``protobuf::protoc`` library is not usable when loaded +from the CMake-config files: its ``IMPORTED_LOCATION`` variable is not defined. + +This module is designed to provide a single, uniform, ``find_package()`` +module that always produces the same outputs: + +- It always generates the ``protobuf::*`` targets. +- It always defines ``ProtobufWithTargets_FOUND`` and + ``ProtobufWithTargets_VERSION``. +- It *prefers* using the CMake config files if they are available. +- It fallsback on the ``FindProtobuf`` module if the config files are not found. +- It populates any missing targets and their properties. + +The following ``IMPORTED`` targets are defined: + +``protobuf::libprotobuf`` + The protobuf library. +``protobuf::libprotobuf-lite`` + The protobuf lite library. +``protobuf::libprotoc`` + The protoc library. +``protobuf::protoc`` + The protoc compiler. + +Example: + +.. code-block:: cmake + + find_package(ProtobufWithTargets REQUIRED) + add_executable(bar bar.cc) + target_link_libraries(bar PRIVATE protobuf::libprotobuf) + +#]=======================================================================] + +if (protobuf_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "protobuf_USE_STATIC_LIBS = ${protobuf_USE_STATIC_LIBS}" + " ProtobufWithTargets = ${ProtobufWithTargets_FOUND}") +endif () + +# Always load thread support, even on Windows. +find_package(Threads REQUIRED) + +# First try to use the ``protobufConfig.cmake`` or ``protobuf-config.cmake`` +# file if it was installed. This is common on systems (or package managers) +# where protobuf was compiled and installed with `CMake`. Note that on Linux +# this *must* be all lowercase ``protobuf``, while on Windows it does not +# matter. +find_package(Protobuf CONFIG QUIET) + +if (protobuf_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Protobuf_FOUND = ${Protobuf_FOUND}" + " Protobuf_VERSION = ${Protobuf_VERSION}") +endif () + +if (NOT Protobuf_FOUND) + find_package(Protobuf QUIET) +endif () + +if (Protobuf_FOUND) + set(ProtobufWithTargets_FOUND 1) + set(ProtobufWithTargets_VERSION ${Protobuf_VERSION}) + + if (NOT TARGET protobuf::libprotobuf) + add_library(protobuf::libprotobuf INTERFACE IMPORTED) + set_property( + TARGET protobuf::libprotobuf PROPERTY INTERFACE_INCLUDE_DIRECTORIES + ${Protobuf_INCLUDE_DIR}) + set_property( + TARGET protobuf::libprotobuf + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES ${Protobuf_LIBRARY} + Threads::Threads) + endif () + + if (NOT TARGET protobuf::libprotobuf-lite) + add_library(protobuf::libprotobuf-lite INTERFACE IMPORTED) + set_property( + TARGET protobuf::libprotobuf-lite + PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Protobuf_INCLUDE_DIR}) + set_property( + TARGET protobuf::libprotobuf-lite + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES ${Protobuf_LITE_LIBRARY} + Threads::Threads) + endif () + + if (NOT TARGET protobuf::libprotoc) + add_library(protobuf::libprotoc INTERFACE IMPORTED) + set_property( + TARGET protobuf::libprotoc PROPERTY INTERFACE_INCLUDE_DIRECTORIES + ${Protobuf_INCLUDE_DIR}) + set_property( + TARGET protobuf::libprotoc + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES ${Protobuf_PROTOC_LIBRARY} + Threads::Threads) + endif () + + if (NOT TARGET protobuf::protoc) + add_executable(protobuf::protoc IMPORTED) + + # Discover the protoc compiler location. + find_program( + _protobuf_PROTOC_EXECUTABLE + NAMES protoc + DOC "The Google Protocol Buffers Compiler") + if (protobuf_DEBUG) + message( + STATUS + "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "ProtobufWithTargets_FOUND = ${ProtobufWithTargets_FOUND}" + " ProtobufWithTargets_VERSION = ${ProtobufWithTargets_VERSION}" + " EXE = ${_protobuf_PROTOC_EXECUTABLE}") + endif () + set_property(TARGET protobuf::protoc + PROPERTY IMPORTED_LOCATION ${_protobuf_PROTOC_EXECUTABLE}) + set_property( + TARGET protobuf::protoc PROPERTY IMPORTED_LOCATION_DEBUG + ${_protobuf_PROTOC_EXECUTABLE}) + set_property( + TARGET protobuf::protoc PROPERTY IMPORTED_LOCATION_RELEASE + ${_protobuf_PROTOC_EXECUTABLE}) + unset(_protobuf_PROTOC_EXECUTABLE) + + if (protobuf_DEBUG) + get_target_property(_protobuf_PROTOC_EXECUTABLE protobuf::protoc + IMPORTED_LOCATION) + message( + STATUS + "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "LOCATION=${_protobuf_PROTOC_EXECUTABLE}") + endif () + endif () +endif () + +if (protobuf_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "ProtobufWithTargets_FOUND = ${ProtobufWithTargets_FOUND}" + " ProtobufWithTargets_VERSION = ${ProtobufWithTargets_VERSION}") +endif () + +if (protobuf_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "ProtobufWithTargets_FOUND = ${ProtobufWithTargets_FOUND}" + " ProtobufWithTargets_VERSION = ${ProtobufWithTargets_VERSION}") + if (ProtobufWithTargets_FOUND) + foreach (_target protobuf::libprotobuf protobuf::libprotobuf-lite + protobuf::libprotoc) + if (NOT TARGET ${_target}) + message( + STATUS + "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "target=${_target} is NOT a target") + endif () + endforeach () + unset(_target) + endif () +endif () + +find_package_handle_standard_args( + ProtobufWithTargets REQUIRED_VARS ProtobufWithTargets_FOUND + ProtobufWithTargets_VERSION) diff --git a/cmake/FindgRPC.cmake b/cmake/FindgRPC.cmake new file mode 100644 index 000000000..7ca10ae52 --- /dev/null +++ b/cmake/FindgRPC.cmake @@ -0,0 +1,357 @@ +# ~~~ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ~~~ + +#[=======================================================================[.rst: +FindgRPC +-------- + +Locate and configure the ``gRPC`` library. + +The following variables can be set and are optional: + +``gRPC_DEBUG`` + Show debug messages. +``gRPC_USE_STATIC_LIBS`` + Set to ON to force the use of the static libraries. + Default is OFF. + +Defines the following variables: + +``gRPC_FOUND`` + Found the gRPC library +``gRPC_VERSION`` + Version of package found. + +The following ``IMPORTED`` targets are also defined: + +``gRPC::grpc++`` + The gRPC C++ library. +``gRPC::grpc`` + The gRPC C core library. +``gRPC::cpp_plugin`` + The C++ plugin for the Protobuf protoc compiler. + +The following cache variables are also available to set or use: + +Example: + +.. code-block:: cmake + + find_package(gRPC REQUIRED) + add_executable(bar bar.cc) + target_link_libraries(bar PRIVATE gRPC::grpc++) + +#]=======================================================================] + +if (gRPC_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "gRPC_USE_STATIC_LIBS = ${gRPC_USE_STATIC_LIBS}" + " gRPC_FOUND = ${gRPC_FOUND}") +endif () + +# gRPC always requires Thread support. +find_package(Threads REQUIRED) + +# Load the module to find protobuf with proper targets. Do not use +# `find_package()` because we (have to) install this module in non-standard +# locations. +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") +find_package(ProtobufWithTargets) + +# The gRPC::grpc_cpp_plugin target is sometimes defined, but without a +# IMPORTED_LOCATION +function (_grpc_fix_grpc_cpp_plugin_target) + # The target may already exist, do not create it again if it does. + if (NOT TARGET gRPC::grpc_cpp_plugin) + add_executable(gRPC::grpc_cpp_plugin IMPORTED) + endif () + get_target_property(_gRPC_CPP_PLUGIN_EXECUTABLE gRPC::grpc_cpp_plugin + IMPORTED_LOCATION) + if (gRPC_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "LOCATION=${_gRPC_CPP_PLUGIN_EXECUTABLE}") + endif () + # Even if the target exists, gRPC CMake support files do not define the + # executable for the imported target (at least they do not in v1.19.1), so + # we need to define it ourselves. + if (NOT _gRPC_CPP_PLUGIN_EXECUTABLE) + find_program(_gRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin + DOC "The gRPC C++ plugin for protoc") + mark_as_advanced(_gRPC_CPP_PLUGIN_EXECUTABLE) + if (_gRPC_CPP_PLUGIN_EXECUTABLE) + set_property( + TARGET gRPC::grpc_cpp_plugin + PROPERTY IMPORTED_LOCATION ${_gRPC_CPP_PLUGIN_EXECUTABLE}) + else () + set(gRPC_FOUND "grpc_cpp_plugin-NOTFOUND") + endif () + endif () +endfunction () + +# The gRPC::* targets sometimes lack the right definitions to compile cleanly on +# WIN32 +function (_grpc_fix_grpc_target_definitions) + # Including gRPC headers without this definition results in a build error. + if (WIN32) + set_property( + TARGET gRPC::grpc + APPEND + PROPERTY INTERFACE_COMPILE_DEFINITIONS _WIN32_WINNT=0x600) + set_property( + TARGET gRPC::grpc++ + APPEND + PROPERTY INTERFACE_COMPILE_DEFINITIONS _WIN32_WINNT=0x600) + endif () +endfunction () + +# First try to use the `gRPCConfig.cmake` or `grpc-config.cmake` file if it was +# installed. This is common on systems (or package managers) where gRPC was +# compiled and installed with `CMake`. + +find_package(gRPC NO_MODULE QUIET) + +if (gRPC_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "NO_MODULE result gRPC_FOUND = ${gRPC_FOUND}") +endif () + +if (gRPC_FOUND) + _grpc_fix_grpc_cpp_plugin_target() + _grpc_fix_grpc_target_definitions() + return() +endif () + +include(SelectLibraryConfigurations) + +# Internal function: search for normal library as well as a debug one if the +# debug one is specified also include debug/optimized keywords in *_LIBRARIES +# variable +function (_gRPC_find_library name filename) + if (${name}_LIBRARY) + # Use result recorded by a previous call. + return() + else () + find_library(${name}_LIBRARY_RELEASE NAMES ${filename}) + mark_as_advanced(${name}_LIBRARY_RELEASE) + + find_library(${name}_LIBRARY_DEBUG NAMES ${filename}d ${filename}) + mark_as_advanced(${name}_LIBRARY_DEBUG) + + select_library_configurations(${name}) + + if (gRPC_DEBUG) + message( + STATUS + "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "${name} ${filename} RELEASE=${${name}_LIBRARY}" + " DEBUG=${${name}_LIBRARY_DEBUG} DEFAULT=${${name}_LIBRARY}" + ) + endif () + + set(${name}_LIBRARY + "${${name}_LIBRARY}" + PARENT_SCOPE) + endif () +endfunction () + +# +# Main +# + +# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES +if (_gRPC_USE_STATIC_LIBS) + set(_gRPC_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + if (WIN32) + set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) + else () + set(CMAKE_FIND_LIBRARY_SUFFIXES .a) + endif () +endif () + +_grpc_find_library(_gRPC_grpc grpc) +_grpc_find_library(_gRPC_grpc++ grpc++) + +if (NOT _gRPC_INCLUDE_DIR) + find_path(_gRPC_INCLUDE_DIR grpcpp/grpcpp.h) + mark_as_advanced(_gRPC_INCLUDE_DIR) +endif () + +if (gRPC_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " _gRPC_grpc_LIBRARY = ${_gRPC_grpc_LIBRARY}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " _gRPC_grpc++_LIBRARY = ${_gRPC_grpc++_LIBRARY}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " _gRPC_INCLUDE_DIR = ${_gRPC_INCLUDE_DIR}") +endif () + +if (_gRPC_grpc_LIBRARY) + if (NOT TARGET gRPC::grpc) + add_library(gRPC::grpc UNKNOWN IMPORTED) + set_target_properties( + gRPC::grpc PROPERTIES INTERFACE_INCLUDE_DIRECTORIES + "${_gRPC_INCLUDE_DIR}") + if (EXISTS "${_gRPC_grpc_LIBRARY}") + set_target_properties(gRPC::grpc PROPERTIES IMPORTED_LOCATION + "${_gRPC_grpc_LIBRARY}") + endif () + if (EXISTS "${_gRPC_grpc_LIBRARY_RELEASE}") + set_property( + TARGET gRPC::grpc + APPEND + PROPERTY IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties( + gRPC::grpc PROPERTIES IMPORTED_LOCATION_RELEASE + "${_gRPC_grpc_LIBRARY_RELEASE}") + endif () + if (EXISTS "${_gRPC_grpc_LIBRARY_DEBUG}") + set_property( + TARGET gRPC::grpc + APPEND + PROPERTY IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties( + gRPC::grpc PROPERTIES IMPORTED_LOCATION_DEBUG + "${_gRPC_grpc_LIBRARY_DEBUG}") + endif () + set_property( + TARGET gRPC::grpc + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES protobuf::libprotobuf + Threads::Threads) + endif () +endif () + +if (_gRPC_grpc++_LIBRARY) + if (NOT TARGET gRPC::grpc++) + add_library(gRPC::grpc++ UNKNOWN IMPORTED) + set_target_properties( + gRPC::grpc++ PROPERTIES INTERFACE_INCLUDE_DIRECTORIES + "${_gRPC++_INCLUDE_DIR}") + if (EXISTS "${_gRPC_grpc++_LIBRARY}") + set_target_properties( + gRPC::grpc++ PROPERTIES IMPORTED_LOCATION + "${_gRPC_grpc++_LIBRARY}") + endif () + if (EXISTS "${_gRPC_grpc++_LIBRARY_RELEASE}") + set_property( + TARGET gRPC::grpc++ + APPEND + PROPERTY IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties( + gRPC::grpc++ PROPERTIES IMPORTED_LOCATION_RELEASE + "${_gRPC_grpc++_LIBRARY_RELEASE}") + endif () + if (EXISTS "${_gRPC_grpc++_LIBRARY_DEBUG}") + set_property( + TARGET gRPC::grpc++ + APPEND + PROPERTY IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties( + gRPC::grpc++ PROPERTIES IMPORTED_LOCATION_DEBUG + "${_gRPC_grpc++_LIBRARY_DEBUG}") + endif () + set_property( + TARGET gRPC::grpc++ + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES gRPC::grpc protobuf::libprotobuf + Threads::Threads) + if (CMAKE_VERSION VERSION_GREATER 3.8) + # gRPC++ requires C++14 (soon), but only CMake-3.8 introduced a + # compiler feature to meet that requirement. + set_property( + TARGET gRPC::grpc++ + APPEND + PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_14) + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + # CMake 3.5 is still alive and kicking in some older distros, use + # the compiler-specific versions in these cases. + set_property( + TARGET gRPC::grpc++ + APPEND + PROPERTY INTERFACE_COMPILE_OPTIONS "-std=c++14") + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set_property( + TARGET gRPC::grpc++ + APPEND + PROPERTY INTERFACE_COMPILE_OPTIONS "-std=c++14") + else () + message( + WARNING + "gRPC::grpc++ requires C++14, but this module" + " (${CMAKE_CURRENT_LIST_FILE})" + " cannot enable it for the library target in your CMake and" + " compiler versions. You need to enable C++14 in the" + " CMakeLists.txt for your project. Consider filing a bug" + " so we can fix this problem.") + endif () + endif () +endif () + +# Restore original find library prefixes +if (_gRPC_USE_STATIC_LIBS) + set(CMAKE_FIND_LIBRARY_PREFIXES "${_gRPC_ORIG_FIND_LIBRARY_PREFIXES}") +endif () + +file( + WRITE "${CMAKE_BINARY_DIR}/get_gRPC_version.cc" + [====[ +#include +#include +int main() { + std::cout << grpc::Version(); // no newline to simplify CMake module + return 0; +} + ]====]) + +try_run( + _gRPC_GET_VERSION_STATUS + _gRPC_GET_VERSION_COMPILE_STATUS + "${CMAKE_BINARY_DIR}" + "${CMAKE_BINARY_DIR}/get_gRPC_version.cc" + LINK_LIBRARIES + gRPC::grpc++ + gRPC::grpc + COMPILE_OUTPUT_VARIABLE _gRPC_GET_VERSION_COMPILE_OUTPUT + RUN_OUTPUT_VARIABLE gRPC_VERSION) + +file(REMOVE "${CMAKE_BINARY_DIR}/get_gRPC_version.cc") + +_grpc_fix_grpc_cpp_plugin_target() + +if (gRPC_DEBUG) + foreach ( + _var + _gRPC_CPP_PLUGIN_EXECUTABLE + _gRPC_VERSION_RAW + _gRPC_GET_VERSION_STATUS + _gRPC_GET_VERSION_COMPILE_STATUS + _gRPC_GET_VERSION_COMPILE_OUTPUT + _gRPC_grpc_LIBRARY + _gRPC_grpc++_LIBRARY + _gRPC_INCLUDE_DIR) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "${_var} = ${${_var}}") + endforeach () + unset(_var) +endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + gRPC + REQUIRED_VARS _gRPC_grpc_LIBRARY _gRPC_INCLUDE_DIR + VERSION_VAR gRPC_VERSION) diff --git a/cmake/OpenCensusHelpers.cmake b/cmake/OpenCensusHelpers.cmake new file mode 100644 index 000000000..df894ea14 --- /dev/null +++ b/cmake/OpenCensusHelpers.cmake @@ -0,0 +1,90 @@ +# Copyright 2018, OpenCensus Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Prepends opencensus_ to all deps that aren't in a :: namespace. +function(prepend_opencensus OUT DEPS) + set(_DEPS "") + foreach(dep ${DEPS}) + if("${dep}" MATCHES "::") + list(APPEND _DEPS "${dep}") + else() + list(APPEND _DEPS "opencensus_${dep}") + endif() + endforeach() + set(${OUT} ${_DEPS} PARENT_SCOPE) +endfunction() + +# Helper function like bazel's cc_test. Usage: +# +# opencensus_test(trace_some_test internal/some_test.cc dep1 dep2...) +function(opencensus_test NAME SRC) + if(BUILD_TESTING) + set(_NAME "opencensus_${NAME}") + add_executable(${_NAME} ${SRC}) + prepend_opencensus(DEPS "${ARGN}") + target_link_libraries(${_NAME} + "${DEPS}" + gmock + gtest_main) + add_test(NAME ${_NAME} COMMAND ${_NAME}) + endif() +endfunction() + +# Helper function like bazel's cc_benchmark. Usage: +# +# opencensus_benchmark(trace_some_benchmark internal/some_benchmark.cc dep1 +# dep2...) +function(opencensus_benchmark NAME SRC) + if(BUILD_TESTING) + set(_NAME "opencensus_${NAME}") + add_executable(${_NAME} ${SRC}) + prepend_opencensus(DEPS "${ARGN}") + target_link_libraries(${_NAME} "${DEPS}" benchmark) + endif() +endfunction() + +# Helper function like bazel's cc_library. Libraries are namespaced as +# opencensus_* and public libraries are also aliased as opencensus-cpp::*. +function(opencensus_lib NAME) + cmake_parse_arguments(ARG + "PUBLIC" + "" + "SRCS;DEPS" + ${ARGN}) + set(_NAME "opencensus_${NAME}") + prepend_opencensus(ARG_DEPS "${ARG_DEPS}") + if(ARG_SRCS) + add_library(${_NAME} ${ARG_SRCS}) + target_link_libraries(${_NAME} PUBLIC ${ARG_DEPS} opencensus_api) + else() + add_library(${_NAME} INTERFACE) + target_link_libraries(${_NAME} INTERFACE ${ARG_DEPS} opencensus_api) + endif() + if(ARG_PUBLIC) + add_library(opencensus::${NAME} ALIAS ${_NAME}) + endif() +endfunction() + +# Helper function for fuzzing. Usage: +# +# opencensus_fuzzer(trace_some_fuzzer internal/some_fuzzer.cc dep1 dep2...) +function(opencensus_fuzzer NAME SRC) + if(FUZZER) + set(_NAME "opencensus_${NAME}") + add_executable(${_NAME} ${SRC}) + prepend_opencensus(DEPS "${ARGN}") + target_link_libraries(${_NAME} "${DEPS}" ${FUZZER}) + target_compile_options(${_NAME} PRIVATE ${FUZZER}) + endif() +endfunction() diff --git a/dep/build.sh b/dep/build.sh deleted file mode 100644 index d33e7c4c3..000000000 --- a/dep/build.sh +++ /dev/null @@ -1,29 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -BASEDIR=$(dirname "$0") -if [[ ! -d ${BASEDIR}/rocketmq_amd64/usr/local/include/rocketmq ]]; then - mkdir -p ${BASEDIR}/rocketmq_amd64/usr/local/include/rocketmq -fi - -if [[ ! -d ${BASEDIR}/rocketmq_amd64/usr/local/lib ]]; then - mkdir -p ${BASEDIR}/rocketmq_amd64/usr/local/lib -fi - -cp -R ${BASEDIR}/../include/* ${BASEDIR}/rocketmq_amd64/usr/local/include/rocketmq -cp ${BASEDIR}/../bin/librocketmq.so ${BASEDIR}/rocketmq_amd64/usr/local/lib/ -cp ${BASEDIR}/../bin/librocketmq.a ${BASEDIR}/rocketmq_amd64/usr/local/lib/ - -VERSION=`cat ${BASEDIR}/rocketmq_amd64/DEBIAN/control | grep Version | awk -F ':' '{print $2}'| sed 's/^ *//'` -dpkg-deb --build ${BASEDIR}/rocketmq_amd64 rocketmq-client-cpp-${VERSION}.amd64.deb diff --git a/dep/rocketmq_amd64/DEBIAN/control b/dep/rocketmq_amd64/DEBIAN/control deleted file mode 100644 index 53fa7fc84..000000000 --- a/dep/rocketmq_amd64/DEBIAN/control +++ /dev/null @@ -1,8 +0,0 @@ -Package: RocketMQ -Version: 2.2.0 -Architecture: amd64 -Essential: no -Priority: optional -Depends: -Maintainer: "Apache RocketMQ" -Description: This package is C++ client of Apache RocketMQ for debian and its derivertives like Ubuntu. diff --git a/dep/rocketmq_amd64/DEBIAN/postinst b/dep/rocketmq_amd64/DEBIAN/postinst deleted file mode 100755 index 9d78f353a..000000000 --- a/dep/rocketmq_amd64/DEBIAN/postinst +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -ldconfig \ No newline at end of file diff --git a/distribution/deploy.sh b/distribution/deploy.sh deleted file mode 100755 index ae6c9c6a6..000000000 --- a/distribution/deploy.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -VERSION="2.2.0" -PKG_NAME="rocketmq-client-cpp" -CWD_DIR=$(cd "$(dirname "$0")"; pwd) -DEPLOY_BUILD_HOME=${CWD_DIR}/${PKG_NAME} - -# ##==================================================================== -#make -# ##==================================================================== -# # deploy -rm -rf ${DEPLOY_BUILD_HOME} -mkdir -p ${DEPLOY_BUILD_HOME}/lib -cp -rf ${CWD_DIR}/../bin/*.a ${DEPLOY_BUILD_HOME}/lib/ -if test "$(uname)" = "Linux"; then -cp -rf ${CWD_DIR}/../bin/*.so ${DEPLOY_BUILD_HOME}/lib/ -fi -if test "$(uname)" = "Darwin"; then -cp -rf ${CWD_DIR}/../bin/*.dylib ${DEPLOY_BUILD_HOME}/lib/ -fi -cp -rf ${CWD_DIR}/../include ${DEPLOY_BUILD_HOME}/ -cp -rf ${CWD_DIR}/../example ${DEPLOY_BUILD_HOME}/ -cp -rf ${CWD_DIR}/../README.md ${DEPLOY_BUILD_HOME}/ -cp -rf ${CWD_DIR}/../LICENSE ${DEPLOY_BUILD_HOME}/LICENSE -cp -rf ${CWD_DIR}/../NOTICE ${DEPLOY_BUILD_HOME}/NOTICE - -cd ${CWD_DIR} && tar -cvzf ./${PKG_NAME}-${VERSION}-bin-release.tar.gz ./${PKG_NAME} &> /dev/null -rm -rf ${DEPLOY_BUILD_HOME} -# # ##==================================================================== -#make clean diff --git a/docs/assets/BasicMode.png b/docs/assets/BasicMode.png new file mode 100644 index 000000000..f04258515 Binary files /dev/null and b/docs/assets/BasicMode.png differ diff --git a/docs/assets/class_diagram.png b/docs/assets/class_diagram.png new file mode 100644 index 000000000..b22e3e467 Binary files /dev/null and b/docs/assets/class_diagram.png differ diff --git a/docs/gtest.md b/docs/gtest.md new file mode 100644 index 000000000..0c40d9d7f --- /dev/null +++ b/docs/gtest.md @@ -0,0 +1,8 @@ +## Run a single test case +1. Get all test cases by running tests with --gtest_list_tests +1. Parse this data into your GUI +1. Select test cases you want ro run +1. Run test executable with option --gtest_filter= + +## Run tests multiple times +bazel test --runs_per_test=10 //... \ No newline at end of file diff --git a/docs/test_coverage.md b/docs/test_coverage.md new file mode 100644 index 000000000..2c836a9ce --- /dev/null +++ b/docs/test_coverage.md @@ -0,0 +1,15 @@ +### Generate Coverage Data +```text + bazel coverage -s \ + --instrument_test_targets \ + --experimental_cc_coverage \ + --combined_report=lcov \ + --coverage_report_generator=@bazel_tools//tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator:Main \ + //src/test/... +``` + +### Generate HTML pages +```text +genhtml bazel-out/_coverage/_coverage_report.dat \ + --output-directory coverage_html +``` diff --git a/example/AsyncProducer.cpp b/example/AsyncProducer.cpp deleted file mode 100644 index 5be900c6e..000000000 --- a/example/AsyncProducer.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "common.h" - -using namespace rocketmq; - -std::atomic g_quit; -std::mutex g_mtx; -std::condition_variable g_finished; -SendCallback* g_callback = NULL; -TpsReportService g_tps; - -class MySendCallback : public SendCallback { - virtual void onSuccess(SendResult& sendResult) { - g_msgCount--; - g_tps.Increment(); - if (g_msgCount.load() <= 0) { - std::unique_lock lck(g_mtx); - g_finished.notify_one(); - } - } - virtual void onException(MQException& e) { cout << "send Exception\n"; } -}; - -class MyAutoDeleteSendCallback : public AutoDeleteSendCallBack { - public: - virtual ~MyAutoDeleteSendCallback() {} - virtual void onSuccess(SendResult& sendResult) { - g_msgCount--; - if (g_msgCount.load() <= 0) { - std::unique_lock lck(g_mtx); - g_finished.notify_one(); - } - } - virtual void onException(MQException& e) { std::cout << "send Exception" << e << "\n"; } -}; - -void AsyncProducerWorker(RocketmqSendAndConsumerArgs* info, DefaultMQProducer* producer) { - while (!g_quit.load()) { - if (g_msgCount.load() <= 0) { - std::unique_lock lck(g_mtx); - g_finished.notify_one(); - } - MQMessage msg(info->topic, // topic - "*", // tag - info->body); // body - - if (info->IsAutoDeleteSendCallback) { - g_callback = new MyAutoDeleteSendCallback(); // auto delete - } - - try { - producer->send(msg, g_callback); - } catch (MQException& e) { - std::cout << e << endl; // if catch excepiton , need re-send this msg by - // service - } - } -} - -int main(int argc, char* argv[]) { - RocketmqSendAndConsumerArgs info; - if (!ParseArgs(argc, argv, &info)) { - exit(-1); - } - - DefaultMQProducer producer("please_rename_unique_group_name"); - if (!info.IsAutoDeleteSendCallback) { - g_callback = new MySendCallback(); - } - - PrintRocketmqSendAndConsumerArgs(info); - - if (!info.namesrv.empty()) - producer.setNamesrvAddr(info.namesrv); - - producer.setGroupName(info.groupname); - producer.setInstanceName(info.groupname); - producer.setNamesrvDomain(info.namesrv_domain); - producer.start(); - g_tps.start(); - std::vector> work_pool; - auto start = std::chrono::system_clock::now(); - int msgcount = g_msgCount.load(); - for (int j = 0; j < info.thread_count; j++) { - std::shared_ptr th = std::make_shared(AsyncProducerWorker, &info, &producer); - work_pool.push_back(th); - } - - { - std::unique_lock lck(g_mtx); - g_finished.wait(lck); - g_quit.store(true); - } - - auto end = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - - std::cout << "per msg time: " << duration.count() / (double)msgcount << "ms \n" - << "========================finished==============================\n"; - - producer.shutdown(); - for (size_t th = 0; th != work_pool.size(); ++th) { - work_pool[th]->join(); - } - if (!info.IsAutoDeleteSendCallback) { - delete g_callback; - } - return 0; -} diff --git a/example/AsyncPushConsumer.cpp b/example/AsyncPushConsumer.cpp deleted file mode 100644 index 671c2e474..000000000 --- a/example/AsyncPushConsumer.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include -#include -#include -#include - -#include "common.h" - -std::mutex g_mtx; -std::condition_variable g_finished; -TpsReportService g_tps; -using namespace rocketmq; - -class MyMsgListener : public MessageListenerConcurrently { - public: - MyMsgListener() {} - virtual ~MyMsgListener() {} - - virtual ConsumeStatus consumeMessage(const std::vector& msgs) { - g_msgCount.store(g_msgCount.load() - msgs.size()); - for (size_t i = 0; i < msgs.size(); ++i) { - g_tps.Increment(); - } - - if (g_msgCount.load() <= 0) { - std::unique_lock lck(g_mtx); - g_finished.notify_one(); - } - return CONSUME_SUCCESS; - } -}; - -int main(int argc, char* argv[]) { - RocketmqSendAndConsumerArgs info; - if (!ParseArgs(argc, argv, &info)) { - exit(-1); - } - PrintRocketmqSendAndConsumerArgs(info); - DefaultMQPushConsumer consumer("please_rename_unique_group_name"); - DefaultMQProducer producer("please_rename_unique_group_name"); - - producer.setNamesrvAddr(info.namesrv); - producer.setGroupName("msg-persist-group_producer_sandbox"); - producer.setNamesrvDomain(info.namesrv_domain); - producer.start(); - - consumer.setNamesrvAddr(info.namesrv); - consumer.setGroupName(info.groupname); - consumer.setNamesrvDomain(info.namesrv_domain); - consumer.setConsumeFromWhere(CONSUME_FROM_LAST_OFFSET); - - consumer.setInstanceName(info.groupname); - - consumer.subscribe(info.topic, "*"); - consumer.setConsumeThreadCount(15); - consumer.setTcpTransportTryLockTimeout(1000); - consumer.setTcpTransportConnectTimeout(400); - - MyMsgListener msglistener; - consumer.registerMessageListener(&msglistener); - - try { - consumer.start(); - } catch (MQClientException& e) { - cout << e << endl; - } - g_tps.start(); - - int msgcount = g_msgCount.load(); - for (int i = 0; i < msgcount; ++i) { - MQMessage msg(info.topic, // topic - "*", // tag - info.body); // body - - try { - producer.send(msg); - } catch (MQException& e) { - std::cout << e << endl; // if catch excepiton , need re-send this msg by - // service - } - } - - { - std::unique_lock lck(g_mtx); - g_finished.wait(lck); - } - producer.shutdown(); - consumer.shutdown(); - return 0; -} \ No newline at end of file diff --git a/example/BatchProducer.cpp b/example/BatchProducer.cpp deleted file mode 100644 index 18f33cbf8..000000000 --- a/example/BatchProducer.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "common.h" - -using namespace rocketmq; -using namespace std; -std::atomic g_quit; -std::mutex g_mtx; -std::condition_variable g_finished; -TpsReportService g_tps; - -void SyncProducerWorker(RocketmqSendAndConsumerArgs* info, DefaultMQProducer* producer) { - while (!g_quit.load()) { - if (g_msgCount.load() <= 0) { - std::unique_lock lck(g_mtx); - g_finished.notify_one(); - break; - } - - vector msgs; - MQMessage msg1(info->topic, "*", info->body); - msg1.setProperty("property1", "value1"); - MQMessage msg2(info->topic, "*", info->body); - msg2.setProperty("property1", "value1"); - msg2.setProperty("property2", "value2"); - MQMessage msg3(info->topic, "*", info->body); - msg3.setProperty("property1", "value1"); - msg3.setProperty("property2", "value2"); - msg3.setProperty("property3", "value3"); - msgs.push_back(msg1); - msgs.push_back(msg2); - msgs.push_back(msg3); - try { - auto start = std::chrono::system_clock::now(); - SendResult sendResult = producer->send(msgs); - g_tps.Increment(); - --g_msgCount; - auto end = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - if (duration.count() >= 500) { - std::cout << "send RT more than: " << duration.count() << " ms with msgid: " << sendResult.getMsgId() << endl; - } - } catch (const MQException& e) { - std::cout << "send failed: " << e.what() << std::endl; - std::unique_lock lck(g_mtx); - g_finished.notify_one(); - return; - } - } -} - -int main(int argc, char* argv[]) { - RocketmqSendAndConsumerArgs info; - if (!ParseArgs(argc, argv, &info)) { - exit(-1); - } - PrintRocketmqSendAndConsumerArgs(info); - DefaultMQProducer producer("please_rename_unique_group_name"); - producer.setNamesrvAddr(info.namesrv); - producer.setNamesrvDomain(info.namesrv_domain); - producer.setGroupName(info.groupname); - producer.setInstanceName(info.groupname); - producer.setSessionCredentials("mq acesskey", "mq secretkey", "ALIYUN"); - producer.setSendMsgTimeout(500); - producer.setTcpTransportTryLockTimeout(1000); - producer.setTcpTransportConnectTimeout(400); - - producer.start(); - std::vector> work_pool; - auto start = std::chrono::system_clock::now(); - int msgcount = g_msgCount.load(); - g_tps.start(); - - int threadCount = info.thread_count; - for (int j = 0; j < threadCount; j++) { - std::shared_ptr th = std::make_shared(SyncProducerWorker, &info, &producer); - work_pool.push_back(th); - } - - { - std::unique_lock lck(g_mtx); - g_finished.wait(lck); - g_quit.store(true); - } - - auto end = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - - std::cout << "per msg time: " << duration.count() / (double)msgcount << "ms \n" - << "========================finished==============================\n"; - - for (size_t th = 0; th != work_pool.size(); ++th) { - work_pool[th]->join(); - } - - producer.shutdown(); - - return 0; -} diff --git a/example/CAsyncProducer.c b/example/CAsyncProducer.c deleted file mode 100644 index 9837c1ff5..000000000 --- a/example/CAsyncProducer.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "CCommon.h" -#include "CMessage.h" -#include "CProducer.h" -#include "CSendResult.h" -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -void thread_sleep(unsigned milliseconds) { -#ifdef _WIN32 - Sleep(milliseconds); -#else - usleep(milliseconds * 1000); // takes microseconds -#endif -} - -void SendSuccessCallback(CSendResult result) { - printf("async send success, msgid:%s\n", result.msgId); -} - -void SendExceptionCallback(CMQException e) { - char msg[1024]; - snprintf(msg, sizeof(msg), "error:%d, msg:%s, file:%s:%d", e.error, e.msg, e.file, e.line); - printf("async send exception %s\n", msg); -} - -void StartSendMessage(CProducer* producer) { - int i = 0; - int ret_code = 0; - char body[128]; - CMessage* msg = CreateMessage("T_TestTopic"); - SetMessageTags(msg, "Test_Tag"); - SetMessageKeys(msg, "Test_Keys"); - for (i = 0; i < 10; i++) { - memset(body, 0, sizeof(body)); - snprintf(body, sizeof(body), "new message body, index %d", i); - SetMessageBody(msg, body); - ret_code = SendMessageAsync(producer, msg, SendSuccessCallback, SendExceptionCallback); - printf("async send message[%d] return code: %d\n", i, ret_code); - thread_sleep(1000); - } - DestroyMessage(msg); -} - -void CreateProducerAndStartSendMessage(int i) { - printf("Producer Initializing.....\n"); - CProducer* producer = CreateProducer("Group_producer"); - SetProducerNameServerAddress(producer, "127.0.0.1:9876"); - if (i == 1) { - SetProducerSendMsgTimeout(producer, 3); - } - StartProducer(producer); - printf("Producer start.....\n"); - StartSendMessage(producer); - ShutdownProducer(producer); - DestroyProducer(producer); - printf("Producer Shutdown!\n"); -} - -int main(int argc, char* argv[]) { - printf("Send Async successCallback.....\n"); - CreateProducerAndStartSendMessage(0); - - printf("Send Async exceptionCallback.....\n"); - CreateProducerAndStartSendMessage(1); - return 0; -} diff --git a/example/CBatchProducer.c b/example/CBatchProducer.c deleted file mode 100644 index 148d9f068..000000000 --- a/example/CBatchProducer.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include "CBatchMessage.h" -#include "CCommon.h" -#include "CMessage.h" -#include "CProducer.h" -#include "CSendResult.h" - -void StartSendMessage(CProducer* producer) { - int i = 0; - int ret_code = 0; - char body[128]; - CBatchMessage* batchMessage = CreateBatchMessage("T_TestTopic"); - - for (i = 0; i < 10; i++) { - CMessage* msg = CreateMessage("T_TestTopic"); - SetMessageTags(msg, "Test_Tag"); - SetMessageKeys(msg, "Test_Keys"); - memset(body, 0, sizeof(body)); - snprintf(body, sizeof(body), "new message body, index %d", i); - SetMessageBody(msg, body); - AddMessage(batchMessage, msg); - } - CSendResult result; - ret_code = SendBatchMessage(producer, batchMessage, &result); - printf("SendBatchMessage %s .....\n", ret_code == 0 ? "Success" : ret_code == 11 ? "FAILED" : " It is null value"); - DestroyBatchMessage(batchMessage); -} - -void CreateProducerAndStartSendMessage() { - printf("Producer Initializing.....\n"); - CProducer* producer = CreateProducer("Group_producer"); - SetProducerNameServerAddress(producer, "127.0.0.1:9876"); - StartProducer(producer); - printf("Producer start.....\n"); - StartSendMessage(producer); - ShutdownProducer(producer); - DestroyProducer(producer); - printf("Producer Shutdown!\n"); -} - -int main(int argc, char* argv[]) { - printf("Send Batch.....\n"); - CreateProducerAndStartSendMessage(); - return 0; -} diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt deleted file mode 100755 index daf9c3bc2..000000000 --- a/example/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -project(example) - -set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin) - -include_directories(${CMAKE_SOURCE_DIR}/include) -include_directories(${Boost_INCLUDE_DIRS}) - -link_directories(${Boost_LIBRARY_DIRS}) -link_directories(${LIBEVENT_LIBRARY}) -link_directories(${JSONCPP_LIBRARY}) -link_directories(${OPENSSL_LIBRARIES_DIR}) - -#if (BUILD_ROCKETMQ_SHARED) -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ALL_DYN_LINK -shared ") -#endif() - -file(GLOB files "*.c*") -foreach(file ${files}) - get_filename_component(basename ${file} NAME_WE) - add_executable(${basename} ${file}) - if(MSVC) - if(CMAKE_CONFIGURATION_TYPES STREQUAL "Release") - set_target_properties( ${basename} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT" ) - else() - set_target_properties( ${basename} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMTD" ) - endif() - endif() - - if (MSVC) - if (BUILD_ROCKETMQ_SHARED) - target_link_libraries (${basename} rocketmq_shared ${deplibs} - ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${LIBEVENT_LIBRARIES} ${JSONCPP_LIBRARIES}) - else() - target_link_libraries (${basename} rocketmq_static ${deplibs} - ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${LIBEVENT_LIBRARIES} ${JSONCPP_LIBRARIES}) - endif() - else() - if (BUILD_ROCKETMQ_SHARED) - target_link_libraries (${basename} rocketmq_shared) - else() - target_link_libraries (${basename} rocketmq_static) - endif() - endif() - -endforeach() diff --git a/example/COrderlyAsyncProducer.c b/example/COrderlyAsyncProducer.c deleted file mode 100644 index b48fb5485..000000000 --- a/example/COrderlyAsyncProducer.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "CCommon.h" -#include "CMessage.h" -#include "CProducer.h" -#include "CSendResult.h" -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -void thread_sleep(unsigned milliseconds) { -#ifdef _WIN32 - Sleep(milliseconds); -#else - usleep(milliseconds * 1000); // takes microseconds -#endif -} - -void SendSuccessCallback(CSendResult result) { - printf("async send success, msgid:%s\n", result.msgId); -} - -void SendExceptionCallback(CMQException e) { - char msg[1024]; - snprintf(msg, sizeof(msg), "error:%d, msg:%s, file:%s:%d", e.error, e.msg, e.file, e.line); - printf("async send exception %s\n", msg); -} - -int aQueueSelectorCallback(int size, CMessage* msg, void* arg) { - return 0; -}; - -void StartSendMessage(CProducer* producer) { - int i = 0; - int ret_code = 0; - char body[128]; - CMessage* msg = CreateMessage("topic_COrderlyAsyncProducer"); - SetMessageTags(msg, "Test_Tag"); - SetMessageKeys(msg, "Test_Keys"); - for (i = 0; i < 10; i++) { - memset(body, 0, sizeof(body)); - snprintf(body, sizeof(body), "new message body, index %d", i); - SetMessageBody(msg, body); - ret_code = SendMessageOrderlyAsync(producer, msg, aQueueSelectorCallback, (void*)&i, SendSuccessCallback, - SendExceptionCallback); - printf("async send message[%d] return code: %d\n", i, ret_code); - thread_sleep(1000); - } - DestroyMessage(msg); -} - -void CreateProducerAndStartSendMessage(int i) { - printf("Producer Initializing.....\n"); - CProducer* producer = CreateProducer("FooBarGroup1"); - SetProducerNameServerAddress(producer, "192.168.0.149:9876"); - if (i == 1) { - SetProducerSendMsgTimeout(producer, 3); - } - StartProducer(producer); - printf("Producer start.....\n"); - StartSendMessage(producer); - ShutdownProducer(producer); - DestroyProducer(producer); - printf("Producer Shutdown!\n"); -} - -int main(int argc, char* argv[]) { - printf("COrderlyAsyncProducer successCallback.....\n"); - CreateProducerAndStartSendMessage(0); - - printf("COrderlyAsyncProducer exceptionCallback.....\n"); - CreateProducerAndStartSendMessage(1); - return 0; -} diff --git a/example/OrderProducer.cpp b/example/OrderProducer.cpp deleted file mode 100644 index 402e916d9..000000000 --- a/example/OrderProducer.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include -#include -#include - -#include "common.h" - -using namespace rocketmq; - -std::condition_variable g_finished; -std::mutex g_mtx; -std::atomic g_quit(false); - -class SelectMessageQueueByHash : public MessageQueueSelector { - public: - MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) { - int orderId = *static_cast(arg); - int index = orderId % mqs.size(); - return mqs[index]; - } -}; - -SelectMessageQueueByHash g_mySelector; - -void ProducerWorker(RocketmqSendAndConsumerArgs* info, DefaultMQProducer* producer) { - while (!g_quit.load()) { - if (g_msgCount.load() <= 0) { - std::unique_lock lck(g_mtx); - g_finished.notify_one(); - } - MQMessage msg(info->topic, // topic - "*", // tag - info->body); // body - - int orderId = 1; - SendResult sendResult = - producer->send(msg, &g_mySelector, static_cast(&orderId), info->retrytimes, info->SelectUnactiveBroker); - --g_msgCount; - } -} - -int main(int argc, char* argv[]) { - RocketmqSendAndConsumerArgs info; - if (!ParseArgs(argc, argv, &info)) { - exit(-1); - } - - DefaultMQProducer producer("please_rename_unique_group_name"); - PrintRocketmqSendAndConsumerArgs(info); - - producer.setNamesrvAddr(info.namesrv); - producer.setNamesrvDomain(info.namesrv_domain); - producer.setGroupName(info.groupname); - producer.setInstanceName(info.groupname); - - producer.start(); - - int msgcount = g_msgCount.load(); - std::vector> work_pool; - - int threadCount = info.thread_count; - for (int j = 0; j < threadCount; j++) { - std::shared_ptr th = std::make_shared(ProducerWorker, &info, &producer); - work_pool.push_back(th); - } - - auto start = std::chrono::system_clock::now(); - { - std::unique_lock lck(g_mtx); - g_finished.wait(lck); - g_quit.store(true); - } - - auto end = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - - std::cout << "per msg time: " << duration.count() / (double)msgcount << "ms \n" - << "========================finished==============================\n"; - - for (size_t th = 0; th != work_pool.size(); ++th) { - work_pool[th]->join(); - } - - producer.shutdown(); - - return 0; -} diff --git a/example/OrderlyPushConsumer.cpp b/example/OrderlyPushConsumer.cpp deleted file mode 100644 index 174e2b3a9..000000000 --- a/example/OrderlyPushConsumer.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" - -using namespace rocketmq; - -std::condition_variable g_finished; -std::mutex g_mtx; -std::atomic g_consumedCount(0); -std::atomic g_quit(false); -TpsReportService g_tps; - -class MyMsgListener : public MessageListenerOrderly { - public: - MyMsgListener() {} - virtual ~MyMsgListener() {} - - virtual ConsumeStatus consumeMessage(const vector& msgs) { - if (g_consumedCount.load() >= g_msgCount) { - std::unique_lock lK(g_mtx); - g_quit.store(true); - g_finished.notify_one(); - } - for (size_t i = 0; i < msgs.size(); i++) { - ++g_consumedCount; - g_tps.Increment(); - } - return CONSUME_SUCCESS; - } -}; - -int main(int argc, char* argv[]) { - RocketmqSendAndConsumerArgs info; - if (!ParseArgs(argc, argv, &info)) { - exit(-1); - } - PrintRocketmqSendAndConsumerArgs(info); - DefaultMQPushConsumer consumer("please_rename_unique_group_name"); - DefaultMQProducer producer("please_rename_unique_group_name"); - - producer.setNamesrvAddr(info.namesrv); - producer.setGroupName("msg-persist-group_producer_sandbox"); - producer.start(); - - consumer.setNamesrvAddr(info.namesrv); - consumer.setNamesrvDomain(info.namesrv_domain); - consumer.setGroupName(info.groupname); - consumer.setConsumeFromWhere(CONSUME_FROM_LAST_OFFSET); - consumer.subscribe(info.topic, "*"); - consumer.setConsumeThreadCount(info.thread_count); - consumer.setConsumeMessageBatchMaxSize(31); - if (info.syncpush) - consumer.setAsyncPull(false); - - MyMsgListener msglistener; - consumer.registerMessageListener(&msglistener); - g_tps.start(); - - try { - consumer.start(); - } catch (MQClientException& e) { - std::cout << e << std::endl; - } - - int msgcount = g_msgCount.load(); - for (int i = 0; i < msgcount; ++i) { - MQMessage msg(info.topic, // topic - "*", // tag - info.body); // body - - try { - producer.send(msg); - } catch (MQException& e) { - std::cout << e << endl; // if catch excepiton , need re-send this msg by - // service - } - } - - while (!g_quit.load()) { - std::unique_lock lk(g_mtx); - g_finished.wait(lk); - } - - producer.shutdown(); - consumer.shutdown(); - return 0; -} diff --git a/example/Producer.c b/example/Producer.c deleted file mode 100644 index 9ae23a94d..000000000 --- a/example/Producer.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "CCommon.h" -#include "CMessage.h" -#include "CProducer.h" -#include "CSendResult.h" -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -void thread_sleep(unsigned int milliseconds) { -#ifdef _WIN32 - Sleep(milliseconds); -#else - usleep(milliseconds * 1000); // suspend execution for microsecond intervals -#endif -} - -void StartSendMessage(CProducer* producer) { - int i = 0; - char body[256]; - CMessage* msg = CreateMessage("T_TestTopic"); - SetMessageTags(msg, "Test_Tag"); - SetMessageKeys(msg, "Test_Keys"); - CSendResult result; - for (i = 0; i < 3; i++) { - memset(body, 0, sizeof(body)); - snprintf(body, sizeof(body), "new message body, index %d", i); - SetMessageBody(msg, body); - int status = SendMessageSync(producer, msg, &result); - if (status == OK) { - printf("send message[%d] result status:%d, msgId:%s\n", i, (int)result.sendStatus, result.msgId); - } else { - printf("send message[%d] failed !\n", i); - } - thread_sleep(1000); - } - DestroyMessage(msg); -} - -int main(int argc, char* argv[]) { - CProducer* producer = CreateProducer("Group_producer"); - SetProducerNameServerAddress(producer, "127.0.0.1:9876"); - StartProducer(producer); - printf("Producer initialized. \n"); - - StartSendMessage(producer); - - ShutdownProducer(producer); - DestroyProducer(producer); - printf("Producer stopped !\n"); - - return 0; -} diff --git a/example/PullConsumeMessage.c b/example/PullConsumeMessage.c deleted file mode 100644 index f9b9bc217..000000000 --- a/example/PullConsumeMessage.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "CCommon.h" -#include "CMessageExt.h" -#include "CMessageQueue.h" -#include "CPullConsumer.h" -#include "CPullResult.h" -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -void thread_sleep(unsigned milliseconds) { -#ifdef _WIN32 - Sleep(milliseconds); -#else - usleep(milliseconds * 1000); // takes microseconds -#endif -} - -int main(int argc, char* argv[]) { - int i = 0, j = 0; - int ret = 0; - int size = 0; - CMessageQueue* mqs = NULL; - printf("PullConsumer Initializing....\n"); - CPullConsumer* consumer = CreatePullConsumer("Group_Consumer_Test"); - SetPullConsumerNameServerAddress(consumer, "172.17.0.2:9876"); - StartPullConsumer(consumer); - printf("Pull Consumer Start...\n"); - for (i = 1; i <= 5; i++) { - printf("FetchSubscriptionMessageQueues : %d times\n", i); - ret = FetchSubscriptionMessageQueues(consumer, "T_TestTopic", &mqs, &size); - if (ret != OK) { - printf("Get MQ Queue Failed,ErrorCode:%d\n", ret); - } - printf("Get MQ Size:%d\n", size); - for (j = 0; j < size; j++) { - int noNewMsg = 0; - long long tmpoffset = 0; - printf("Pull Message For Topic:%s,Queue:%s,QueueId:%d\n", mqs[j].topic, mqs[j].brokerName, mqs[j].queueId); - do { - int k = 0; - CPullResult pullResult = Pull(consumer, &mqs[j], "*", tmpoffset, 32); - if (pullResult.pullStatus != E_BROKER_TIMEOUT) { - tmpoffset = pullResult.nextBeginOffset; - } - printf("PullStatus:%d,MaxOffset:%lld,MinOffset:%lld,NextBegainOffset:%lld", pullResult.pullStatus, - pullResult.maxOffset, pullResult.minOffset, pullResult.nextBeginOffset); - switch (pullResult.pullStatus) { - case E_FOUND: - printf("Get Message Size:%d\n", pullResult.size); - for (k = 0; k < pullResult.size; ++k) { - printf("Got Message ID:%s,Body:%s\n", GetMessageId(pullResult.msgFoundList[k]), - GetMessageBody(pullResult.msgFoundList[k])); - } - break; - case E_NO_MATCHED_MSG: - noNewMsg = 1; - break; - default: - noNewMsg = 0; - } - ReleasePullResult(pullResult); - thread_sleep(100); - } while (noNewMsg == 0); - thread_sleep(1000); - } - thread_sleep(2000); - ReleaseSubscriptionMessageQueue(mqs); - } - thread_sleep(5000); - ShutdownPullConsumer(consumer); - DestroyPullConsumer(consumer); - printf("PullConsumer Shutdown!\n"); - return 0; -} diff --git a/example/PullConsumer.cpp b/example/PullConsumer.cpp deleted file mode 100644 index e85a82188..000000000 --- a/example/PullConsumer.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include -#include -#include - -#include "common.h" - -using namespace rocketmq; - -std::map g_offseTable; - -void putMessageQueueOffset(MQMessageQueue mq, uint64_t offset) { - g_offseTable[mq] = offset; -} - -uint64_t getMessageQueueOffset(MQMessageQueue mq) { - map::iterator it = g_offseTable.find(mq); - if (it != g_offseTable.end()) { - return it->second; - } - return 0; -} - -int main(int argc, char* argv[]) { - RocketmqSendAndConsumerArgs info; - if (!ParseArgs(argc, argv, &info)) { - exit(-1); - } - PrintRocketmqSendAndConsumerArgs(info); - DefaultMQPullConsumer consumer("please_rename_unique_group_name"); - consumer.setNamesrvAddr(info.namesrv); - consumer.setNamesrvDomain(info.namesrv_domain); - consumer.setGroupName(info.groupname); - consumer.setInstanceName(info.groupname); - consumer.registerMessageQueueListener(info.topic, NULL); - consumer.start(); - std::vector mqs; - - try { - consumer.fetchSubscribeMessageQueues(info.topic, mqs); - auto iter = mqs.begin(); - for (; iter != mqs.end(); ++iter) { - std::cout << "mq:" << (*iter).toString() << endl; - } - } catch (MQException& e) { - std::cout << e << endl; - } - - auto start = std::chrono::system_clock::now(); - auto iter = mqs.begin(); - for (; iter != mqs.end(); ++iter) { - MQMessageQueue mq = (*iter); - // if cluster model - // putMessageQueueOffset(mq, g_consumer.fetchConsumeOffset(mq,true)); - // if broadcast model - // putMessageQueueOffset(mq, your last consume offset); - - bool noNewMsg = false; - do { - try { - PullResult result = consumer.pull(mq, "*", getMessageQueueOffset(mq), 32); - g_msgCount += result.msgFoundList.size(); - std::cout << result.msgFoundList.size() << std::endl; - // if pull request timeout or received NULL response, pullStatus will be - // setted to BROKER_TIMEOUT, - // And nextBeginOffset/minOffset/MaxOffset will be setted to 0 - if (result.pullStatus != BROKER_TIMEOUT) { - putMessageQueueOffset(mq, result.nextBeginOffset); - PrintPullResult(&result); - } else { - cout << "broker timeout occur" << endl; - } - switch (result.pullStatus) { - case FOUND: - case NO_MATCHED_MSG: - case OFFSET_ILLEGAL: - case BROKER_TIMEOUT: - break; - case NO_NEW_MSG: - noNewMsg = true; - break; - default: - break; - } - } catch (MQClientException& e) { - std::cout << e << std::endl; - } - } while (!noNewMsg); - } - - auto end = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - - std::cout << "msg count: " << g_msgCount.load() << "\n"; - std::cout << "per msg time: " << duration.count() / (double)g_msgCount.load() << "ms \n" - << "========================finished==============================\n"; - - consumer.shutdown(); - return 0; -} diff --git a/example/PushConsumeMessage.c b/example/PushConsumeMessage.c deleted file mode 100644 index 85ea28c0a..000000000 --- a/example/PushConsumeMessage.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "CCommon.h" -#include "CMessageExt.h" -#include "CPushConsumer.h" -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -void thread_sleep(unsigned milliseconds) { -#ifdef _WIN32 - Sleep(milliseconds); -#else - usleep(milliseconds * 1000); // takes microseconds -#endif -} - -int doConsumeMessage(struct CPushConsumer* consumer, CMessageExt* msgExt) { - printf("Hello,doConsumeMessage by Application!\n"); - printf("Msg Topic:%s\n", GetMessageTopic(msgExt)); - printf("Msg Tags:%s\n", GetMessageTags(msgExt)); - printf("Msg Keys:%s\n", GetMessageKeys(msgExt)); - printf("Msg Body:%s\n", GetMessageBody(msgExt)); - return E_CONSUME_SUCCESS; -} - -int main(int argc, char* argv[]) { - int i = 0; - printf("PushConsumer Initializing....\n"); - CPushConsumer* consumer = CreatePushConsumer("Group_Consumer_Test"); - SetPushConsumerNameServerAddress(consumer, "172.17.0.2:9876"); - Subscribe(consumer, "T_TestTopic", "*"); - RegisterMessageCallback(consumer, doConsumeMessage); - StartPushConsumer(consumer); - printf("Push Consumer Start...\n"); - for (i = 0; i < 10; i++) { - printf("Now Running : %d S\n", i * 10); - thread_sleep(10000); - } - ShutdownPushConsumer(consumer); - DestroyPushConsumer(consumer); - printf("PushConsumer Shutdown!\n"); - return 0; -} diff --git a/example/PushConsumer.cpp b/example/PushConsumer.cpp deleted file mode 100644 index 0b69ca5b9..000000000 --- a/example/PushConsumer.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" - -std::mutex g_mtx; -std::condition_variable g_finished; -TpsReportService g_tps; - -using namespace rocketmq; - -class MyMsgListener : public MessageListenerConcurrently { - public: - MyMsgListener() {} - virtual ~MyMsgListener() {} - - virtual ConsumeStatus consumeMessage(const std::vector& msgs) { - g_msgCount.store(g_msgCount.load() - msgs.size()); - for (size_t i = 0; i < msgs.size(); ++i) { - g_tps.Increment(); - // cout << "msg body: "<< msgs[i].getBody() << endl; - } - - if (g_msgCount.load() <= 0) { - std::unique_lock lck(g_mtx); - g_finished.notify_one(); - } - return CONSUME_SUCCESS; - } -}; - -int main(int argc, char* argv[]) { - RocketmqSendAndConsumerArgs info; - if (!ParseArgs(argc, argv, &info)) { - exit(-1); - } - PrintRocketmqSendAndConsumerArgs(info); - DefaultMQPushConsumer consumer("please_rename_unique_group_name"); - DefaultMQProducer producer("please_rename_unique_group_name"); - producer.setSessionCredentials("AccessKey", "SecretKey", "ALIYUN"); - producer.setTcpTransportTryLockTimeout(1000); - producer.setTcpTransportConnectTimeout(400); - producer.setNamesrvDomain(info.namesrv_domain); - producer.setNamesrvAddr(info.namesrv); - producer.setGroupName("msg-persist-group_producer_sandbox"); - producer.start(); - - consumer.setNamesrvAddr(info.namesrv); - consumer.setGroupName(info.groupname); - consumer.setSessionCredentials("AccessKey", "SecretKey", "ALIYUN"); - consumer.setConsumeThreadCount(info.thread_count); - consumer.setNamesrvDomain(info.namesrv_domain); - consumer.setConsumeFromWhere(CONSUME_FROM_LAST_OFFSET); - - if (info.syncpush) - consumer.setAsyncPull(false); // set sync pull - if (info.broadcasting) { - consumer.setMessageModel(rocketmq::BROADCASTING); - } - - consumer.setInstanceName(info.groupname); - - consumer.subscribe(info.topic, "*"); - consumer.setConsumeThreadCount(15); - consumer.setTcpTransportTryLockTimeout(1000); - consumer.setTcpTransportConnectTimeout(400); - - MyMsgListener msglistener; - consumer.registerMessageListener(&msglistener); - - try { - consumer.start(); - } catch (MQClientException& e) { - cout << e << endl; - } - g_tps.start(); - - int msgcount = g_msgCount.load(); - for (int i = 0; i < msgcount; ++i) { - MQMessage msg(info.topic, // topic - "*", // tag - info.body); // body - - // std::this_thread::sleep_for(std::chrono::seconds(100000)); - try { - producer.send(msg); - } catch (MQException& e) { - std::cout << e << endl; // if catch excepiton , need re-send this msg by - // service - } - } - - { - std::unique_lock lck(g_mtx); - g_finished.wait(lck); - } - producer.shutdown(); - consumer.shutdown(); - return 0; -} diff --git a/example/PushConsumerOrderly.c b/example/PushConsumerOrderly.c deleted file mode 100644 index 9601799a7..000000000 --- a/example/PushConsumerOrderly.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef WIN32 -#include -#endif -#include -#include "CCommon.h" -#include "CMessageExt.h" -#include "CPushConsumer.h" - -int doConsumeMessage(struct CPushConsumer* consumer, CMessageExt* msgExt) { - printf("Hello,doConsumeMessage by Application!\n"); - printf("Msg Topic:%s\n", GetMessageTopic(msgExt)); - printf("Msg Tags:%s\n", GetMessageTags(msgExt)); - printf("Msg Keys:%s\n", GetMessageKeys(msgExt)); - printf("Msg Body:%s\n", GetMessageBody(msgExt)); - return E_CONSUME_SUCCESS; -} - -int main(int argc, char* argv[]) { - int i = 0; - printf("PushConsumer Initializing....\n"); - CPushConsumer* consumer = CreatePushConsumer("Group_Consumer_Test"); - SetPushConsumerNameServerAddress(consumer, "127.0.0.1:9876"); - Subscribe(consumer, "test", "*"); - RegisterMessageCallbackOrderly(consumer, doConsumeMessage); - StartPushConsumer(consumer); - printf("Push Consumer Start...\n"); - for (i = 0; i < 10; i++) { - printf("Now Running : %d S\n", i * 10); - sleep(10); - } - ShutdownPushConsumer(consumer); - DestroyPushConsumer(consumer); - printf("PushConsumer Shutdown!\n"); - return 0; -} diff --git a/example/README.md b/example/README.md deleted file mode 100644 index 378a4ead3..000000000 --- a/example/README.md +++ /dev/null @@ -1 +0,0 @@ -1. AsyncProducer 2. OrderlyProducer 3. SyncProducer diff --git a/example/SendDelayMsg.cpp b/example/SendDelayMsg.cpp deleted file mode 100644 index 860f5b33d..000000000 --- a/example/SendDelayMsg.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "common.h" - -using namespace rocketmq; - -int main(int argc, char* argv[]) { - RocketmqSendAndConsumerArgs info; - if (!ParseArgs(argc, argv, &info)) { - exit(-1); - } - PrintRocketmqSendAndConsumerArgs(info); - DefaultMQProducer producer("please_rename_unique_group_name"); - producer.setNamesrvAddr(info.namesrv); - producer.setNamesrvDomain(info.namesrv_domain); - producer.setGroupName(info.groupname); - producer.setInstanceName(info.groupname); - - producer.setSendMsgTimeout(500); - producer.setTcpTransportTryLockTimeout(1000); - producer.setTcpTransportConnectTimeout(400); - - producer.start(); - - MQMessage msg(info.topic, // topic - "*", // tag - info.body); // body - - // messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h - // 2h - msg.setDelayTimeLevel(5); // 1m - try { - SendResult sendResult = producer.send(msg, info.SelectUnactiveBroker); - } catch (const MQException& e) { - std::cout << "send failed: " << std::endl; - } - - producer.shutdown(); - return 0; -} diff --git a/example/SyncProducer.cpp b/example/SyncProducer.cpp deleted file mode 100644 index 5d66da259..000000000 --- a/example/SyncProducer.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "common.h" - -using namespace rocketmq; - -std::atomic g_quit; -std::mutex g_mtx; -std::condition_variable g_finished; -TpsReportService g_tps; - -void SyncProducerWorker(RocketmqSendAndConsumerArgs* info, DefaultMQProducer* producer) { - while (!g_quit.load()) { - if (g_msgCount.load() <= 0) { - std::unique_lock lck(g_mtx); - g_finished.notify_one(); - } - MQMessage msg(info->topic, // topic - "*", // tag - info->body); // body - try { - auto start = std::chrono::system_clock::now(); - SendResult sendResult = producer->send(msg, info->SelectUnactiveBroker); - g_tps.Increment(); - --g_msgCount; - auto end = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - if (duration.count() >= 500) { - std::cout << "send RT more than: " << duration.count() << " ms with msgid: " << sendResult.getMsgId() << endl; - } - } catch (const MQException& e) { - std::cout << "send failed: " << std::endl; - } - } -} - -int main(int argc, char* argv[]) { - RocketmqSendAndConsumerArgs info; - if (!ParseArgs(argc, argv, &info)) { - exit(-1); - } - PrintRocketmqSendAndConsumerArgs(info); - DefaultMQProducer producer("please_rename_unique_group_name"); - producer.setNamesrvAddr(info.namesrv); - producer.setNamesrvDomain(info.namesrv_domain); - producer.setGroupName(info.groupname); - producer.setInstanceName(info.groupname); - producer.setSessionCredentials("mq acesskey", "mq secretkey", "ALIYUN"); - producer.setSendMsgTimeout(500); - producer.setTcpTransportTryLockTimeout(1000); - producer.setTcpTransportConnectTimeout(400); - - producer.start(); - std::vector> work_pool; - auto start = std::chrono::system_clock::now(); - int msgcount = g_msgCount.load(); - g_tps.start(); - - int threadCount = info.thread_count; - for (int j = 0; j < threadCount; j++) { - std::shared_ptr th = std::make_shared(SyncProducerWorker, &info, &producer); - work_pool.push_back(th); - } - - { - std::unique_lock lck(g_mtx); - g_finished.wait(lck); - g_quit.store(true); - } - - auto end = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - - std::cout << "per msg time: " << duration.count() / (double)msgcount << "ms \n" - << "========================finished==============================\n"; - - for (size_t th = 0; th != work_pool.size(); ++th) { - work_pool[th]->join(); - } - - producer.shutdown(); - - return 0; -} diff --git a/example/TransactionProducer.cpp b/example/TransactionProducer.cpp deleted file mode 100644 index 1aabb0887..000000000 --- a/example/TransactionProducer.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include "TransactionListener.h" -#include "TransactionMQProducer.h" -#include "TransactionSendResult.h" -#include "common.h" - -using namespace rocketmq; - -std::atomic g_quit; -std::mutex g_mtx; -std::condition_variable g_finished; -TpsReportService g_tps; - -class MyTransactionListener : public TransactionListener { - virtual LocalTransactionState executeLocalTransaction(const MQMessage& msg, void* arg) { - if (!arg) { - std::cout << "executeLocalTransaction transactionId:" << msg.getTransactionId() - << ", return state: COMMIT_MESAGE " << endl; - return LocalTransactionState::COMMIT_MESSAGE; - } - - LocalTransactionState state = (LocalTransactionState)(*(int*)arg % 3); - std::cout << "executeLocalTransaction transactionId:" << msg.getTransactionId() << ", return state: " << state - << endl; - return state; - } - - virtual LocalTransactionState checkLocalTransaction(const MQMessageExt& msg) { - std::cout << "checkLocalTransaction enter msg:" << msg.toString() << endl; - return LocalTransactionState::COMMIT_MESSAGE; - } -}; - -void SyncProducerWorker(RocketmqSendAndConsumerArgs* info, TransactionMQProducer* producer) { - while (!g_quit.load()) { - if (g_msgCount.load() <= 0) { - std::this_thread::sleep_for(std::chrono::seconds(60)); - std::unique_lock lck(g_mtx); - g_finished.notify_one(); - break; - } - - MQMessage msg(info->topic, // topic - "*", // tag - info->body); // body - try { - auto start = std::chrono::system_clock::now(); - std::cout << "before sendMessageInTransaction" << endl; - LocalTransactionState state = LocalTransactionState::UNKNOWN; - TransactionSendResult sendResult = producer->sendMessageInTransaction(msg, &state); - std::cout << "after sendMessageInTransaction msgId: " << sendResult.getMsgId() << endl; - g_tps.Increment(); - --g_msgCount; - auto end = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - if (duration.count() >= 500) { - std::cout << "send RT more than: " << duration.count() << " ms with msgid: " << sendResult.getMsgId() << endl; - } - } catch (const MQException& e) { - std::cout << "send failed: " << e.what() << std::endl; - } - } -} - -int main(int argc, char* argv[]) { - RocketmqSendAndConsumerArgs info; - if (!ParseArgs(argc, argv, &info)) { - exit(-1); - } - PrintRocketmqSendAndConsumerArgs(info); - TransactionMQProducer producer("please_rename_unique_group_name"); - producer.setNamesrvAddr(info.namesrv); - producer.setNamesrvDomain(info.namesrv_domain); - producer.setGroupName(info.groupname); - producer.setInstanceName(info.groupname); - producer.setSessionCredentials("mq acesskey", "mq secretkey", "ALIYUN"); - producer.setSendMsgTimeout(500); - producer.setTcpTransportTryLockTimeout(1000); - producer.setTcpTransportConnectTimeout(400); - producer.setLogLevel(eLOG_LEVEL_DEBUG); - producer.setTransactionListener(new MyTransactionListener()); - producer.start(); - std::vector> work_pool; - auto start = std::chrono::system_clock::now(); - int msgcount = g_msgCount.load(); - g_tps.start(); - - int threadCount = info.thread_count; - for (int j = 0; j < threadCount; j++) { - std::shared_ptr th = std::make_shared(SyncProducerWorker, &info, &producer); - work_pool.push_back(th); - } - - { - std::unique_lock lck(g_mtx); - g_finished.wait(lck); - g_quit.store(true); - } - - auto end = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - - std::cout << "per msg time: " << duration.count() / (double)msgcount << "ms \n" - << "========================finished==============================\n"; - - for (size_t th = 0; th != work_pool.size(); ++th) { - work_pool[th]->join(); - } - - producer.shutdown(); - - return 0; -} diff --git a/example/common.h b/example/common.h deleted file mode 100644 index 065b759e8..000000000 --- a/example/common.h +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef ROCKETMQ_CLIENT4CPP_EXAMPLE_COMMON_H_ -#define ROCKETMQ_CLIENT4CPP_EXAMPLE_COMMON_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef WIN32 -#include "unistd.h" -#endif -#include "Arg_helper.h" -#include "DefaultMQProducer.h" -#include "DefaultMQPullConsumer.h" -#include "DefaultMQPushConsumer.h" -using namespace std; - -std::atomic g_msgCount(1); - -class RocketmqSendAndConsumerArgs { - public: - RocketmqSendAndConsumerArgs() - : body("msgbody for test"), - thread_count(std::thread::hardware_concurrency()), - broadcasting(false), - syncpush(false), - SelectUnactiveBroker(false), - IsAutoDeleteSendCallback(false), - retrytimes(5), - PrintMoreInfo(false) {} - - public: - std::string namesrv; - std::string namesrv_domain; - std::string groupname; - std::string topic; - std::string body; - int thread_count; - bool broadcasting; - bool syncpush; - bool SelectUnactiveBroker; // default select active broker - bool IsAutoDeleteSendCallback; - int retrytimes; // default retry 5 times; - bool PrintMoreInfo; -}; - -class TpsReportService { - public: - TpsReportService() : tps_interval_(1), quit_flag_(false), tps_count_(0) {} - - ~TpsReportService() { - quit_flag_.store(true); - if (tps_thread_ == nullptr) { - std::cout << "tps_thread_ is null" << std::endl; - return; - } - if (tps_thread_->joinable()) { - tps_thread_->join(); - } - } - - void start() { - if (tps_thread_ != nullptr) { - std::cout << "tps_thread_ is not null" << std::endl; - return; - } - tps_thread_.reset(new std::thread(std::bind(&TpsReportService::TpsReport, this))); - } - - void Increment() { ++tps_count_; } - - void TpsReport() { - while (!quit_flag_.load()) { - std::this_thread::sleep_for(tps_interval_); - std::cout << "tps: " << tps_count_.load() << std::endl; - tps_count_.store(0); - } - } - - private: - std::chrono::seconds tps_interval_; - std::shared_ptr tps_thread_; - std::atomic quit_flag_; - std::atomic tps_count_; -}; - -void PrintPullResult(rocketmq::PullResult* result) { - std::cout << result->toString() << std::endl; - if (result->pullStatus == rocketmq::FOUND) { - std::cout << result->toString() << endl; - std::vector::iterator it = result->msgFoundList.begin(); - for (; it != result->msgFoundList.end(); ++it) { - cout << "=======================================================" << endl << (*it).toString() << endl; - } - } -} - -static void PrintRocketmqSendAndConsumerArgs(const RocketmqSendAndConsumerArgs& info) { - std::cout << "nameserver: " << info.namesrv << endl - << "topic: " << info.topic << endl - << "groupname: " << info.groupname << endl - << "produce content: " << info.body << endl - << "msg count: " << g_msgCount.load() << endl - << "thread count: " << info.thread_count << endl; -} - -static void help() { - std::cout << "need option,like follow: \n" - << "-n nameserver addr, if not set -n and -i ,no nameSrv will be got \n" - "-i nameserver domain name, if not set -n and -i ,no nameSrv will be " - "got \n" - "-g groupname \n" - "-t msg topic \n" - "-m messagecout(default value: 1) \n" - "-c content(default value: only test ) \n" - "-b (BROADCASTING model, default value: CLUSTER) \n" - "-s sync push(default is async push)\n" - "-r setup retry times(default value: 5 times)\n" - "-u select active broker to send msg(default value: false)\n" - "-d use AutoDeleteSendcallback by cpp client(defalut value: false) \n" - "-T thread count of send msg or consume msg(defalut value: system cpu " - "core number) \n" - "-v print more details information \n"; -} - -static bool ParseArgs(int argc, char* argv[], RocketmqSendAndConsumerArgs* info) { -#ifndef WIN32 - int ch; - while ((ch = getopt(argc, argv, "n:i:g:t:m:c:b:s:h:r:T:bu")) != -1) { - switch (ch) { - case 'n': - info->namesrv.insert(0, optarg); - break; - case 'i': - info->namesrv_domain.insert(0, optarg); - break; - case 'g': - info->groupname.insert(0, optarg); - break; - case 't': - info->topic.insert(0, optarg); - break; - case 'm': - g_msgCount.store(atoi(optarg)); - break; - case 'c': - info->body.insert(0, optarg); - break; - case 'b': - info->broadcasting = true; - break; - case 's': - info->syncpush = true; - break; - case 'r': - info->retrytimes = atoi(optarg); - break; - case 'u': - info->SelectUnactiveBroker = true; - break; - case 'T': - info->thread_count = atoi(optarg); - break; - case 'v': - info->PrintMoreInfo = true; - break; - case 'h': - help(); - return false; - default: - help(); - return false; - } - } -#else - rocketmq::Arg_helper arg_help(argc, argv); - info->namesrv = arg_help.get_option_value("-n"); - info->namesrv_domain = arg_help.get_option_value("-i"); - info->groupname = arg_help.get_option_value("-g"); - info->topic = arg_help.get_option_value("-t"); - info->broadcasting = atoi(arg_help.get_option_value("-b").c_str()); - string msgContent(arg_help.get_option_value("-c")); - if (!msgContent.empty()) - info->body = msgContent; - info->syncpush = atoi(arg_help.get_option_value("-s").c_str()); - int retrytimes = atoi(arg_help.get_option_value("-r").c_str()); - if (retrytimes > 0) - info->retrytimes = retrytimes; - info->SelectUnactiveBroker = atoi(arg_help.get_option_value("-u").c_str()); - int thread_count = atoi(arg_help.get_option_value("-T").c_str()); - if (thread_count > 0) - info->thread_count = thread_count; - info->PrintMoreInfo = atoi(arg_help.get_option_value("-v").c_str()); - g_msgCount = atoi(arg_help.get_option_value("-m").c_str()); -#endif - if (info->groupname.empty() || info->topic.empty() || (info->namesrv_domain.empty() && info->namesrv.empty())) { - std::cout << "please use -g to setup groupname and -t setup topic \n"; - help(); - return false; - } - return true; -} -#endif // ROCKETMQ_CLIENT4CPP_EXAMPLE_COMMON_H_ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 000000000..fb5fbfd08 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,7 @@ +function(add_example name file) + add_executable(${name} ${file}) + target_link_libraries(${name} PRIVATE api ZLIB::ZLIB rocketmq) +endfunction() + +add_example(ons_example_producer ons/ExampleProducer.cpp) +add_example(ons_example_push_consumer ons/ExamplePushConsumer.cpp) \ No newline at end of file diff --git a/examples/ons/ExampleAsyncProducer.cpp b/examples/ons/ExampleAsyncProducer.cpp new file mode 100644 index 000000000..5a560d8a0 --- /dev/null +++ b/examples/ons/ExampleAsyncProducer.cpp @@ -0,0 +1,98 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "ons/ONSCallback.h" +#include "ons/ONSFactory.h" +#include "rocketmq/Logger.h" +#include +#include +#include +#include +#include + +using namespace std; +using namespace ons; + +std::mutex m1; +std::mutex m2; +std::condition_variable cv; + +class MyCallback : public SendCallbackONS { +public: + void onSuccess(SendResultONS& send_result) override { + std::lock_guard lg(m2); + success_num++; + std::cout << "send success, message_id: " << send_result.getMessageId() << ", total: " << success_num << std::endl; + if (success_num + failed_num == total) { + cv.notify_all(); + } + } + + void onException(ONSClientException& e) override { + std::lock_guard lg(m2); + failed_num++; + std::cout << "send failure, total: " << failed_num << std::endl; + std::cout << e.what() << std::endl; + if (success_num + failed_num == total) { + cv.notify_all(); + } + } + + static int success_num; + static int failed_num; + static int total; +}; + +int MyCallback::success_num = 0; +int MyCallback::failed_num = 0; +int MyCallback::total = 256; + +int main(int argc, char* argv[]) { + rocketmq::Logger& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Debug); + logger.init(); + + std::cout << "=======Before send message=======" << std::endl; + ONSFactoryProperty factoryInfo; + + /* + factoryInfo.setFactoryProperty(ONSFactoryProperty::GroupId, "Your-GroupId"); + factoryInfo.setFactoryProperty(ONSFactoryProperty::AccessKey, "Your-Access-Key"); + factoryInfo.setFactoryProperty(ONSFactoryProperty::SecretKey, "Your-Secret-Key"); + factoryInfo.setFactoryProperty(ONSFactoryProperty::NAMESRV_ADDR, "Your-Access-Point-URL"); + */ + + Producer* producer = nullptr; + producer = ONSFactory::getInstance()->createProducer(factoryInfo); + producer->start(); + Message msg("cpp_sdk_standard", "Your Tag", "Your Key", "This message body."); + + MyCallback m_callback; + for (int i = 0; i < MyCallback::total; ++i) { + msg.setTag(std::to_string(i)); + producer->sendAsync(msg, &m_callback); + } + + { + std::unique_lock lk(m1); + cv.wait(lk); + } + + producer->shutdown(); + std::cout << "=======After sending messages=======" << std::endl; + + return 0; +} \ No newline at end of file diff --git a/examples/ons/ExampleBroadcastingPushConsumer.cpp b/examples/ons/ExampleBroadcastingPushConsumer.cpp new file mode 100644 index 000000000..a355f30e4 --- /dev/null +++ b/examples/ons/ExampleBroadcastingPushConsumer.cpp @@ -0,0 +1,77 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include +#include +#include +#include + +#include "ons/MessageModel.h" +#include "ons/ONSFactory.h" + +#include "rocketmq/Logger.h" + +using namespace std; +using namespace ons; + +std::mutex console_mtx; + +class ExampleMessageListener : public MessageListener { +public: + Action consume(const Message& message, ConsumeContext& context) noexcept override { + std::lock_guard lk(console_mtx); + auto latency = std::chrono::system_clock::now() - message.getStoreTimestamp(); + auto latency2 = std::chrono::system_clock::now() - message.getBornTimestamp(); + std::cout << "Received a message. Topic: " << message.getTopic() << ", MsgId: " << message.getMsgID() + << ", Body-size: " << message.getBody().size() + << ", Current - Store-Time: " << std::chrono::duration_cast(latency).count() + << "ms, Current - Born-Time: " << std::chrono::duration_cast(latency2).count() + << "ms" << std::endl; + return Action::CommitMessage; + } +}; + +int main(int argc, char* argv[]) { + auto& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Debug); + logger.init(); + + std::cout << "=======Before consuming messages=======" << std::endl; + ONSFactoryProperty factory_property; + factory_property.setMessageModel(ONS_NAMESPACE::MessageModel::BROADCASTING); + + factory_property.setFactoryProperty(ons::ONSFactoryProperty::GroupId, "GID_cpp_sdk_standard"); + + PushConsumer* consumer = ONSFactory::getInstance()->createPushConsumer(factory_property); + + const char* topic = "cpp_sdk_standard"; + const char* tag = "*"; + + // register your own listener here to handle the messages received. + auto* messageListener = new ExampleMessageListener(); + consumer->subscribe(topic, tag); + consumer->registerMessageListener(messageListener); + + // Start this consumer + consumer->start(); + + // Keep main thread running until process finished. + std::this_thread::sleep_for(std::chrono::minutes(15)); + + consumer->shutdown(); + std::cout << "=======After consuming messages======" << std::endl; + return 0; +} \ No newline at end of file diff --git a/examples/ons/ExampleBroadcastingPushConsumerWithOffsetStore.cpp b/examples/ons/ExampleBroadcastingPushConsumerWithOffsetStore.cpp new file mode 100644 index 000000000..59da76202 --- /dev/null +++ b/examples/ons/ExampleBroadcastingPushConsumerWithOffsetStore.cpp @@ -0,0 +1,94 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include +#include +#include +#include + +#include "ons/MessageModel.h" +#include "ons/ONSClient.h" +#include "ons/ONSFactory.h" +#include "ons/OffsetStore.h" + +#include "rocketmq/Logger.h" + +using namespace std; +using namespace ons; + +std::mutex console_mtx; + +class ExampleMessageListener : public MessageListener { +public: + Action consume(const Message& message, ConsumeContext& context) noexcept override { + std::lock_guard lk(console_mtx); + auto latency = std::chrono::system_clock::now() - message.getStoreTimestamp(); + auto latency2 = std::chrono::system_clock::now() - message.getBornTimestamp(); + std::cout << "Received a message. Topic: " << message.getTopic() << ", MsgId: " << message.getMsgID() + << ", Body-size: " << message.getBody().size() + << ", Current - Store-Time: " << std::chrono::duration_cast(latency).count() + << "ms, Current - Born-Time: " << std::chrono::duration_cast(latency2).count() + << "ms" << std::endl; + return Action::CommitMessage; + } +}; + +class SampleOffsetStore : public ONS_NAMESPACE::OffsetStore { +public: + bool readOffset(const std::string& queue, std::int64_t& offset) override { + offset = 0; + return true; + } + + void writeOffset(const std::string& queue, std::int64_t offset) override { + std::cout << "Save " << queue << ":" << offset << std::endl; + } +}; + +int main(int argc, char* argv[]) { + auto& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Debug); + logger.init(); + + std::cout << "=======Before consuming messages=======" << std::endl; + ONSFactoryProperty factory_property; + factory_property.setMessageModel(ONS_NAMESPACE::MessageModel::BROADCASTING); + + factory_property.setFactoryProperty(ons::ONSFactoryProperty::GroupId, "GID_cpp_sdk_standard"); + + PushConsumer* consumer = ONSFactory::getInstance()->createPushConsumer(factory_property); + + const char* topic = "cpp_sdk_standard"; + const char* tag = "*"; + + // register your own listener here to handle the messages received. + auto* messageListener = new ExampleMessageListener(); + consumer->subscribe(topic, tag); + consumer->registerMessageListener(messageListener); + + auto offset_store = new SampleOffsetStore(); + consumer->withOffsetStore(std::unique_ptr(offset_store)); + + // Start this consumer + consumer->start(); + + // Keep main thread running until process finished. + std::this_thread::sleep_for(std::chrono::minutes(15)); + + consumer->shutdown(); + std::cout << "=======After consuming messages======" << std::endl; + return 0; +} \ No newline at end of file diff --git a/examples/ons/ExampleOnewayProducer.cpp b/examples/ons/ExampleOnewayProducer.cpp new file mode 100644 index 000000000..622312f9a --- /dev/null +++ b/examples/ons/ExampleOnewayProducer.cpp @@ -0,0 +1,59 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "ons/ONSFactory.h" +#include "rocketmq/Logger.h" +#include +#include +#include + +using namespace std; +using namespace ons; + +int main(int argc, char* argv[]) { + rocketmq::Logger& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Debug); + logger.init(); + + std::cout << "=======Before sending messages=======" << std::endl; + ONSFactoryProperty factoryInfo; + + /* + factoryInfo.setFactoryProperty(ONSFactoryProperty::GroupId, "Your-GroupId"); + factoryInfo.setFactoryProperty(ONSFactoryProperty::AccessKey, "Your-Access-Key"); + factoryInfo.setFactoryProperty(ONSFactoryProperty::SecretKey, "Your-Secret-Key"); + factoryInfo.setFactoryProperty(ONSFactoryProperty::NAMESRV_ADDR, "Your-Access-Point-URL"); + */ + + Producer* producer = nullptr; + producer = ONSFactory::getInstance()->createProducer(factoryInfo); + producer->start(); + Message msg("cpp_sdk_standard", "tagA", "ORDERID_100", "hello MQ_lingchu"); + + auto start = std::chrono::system_clock::now(); + int count = 32; + for (int i = 0; i < count; ++i) { + producer->sendOneway(msg); + } + auto interval = std::chrono::system_clock::now() - start; + std::cout << "Send " << count << " messages OK, costs " + << std::chrono::duration_cast(interval).count() << "ms" << std::endl; + + std::this_thread::sleep_for(std::chrono::seconds(1)); + producer->shutdown(); + std::cout << "=======After sending messages=======" << std::endl; + return 0; +} \ No newline at end of file diff --git a/examples/ons/ExampleOrderProducer.cpp b/examples/ons/ExampleOrderProducer.cpp new file mode 100644 index 000000000..87c25449e --- /dev/null +++ b/examples/ons/ExampleOrderProducer.cpp @@ -0,0 +1,38 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include +#include + +#include "ons/ONSFactory.h" +#include "ons/ONSFactoryProperty.h" + +using namespace ons; + +int main(int argc, char* argv[]) { + ONSFactoryProperty factory_property; + auto order_producer = ONSFactory::getInstance()->createOrderProducer(factory_property); + order_producer->start(); + + Message message("cpp_sdk_standard", "TagA", "Sample Body"); + SendResultONS send_result = order_producer->send(message, "message-group-0"); + + std::cout << send_result.getMessageId() << std::endl; + + order_producer->shutdown(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/ons/ExampleOrderPushConsumer.cpp b/examples/ons/ExampleOrderPushConsumer.cpp new file mode 100644 index 000000000..a934080d9 --- /dev/null +++ b/examples/ons/ExampleOrderPushConsumer.cpp @@ -0,0 +1,63 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include +#include +#include +#include + +#include "ons/ONSFactory.h" +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" + +using namespace ons; + +class ExampleMessageOrderListener : public ons::MessageOrderListener { +public: + OrderAction consume(const Message& message, const ConsumeOrderContext& context) noexcept override { + SPDLOG_INFO("Consume message[MsgId={}] OK", message.getMsgID()); + std::cout << "Consume Message[MsgId=" << message.getMsgID() << "] OK" << std::endl; + return OrderAction::Success; + } +}; + +int main(int argc, char* argv[]) { + rocketmq::Logger& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Debug); + logger.init(); + + std::cout << "=======Before consuming messages=======" << std::endl; + ONSFactoryProperty factory_property; + + factory_property.setFactoryProperty(ons::ONSFactoryProperty::GroupId, "GID_cpp_sdk_standard"); + + OrderConsumer* consumer = ONSFactory::getInstance()->createOrderConsumer(factory_property); + const char* topic = "cpp_sdk_standard"; + const char* tag = "*"; + + consumer->subscribe(topic, tag); + + auto listener = new ExampleMessageOrderListener; + consumer->registerMessageListener(listener); + + consumer->start(); + + std::this_thread::sleep_for(std::chrono::minutes(3)); + + consumer->shutdown(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/ons/ExampleProducer.cpp b/examples/ons/ExampleProducer.cpp new file mode 100644 index 000000000..911d01cc1 --- /dev/null +++ b/examples/ons/ExampleProducer.cpp @@ -0,0 +1,87 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "ons/ONSFactory.h" +#include "rocketmq/Logger.h" + +#include +#include +#include + +using namespace std; +using namespace ons; + +int main(int argc, char* argv[]) { + rocketmq::Logger& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Debug); + logger.init(); + + std::cout << "=======Before sending messages=======" << std::endl; + ONSFactoryProperty factoryInfo; + + /* + factoryInfo.setFactoryProperty(ONSFactoryProperty::GroupId, "Your-GroupId"); + factoryInfo.setFactoryProperty(ONSFactoryProperty::AccessKey, "Your-Access-Key"); + factoryInfo.setFactoryProperty(ONSFactoryProperty::SecretKey, "Your-Secret-Key"); + factoryInfo.setFactoryProperty(ONSFactoryProperty::NAMESRV_ADDR, "Your-Access-Point-URL"); + */ + + Producer* producer = nullptr; + producer = ONSFactory::getInstance()->createProducer(factoryInfo); + producer->start(); + Message msg("cpp_sdk_standard", "Your Tag", "Your Key", "This message body."); + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // Send with exception // + //////////////////////////////////////////////////////////////////////////////////////////////////// + auto start = std::chrono::system_clock::now(); + int count = 32; + for (int i = 0; i < count; ++i) { + try { + SendResultONS sendResult = producer->send(msg); + std::cout << "Message ID: " << sendResult.getMessageId() << std::endl; + } catch (ONSClientException& e) { + std::cout << "ErrorCode: " << e.what() << std::endl; + } + } + auto interval = std::chrono::system_clock::now() - start; + auto ms = std::chrono::duration_cast(interval).count(); + std::cout << "Send " << count << " messages OK, costs " << ms << "ms. AVG QPS: " << count * 1000 * 1.0 / ms + << " AVG Latency: " << ms * 1.0 / count << "ms" << std::endl; + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // Send without exception support // + //////////////////////////////////////////////////////////////////////////////////////////////////// + start = std::chrono::system_clock::now(); + for (int i = 0; i < count; i++) { + std::error_code ec; + SendResultONS send_result = producer->send(msg, ec); + if (ec) { + std::cerr << "Failed to send message. Cause: " << ec.message() << std::endl; + continue; + } + std::cout << "MessageID: " << send_result.getMessageId() << std::endl; + } + interval = std::chrono::system_clock::now() - start; + ms = std::chrono::duration_cast(interval).count(); + std::cout << "Send " << count << " messages OK, costs " << ms << "ms. AVG QPS: " << count * 1000 * 1.0 / ms + << " AVG Latency: " << ms * 1.0 / count << "ms" << std::endl; + + // Keep main thread running until process finished. + producer->shutdown(); + std::cout << "=======After sending messages=======" << std::endl; + return 0; +} \ No newline at end of file diff --git a/examples/ons/ExamplePushConsumer.cpp b/examples/ons/ExamplePushConsumer.cpp new file mode 100644 index 000000000..ddacea2fa --- /dev/null +++ b/examples/ons/ExamplePushConsumer.cpp @@ -0,0 +1,73 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "ons/ONSFactory.h" +#include "rocketmq/Logger.h" + +#include +#include +#include +#include + +using namespace std; +using namespace ons; + +std::mutex console_mtx; + +class ExampleMessageListener : public MessageListener { +public: + Action consume(const Message& message, ConsumeContext& context) noexcept override { + std::lock_guard lk(console_mtx); + auto latency = std::chrono::system_clock::now() - message.getStoreTimestamp(); + auto latency2 = std::chrono::system_clock::now() - message.getBornTimestamp(); + std::cout << "Received a message. Topic: " << message.getTopic() << ", MsgId: " << message.getMsgID() + << ", Body-size: " << message.getBody().size() << ", Tag: " << message.getTag() + << ", Current - Store-Time: " << std::chrono::duration_cast(latency).count() + << "ms, Current - Born-Time: " << std::chrono::duration_cast(latency2).count() + << "ms" << std::endl; + return Action::CommitMessage; + } +}; + +int main(int argc, char* argv[]) { + rocketmq::Logger& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Info); + logger.init(); + + std::cout << "=======Before consuming messages=======" << std::endl; + ONSFactoryProperty factory_property; + + factory_property.setFactoryProperty(ons::ONSFactoryProperty::GroupId, "GID_cpp_sdk_standard"); + + PushConsumer* consumer = ONSFactory::getInstance()->createPushConsumer(factory_property); + const char* topic = "cpp_sdk_standard"; + const char* tag = "*"; + + // register your own listener here to handle the messages received. + auto* messageListener = new ExampleMessageListener(); + consumer->subscribe(topic, tag); + consumer->registerMessageListener(messageListener); + + // Start this consumer + consumer->start(); + + // Keep main thread running until process finished. + std::this_thread::sleep_for(std::chrono::minutes(15)); + + consumer->shutdown(); + std::cout << "=======After consuming messages======" << std::endl; + return 0; +} \ No newline at end of file diff --git a/examples/ons/ExamplePushConsumerWithThrottle.cpp b/examples/ons/ExamplePushConsumerWithThrottle.cpp new file mode 100644 index 000000000..b7c0068c3 --- /dev/null +++ b/examples/ons/ExamplePushConsumerWithThrottle.cpp @@ -0,0 +1,78 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include +#include +#include +#include + +#include "ons/MessageModel.h" +#include "ons/ONSFactory.h" + +#include "rocketmq/Logger.h" + +using namespace std; +using namespace ons; + +std::mutex console_mtx; + +class ExampleMessageListener : public MessageListener { +public: + Action consume(const Message& message, ConsumeContext& context) noexcept override { + std::lock_guard lk(console_mtx); + auto latency = std::chrono::system_clock::now() - message.getStoreTimestamp(); + auto latency2 = std::chrono::system_clock::now() - message.getBornTimestamp(); + std::cout << "Received a message. Topic: " << message.getTopic() << ", MsgId: " << message.getMsgID() + << ", Body-size: " << message.getBody().size() << ", Tag: " << message.getTag() + << ", Current - Store-Time: " << std::chrono::duration_cast(latency).count() + << "ms, Current - Born-Time: " << std::chrono::duration_cast(latency2).count() + << "ms" << std::endl; + return Action::CommitMessage; + } +}; + +int main(int argc, char* argv[]) { + auto& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Debug); + logger.init(); + + const char* topic = "cpp_sdk_standard"; + const char* tag = "*"; + + std::cout << "=======Before consuming messages=======" << std::endl; + ONSFactoryProperty factory_property; + factory_property.setFactoryProperty(ons::ONSFactoryProperty::GroupId, "GID_cpp_sdk_standard"); + + // Client-side throttling + factory_property.throttle(topic, 16); + + PushConsumer* consumer = ONSFactory::getInstance()->createPushConsumer(factory_property); + + // register your own listener here to handle the messages received. + auto* messageListener = new ExampleMessageListener(); + consumer->subscribe(topic, tag); + consumer->registerMessageListener(messageListener); + + // Start this consumer + consumer->start(); + + // Keep main thread running until process finished. + std::this_thread::sleep_for(std::chrono::minutes(15)); + + consumer->shutdown(); + std::cout << "=======After consuming messages======" << std::endl; + return 0; +} \ No newline at end of file diff --git a/examples/ons/ExampleTimedMessage.cpp b/examples/ons/ExampleTimedMessage.cpp new file mode 100644 index 000000000..8aa394af2 --- /dev/null +++ b/examples/ons/ExampleTimedMessage.cpp @@ -0,0 +1,52 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "ons/ONSFactory.h" +#include "rocketmq/Logger.h" +#include +#include + +using namespace std; +using namespace ons; + +int main(int argc, char* argv[]) { + rocketmq::Logger& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Debug); + logger.init(); + + std::cout << "=======Before sending messages=======" << std::endl; + ONSFactoryProperty factoryInfo; + + Producer* producer = nullptr; + producer = ONSFactory::getInstance()->createProducer(factoryInfo); + producer->start(); + Message message("cpp_sdk_standard", "Your Tag", "Your Key", "This message body."); + + // Set a timepoint in the future, after which this message will be available. + message.setStartDeliverTime(std::chrono::system_clock::now() + std::chrono::seconds(10)); + + try { + SendResultONS sendResult = producer->send(message); + std::cout << "Message ID: " << sendResult.getMessageId() << std::endl; + } catch (ONSClientException& e) { + std::cout << "ErrorCode: " << e.what() << std::endl; + } + + // Keep main thread running until process finished. + producer->shutdown(); + std::cout << "=======After sending messages=======" << std::endl; + return 0; +} \ No newline at end of file diff --git a/examples/ons/ExampleTransactionProducer.cpp b/examples/ons/ExampleTransactionProducer.cpp new file mode 100644 index 000000000..c7876257f --- /dev/null +++ b/examples/ons/ExampleTransactionProducer.cpp @@ -0,0 +1,59 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "ons/TransactionProducer.h" + +#include +#include + +#include "absl/memory/memory.h" + +#include "ons/LocalTransactionChecker.h" +#include "ons/ONSFactory.h" +#include "ons/TransactionStatus.h" + +using namespace ons; + +namespace ons { + +class ExampleLocalTransactionChecker : public LocalTransactionChecker { +public: + TransactionStatus check(const Message& msg) noexcept override { return TransactionStatus::CommitTransaction; } +}; + +class ExampleLocalTransactionExecutor : public LocalTransactionExecuter { +public: + TransactionStatus execute(const Message& msg) noexcept override { return TransactionStatus::CommitTransaction; } +}; + +} // namespace ons + +int main(int argc, char* argv[]) { + ONSFactoryProperty factory_property; + + auto checker = absl::make_unique(); + auto executor = absl::make_unique(); + + auto transaction_producer = ONSFactory::getInstance()->createTransactionProducer(factory_property, checker.get()); + transaction_producer->start(); + + Message message("cpp_sdk_standard", "TagA", "Sample Body"); + transaction_producer->send(message, executor.get()); + + transaction_producer->shutdown(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/rocketmq/BUILD.bazel b/examples/rocketmq/BUILD.bazel new file mode 100644 index 000000000..9c7508aae --- /dev/null +++ b/examples/rocketmq/BUILD.bazel @@ -0,0 +1,147 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_binary") + +cc_binary( + name = "example_producer", + srcs = [ + "ExampleProducer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_fifo_producer", + srcs = [ + "ExampleFifoProducer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_async_producer", + srcs = [ + "ExampleAsyncProducer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "sql_producer", + srcs = [ + "SqlProducer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_push_consumer", + srcs = [ + "ExamplePushConsumer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_fifo_push_consumer", + srcs = [ + "ExampleFifoPushConsumer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "push_consumer_with_custom_executor", + srcs = [ + "PushConsumerWithCustomExecutor.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "push_consumer_with_throttle", + srcs = [ + "PushConsumerWithThrottle.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "sql_consumer", + srcs = [ + "SqlConsumer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_pull_consumer", + srcs = [ + "ExamplePullConsumer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "benchmark_push_consumer", + srcs = [ + "BenchmarkPushConsumer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_broadcast_push_consumer", + srcs = [ + "ExampleBroadcastPushConsumer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_transaction_producer", + srcs = [ + "ExampleTransactionProducer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) \ No newline at end of file diff --git a/examples/rocketmq/BenchmarkPushConsumer.cpp b/examples/rocketmq/BenchmarkPushConsumer.cpp new file mode 100644 index 000000000..400fa8071 --- /dev/null +++ b/examples/rocketmq/BenchmarkPushConsumer.cpp @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQPushConsumer.h" + +#include +#include +#include +#include +#include + +using namespace rocketmq; + +class CounterMessageListener : public StandardMessageListener { +public: + explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) { + } + + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { + counter_.fetch_add(msgs.size()); + return ConsumeMessageResult::SUCCESS; + } + +private: + std::atomic_long& counter_; +}; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + std::atomic_long counter(0); + + DefaultMQPushConsumer push_consumer("CID_sample"); + MessageListener* listener = new CounterMessageListener(counter); + + push_consumer.setGroupName("CID_sample"); + push_consumer.setInstanceName("CID_sample_member_0"); + push_consumer.subscribe("TopicTest", "*"); + push_consumer.setNamesrvAddr("11.167.164.105:9876"); + push_consumer.registerMessageListener(listener); + push_consumer.start(); + + std::atomic_bool stopped(false); + std::thread report_thread([&counter, &stopped]() { + while (!stopped) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + long qps; + while (true) { + qps = counter.load(std::memory_order_relaxed); + if (counter.compare_exchange_weak(qps, 0, std::memory_order_relaxed)) { + break; + } + } + std::cout << "QPS: " << qps << std::endl; + } + }); + + std::this_thread::sleep_for(std::chrono::minutes(30)); + stopped.store(true); + + if (report_thread.joinable()) { + report_thread.join(); + } + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/examples/rocketmq/ExampleAsyncProducer.cpp b/examples/rocketmq/ExampleAsyncProducer.cpp new file mode 100644 index 000000000..083cb34a0 --- /dev/null +++ b/examples/rocketmq/ExampleAsyncProducer.cpp @@ -0,0 +1,257 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/ErrorCode.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace rocketmq; + +int getAndReset(std::atomic_int& counter) { + int current; + while (true) { + current = counter.load(std::memory_order_relaxed); + if (counter.compare_exchange_weak(current, 0, std::memory_order_relaxed)) { + break; + } + } + return current; +} + +const std::string& alphaNumeric() { + static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + return alpha_numeric; +} + +std::string randomString(std::string::size_type len) { + std::string result; + result.reserve(len); + std::random_device rd; + std::mt19937 generator(rd()); + std::string source(alphaNumeric()); + std::string::size_type generated = 0; + while (generated < len) { + std::shuffle(source.begin(), source.end(), generator); + std::string::size_type delta = std::min({len - generated, source.length()}); + result.append(source.substr(0, delta)); + generated += delta; + } + return result; +} + +template +class RateLimiter { +public: + explicit RateLimiter(int permit) : permits_{0}, interval_(1000 / PARTITION), stopped_(false) { + int avg = permit / PARTITION; + for (auto& i : partition_) { + i = avg; + } + + int r = permit % PARTITION; + + if (r) { + int step = PARTITION / r; + for (int i = 0; i < r; ++i) { + partition_[i * step]++; + } + } + + auto lambda_tick = [this]() { + while (!stopped_) { + tick(); + } + }; + + thread_tick_ = std::thread(lambda_tick); + } + + ~RateLimiter() { + stopped_.store(true, std::memory_order_relaxed); + if (thread_tick_.joinable()) { + thread_tick_.join(); + } + } + + std::array& partition() { + return permits_; + } + + int slot() { + auto current = std::chrono::steady_clock::now(); + long ms = std::chrono::duration_cast(current.time_since_epoch()).count(); + return ms / interval_ % PARTITION; + } + + void acquire() { + int idx = slot(); + { + std::unique_lock lk(mtx_); + if (permits_[idx] > 0) { + --permits_[idx]; + return; + } + + // Reuse quota of the past half of the cycle + for (int j = 1; j <= PARTITION / 2; ++j) { + int index = idx - j; + if (index < 0) { + index += PARTITION; + } + if (permits_[index] > 0) { + --permits_[index]; + return; + } + } + + cv_.wait(lk, [this]() { + int idx = slot(); + return permits_[idx] > 0; + }); + idx = slot(); + --permits_[idx]; + } + } + + void tick() { + std::this_thread::sleep_for(std::chrono::milliseconds(1000 / PARTITION)); + int idx = slot(); + { + std::unique_lock lk(mtx_); + permits_[idx] = partition_[idx]; + cv_.notify_all(); + } + } + +private: + std::array partition_; + std::array permits_; + int interval_; + std::mutex mtx_; + std::condition_variable cv_; + + std::atomic_bool stopped_; + std::thread thread_tick_; +}; + +class SampleSendCallback : public rocketmq::SendCallback { +public: + SampleSendCallback(std::atomic_int& counter, std::atomic_int& error) : counter_(counter), error_(error) { + } + + void onSuccess(SendResult& send_result) noexcept override { + counter_.fetch_add(1, std::memory_order_relaxed); + } + + void onFailure(const std::error_code& ec) noexcept override { + error_.fetch_add(1, std::memory_order_relaxed); + } + +private: + std::atomic_int& counter_; + std::atomic_int& error_; +}; + +int main(int argc, char* argv[]) { + std::string::size_type body_size = 1024; + int duration = 300; + int qps = 1000; + + if (argc > 1) { + body_size = std::stoi(argv[1]); + } + + if (argc > 2) { + duration = std::stoi(argv[2]); + } + + if (argc > 3) { + qps = std::stoi(argv[3]); + } + + Logger& logger = getLogger(); + logger.setLevel(Level::Info); + logger.init(); + + const char* topic = "cpp_sdk_standard"; + const char* tag = "TagA"; + DefaultMQProducer producer("TestGroup"); + producer.setNamesrvAddr("47.98.116.189:80"); + producer.compressBodyThreshold(256); + const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; + producer.setCredentialsProvider(std::make_shared()); + producer.setResourceNamespace(resource_namespace); + producer.setRegion("cn-hangzhou-pre"); + MQMessage message; + message.setTopic(topic); + message.setTags(tag); + message.setKey("Yuck! Why-plural?"); + message.setBody(randomString(body_size)); + + std::cout << "Message Body: " << message.getBody() << std::endl; + + std::atomic_bool stopped(false); + + auto lambda = [&stopped, duration]() { + std::cout << "Benchmark will stop in " << duration << " seconds!" << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(duration)); + stopped.store(true); + }; + + std::thread t(lambda); + + std::atomic_int success(0); + std::atomic_int error(0); + + auto stats_lambda = [&stopped, &success, &error]() { + while (!stopped) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "Success:" << getAndReset(success) << ", Error: " << getAndReset(error) << std::endl; + } + }; + + std::thread stats_thread(stats_lambda); + + RateLimiter<10> rate_limiter(qps); + SampleSendCallback send_callback(success, error); + + try { + producer.start(); + while (!stopped) { + rate_limiter.acquire(); + producer.send(message, &send_callback, true); + } + } catch (...) { + std::cerr << "Ah...No!!!" << std::endl; + } + + if (stats_thread.joinable()) { + stats_thread.join(); + } + + if (t.joinable()) { + t.join(); + } + producer.shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/rocketmq/ExampleBroadcastPushConsumer.cpp b/examples/rocketmq/ExampleBroadcastPushConsumer.cpp new file mode 100644 index 000000000..529f1e603 --- /dev/null +++ b/examples/rocketmq/ExampleBroadcastPushConsumer.cpp @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQPushConsumer.h" + +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" + +#include +#include +#include +#include + +using namespace rocketmq; + +class SampleMQMessageListener : public StandardMessageListener { +public: + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { + for (const MQMessageExt& msg : msgs) { + SPDLOG_INFO("Receive a message. MessageId={}", msg.getMsgId()); + std::cout << "Received a message. MessageId: " << msg.getMsgId() << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + return ConsumeMessageResult::SUCCESS; + } +}; + +int main(int argc, char* argv[]) { + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + const char* cid = "GID_cpp_sdk_standard"; + const char* topic = "cpp_sdk_standard"; + const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; + + DefaultMQPushConsumer push_consumer(cid); + push_consumer.setMessageModel(MessageModel::BROADCASTING); + push_consumer.setResourceNamespace(resource_namespace); + push_consumer.setCredentialsProvider(std::make_shared()); + push_consumer.setNamesrvAddr("121.43.42.193:80"); + MessageListener* listener = new SampleMQMessageListener; + push_consumer.setGroupName(cid); + push_consumer.subscribe(topic, "*"); + push_consumer.registerMessageListener(listener); + push_consumer.start(); + + std::this_thread::sleep_for(std::chrono::minutes(60)); + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/examples/rocketmq/ExampleFifoProducer.cpp b/examples/rocketmq/ExampleFifoProducer.cpp new file mode 100644 index 000000000..c4d9e2304 --- /dev/null +++ b/examples/rocketmq/ExampleFifoProducer.cpp @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQProducer.h" +#include +#include +#include +#include + +using namespace rocketmq; + +const std::string& alphaNumeric() { + static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + return alpha_numeric; +} + +std::string randomString(std::string::size_type len) { + std::string result; + result.reserve(len); + std::random_device rd; + std::mt19937 generator(rd()); + std::string source(alphaNumeric()); + std::string::size_type generated = 0; + while (generated < len) { + std::shuffle(source.begin(), source.end(), generator); + std::string::size_type delta = std::min({len - generated, source.length()}); + result.append(source.substr(0, delta)); + generated += delta; + } + return result; +} + +int main(int argc, char* argv[]) { + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQProducer producer("TestGroup"); + + const char* topic = "lingchu_test_order_topic"; + const char* name_server = "120.25.100.131:8081"; + + producer.setNamesrvAddr(name_server); + producer.compressBodyThreshold(256); + const char* resource_namespace = "MQ_INST_1080056302921134_BXyTLppt"; + // producer.setRegion("cn-hangzhou-pre"); + producer.setResourceNamespace(resource_namespace); + producer.setCredentialsProvider(std::make_shared()); + + std::atomic_bool stopped; + std::atomic_long count(0); + + auto stats_lambda = [&] { + while (!stopped.load(std::memory_order_relaxed)) { + long cnt = count.load(std::memory_order_relaxed); + while (count.compare_exchange_weak(cnt, 0)) { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "QPS: " << cnt << std::endl; + } + }; + + std::thread stats_thread(stats_lambda); + + std::string body = randomString(1024 * 4); + std::cout << "Message body size: " << body.length() << std::endl; + + try { + producer.start(); + for (int i = 0; i < 16; ++i) { + std::string sharding_key = "sharding-key-" + std::to_string(i); + MQMessage message; + message.setTopic(topic); + message.setTags("TagA"); + message.setKey("Yuck! Why-plural?"); + message.setBody(body); + + SendResult sendResult = producer.send(message, sharding_key); + std::cout << sendResult.getMessageQueue().simpleName() << ": " << sendResult.getMsgId() << std::endl; + count++; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } catch (...) { + std::cerr << "Ah...No!!!" << std::endl; + } + + stopped.store(true, std::memory_order_relaxed); + if (stats_thread.joinable()) { + stats_thread.join(); + } + + producer.shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/rocketmq/ExampleFifoPushConsumer.cpp b/examples/rocketmq/ExampleFifoPushConsumer.cpp new file mode 100644 index 000000000..df039d7f8 --- /dev/null +++ b/examples/rocketmq/ExampleFifoPushConsumer.cpp @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "rocketmq/Logger.h" + +#include "rocketmq/MessageListener.h" +#include "spdlog/spdlog.h" + +#include "rocketmq/DefaultMQPushConsumer.h" + +using namespace rocketmq; + +class SampleMQMessageListener : public FifoMessageListener { +public: + ConsumeMessageResult consumeMessage(const MQMessageExt& message) override { + SPDLOG_INFO("Consume message[Topic={}, MessageId={}] OK", message.getTopic(), message.getMsgId()); + std::cout << "Consume Message[MsgId=" << message.getMsgId() << "] OK. Body Size: " << message.getBody().size() + << std::endl; + // std::this_thread::sleep_for(std::chrono::seconds(1)); + return ConsumeMessageResult::SUCCESS; + } +}; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + const char* group_id = "GID_lingchu_test_order"; + const char* topic = "lingchu_test_order_topic"; + const char* resource_namespace = "MQ_INST_1080056302921134_BXyTLppt"; + + DefaultMQPushConsumer push_consumer(group_id); + push_consumer.setResourceNamespace(resource_namespace); + push_consumer.setCredentialsProvider(std::make_shared()); + push_consumer.setNamesrvAddr("120.25.100.131:8081"); + FifoMessageListener* listener = new SampleMQMessageListener(); + push_consumer.setInstanceName("instance_0"); + push_consumer.subscribe(topic, "*"); + push_consumer.registerMessageListener(listener); + push_consumer.setConsumeThreadCount(4); + push_consumer.start(); + + std::this_thread::sleep_for(std::chrono::minutes(30)); + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/examples/rocketmq/ExampleProducer.cpp b/examples/rocketmq/ExampleProducer.cpp new file mode 100644 index 000000000..3e1cfc54c --- /dev/null +++ b/examples/rocketmq/ExampleProducer.cpp @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQProducer.h" +#include +#include +#include +#include + +using namespace rocketmq; + +const std::string& alphaNumeric() { + static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + return alpha_numeric; +} + +std::string randomString(std::string::size_type len) { + std::string result; + result.reserve(len); + std::random_device rd; + std::mt19937 generator(rd()); + std::string source(alphaNumeric()); + std::string::size_type generated = 0; + while (generated < len) { + std::shuffle(source.begin(), source.end(), generator); + std::string::size_type delta = std::min({len - generated, source.length()}); + result.append(source.substr(0, delta)); + generated += delta; + } + return result; +} + +int main(int argc, char* argv[]) { + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQProducer producer("TestGroup"); + + const char* topic = "cpp_sdk_standard"; + const char* name_server = "mq-inst-1080056302921134-bxuibml7.mq.cn-hangzhou.aliyuncs.com:80"; + + producer.setNamesrvAddr(name_server); + producer.compressBodyThreshold(256); + const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; + producer.setRegion("cn-hangzhou-pre"); + producer.setResourceNamespace(resource_namespace); + producer.setCredentialsProvider(std::make_shared()); + + std::atomic_bool stopped; + std::atomic_long count(0); + + auto stats_lambda = [&] { + while (!stopped.load(std::memory_order_relaxed)) { + long cnt = count.load(std::memory_order_relaxed); + while (count.compare_exchange_weak(cnt, 0)) { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "QPS: " << cnt << std::endl; + } + }; + + std::thread stats_thread(stats_lambda); + + std::string body = randomString(1024 * 4); + std::cout << "Message body size: " << body.length() << std::endl; + + try { + producer.start(); + for (int i = 0; i < 16; ++i) { + MQMessage message; + message.setTopic(topic); + message.setTags("TagA"); + message.setKey("Yuck! Why-plural?"); + message.setBody(body); + + SendResult sendResult = producer.send(message); + std::cout << sendResult.getMessageQueue().simpleName() << ": " << sendResult.getMsgId() << std::endl; + count++; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } catch (...) { + std::cerr << "Ah...No!!!" << std::endl; + } + + stopped.store(true, std::memory_order_relaxed); + if (stats_thread.joinable()) { + stats_thread.join(); + } + + producer.shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/rocketmq/ExamplePullConsumer.cpp b/examples/rocketmq/ExamplePullConsumer.cpp new file mode 100644 index 000000000..729963361 --- /dev/null +++ b/examples/rocketmq/ExamplePullConsumer.cpp @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "rocketmq/CredentialsProvider.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "rocketmq/Logger.h" + +int main(int argc, char* argv[]) { + const char* group = "GID_group003"; + const char* topic = "yc001"; + const char* resource_namespace = "MQ_INST_1973281269661160_BXmPlOA6"; + const char* name_server_list = "ipv4:11.165.223.199:9876"; + + rocketmq::Logger& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Debug); + logger.init(); + + rocketmq::DefaultMQPullConsumer pull_consumer(group); + pull_consumer.setResourceNamespace(resource_namespace); + pull_consumer.setCredentialsProvider(std::make_shared()); + pull_consumer.setNamesrvAddr(name_server_list); + pull_consumer.start(); + + std::future> future = pull_consumer.queuesFor(topic); + auto queues = future.get(); + + for (const auto& queue : queues) { + rocketmq::OffsetQuery offset_query; + offset_query.message_queue = queue; + offset_query.policy = rocketmq::QueryOffsetPolicy::BEGINNING; + auto offset_future = pull_consumer.queryOffset(offset_query); + int64_t offset = offset_future.get(); + std::cout << "offset: " << offset << std::endl; + } + + pull_consumer.shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/rocketmq/ExamplePushConsumer.cpp b/examples/rocketmq/ExamplePushConsumer.cpp new file mode 100644 index 000000000..d45e52af3 --- /dev/null +++ b/examples/rocketmq/ExamplePushConsumer.cpp @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "rocketmq/Logger.h" + +#include "spdlog/spdlog.h" + +#include "rocketmq/DefaultMQPushConsumer.h" + +using namespace rocketmq; + +class SampleMQMessageListener : public StandardMessageListener { +public: + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { + for (const MQMessageExt& msg : msgs) { + SPDLOG_INFO("Consume message[Topic={}, MessageId={}] OK", msg.getTopic(), msg.getMsgId()); + std::cout << "Consume Message[MsgId=" << msg.getMsgId() << "] OK. Body Size: " << msg.getBody().size() + << std::endl; + // std::this_thread::sleep_for(std::chrono::seconds(1)); + } + return ConsumeMessageResult::SUCCESS; + } +}; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + const char* group_id = "GID_cpp_sdk_standard"; + const char* topic = "cpp_sdk_standard"; + const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; + const char* name_server = "mq-inst-1080056302921134-bxuibml7.mq.cn-hangzhou.aliyuncs.com:80"; + + DefaultMQPushConsumer push_consumer(group_id); + push_consumer.setResourceNamespace(resource_namespace); + push_consumer.setCredentialsProvider(std::make_shared()); + push_consumer.setNamesrvAddr(name_server); + MessageListener* listener = new SampleMQMessageListener; + push_consumer.setInstanceName("instance_0"); + push_consumer.subscribe(topic, "*"); + push_consumer.registerMessageListener(listener); + push_consumer.setConsumeThreadCount(4); + push_consumer.start(); + + std::this_thread::sleep_for(std::chrono::minutes(30)); + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/examples/rocketmq/ExampleTransactionProducer.cpp b/examples/rocketmq/ExampleTransactionProducer.cpp new file mode 100644 index 000000000..1041b9267 --- /dev/null +++ b/examples/rocketmq/ExampleTransactionProducer.cpp @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQProducer.h" +#include + +using namespace ROCKETMQ_NAMESPACE; + +int main(int argc, char* argv[]) { + DefaultMQProducer producer("TestGroup"); + + const char* topic = "cpp_sdk_standard"; + const char* name_server = "47.98.116.189:80"; + + producer.setNamesrvAddr(name_server); + producer.compressBodyThreshold(256); + const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; + producer.setRegion("cn-hangzhou-pre"); + producer.setResourceNamespace(resource_namespace); + producer.setCredentialsProvider(std::make_shared()); + + MQMessage message; + message.setTopic(topic); + message.setTags("TagA"); + message.setKey("Yuck! Why-plural?"); + message.setBody("ABC"); + + producer.start(); + + auto transaction = producer.prepare(message); + + transaction->commit(); + + std::this_thread::sleep_for(std::chrono::minutes(30)); + + producer.shutdown(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/rocketmq/PushConsumerWithCustomExecutor.cpp b/examples/rocketmq/PushConsumerWithCustomExecutor.cpp new file mode 100644 index 000000000..82dd57de4 --- /dev/null +++ b/examples/rocketmq/PushConsumerWithCustomExecutor.cpp @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/State.h" +#include +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ExecutorImpl { +public: + ExecutorImpl() : state_(State::CREATED) { + } + + virtual ~ExecutorImpl() { + switch (state_.load(std::memory_order_relaxed)) { + case CREATED: + case STOPPING: + case STOPPED: + break; + + case STARTING: + case STARTED: + state_.store(State::STOPPED); + if (worker_.joinable()) { + worker_.join(); + } + break; + } + } + + void submit(const std::function& task) { + if (State::STOPPED == state_.load(std::memory_order_relaxed)) { + return; + } + + { + std::unique_lock lock(task_mtx_); + tasks_.push_back(task); + } + cv_.notify_one(); + } + + void start() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTING)) { + worker_ = std::thread(std::bind(&ExecutorImpl::loop, this)); + state_.store(State::STARTED); + } + } + + void stop() { + state_.store(State::STOPPED); + if (worker_.joinable()) { + worker_.join(); + } + } + +private: + void loop() { + while (state_.load(std::memory_order_relaxed) != State::STOPPED) { + std::function func; + { + std::unique_lock lk(task_mtx_); + if (!tasks_.empty()) { + func = tasks_.back(); + } + } + + if (func) { + func(); + } else { + std::unique_lock lk(task_mtx_); + cv_.wait_for(lk, std::chrono::seconds(3), + [&]() { return state_.load(std::memory_order_relaxed) == State::STOPPED || !tasks_.empty(); }); + } + } + } + + std::atomic state_; + std::vector> tasks_; + std::mutex task_mtx_; + std::condition_variable cv_; + std::thread worker_; +}; + +class SampleMQMessageListener : public StandardMessageListener { +public: + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { + std::lock_guard lk(console_mtx_); + for (const MQMessageExt& msg : msgs) { + std::cout << "Topic=" << msg.getTopic() << ", MsgId=" << msg.getMsgId() << ", Body=" << msg.getBody() + << std::endl; + } + return ConsumeMessageResult::SUCCESS; + } + +private: + std::mutex console_mtx_; +}; + +ROCKETMQ_NAMESPACE_END + +int main(int argc, char* argv[]) { + using namespace ROCKETMQ_NAMESPACE; + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQPushConsumer push_consumer("TestGroup"); + MessageListener* listener = new SampleMQMessageListener; + + auto pool = new ExecutorImpl; + pool->start(); + push_consumer.setCustomExecutor(std::bind(&ExecutorImpl::submit, pool, std::placeholders::_1)); + push_consumer.setGroupName("TestGroup"); + push_consumer.setInstanceName("CID_sample_member_0"); + push_consumer.subscribe("TestTopic", "*"); + push_consumer.setNamesrvAddr("11.167.164.105:9876"); + push_consumer.registerMessageListener(listener); + push_consumer.start(); + + std::this_thread::sleep_for(std::chrono::minutes(30)); + pool->stop(); + delete pool; + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/examples/rocketmq/PushConsumerWithThrottle.cpp b/examples/rocketmq/PushConsumerWithThrottle.cpp new file mode 100644 index 000000000..c03b6c54e --- /dev/null +++ b/examples/rocketmq/PushConsumerWithThrottle.cpp @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQPushConsumer.h" + +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class CounterMessageListener : public StandardMessageListener { +public: + explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) { + } + + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { + counter_.fetch_add(msgs.size()); + return ConsumeMessageResult::SUCCESS; + } + +private: + std::atomic_long& counter_; +}; + +ROCKETMQ_NAMESPACE_END + +int main(int argc, char* argv[]) { + + using namespace ROCKETMQ_NAMESPACE; + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + std::atomic_long counter(0); + + DefaultMQPushConsumer push_consumer("TestGroup"); + MessageListener* listener = new CounterMessageListener(counter); + + push_consumer.setGroupName("TestGroup"); + push_consumer.setInstanceName("CID_sample_member_0"); + push_consumer.subscribe("TestTopic", "*"); + push_consumer.setNamesrvAddr("11.167.164.105:9876"); + push_consumer.registerMessageListener(listener); + push_consumer.setThrottle("TestTopic", 20); + push_consumer.start(); + + std::atomic_bool stopped(false); + std::thread report_thread([&counter, &stopped]() { + while (!stopped) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + long qps; + while (true) { + qps = counter.load(std::memory_order_relaxed); + if (counter.compare_exchange_weak(qps, 0, std::memory_order_relaxed)) { + break; + } + } + std::cout << "QPS: " << qps << std::endl; + } + }); + + std::this_thread::sleep_for(std::chrono::seconds(10)); + stopped.store(true); + + if (report_thread.joinable()) { + report_thread.join(); + } + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/examples/rocketmq/SqlConsumer.cpp b/examples/rocketmq/SqlConsumer.cpp new file mode 100644 index 000000000..c07e4a034 --- /dev/null +++ b/examples/rocketmq/SqlConsumer.cpp @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQPushConsumer.h" + +#include +#include +#include +#include + +using namespace rocketmq; + +class SampleMQMessageListener : public StandardMessageListener { +public: + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { + std::lock_guard lk(console_mtx_); + for (const MQMessageExt& msg : msgs) { + std::cout << "Topic=" << msg.getTopic() << ", MsgId=" << msg.getMsgId() << ", Tag=" << msg.getTags() + << ", a=" << msg.getProperty("a") << ", Body=" << msg.getBody() << std::endl; + } + return ConsumeMessageResult::SUCCESS; + } + +private: + std::mutex console_mtx_; +}; + +int main(int argc, char* argv[]) { + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQPushConsumer push_consumer("TestGroup"); + MessageListener* listener = new SampleMQMessageListener; + + push_consumer.setGroupName("TestGroup"); + push_consumer.setInstanceName("CID_sample_member_0"); + std::string sql_filter("(TAGS is not null and TAGS in ('TagA', 'TagB')) and (a is not null and a between 0 and 3)"); + push_consumer.subscribe("TestTopic", sql_filter, ExpressionType::SQL92); + // push_consumer.setNamesrvAddr("11.167.164.105:9876"); + push_consumer.registerMessageListener(listener); + push_consumer.start(); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/examples/rocketmq/SqlProducer.cpp b/examples/rocketmq/SqlProducer.cpp new file mode 100644 index 000000000..bc0fb4744 --- /dev/null +++ b/examples/rocketmq/SqlProducer.cpp @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQProducer.h" +#include + +using namespace rocketmq; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQProducer producer("PID_sample"); + producer.setNamesrvAddr("11.167.164.105:9876"); + + MQMessage message; + message.setTopic("TestTopic"); + try { + producer.start(); + for (int i = 0; i < 8; ++i) { + std::string body = std::to_string(i); + message.setBody(body); + message.setProperty("a", std::to_string(i % 5)); + switch (i % 3) { + case 0: + message.setTags("TagA"); + break; + case 1: + message.setTags("TagB"); + break; + case 2: + message.setTags("TagC"); + break; + } + SendResult sendResult = producer.send(message); + std::cout << "Message sent with msgId=" << sendResult.getMsgId() + << ", Queue=" << sendResult.getMessageQueue().simpleName() << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } catch (...) { + std::cerr << "Ah...No!!!" << std::endl; + } + producer.shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/format.sh b/format.sh deleted file mode 100755 index 7aa812efe..000000000 --- a/format.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -TMPFILE=".clang_format_file.tmp" -FORMAT="{BasedOnStyle: Chromium, ColumnLimit: 120, TabWidth: 2}" - -function Usage -{ - echo "Usage: $0 want-format-file|want-format-dir ..." - #echo "Currently only format a file or dir at a time" -} - -#Setp1 check clang-format support -if ! which clang-format &> /dev/null; then - echo -e "\033[32m !!!!!!please install clang-format \033[0m" - exit 1 -fi - - -#Setp2 check weather incoming format file -if [ ! $# -ge 1 ];then - Usage - exit 1 -fi - -for dest in "$@" -do - if [ ! -e $dest ]; then - echo -e "\033[32m $dest not exists,please check this file weather exists \033[0m" - fi -done - - -#Setp3 get filelist -for dest in $* -do - if [ -f $dest ];then - files="$files $dest" - elif [ -d $dest ];then - files="$files `ls $dest/*.cpp $dest/*.h $dest/*.cc 2>/dev/null`" - else - echo -e "\033[32m $dest sorry current $0 only support regular file or dir \033[0m" - fi -done - -#Setp4 use clang-format format dest file -for file in $files -do - echo $file - clang-format $file > $TMPFILE - - if [ -e $TMPFILE ];then - filesize=`wc -c $TMPFILE |cut -d " " -f1` - if [ $filesize -eq 0 ];then - echo -e "\033[32m formt file error,May be because of the size of the source file is 0, or format program error \033[0m" - exit 1 - fi - fi - - #Setp4 replace source file - mv -f $TMPFILE $file -done diff --git a/include/Arg_helper.h b/include/Arg_helper.h deleted file mode 100644 index bc2896232..000000000 --- a/include/Arg_helper.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _ARG_HELPER_H_ -#define _ARG_HELPER_H_ - -#include -#include -#include "RocketMQClient.h" - -namespace rocketmq { -class ROCKETMQCLIENT_API Arg_helper { - public: - Arg_helper(int argc, char* argv[]); - Arg_helper(std::string arg_str_); - std::string get_option(int idx_) const; - bool is_enable_option(std::string opt_) const; - std::string get_option_value(std::string opt_) const; - - private: - std::vector m_args; -}; - -} // namespace rocketmq - -#endif //& msgs); - virtual SendResult send(std::vector& msgs, const MQMessageQueue& mq); - virtual void send(MQMessage& msg, SendCallback* pSendCallback, bool bSelectActiveBroker = false); - virtual void send(MQMessage& msg, const MQMessageQueue& mq, SendCallback* pSendCallback); - virtual void send(MQMessage& msg, MessageQueueSelector* selector, void* arg, SendCallback* pSendCallback); - virtual void sendOneway(MQMessage& msg, bool bSelectActiveBroker = false); - virtual void sendOneway(MQMessage& msg, const MQMessageQueue& mq); - virtual void sendOneway(MQMessage& msg, MessageQueueSelector* selector, void* arg); - - const std::string& getNamesrvAddr() const; - void setNamesrvAddr(const std::string& namesrvAddr); - - void setSessionCredentials(const std::string& accessKey, - const std::string& secretKey, - const std::string& accessChannel); - const SessionCredentials& getSessionCredentials() const; - - const std::string& getNamesrvDomain() const; - void setNamesrvDomain(const std::string& namesrvDomain); - - const std::string& getNameSpace() const; - void setNameSpace(const std::string& nameSpace); - - const std::string& getGroupName() const; - void setGroupName(const std::string& groupname); - - const std::string& getInstanceName() const; - void setInstanceName(const std::string& instanceName); - - /** - * Log configuration interface, default LOG_LEVEL is LOG_LEVEL_INFO, default - * log file num is 3, each log size is 100M - **/ - void setLogLevel(elogLevel inputLevel); - elogLevel getLogLevel(); - void setLogPath(const std::string& logPath); - void setLogFileSizeAndNum(int fileNum, long perFileSize); // perFileSize is MB unit - - int getSendMsgTimeout() const; - void setSendMsgTimeout(int sendMsgTimeout); - - /* - * If msgBody size is large than compressMsgBodyOverHowmuch - * rocketmq cpp will compress msgBody according to compressLevel - */ - int getCompressMsgBodyOverHowmuch() const; - void setCompressMsgBodyOverHowmuch(int compressMsgBodyOverHowmuch); - int getCompressLevel() const; - void setCompressLevel(int compressLevel); - - int getMaxMessageSize() const; - void setMaxMessageSize(int maxMessageSize); - - int getRetryTimes() const; - void setRetryTimes(int times); - - int getRetryTimes4Async() const; - void setRetryTimes4Async(int times); - - /** Set TcpTransport pull thread num, which dermine the num of threads to - * distribute network data, - * 1. its default value is CPU num, it must be setted before producer/consumer - * start, minimum value is CPU num; - * 2. this pullThread num must be tested on your environment to find the best - * value for RT of sendMsg or delay time of consume msg before you change it; - * 3. producer and consumer need different pullThread num, if set this num, - * producer and consumer must set different instanceName. - **/ - void setTcpTransportPullThreadNum(int num); - int getTcpTransportPullThreadNum() const; - - /** Timeout of tcp connect, it is same meaning for both producer and consumer; - * 1. default value is 3000ms - * 2. input parameter could only be milliSecond, suggestion value is - * 1000-3000ms; - **/ - void setTcpTransportConnectTimeout(uint64_t timeout); // ms - uint64_t getTcpTransportConnectTimeout() const; - - /** Timeout of tryLock tcpTransport before sendMsg/pullMsg, if timeout, - * returns NULL - * 1. paremeter unit is ms, default value is 3000ms, the minimun value is 1000ms - * suggestion value is 3000ms; - * 2. if configured with value smaller than 1000ms, the tryLockTimeout value - * will be setted to 1000ms - **/ - void setTcpTransportTryLockTimeout(uint64_t timeout); // ms - uint64_t getTcpTransportTryLockTimeout() const; - - void setUnitName(std::string unitName); - const std::string& getUnitName() const; - void setMessageTrace(bool messageTrace); - bool getMessageTrace() const; - - void setEnableSsl(bool enableSsl); - bool getEnableSsl() const; - - void setSslPropertyFile(const std::string& sslPropertyFile); - const std::string& getSslPropertyFile() const; - - private: - DefaultMQProducerImpl* impl; -}; -} // namespace rocketmq -#endif diff --git a/include/DefaultMQPullConsumer.h b/include/DefaultMQPullConsumer.h deleted file mode 100644 index 0883bca0b..000000000 --- a/include/DefaultMQPullConsumer.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEFAULTMQPULLCONSUMER_H__ -#define __DEFAULTMQPULLCONSUMER_H__ - -#include -#include -#include "AsyncCallback.h" -#include "ConsumeType.h" -#include "MQClient.h" -#include "MQMessage.h" -#include "MQMessageExt.h" -#include "MQMessageQueue.h" -#include "MQueueListener.h" -#include "PullResult.h" -#include "RocketMQClient.h" -#include "SessionCredentials.h" - -namespace rocketmq { -class SubscriptionData; -class DefaultMQPullConsumerImpl; -class ROCKETMQCLIENT_API DefaultMQPullConsumer { - public: - DefaultMQPullConsumer(const std::string& groupname); - virtual ~DefaultMQPullConsumer(); - - virtual void start(); - virtual void shutdown(); - virtual std::string version(); - - const std::string& getNamesrvAddr() const; - void setNamesrvAddr(const std::string& namesrvAddr); - - void setSessionCredentials(const std::string& accessKey, - const std::string& secretKey, - const std::string& accessChannel); - const SessionCredentials& getSessionCredentials() const; - - const std::string& getNamesrvDomain() const; - void setNamesrvDomain(const std::string& namesrvDomain); - - const std::string& getInstanceName() const; - void setInstanceName(const std::string& instanceName); - - const std::string& getNameSpace() const; - void setNameSpace(const std::string& nameSpace); - - const std::string& getGroupName() const; - void setGroupName(const std::string& groupname); - - void setEnableSsl(bool enableSsl); - bool getEnableSsl() const; - - void setSslPropertyFile(const std::string& sslPropertyFile); - const std::string& getSslPropertyFile() const; - - /** - * Log configuration interface, default LOG_LEVEL is LOG_LEVEL_INFO, default - * log file num is 3, each log size is 100M - **/ - void setLogLevel(elogLevel inputLevel); - elogLevel getLogLevel(); - void setLogPath(const std::string& logPath); - void setLogFileSizeAndNum(int fileNum, long perFileSize); // perFileSize is MB unit - - virtual void fetchSubscribeMessageQueues(const std::string& topic, std::vector& mqs); - - /** - * Pull message from specified queue, if no msg in queue, return directly - * - * @param mq - * specify the pulled queue - * @param subExpression - * set filter expression for pulled msg, broker will filter msg actively - * Now only OR operation is supported, eg: "tag1 || tag2 || tag3" - * if subExpression is setted to "null" or "*", all msg will be subscribed - * @param offset - * specify the started pull offset - * @param maxNums - * specify max msg num by per pull - * @return - * PullResult - */ - virtual PullResult pull(const MQMessageQueue& mq, const std::string& subExpression, int64 offset, int maxNums); - virtual void pull(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums, - PullCallback* pPullCallback); - - /** - * Pull msg from specified queue, if no msg, broker will suspend the pull request 20s - * - * @param mq - * specify the pulled queue - * @param subExpression - * set filter expression for pulled msg, broker will filter msg actively - * Now only OR operation is supported, eg: "tag1 || tag2 || tag3" - * if subExpression is setted to "null" or "*", all msg will be subscribed - * @param offset - * specify the started pull offset - * @param maxNums - * specify max msg num by per pull - * @return - * accroding to PullResult - */ - virtual PullResult pullBlockIfNotFound(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums); - virtual void pullBlockIfNotFound(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums, - PullCallback* pPullCallback); - - void persistConsumerOffset(); - void persistConsumerOffsetByResetOffset(); - void updateTopicSubscribeInfo(const std::string& topic, std::vector& info); - ConsumeFromWhere getConsumeFromWhere(); - void getSubscriptions(std::vector&); - void updateConsumeOffset(const MQMessageQueue& mq, int64 offset); - void removeConsumeOffset(const MQMessageQueue& mq); - - void registerMessageQueueListener(const std::string& topic, MQueueListener* pListener); - - int64 fetchConsumeOffset(const MQMessageQueue& mq, bool fromStore); - - void fetchMessageQueuesInBalance(const std::string& topic, std::vector mqs); - - void persistConsumerOffset4PullConsumer(const MQMessageQueue& mq); - - private: - DefaultMQPullConsumerImpl* impl; -}; -} // namespace rocketmq -#endif diff --git a/include/DefaultMQPushConsumer.h b/include/DefaultMQPushConsumer.h deleted file mode 100644 index 5d3bc3523..000000000 --- a/include/DefaultMQPushConsumer.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEFAULTMQPUSHCONSUMER_H__ -#define __DEFAULTMQPUSHCONSUMER_H__ - -#include -#include "AsyncCallback.h" -#include "ConsumeType.h" -#include "MQClient.h" -#include "MQMessageListener.h" -#include "MQMessageQueue.h" -#include "SessionCredentials.h" - -namespace rocketmq { -class DefaultMQPushConsumerImpl; -class ROCKETMQCLIENT_API DefaultMQPushConsumer { - public: - DefaultMQPushConsumer(const std::string& groupname); - - virtual ~DefaultMQPushConsumer(); - - virtual void start(); - virtual void shutdown(); - virtual std::string version(); - - const std::string& getNamesrvAddr() const; - void setNamesrvAddr(const std::string& namesrvAddr); - - void setSessionCredentials(const std::string& accessKey, - const std::string& secretKey, - const std::string& accessChannel); - const SessionCredentials& getSessionCredentials() const; - - void subscribe(const std::string& topic, const std::string& subExpression); - - void registerMessageListener(MQMessageListener* pMessageListener); - MessageListenerType getMessageListenerType(); - - MessageModel getMessageModel() const; - void setMessageModel(MessageModel messageModel); - - void setConsumeFromWhere(ConsumeFromWhere consumeFromWhere); - ConsumeFromWhere getConsumeFromWhere(); - - const std::string& getNamesrvDomain() const; - void setNamesrvDomain(const std::string& namesrvDomain); - - const std::string& getInstanceName() const; - void setInstanceName(const std::string& instanceName); - - const std::string& getNameSpace() const; - void setNameSpace(const std::string& nameSpace); - - const std::string& getGroupName() const; - void setGroupName(const std::string& groupname); - - void setEnableSsl(bool enableSsl); - bool getEnableSsl() const; - - void setSslPropertyFile(const std::string& sslPropertyFile); - const std::string& getSslPropertyFile() const; - - /** - * Log configuration interface, default LOG_LEVEL is LOG_LEVEL_INFO, default - * log file num is 3, each log size is 100M - **/ - void setLogLevel(elogLevel inputLevel); - elogLevel getLogLevel(); - void setLogPath(const std::string& logPath); - void setLogFileSizeAndNum(int fileNum, long perFileSize); // perFileSize is MB unit - - void setConsumeThreadCount(int threadCount); - int getConsumeThreadCount() const; - - void setMaxReconsumeTimes(int maxReconsumeTimes); - int getMaxReconsumeTimes() const; - - void setPullMsgThreadPoolCount(int threadCount); - int getPullMsgThreadPoolCount() const; - - void setConsumeMessageBatchMaxSize(int consumeMessageBatchMaxSize); - int getConsumeMessageBatchMaxSize() const; - - /** - * Set max cache msg size perQueue in memory if consumer could not consume msgs - * immediately - * default maxCacheMsgSize perQueue is 1000, set range is:1~65535 - **/ - void setMaxCacheMsgSizePerQueue(int maxCacheSize); - int getMaxCacheMsgSizePerQueue() const; - - /** Set TcpTransport pull thread num, which dermine the num of threads to - * distribute network data, - * 1. its default value is CPU num, it must be setted before producer/consumer - * start, minimum value is CPU num; - * 2. this pullThread num must be tested on your environment to find the best - * value for RT of sendMsg or delay time of consume msg before you change it; - * 3. producer and consumer need different pullThread num, if set this num, - * producer and consumer must set different instanceName. - **/ - void setTcpTransportPullThreadNum(int num); - int getTcpTransportPullThreadNum() const; - - /** Timeout of tcp connect, it is same meaning for both producer and consumer; - * 1. default value is 3000ms - * 2. input parameter could only be milliSecond, suggestion value is - * 1000-3000ms; - **/ - void setTcpTransportConnectTimeout(uint64_t timeout); // ms - uint64_t getTcpTransportConnectTimeout() const; - - /** Timeout of tryLock tcpTransport before sendMsg/pullMsg, if timeout, - * returns NULL - * 1. paremeter unit is ms, default value is 3000ms, the minimun value is 1000ms - * suggestion value is 3000ms; - * 2. if configured with value smaller than 1000ms, the tryLockTimeout value - * will be setted to 1000ms - **/ - void setTcpTransportTryLockTimeout(uint64_t timeout); // ms - uint64_t getTcpTransportTryLockTimeout() const; - - void setUnitName(std::string unitName); - const std::string& getUnitName() const; - - void setAsyncPull(bool asyncFlag); - void setMessageTrace(bool messageTrace); - bool getMessageTrace() const; - - private: - DefaultMQPushConsumerImpl* impl; -}; -} // namespace rocketmq -#endif diff --git a/include/MQClient.h b/include/MQClient.h deleted file mode 100644 index db48d2092..000000000 --- a/include/MQClient.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __MQADMIN_H__ -#define __MQADMIN_H__ - -namespace rocketmq { - -enum elogLevel { - eLOG_LEVEL_FATAL = 1, - eLOG_LEVEL_ERROR = 2, - eLOG_LEVEL_WARN = 3, - eLOG_LEVEL_INFO = 4, - eLOG_LEVEL_DEBUG = 5, - eLOG_LEVEL_TRACE = 6, - eLOG_LEVEL_LEVEL_NUM = 7 -}; - -} // namespace rocketmq -#endif diff --git a/include/MQClientException.h b/include/MQClientException.h deleted file mode 100644 index 883cd64ae..000000000 --- a/include/MQClientException.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __MQCLIENTEXCEPTION_H__ -#define __MQCLIENTEXCEPTION_H__ - -#include -#include -#include -#include -#include -#include "RocketMQClient.h" - -namespace rocketmq { -class ROCKETMQCLIENT_API MQException : public std::exception { - public: - MQException(const std::string& msg, int error, const char* file, int line) throw() - : m_error(error), m_line(line), m_file(file) { - try { - std::stringstream ss; - ss << "msg: " << msg << ",error:" << error << ",in file <" << file << "> line:" << line; - m_msg = ss.str(); - } catch (...) { - } - } - - MQException(const std::string& msg, int error, const char* file, const char* type, int line) throw() - : m_error(error), m_line(line), m_file(file), m_type(type) { - try { - std::stringstream ss; - ss << "msg: " << msg << ",error:" << error << ",in file <" << file << "> line:" << line; - m_msg = ss.str(); - } catch (...) { - } - } - - virtual ~MQException() throw() {} - const char* what() const throw() { return m_msg.c_str(); } - int GetError() const throw() { return m_error; } - virtual const char* GetType() const throw() { return m_type.c_str(); } - int GetLine() { return m_line; } - const char* GetFile() { return m_file.c_str(); } - - protected: - int m_error; - int m_line; - std::string m_msg; - std::string m_file; - std::string m_type; -}; - -inline std::ostream& operator<<(std::ostream& os, const MQException& e) { - os << "Type: " << e.GetType() << " , " << e.what(); - return os; -} - -#define DEFINE_MQCLIENTEXCEPTION(name) \ - class ROCKETMQCLIENT_API name : public MQException { \ - public: \ - name(const std::string& msg, int error, const char* file, int line) throw() \ - : MQException(msg, error, file, #name, line) {} \ - virtual const char* GetType() const throw() { return m_type.c_str(); } \ - }; - -DEFINE_MQCLIENTEXCEPTION(MQClientException) -DEFINE_MQCLIENTEXCEPTION(MQBrokerException) -DEFINE_MQCLIENTEXCEPTION(InterruptedException) -DEFINE_MQCLIENTEXCEPTION(RemotingException) -DEFINE_MQCLIENTEXCEPTION(UnknownHostException) - -#define THROW_MQEXCEPTION(e, msg, err) throw e(msg, err, __FILE__, __LINE__) -#define NEW_MQEXCEPTION(e, msg, err) e(msg, err, __FILE__, __LINE__) - -} // namespace rocketmq -#endif diff --git a/include/MQMessage.h b/include/MQMessage.h deleted file mode 100644 index dd2f7c010..000000000 --- a/include/MQMessage.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __MESSAGE_H__ -#define __MESSAGE_H__ - -#include -#include -#include -#include -#include "RocketMQClient.h" - -namespace rocketmq { -class ROCKETMQCLIENT_API MQMessage { - public: - MQMessage(); - MQMessage(const std::string& topic, const std::string& body); - MQMessage(const std::string& topic, const std::string& tags, const std::string& body); - MQMessage(const std::string& topic, const std::string& tags, const std::string& keys, const std::string& body); - MQMessage(const std::string& topic, - const std::string& tags, - const std::string& keys, - const int flag, - const std::string& body, - bool waitStoreMsgOK); - - virtual ~MQMessage(); - MQMessage(const MQMessage& other); - MQMessage& operator=(const MQMessage& other); - - void setProperty(const std::string& name, const std::string& value); - const std::string& getProperty(const std::string& name) const; - - const std::string& getTopic() const; - void setTopic(const std::string& topic); - void setTopic(const char* body, int len); - - const std::string& getTags() const; - void setTags(const std::string& tags); - - const std::string& getKeys() const; - void setKeys(const std::string& keys); - void setKeys(const std::vector& keys); - - int getDelayTimeLevel() const; - void setDelayTimeLevel(int level); - - bool isWaitStoreMsgOK() const; - void setWaitStoreMsgOK(bool waitStoreMsgOK); - - int getFlag() const; - void setFlag(int flag); - - int getSysFlag() const; - void setSysFlag(int sysFlag); - - const std::string& getBody() const; - - void setBody(const char* body, int len); - void setBody(const std::string& body); - - void setTransactionId(const std::string& id) { m_transactionId = id; } - std::string getTransactionId() const { return m_transactionId; } - - std::map getProperties() const; - void setProperties(std::map& properties); - - const std::string toString() const { - std::stringstream ss; - std::string tags = getTags(); - ss << "Message [topic=" << m_topic << ", flag=" << m_flag << ", tag=" << tags << "]"; - return ss.str(); - } - - protected: - friend class MQDecoder; - void setPropertyInternal(const std::string& name, const std::string& value); - void setPropertiesInternal(std::map& properties); - - void Init(const std::string& topic, - const std::string& tags, - const std::string& keys, - const int flag, - const std::string& body, - bool waitStoreMsgOK); - - public: - static const std::string PROPERTY_KEYS; - static const std::string PROPERTY_TAGS; - static const std::string PROPERTY_WAIT_STORE_MSG_OK; - static const std::string PROPERTY_DELAY_TIME_LEVEL; - static const std::string PROPERTY_RETRY_TOPIC; - static const std::string PROPERTY_REAL_TOPIC; - static const std::string PROPERTY_REAL_QUEUE_ID; - static const std::string PROPERTY_TRANSACTION_PREPARED; - static const std::string PROPERTY_PRODUCER_GROUP; - static const std::string PROPERTY_MIN_OFFSET; - static const std::string PROPERTY_MAX_OFFSET; - - static const std::string PROPERTY_BUYER_ID; - static const std::string PROPERTY_ORIGIN_MESSAGE_ID; - static const std::string PROPERTY_TRANSFER_FLAG; - static const std::string PROPERTY_CORRECTION_FLAG; - static const std::string PROPERTY_MQ2_FLAG; - static const std::string PROPERTY_RECONSUME_TIME; - static const std::string PROPERTY_MSG_REGION; - static const std::string PROPERTY_TRACE_SWITCH; - static const std::string PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX; - static const std::string PROPERTY_MAX_RECONSUME_TIMES; - static const std::string PROPERTY_CONSUME_START_TIMESTAMP; - static const std::string PROPERTY_TRANSACTION_PREPARED_QUEUE_OFFSET; - static const std::string PROPERTY_TRANSACTION_CHECK_TIMES; - static const std::string PROPERTY_CHECK_IMMUNITY_TIME_IN_SECONDS; - - static const std::string KEY_SEPARATOR; - - protected: - int m_sysFlag; - - private: - std::string m_topic; - int m_flag; - std::string m_body; - std::string m_transactionId; - std::map m_properties; -}; -} // namespace rocketmq -#endif diff --git a/include/MQMessageExt.h b/include/MQMessageExt.h deleted file mode 100644 index 969f04bce..000000000 --- a/include/MQMessageExt.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __MESSAGEEXT_H__ -#define __MESSAGEEXT_H__ - -#ifdef WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#else -#include -#endif - -#include "MQMessage.h" -#include "RocketMQClient.h" - -namespace rocketmq { -// -#include "MQMessageExt.h" -#include "MQMessageQueue.h" - -namespace rocketmq { -//& msgs) = 0; - virtual MessageListenerType getMessageListenerType() { return messageListenerDefaultly; } -}; - -class ROCKETMQCLIENT_API MessageListenerOrderly : public MQMessageListener { - public: - virtual ~MessageListenerOrderly() {} - virtual ConsumeStatus consumeMessage(const std::vector& msgs) = 0; - virtual MessageListenerType getMessageListenerType() { return messageListenerOrderly; } -}; - -class ROCKETMQCLIENT_API MessageListenerConcurrently : public MQMessageListener { - public: - virtual ~MessageListenerConcurrently() {} - virtual ConsumeStatus consumeMessage(const std::vector& msgs) = 0; - virtual MessageListenerType getMessageListenerType() { return messageListenerConcurrently; } -}; - -} // namespace rocketmq -#endif diff --git a/include/MQMessageQueue.h b/include/MQMessageQueue.h deleted file mode 100644 index 0879a94de..000000000 --- a/include/MQMessageQueue.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __MQMESSAGEQUEUE_H__ -#define __MQMESSAGEQUEUE_H__ - -#include -#include -#include -#include "RocketMQClient.h" - -namespace rocketmq { -class ROCKETMQCLIENT_API MQMessageQueue { - public: - MQMessageQueue(); - MQMessageQueue(const std::string& topic, const std::string& brokerName, int queueId); - MQMessageQueue(const MQMessageQueue& other); - MQMessageQueue& operator=(const MQMessageQueue& other); - - std::string getTopic() const; - void setTopic(const std::string& topic); - - std::string getBrokerName() const; - void setBrokerName(const std::string& brokerName); - - int getQueueId() const; - void setQueueId(int queueId); - - bool operator==(const MQMessageQueue& mq) const; - bool operator<(const MQMessageQueue& mq) const; - int compareTo(const MQMessageQueue& mq) const; - - const std::string toString() const { - std::stringstream ss; - ss << "MessageQueue [topic=" << m_topic << ", brokerName=" << m_brokerName << ", queueId=" << m_queueId << "]"; - - return ss.str(); - } - - private: - std::string m_topic; - std::string m_brokerName; - int m_queueId; -}; -} // namespace rocketmq -#endif diff --git a/include/MQueueListener.h b/include/MQueueListener.h deleted file mode 100644 index f5eac41de..000000000 --- a/include/MQueueListener.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __MESSAGEQUEUELISTENER_H__ -#define __MESSAGEQUEUELISTENER_H__ - -#include -#include "RocketMQClient.h" - -namespace rocketmq { -class ROCKETMQCLIENT_API MQueueListener { - public: - virtual ~MQueueListener() {} - virtual void messageQueueChanged(const std::string& topic, - std::vector& mqAll, - std::vector& mqDivided) = 0; -}; -} // namespace rocketmq -#endif diff --git a/include/PullResult.h b/include/PullResult.h deleted file mode 100644 index 565058b07..000000000 --- a/include/PullResult.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __PULLRESULT_H__ -#define __PULLRESULT_H__ - -#include -#include "MQMessageExt.h" -#include "RocketMQClient.h" - -namespace rocketmq { -enum PullStatus { - FOUND, - NO_NEW_MSG, - NO_MATCHED_MSG, - OFFSET_ILLEGAL, - BROKER_TIMEOUT // indicate pull request timeout or received NULL response -}; - -static const char* EnumStrings[] = {"FOUND", "NO_NEW_MSG", "NO_MATCHED_MSG", "OFFSET_ILLEGAL", "BROKER_TIMEOUT"}; - -class ROCKETMQCLIENT_API PullResult { - public: - PullResult(); - PullResult(PullStatus status); - PullResult(PullStatus pullStatus, int64 nextBeginOffset, int64 minOffset, int64 maxOffset); - - PullResult(PullStatus pullStatus, - int64 nextBeginOffset, - int64 minOffset, - int64 maxOffset, - const std::vector& src); - - virtual ~PullResult(); - - std::string toString() { - std::stringstream ss; - ss << "PullResult [ pullStatus=" << EnumStrings[pullStatus] << ", nextBeginOffset=" << nextBeginOffset - << ", minOffset=" << minOffset << ", maxOffset=" << maxOffset << ", msgFoundList=" << msgFoundList.size() - << " ]"; - return ss.str(); - } - - public: - PullStatus pullStatus; - int64 nextBeginOffset; - int64 minOffset; - int64 maxOffset; - std::vector msgFoundList; -}; -} // namespace rocketmq -#endif diff --git a/include/RocketMQClient.h b/include/RocketMQClient.h deleted file mode 100644 index 26ae37921..000000000 --- a/include/RocketMQClient.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __ROCKETMQCLIENT_H__ -#define __ROCKETMQCLIENT_H__ - -#ifdef WIN32 -#ifdef ROCKETMQCLIENT_EXPORTS -#ifdef _WINDLL -#define ROCKETMQCLIENT_API __declspec(dllexport) -#else -#define ROCKETMQCLIENT_API -#endif -#else -#ifdef ROCKETMQCLIENT_IMPORT -#define ROCKETMQCLIENT_API __declspec(dllimport) -#else -#define ROCKETMQCLIENT_API -#endif -#endif -#else -#define ROCKETMQCLIENT_API -#endif - -/** A platform-independent 8-bit signed integer type. */ -typedef signed char int8; -/** A platform-independent 8-bit unsigned integer type. */ -typedef unsigned char uint8; -/** A platform-independent 16-bit signed integer type. */ -typedef signed short int16; -/** A platform-independent 16-bit unsigned integer type. */ -typedef unsigned short uint16; -/** A platform-independent 32-bit signed integer type. */ -typedef signed int int32; -/** A platform-independent 32-bit unsigned integer type. */ -typedef unsigned int uint32; -/** A platform-independent 64-bit integer type. */ -typedef long long int64; -/** A platform-independent 64-bit unsigned integer type. */ -typedef unsigned long long uint64; - -#endif diff --git a/include/SendResult.h b/include/SendResult.h deleted file mode 100644 index 94ba543e8..000000000 --- a/include/SendResult.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __SENDRESULT_H__ -#define __SENDRESULT_H__ - -#include "MQMessageQueue.h" -#include "RocketMQClient.h" - -namespace rocketmq { - -enum SendStatus { SEND_OK, SEND_FLUSH_DISK_TIMEOUT, SEND_FLUSH_SLAVE_TIMEOUT, SEND_SLAVE_NOT_AVAILABLE }; - -class ROCKETMQCLIENT_API SendResult { - public: - SendResult(); - SendResult(const SendStatus& sendStatus, - const std::string& msgId, - const std::string& offsetMsgId, - const MQMessageQueue& messageQueue, - int64 queueOffset); - SendResult(const SendStatus& sendStatus, - const std::string& msgId, - const std::string& offsetMsgId, - const MQMessageQueue& messageQueue, - int64 queueOffset, - const std::string& regionId, - const bool traceOn); - - virtual ~SendResult(); - SendResult(const SendResult& other); - SendResult& operator=(const SendResult& other); - - void setTransactionId(const std::string& id) { m_transactionId = id; } - - std::string getTransactionId() { return m_transactionId; } - - const std::string& getMsgId() const; - const std::string& getOffsetMsgId() const; - - const std::string& getRegionId() const; - void setRegionId(const std::string& regionId); - SendStatus getSendStatus() const; - MQMessageQueue getMessageQueue() const; - int64 getQueueOffset() const; - bool getTraceOn() const; - - std::string toString() const; - - private: - SendStatus m_sendStatus; - std::string m_msgId; - std::string m_offsetMsgId; - MQMessageQueue m_messageQueue; - int64 m_queueOffset; - std::string m_transactionId; - std::string m_regionId; - bool m_traceOn; -}; - -} // namespace rocketmq -#endif diff --git a/include/SessionCredentials.h b/include/SessionCredentials.h deleted file mode 100644 index e035160ea..000000000 --- a/include/SessionCredentials.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __SESSIONCREDENTIALS_H__ -#define __SESSIONCREDENTIALS_H__ - -#include "RocketMQClient.h" - -namespace rocketmq { - -class SessionCredentials { - public: - static const std::string AccessKey; - static const std::string SecretKey; - static const std::string Signature; - static const std::string SignatureMethod; - static const std::string ONSChannelKey; - - SessionCredentials(std::string input_accessKey, std::string input_secretKey, const std::string& input_authChannel) - : accessKey(input_accessKey), secretKey(input_secretKey), authChannel(input_authChannel) {} - SessionCredentials() : authChannel("ALIYUN") {} - ~SessionCredentials() {} - - std::string getAccessKey() const { return accessKey; } - - void setAccessKey(std::string input_accessKey) { accessKey = input_accessKey; } - - std::string getSecretKey() const { return secretKey; } - - void setSecretKey(std::string input_secretKey) { secretKey = input_secretKey; } - - std::string getSignature() const { return signature; } - - void setSignature(std::string input_signature) { signature = input_signature; } - - std::string getSignatureMethod() const { return signatureMethod; } - - void setSignatureMethod(std::string input_signatureMethod) { signatureMethod = input_signatureMethod; } - - std::string getAuthChannel() const { return authChannel; } - - void setAuthChannel(std::string input_channel) { authChannel = input_channel; } - - bool isValid() const { - if (accessKey.empty() || secretKey.empty() || authChannel.empty()) - return false; - - return true; - } - - private: - std::string accessKey; - std::string secretKey; - std::string signature; - std::string signatureMethod; - std::string authChannel; -}; -} // namespace rocketmq -#endif diff --git a/include/TransactionListener.h b/include/TransactionListener.h deleted file mode 100644 index 6756e96a7..000000000 --- a/include/TransactionListener.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __TRANSACTIONLISTENER_H__ -#define __TRANSACTIONLISTENER_H__ - -#include "MQMessage.h" -#include "MQMessageExt.h" -#include "TransactionSendResult.h" - -namespace rocketmq { -class ROCKETMQCLIENT_API TransactionListener { - public: - virtual ~TransactionListener() {} - /** - * When send transactional prepare(half) message succeed, this method will be invoked to execute local transaction. - * - * @param msg Half(prepare) message - * @param arg Custom business parameter - * @return Transaction state - */ - virtual LocalTransactionState executeLocalTransaction(const MQMessage& msg, void* arg) = 0; - - /** - * When no response to prepare(half) message. broker will send check message to check the transaction status, and this - * method will be invoked to get local transaction status. - * - * @param msg Check message - * @return Transaction state - */ - virtual LocalTransactionState checkLocalTransaction(const MQMessageExt& msg) = 0; -}; -} // namespace rocketmq -#endif diff --git a/include/TransactionMQProducer.h b/include/TransactionMQProducer.h deleted file mode 100644 index 7b9da77cb..000000000 --- a/include/TransactionMQProducer.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __TRANSACTIONMQPRODUCER_H__ -#define __TRANSACTIONMQPRODUCER_H__ - -#include -#include -#include "MQClient.h" -#include "MQMessage.h" -#include "MQMessageExt.h" -#include "SessionCredentials.h" -#include "TransactionListener.h" -#include "TransactionSendResult.h" - -namespace rocketmq { -class TransactionMQProducerImpl; -class ROCKETMQCLIENT_API TransactionMQProducer { - public: - TransactionMQProducer(const std::string& producerGroup); - virtual ~TransactionMQProducer(); - - void start(); - void shutdown(); - std::string version(); - - const std::string& getNamesrvAddr() const; - void setNamesrvAddr(const std::string& namesrvAddr); - const std::string& getNamesrvDomain() const; - void setNamesrvDomain(const std::string& namesrvDomain); - const std::string& getInstanceName() const; - void setInstanceName(const std::string& instanceName); - - const std::string& getNameSpace() const; - void setNameSpace(const std::string& nameSpace); - const std::string& getGroupName() const; - void setGroupName(const std::string& groupname); - void setSessionCredentials(const std::string& accessKey, - const std::string& secretKey, - const std::string& accessChannel); - const SessionCredentials& getSessionCredentials() const; - - void setUnitName(std::string unitName); - const std::string& getUnitName() const; - - int getSendMsgTimeout() const; - void setSendMsgTimeout(int sendMsgTimeout); - void setTcpTransportPullThreadNum(int num); - int getTcpTransportPullThreadNum() const; - - void setTcpTransportConnectTimeout(uint64_t timeout); // ms - uint64_t getTcpTransportConnectTimeout() const; - - void setTcpTransportTryLockTimeout(uint64_t timeout); // ms - uint64_t getTcpTransportTryLockTimeout() const; - - int getCompressMsgBodyOverHowmuch() const; - void setCompressMsgBodyOverHowmuch(int compressMsgBodyOverHowmuch); - int getCompressLevel() const; - void setCompressLevel(int compressLevel); - - int getMaxMessageSize() const; - void setMaxMessageSize(int maxMessageSize); - - void setLogLevel(elogLevel inputLevel); - elogLevel getLogLevel(); - void setLogFileSizeAndNum(int fileNum, long perFileSize); // perFileSize is MB unit - void setMessageTrace(bool messageTrace); - bool getMessageTrace() const; - void setEnableSsl(bool enableSsl); - bool getEnableSsl() const; - void setSslPropertyFile(const std::string& sslPropertyFile); - const std::string& getSslPropertyFile() const; - std::shared_ptr getTransactionListener(); - void setTransactionListener(TransactionListener* listener); - TransactionSendResult sendMessageInTransaction(MQMessage& msg, void* arg); - void checkTransactionState(const std::string& addr, - const MQMessageExt& message, - long tranStateTableOffset, - long commitLogOffset, - const std::string& msgId, - const std::string& transactionId, - const std::string& offsetMsgId); - - private: - TransactionMQProducerImpl* impl; -}; -} // namespace rocketmq - -#endif diff --git a/include/TransactionSendResult.h b/include/TransactionSendResult.h deleted file mode 100644 index 0bb1e480e..000000000 --- a/include/TransactionSendResult.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __TRANSACTIONSENDRESULT_H__ -#define __TRANSACTIONSENDRESULT_H__ - -#include "SendResult.h" - -namespace rocketmq { - -enum LocalTransactionState { COMMIT_MESSAGE, ROLLBACK_MESSAGE, UNKNOWN }; - -class ROCKETMQCLIENT_API TransactionSendResult : public SendResult { - public: - TransactionSendResult() {} - - TransactionSendResult(const SendStatus& sendStatus, - const std::string& msgId, - const std::string& offsetMsgId, - const MQMessageQueue& messageQueue, - int64 queueOffset) - : SendResult(sendStatus, msgId, offsetMsgId, messageQueue, queueOffset) {} - - LocalTransactionState getLocalTransactionState() { return m_localTransactionState; } - - void setLocalTransactionState(LocalTransactionState localTransactionState) { - m_localTransactionState = localTransactionState; - } - - private: - LocalTransactionState m_localTransactionState; -}; -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt deleted file mode 100755 index 015951dcc..000000000 --- a/libs/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -add_subdirectory(signature) diff --git a/libs/signature/CMakeLists.txt b/libs/signature/CMakeLists.txt deleted file mode 100755 index 0348e48e8..000000000 --- a/libs/signature/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -project(signature) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) -set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) - -aux_source_directory(src/ DIR_LIB_SRCS) - -add_library(Signature STATIC ${DIR_LIB_SRCS}) -target_link_libraries(Signature ${deplibs}) -set_target_properties(Signature PROPERTIES OUTPUT_NAME "Signature") - -# install -install(TARGETS Signature DESTINATION lib) -#install (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION include/rocketmq) diff --git a/libs/signature/include/base64.h b/libs/signature/include/base64.h deleted file mode 100644 index 9e2d1331a..000000000 --- a/libs/signature/include/base64.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BASE64_H -#define BASE64_H - -/* Get size_t. */ -#include - -/* Get bool. */ -#include - -#ifdef __cplusplus -namespace rocketmqSignature { -#endif - -/* This uses that the expression (n+(k-1))/k means the smallest - integer >= n/k, i.e., the ceiling of n/k. */ -#define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4) - -extern bool isbase64(char ch); - -extern void base64_encode(const char* in, size_t inlen, char* out, size_t outlen); - -extern size_t base64_encode_alloc(const char* in, size_t inlen, char** out); - -extern bool base64_decode(const char* in, size_t inlen, char* out, size_t* outlen); - -extern bool base64_decode_alloc(const char* in, size_t inlen, char** out, size_t* outlen); - -#ifdef __cplusplus -} -#endif - -#endif /* BASE64_H */ diff --git a/libs/signature/include/hmac.h b/libs/signature/include/hmac.h deleted file mode 100644 index 889ad7dbf..000000000 --- a/libs/signature/include/hmac.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _HMAC_HMAC_H -#define _HMAC_HMAC_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#ifndef SHA1_DIGEST_LEN -#define SHA1_DIGEST_LEN 20 -#endif - -#ifndef SHA256_DIGEST_LEN -#define SHA256_DIGEST_LEN 32 -#endif - -#ifndef SHA512_DIGEST_LEN -#define SHA512_DIGEST_LEN 64 -#endif - -/* - * hmac_sha1: - * hmac_sha256: - * hmac_sha512: - * Calculate Hashed Message Authentication Code with sha1/256/512 algorithm - * Caution: ret_buf should provide enough space for HMAC result. - * - * @key [in]: the secure-key string - * @key_len [in]: the length of secure-key - * @data [in]: data string could be calculated. - * @data_len [in]: the length of data. length is needed because strlen could not take effect. - * @ret_buf [out]: HMAC result stored in ret_buf. - */ - -#ifdef __cplusplus -namespace rocketmqSignature { - -#endif - -extern int hmac_sha1(const void* key, size_t key_len, const void* data, size_t data_len, void* ret_buf); -extern int hmac_sha256(const void* key, size_t key_len, const void* data, size_t data_len, void* ret_buf); -extern int hmac_sha512(const void* key, size_t key_len, const void* data, size_t data_len, void* ret_buf); - -#ifdef __cplusplus -} -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libs/signature/include/param_list.h b/libs/signature/include/param_list.h deleted file mode 100644 index afe87b05e..000000000 --- a/libs/signature/include/param_list.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PARAM_LIST_H -#define PARAM_LIST_H - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -namespace rocketmqSignature { -#endif - -typedef struct _spas_param_node { - char* name; - char* value; - struct _spas_param_node* pnext; -} SPAS_PARAM_NODE; - -typedef struct _spas_param_list { - SPAS_PARAM_NODE* phead; - unsigned int length; /* count of nodes */ - unsigned int size; /* total size of string presentation */ -} SPAS_PARAM_LIST; - -extern SPAS_PARAM_LIST* create_param_list(void); -extern int add_param_to_list(SPAS_PARAM_LIST* list, const char* name, const char* value); -extern void free_param_list(SPAS_PARAM_LIST* list); -extern char* param_list_to_str(const SPAS_PARAM_LIST* list); - -#ifdef __cplusplus -} -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libs/signature/include/sha1.h b/libs/signature/include/sha1.h deleted file mode 100644 index 789d4118c..000000000 --- a/libs/signature/include/sha1.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SHA1_H -#define SHA1_H 1 - -#include -#include - -#ifdef __cplusplus -namespace rocketmqSignature { -#endif - -#define SHA1_DIGEST_SIZE 20 - -/* Structure to save state of computation between the single steps. */ -struct sha1_ctx { - uint32_t A; - uint32_t B; - uint32_t C; - uint32_t D; - uint32_t E; - - uint32_t total[2]; - uint32_t buflen; - uint32_t buffer[32]; -}; - -/* Initialize structure containing state of computation. */ -extern void sha1_init_ctx(struct sha1_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 64!!! */ -extern void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 64. */ -extern void sha1_process_bytes(const void* buffer, size_t len, struct sha1_ctx* ctx); - -/* Process the remaining bytes in the buffer and put result from CTX - in first 20 bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. */ -extern void* sha1_finish_ctx(struct sha1_ctx* ctx, void* resbuf); - -/* Put result from CTX in first 20 bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. */ -extern void* sha1_read_ctx(const struct sha1_ctx* ctx, void* resbuf); - -/* Compute SHA1 message digest for bytes read from STREAM. The - resulting message digest number will be written into the 20 bytes - beginning at RESBLOCK. */ -extern int sha1_stream(FILE* stream, void* resblock); - -/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void* sha1_buffer(const char* buffer, size_t len, void* resblock); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libs/signature/include/sha256.h b/libs/signature/include/sha256.h deleted file mode 100644 index 1726b60f7..000000000 --- a/libs/signature/include/sha256.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SHA256_H -#define SHA256_H 1 - -#include -#include - -#ifdef __cplusplus -namespace rocketmqSignature { -#endif - -/* Structure to save state of computation between the single steps. */ -struct sha256_ctx { - uint32_t state[8]; - - uint32_t total[2]; - size_t buflen; - uint32_t buffer[32]; -}; - -enum { SHA224_DIGEST_SIZE = 28 }; -enum { SHA256_DIGEST_SIZE = 32 }; - -/* Initialize structure containing state of computation. */ -extern void sha256_init_ctx(struct sha256_ctx* ctx); -extern void sha224_init_ctx(struct sha256_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 64!!! */ -extern void sha256_process_block(const void* buffer, size_t len, struct sha256_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 64. */ -extern void sha256_process_bytes(const void* buffer, size_t len, struct sha256_ctx* ctx); - -/* Process the remaining bytes in the buffer and put result from CTX - in first 32 (28) bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. */ -extern void* sha256_finish_ctx(struct sha256_ctx* ctx, void* resbuf); -extern void* sha224_finish_ctx(struct sha256_ctx* ctx, void* resbuf); - -/* Put result from CTX in first 32 (28) bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. */ -extern void* sha256_read_ctx(const struct sha256_ctx* ctx, void* resbuf); -extern void* sha224_read_ctx(const struct sha256_ctx* ctx, void* resbuf); - -/* Compute SHA256 (SHA224) message digest for bytes read from STREAM. The - resulting message digest number will be written into the 32 (28) bytes - beginning at RESBLOCK. */ -extern int sha256_stream(FILE* stream, void* resblock); -extern int sha224_stream(FILE* stream, void* resblock); - -/* Compute SHA256 (SHA224) message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void* sha256_buffer(const char* buffer, size_t len, void* resblock); -extern void* sha224_buffer(const char* buffer, size_t len, void* resblock); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libs/signature/include/sha512.h b/libs/signature/include/sha512.h deleted file mode 100644 index 38328a16c..000000000 --- a/libs/signature/include/sha512.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SHA512_H -#define SHA512_H 1 - -#include - -#include "u64.h" - -#ifdef __cplusplus -namespace rocketmqSignature { -#endif - -/* Structure to save state of computation between the single steps. */ -struct sha512_ctx { - u64 state[8]; - - u64 total[2]; - size_t buflen; - u64 buffer[32]; -}; - -enum { SHA384_DIGEST_SIZE = 48 }; -enum { SHA512_DIGEST_SIZE = 64 }; - -/* Initialize structure containing state of computation. */ -extern void sha512_init_ctx(struct sha512_ctx* ctx); -extern void sha384_init_ctx(struct sha512_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 128!!! */ -extern void sha512_process_block(const void* buffer, size_t len, struct sha512_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 128. */ -extern void sha512_process_bytes(const void* buffer, size_t len, struct sha512_ctx* ctx); - -/* Process the remaining bytes in the buffer and put result from CTX - in first 64 (48) bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. */ -extern void* sha512_finish_ctx(struct sha512_ctx* ctx, void* resbuf); -extern void* sha384_finish_ctx(struct sha512_ctx* ctx, void* resbuf); - -/* Put result from CTX in first 64 (48) bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ -extern void* sha512_read_ctx(const struct sha512_ctx* ctx, void* resbuf); -extern void* sha384_read_ctx(const struct sha512_ctx* ctx, void* resbuf); - -/* Compute SHA512 (SHA384) message digest for bytes read from STREAM. The - resulting message digest number will be written into the 64 (48) bytes - beginning at RESBLOCK. */ -extern int sha512_stream(FILE* stream, void* resblock); -extern int sha384_stream(FILE* stream, void* resblock); - -/* Compute SHA512 (SHA384) message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void* sha512_buffer(const char* buffer, size_t len, void* resblock); -extern void* sha384_buffer(const char* buffer, size_t len, void* resblock); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libs/signature/include/spas_client.h b/libs/signature/include/spas_client.h deleted file mode 100644 index 7bf6aae04..000000000 --- a/libs/signature/include/spas_client.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SPAS_CLIENT_H -#define SPAS_CLIENT_H - -#include "param_list.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -namespace rocketmqSignature { -#endif - -#define SPAS_MAX_KEY_LEN 128 /* max access_key/secret_key length */ -#define SPAS_MAX_PATH 256 /* max credential file path length */ -#define SPAS_ACCESS_KEY_TAG \ - "accessKey" /* access_key tag in credential file \ - */ -#define SPAS_SECRET_KEY_TAG \ - "secretKey" /* secret_key tag in credential file \ - */ -#define SPAS_CREDENTIAL_ENV "SPAS_CREDENTIAL" /* credential file environment variable */ - -typedef enum { - SIGN_HMACSHA1 = 0, /* HmacSHA1 */ - SIGN_HMACSHA256 = 1, /* HmacSHA256 */ -} SPAS_SIGN_ALGORITHM; - -typedef enum { - NO_UPDATE = 0, /* do not update credential */ - UPDATE_BY_ALARM = 1, /* update credential by SIGALRM */ -#ifdef SPAS_MT - UPDATE_BY_THREAD = 2, /* update credential by standalone thread */ -#endif -} CREDENTIAL_UPDATE_MODE; - -typedef enum { - SPAS_NO_ERROR = 0, /* success */ - ERROR_INVALID_PARAM = -1, /* invalid parameter */ - ERROR_NO_CREDENTIAL = -2, /* credential file not specified */ - ERROR_FILE_OPEN = -3, /* file open failed */ - ERROR_MEM_ALLOC = -4, /* memory allocation failed */ - ERROR_MISSING_KEY = -5, /* missing access_key/secret_key */ - ERROR_KEY_LENGTH = -6, /* key length exceed limit */ - ERROR_UPDATE_CREDENTIAL = -7 /* update credential file failed */ - -} SPAS_ERROR_CODE; - -typedef struct _spas_credential { - char access_key[SPAS_MAX_KEY_LEN]; - char secret_key[SPAS_MAX_KEY_LEN]; -} SPAS_CREDENTIAL; - -extern int spas_load_credential(char* path, CREDENTIAL_UPDATE_MODE mode); -extern int spas_set_access_key(char* key); -extern int spas_set_secret_key(char* key); -extern char* spas_get_access_key(void); -extern char* spas_get_secret_key(void); -extern SPAS_CREDENTIAL* spas_get_credential(void); - -#ifdef SPAS_MT - -extern int spas_load_thread_credential(char* path); -extern int spas_set_thread_access_key(char* key); -extern int spas_set_thread_secret_key(char* key); -extern char* spas_get_thread_access_key(void); -extern char* spas_get_thread_secret_key(void); - -#endif - -extern char* spas_get_signature(const SPAS_PARAM_LIST* list, const char* key); -extern char* spas_get_signature2(const SPAS_PARAM_LIST* list, const char* key, SPAS_SIGN_ALGORITHM algorithm); -extern char* spas_sign(const char* data, size_t size, const char* key); -extern char* spas_sign2(const char* data, size_t size, const char* key, SPAS_SIGN_ALGORITHM algorithm); -extern void spas_mem_free(char* pSignature); -extern char* spas_get_version(void); - -#ifdef __cplusplus -} -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libs/signature/include/u64.h b/libs/signature/include/u64.h deleted file mode 100644 index e7580e560..000000000 --- a/libs/signature/include/u64.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -/* Return X rotated left by N bits, where 0 < N < 64. */ -#define u64rol(x, n) u64or(u64shl(x, n), u64shr(x, 64 - n)) - -#ifdef UINT64_MAX - -/* Native implementations are trivial. See below for comments on what - these operations do. */ -typedef uint64_t u64; -#define u64hilo(hi, lo) ((u64)(((u64)(hi) << 32) + (lo))) -#define u64init(hi, lo) u64hilo(hi, lo) -#define u64lo(x) ((u64)(x)) -#define u64lt(x, y) ((x) < (y)) -#define u64and(x, y) ((x) & (y)) -#define u64or(x, y) ((x) | (y)) -#define u64xor(x, y) ((x) ^ (y)) -#define u64plus(x, y) ((x) + (y)) -#define u64shl(x, n) ((x) << (n)) -#define u64shr(x, n) ((x) >> (n)) - -#else - -/* u64 is a 64-bit unsigned integer value. - u64init (HI, LO), is like u64hilo (HI, LO), but for use in - initializer contexts. */ -#ifdef WORDS_BIGENDIAN -typedef struct { uint32_t hi, lo; } u64; -#define u64init(hi, lo) \ - { hi, lo } -#else -typedef struct { uint32_t lo, hi; } u64; -#define u64init(hi, lo) \ - { lo, hi } -#endif - -/* Given the high and low-order 32-bit quantities HI and LO, return a u64 - value representing (HI << 32) + LO. */ -static inline u64 u64hilo(uint32_t hi, uint32_t lo) { - u64 r; - r.hi = hi; - r.lo = lo; - return r; -} - -/* Return a u64 value representing LO. */ -static inline u64 u64lo(uint32_t lo) { - u64 r; - r.hi = 0; - r.lo = lo; - return r; -} - -/* Return X < Y. */ -static inline int u64lt(u64 x, u64 y) { - return x.hi < y.hi || (x.hi == y.hi && x.lo < y.lo); -} - -/* Return X & Y. */ -static inline u64 u64and(u64 x, u64 y) { - u64 r; - r.hi = x.hi & y.hi; - r.lo = x.lo & y.lo; - return r; -} - -/* Return X | Y. */ -static inline u64 u64or(u64 x, u64 y) { - u64 r; - r.hi = x.hi | y.hi; - r.lo = x.lo | y.lo; - return r; -} - -/* Return X ^ Y. */ -static inline u64 u64xor(u64 x, u64 y) { - u64 r; - r.hi = x.hi ^ y.hi; - r.lo = x.lo ^ y.lo; - return r; -} - -/* Return X + Y. */ -static inline u64 u64plus(u64 x, u64 y) { - u64 r; - r.lo = x.lo + y.lo; - r.hi = x.hi + y.hi + (r.lo < x.lo); - return r; -} - -/* Return X << N. */ -static inline u64 u64shl(u64 x, int n) { - u64 r; - if (n < 32) { - r.hi = (x.hi << n) | (x.lo >> (32 - n)); - r.lo = x.lo << n; - } else { - r.hi = x.lo << (n - 32); - r.lo = 0; - } - return r; -} - -/* Return X >> N. */ -static inline u64 u64shr(u64 x, int n) { - u64 r; - if (n < 32) { - r.hi = x.hi >> n; - r.lo = (x.hi << (32 - n)) | (x.lo >> n); - } else { - r.hi = 0; - r.lo = x.hi >> (n - 32); - } - return r; -} - -#endif diff --git a/libs/signature/src/base64.c b/libs/signature/src/base64.c deleted file mode 100755 index 682a72595..000000000 --- a/libs/signature/src/base64.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Get prototype. */ -#include "base64.h" - -/* Get malloc. */ -#include - -/* Get UCHAR_MAX. */ -#include - -#ifdef __cplusplus -namespace rocketmqSignature{ -#endif - -/* C89 compliant way to cast 'char' to 'unsigned char'. */ -#ifdef WIN32 -static _inline unsigned char -#else -static inline unsigned char -#endif -to_uchar (char ch) -{ - return ch; -} - -/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN. - If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as - possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero - terminate the output buffer. */ -void -base64_encode (const char *in, size_t inlen, - char *out, size_t outlen) -{ - static const char b64str[65] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - while (inlen && outlen) - { - *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f]; - if (!--outlen) - break; - *out++ = b64str[((to_uchar (in[0]) << 4) - + (--inlen ? to_uchar (in[1]) >> 4 : 0)) - & 0x3f]; - if (!--outlen) - break; - *out++ = - (inlen - ? b64str[((to_uchar (in[1]) << 2) - + (--inlen ? to_uchar (in[2]) >> 6 : 0)) - & 0x3f] - : '='); - if (!--outlen) - break; - *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '='; - if (!--outlen) - break; - if (inlen) - inlen--; - if (inlen) - in += 3; - } - - if (outlen) - *out = '\0'; -} - -/* Allocate a buffer and store zero terminated base64 encoded data - from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e., - the length of the encoded data, excluding the terminating zero. On - return, the OUT variable will hold a pointer to newly allocated - memory that must be deallocated by the caller. If output string - length would overflow, 0 is returned and OUT is set to NULL. If - memory allocation failed, OUT is set to NULL, and the return value - indicates length of the requested memory block, i.e., - BASE64_LENGTH(inlen) + 1. */ -size_t -base64_encode_alloc (const char *in, size_t inlen, char **out) -{ - size_t outlen = 1 + BASE64_LENGTH (inlen); - - /* Check for overflow in outlen computation. - * - * If there is no overflow, outlen >= inlen. - * - * If the operation (inlen + 2) overflows then it yields at most +1, so - * outlen is 0. - * - * If the multiplication overflows, we lose at least half of the - * correct value, so the result is < ((inlen + 2) / 3) * 2, which is - * less than (inlen + 2) * 0.66667, which is less than inlen as soon as - * (inlen > 4). - */ - if (inlen > outlen) - { - *out = NULL; - return 0; - } - - *out = (char *)malloc (outlen); - if (!*out) - return outlen; - - base64_encode (in, inlen, *out, outlen); - - return outlen - 1; -} - -/* With this approach this file works independent of the charset used - (think EBCDIC). However, it does assume that the characters in the - Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255. POSIX - 1003.1-2001 require that char and unsigned char are 8-bit - quantities, though, taking care of that problem. But this may be a - potential problem on non-POSIX C99 platforms. - - IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_" - as the formal parameter rather than "x". */ -#define B64(_) \ - ((_) == 'A' ? 0 \ - : (_) == 'B' ? 1 \ - : (_) == 'C' ? 2 \ - : (_) == 'D' ? 3 \ - : (_) == 'E' ? 4 \ - : (_) == 'F' ? 5 \ - : (_) == 'G' ? 6 \ - : (_) == 'H' ? 7 \ - : (_) == 'I' ? 8 \ - : (_) == 'J' ? 9 \ - : (_) == 'K' ? 10 \ - : (_) == 'L' ? 11 \ - : (_) == 'M' ? 12 \ - : (_) == 'N' ? 13 \ - : (_) == 'O' ? 14 \ - : (_) == 'P' ? 15 \ - : (_) == 'Q' ? 16 \ - : (_) == 'R' ? 17 \ - : (_) == 'S' ? 18 \ - : (_) == 'T' ? 19 \ - : (_) == 'U' ? 20 \ - : (_) == 'V' ? 21 \ - : (_) == 'W' ? 22 \ - : (_) == 'X' ? 23 \ - : (_) == 'Y' ? 24 \ - : (_) == 'Z' ? 25 \ - : (_) == 'a' ? 26 \ - : (_) == 'b' ? 27 \ - : (_) == 'c' ? 28 \ - : (_) == 'd' ? 29 \ - : (_) == 'e' ? 30 \ - : (_) == 'f' ? 31 \ - : (_) == 'g' ? 32 \ - : (_) == 'h' ? 33 \ - : (_) == 'i' ? 34 \ - : (_) == 'j' ? 35 \ - : (_) == 'k' ? 36 \ - : (_) == 'l' ? 37 \ - : (_) == 'm' ? 38 \ - : (_) == 'n' ? 39 \ - : (_) == 'o' ? 40 \ - : (_) == 'p' ? 41 \ - : (_) == 'q' ? 42 \ - : (_) == 'r' ? 43 \ - : (_) == 's' ? 44 \ - : (_) == 't' ? 45 \ - : (_) == 'u' ? 46 \ - : (_) == 'v' ? 47 \ - : (_) == 'w' ? 48 \ - : (_) == 'x' ? 49 \ - : (_) == 'y' ? 50 \ - : (_) == 'z' ? 51 \ - : (_) == '0' ? 52 \ - : (_) == '1' ? 53 \ - : (_) == '2' ? 54 \ - : (_) == '3' ? 55 \ - : (_) == '4' ? 56 \ - : (_) == '5' ? 57 \ - : (_) == '6' ? 58 \ - : (_) == '7' ? 59 \ - : (_) == '8' ? 60 \ - : (_) == '9' ? 61 \ - : (_) == '+' ? 62 \ - : (_) == '/' ? 63 \ - : -1) - -static const signed char b64[0x100] = { - B64 (0), B64 (1), B64 (2), B64 (3), - B64 (4), B64 (5), B64 (6), B64 (7), - B64 (8), B64 (9), B64 (10), B64 (11), - B64 (12), B64 (13), B64 (14), B64 (15), - B64 (16), B64 (17), B64 (18), B64 (19), - B64 (20), B64 (21), B64 (22), B64 (23), - B64 (24), B64 (25), B64 (26), B64 (27), - B64 (28), B64 (29), B64 (30), B64 (31), - B64 (32), B64 (33), B64 (34), B64 (35), - B64 (36), B64 (37), B64 (38), B64 (39), - B64 (40), B64 (41), B64 (42), B64 (43), - B64 (44), B64 (45), B64 (46), B64 (47), - B64 (48), B64 (49), B64 (50), B64 (51), - B64 (52), B64 (53), B64 (54), B64 (55), - B64 (56), B64 (57), B64 (58), B64 (59), - B64 (60), B64 (61), B64 (62), B64 (63), - B64 (64), B64 (65), B64 (66), B64 (67), - B64 (68), B64 (69), B64 (70), B64 (71), - B64 (72), B64 (73), B64 (74), B64 (75), - B64 (76), B64 (77), B64 (78), B64 (79), - B64 (80), B64 (81), B64 (82), B64 (83), - B64 (84), B64 (85), B64 (86), B64 (87), - B64 (88), B64 (89), B64 (90), B64 (91), - B64 (92), B64 (93), B64 (94), B64 (95), - B64 (96), B64 (97), B64 (98), B64 (99), - B64 (100), B64 (101), B64 (102), B64 (103), - B64 (104), B64 (105), B64 (106), B64 (107), - B64 (108), B64 (109), B64 (110), B64 (111), - B64 (112), B64 (113), B64 (114), B64 (115), - B64 (116), B64 (117), B64 (118), B64 (119), - B64 (120), B64 (121), B64 (122), B64 (123), - B64 (124), B64 (125), B64 (126), B64 (127), - B64 (128), B64 (129), B64 (130), B64 (131), - B64 (132), B64 (133), B64 (134), B64 (135), - B64 (136), B64 (137), B64 (138), B64 (139), - B64 (140), B64 (141), B64 (142), B64 (143), - B64 (144), B64 (145), B64 (146), B64 (147), - B64 (148), B64 (149), B64 (150), B64 (151), - B64 (152), B64 (153), B64 (154), B64 (155), - B64 (156), B64 (157), B64 (158), B64 (159), - B64 (160), B64 (161), B64 (162), B64 (163), - B64 (164), B64 (165), B64 (166), B64 (167), - B64 (168), B64 (169), B64 (170), B64 (171), - B64 (172), B64 (173), B64 (174), B64 (175), - B64 (176), B64 (177), B64 (178), B64 (179), - B64 (180), B64 (181), B64 (182), B64 (183), - B64 (184), B64 (185), B64 (186), B64 (187), - B64 (188), B64 (189), B64 (190), B64 (191), - B64 (192), B64 (193), B64 (194), B64 (195), - B64 (196), B64 (197), B64 (198), B64 (199), - B64 (200), B64 (201), B64 (202), B64 (203), - B64 (204), B64 (205), B64 (206), B64 (207), - B64 (208), B64 (209), B64 (210), B64 (211), - B64 (212), B64 (213), B64 (214), B64 (215), - B64 (216), B64 (217), B64 (218), B64 (219), - B64 (220), B64 (221), B64 (222), B64 (223), - B64 (224), B64 (225), B64 (226), B64 (227), - B64 (228), B64 (229), B64 (230), B64 (231), - B64 (232), B64 (233), B64 (234), B64 (235), - B64 (236), B64 (237), B64 (238), B64 (239), - B64 (240), B64 (241), B64 (242), B64 (243), - B64 (244), B64 (245), B64 (246), B64 (247), - B64 (248), B64 (249), B64 (250), B64 (251), - B64 (252), B64 (253), B64 (254), B64 (255) -}; - -#if UCHAR_MAX == 255 -# define uchar_in_range(c) true -#else -# define uchar_in_range(c) ((c) <= 255) -#endif - -/* Return true if CH is a character from the Base64 alphabet, and - false otherwise. Note that '=' is padding and not considered to be - part of the alphabet. */ -bool -isbase64 (char ch) -{ - return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)]; -} - -/* Decode base64 encoded input array IN of length INLEN to output - array OUT that can hold *OUTLEN bytes. Return true if decoding was - successful, i.e. if the input was valid base64 data, false - otherwise. If *OUTLEN is too small, as many bytes as possible will - be written to OUT. On return, *OUTLEN holds the length of decoded - bytes in OUT. Note that as soon as any non-alphabet characters are - encountered, decoding is stopped and false is returned. This means - that, when applicable, you must remove any line terminators that is - part of the data stream before calling this function. */ -bool -base64_decode (const char *in, size_t inlen, - char *out, size_t *outlen) -{ - size_t outleft = *outlen; - - while (inlen >= 2) - { - if (!isbase64 (in[0]) || !isbase64 (in[1])) - break; - - if (outleft) - { - *out++ = ((b64[to_uchar (in[0])] << 2) - | (b64[to_uchar (in[1])] >> 4)); - outleft--; - } - - if (inlen == 2) - break; - - if (in[2] == '=') - { - if (inlen != 4) - break; - - if (in[3] != '=') - break; - - } - else - { - if (!isbase64 (in[2])) - break; - - if (outleft) - { - *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0) - | (b64[to_uchar (in[2])] >> 2)); - outleft--; - } - - if (inlen == 3) - break; - - if (in[3] == '=') - { - if (inlen != 4) - break; - } - else - { - if (!isbase64 (in[3])) - break; - - if (outleft) - { - *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0) - | b64[to_uchar (in[3])]); - outleft--; - } - } - } - - in += 4; - inlen -= 4; - } - - *outlen -= outleft; - - if (inlen != 0) - return false; - - return true; -} - -/* Allocate an output buffer in *OUT, and decode the base64 encoded - data stored in IN of size INLEN to the *OUT buffer. On return, the - size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL, - if the caller is not interested in the decoded length. *OUT may be - NULL to indicate an out of memory error, in which case *OUTLEN - contains the size of the memory block needed. The function returns - true on successful decoding and memory allocation errors. (Use the - *OUT and *OUTLEN parameters to differentiate between successful - decoding and memory error.) The function returns false if the - input was invalid, in which case *OUT is NULL and *OUTLEN is - undefined. */ -bool -base64_decode_alloc (const char *in, size_t inlen, char **out, - size_t *outlen) -{ - /* This may allocate a few bytes too much, depending on input, - but it's not worth the extra CPU time to compute the exact amount. - The exact amount is 3 * inlen / 4, minus 1 if the input ends - with "=" and minus another 1 if the input ends with "==". - Dividing before multiplying avoids the possibility of overflow. */ - size_t needlen = 3 * (inlen / 4) + 2; - - *out = (char *)malloc (needlen); - if (!*out) - return true; - - if (!base64_decode (in, inlen, *out, &needlen)) - { - free (*out); - *out = NULL; - return false; - } - - if (outlen) - *outlen = needlen; - - return true; -#ifdef __cplusplus -} -#endif - -} diff --git a/libs/signature/src/hmac.c b/libs/signature/src/hmac.c deleted file mode 100755 index b704ccc77..000000000 --- a/libs/signature/src/hmac.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "hmac.h" -#include "sha1.h" -#include "sha256.h" -#include "sha512.h" - -#ifdef __cplusplus -namespace rocketmqSignature{ -#endif - -#define IPAD 0x36 -#define OPAD 0x5c - -int hmac_sha1(const void *key, size_t key_len, const void *data, size_t data_len, void *ret_buf) -{ - uint32_t i; - struct sha1_ctx inner; - struct sha1_ctx outer; - struct sha1_ctx key_hash; - char ipad[64] = {0}; - char opad[64] = {0}; - char key_buf[SHA1_DIGEST_SIZE] = {0}; - char inner_buf[SHA1_DIGEST_SIZE] = {0}; - - if (key == NULL || data == NULL || ret_buf == NULL) return -1; - - if (key_len > 64) { - sha1_init_ctx(&key_hash); - sha1_process_bytes(key, key_len, &key_hash); - sha1_finish_ctx(&key_hash, key_buf); - - key = key_buf; - key_len = SHA1_DIGEST_SIZE; - } - - sha1_init_ctx (&inner); - - for (i = 0; i < 64; i++) { - if (i < key_len) { - ipad[i] = ((const char *)key)[i] ^ IPAD; - opad[i] = ((const char *)key)[i] ^ OPAD; - } else { - ipad[i] = IPAD; - opad[i] = OPAD; - } - } - - sha1_process_block (ipad, 64, &inner); - sha1_process_bytes (data, data_len, &inner); - - sha1_finish_ctx (&inner, inner_buf); - - sha1_init_ctx (&outer); - - sha1_process_block (opad, 64, &outer); - sha1_process_bytes (inner_buf, SHA1_DIGEST_SIZE, &outer); - - sha1_finish_ctx (&outer, ret_buf); - - return 0; -} - -int hmac_sha256(const void *key, size_t key_len, const void *data, size_t data_len, void *ret_buf) -{ - uint32_t i; - struct sha256_ctx inner; - struct sha256_ctx outer; - struct sha256_ctx key_hash; - char ipad[64] = {0}; - char opad[64] = {0}; - char key_buf[SHA256_DIGEST_SIZE] = {0}; - char inner_buf[SHA256_DIGEST_SIZE] = {0}; - - if (key == NULL || data == NULL || ret_buf == NULL) return -1; - - if (key_len > 64) { - sha256_init_ctx(&key_hash); - sha256_process_bytes(key, key_len, &key_hash); - sha256_finish_ctx(&key_hash, key_buf); - - key = key_buf; - key_len = SHA256_DIGEST_SIZE; - } - - sha256_init_ctx (&inner); - - for (i = 0; i < 64; i++) { - if (i < key_len) { - ipad[i] = ((const char *)key)[i] ^ IPAD; - opad[i] = ((const char *)key)[i] ^ OPAD; - } else { - ipad[i] = IPAD; - opad[i] = OPAD; - } - } - - sha256_process_block (ipad, 64, &inner); - sha256_process_bytes (data, data_len, &inner); - - sha256_finish_ctx (&inner, inner_buf); - - sha256_init_ctx (&outer); - - sha256_process_block (opad, 64, &outer); - sha256_process_bytes (inner_buf, SHA256_DIGEST_SIZE, &outer); - - sha256_finish_ctx (&outer, ret_buf); - - return 0; -} - -int hmac_sha512(const void *key, size_t key_len, const void *data, size_t data_len, void *ret_buf) -{ - uint32_t i; - struct sha512_ctx inner; - struct sha512_ctx outer; - struct sha512_ctx key_hash; - char ipad[128] = {0}; - char opad[128] = {0}; - char key_buf[SHA512_DIGEST_SIZE] = {0}; - char inner_buf[SHA512_DIGEST_SIZE] = {0}; - - if (key == NULL || data == NULL || ret_buf == NULL) return -1; - - if (key_len > 128) { - sha512_init_ctx(&key_hash); - sha512_process_bytes(key, key_len, &key_hash); - sha512_finish_ctx(&key_hash, key_buf); - - key = key_buf; - key_len = SHA512_DIGEST_SIZE; - } - - sha512_init_ctx (&inner); - - for (i = 0; i < 128; i++) { - if (i < key_len) { - ipad[i] = ((const char *)key)[i] ^ IPAD; - opad[i] = ((const char *)key)[i] ^ OPAD; - } else { - ipad[i] = IPAD; - opad[i] = OPAD; - } - } - - sha512_process_block (ipad, 128, &inner); - sha512_process_bytes (data, data_len, &inner); - - sha512_finish_ctx (&inner, inner_buf); - - sha512_init_ctx (&outer); - - sha512_process_block (opad, 128, &outer); - sha512_process_bytes (inner_buf, SHA512_DIGEST_SIZE, &outer); - - sha512_finish_ctx (&outer, ret_buf); - - return 0; -} -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/libs/signature/src/param_list.c b/libs/signature/src/param_list.c deleted file mode 100755 index 9f94748d1..000000000 --- a/libs/signature/src/param_list.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include "spas_client.h" - -#ifdef __cplusplus -namespace rocketmqSignature { -#endif - -extern void *_mem_alloc(unsigned int size); -extern void _mem_free(void *ptr); - -static int _nodecmp(SPAS_PARAM_NODE *n1, SPAS_PARAM_NODE *n2) { - int ret = strcmp(n1->name, n2->name); - if (ret == 0) { - ret = strcmp(n1->value, n2->value); - } - return ret; -} - -SPAS_PARAM_LIST *create_param_list() { - return (SPAS_PARAM_LIST *)_mem_alloc(sizeof(SPAS_PARAM_LIST)); -} - -void free_param_list(SPAS_PARAM_LIST *list) { - SPAS_PARAM_NODE *pnode = NULL; - SPAS_PARAM_NODE *pnext = NULL; - if (list == NULL) { - return; - } - pnode = list->phead; - while (pnode != NULL) { - pnext = pnode->pnext; - _mem_free(pnode->name); - _mem_free(pnode->value); - _mem_free(pnode); - pnode = pnext; - } - _mem_free(list); -} - -int add_param_to_list(SPAS_PARAM_LIST *list, const char *name, - const char *value) { - SPAS_PARAM_NODE *pnode = NULL; - SPAS_PARAM_NODE *plast = NULL; - int nlen = 0; - int vlen = 0; - if (list == NULL || name == NULL || value == NULL) { - return ERROR_INVALID_PARAM; - } - nlen = strlen(name); - vlen = strlen(value); - pnode = (SPAS_PARAM_NODE *)_mem_alloc(sizeof(SPAS_PARAM_NODE)); - if (pnode == NULL) { - return ERROR_MEM_ALLOC; - } - pnode->name = (char *)_mem_alloc(nlen + 1); - if (pnode->name == NULL) { - _mem_free(pnode); - return ERROR_MEM_ALLOC; - } - pnode->value = (char *)_mem_alloc(vlen + 1); - if (pnode->value == NULL) { - _mem_free(pnode->name); - _mem_free(pnode); - return ERROR_MEM_ALLOC; - } - memcpy(pnode->name, name, nlen); - memcpy(pnode->value, value, vlen); - if (list->phead == NULL) { - list->phead = pnode; - } else if (_nodecmp(pnode, list->phead) <= 0) { - pnode->pnext = list->phead; - list->phead = pnode; - } else { - plast = list->phead; - while (plast->pnext != NULL) { - if (_nodecmp(pnode, plast->pnext) <= 0) { - pnode->pnext = plast->pnext; - plast->pnext = pnode; - break; - } else { - plast = plast->pnext; - } - } - if (plast->pnext == NULL) { - plast->pnext = pnode; - } - } - list->length++; - list->size += nlen + vlen + 1; /* 1 overhead for '=' */ - return SPAS_NO_ERROR; -} - -char *param_list_to_str(const SPAS_PARAM_LIST *list) { - int size = 0; - int pos = 0; - char *buf = NULL; - SPAS_PARAM_NODE *pnode = NULL; - if (list == NULL) { - return NULL; - } - if (list->length == 0) { - return (char *)_mem_alloc(1); - } - size = list->size + list->length - 1; /* overhead for '&' */ - buf = (char *)_mem_alloc(size); - if (buf == NULL) { - return NULL; - } - pnode = list->phead; - if (pnode != NULL) { - sprintf(buf, "%s=%s", pnode->name, pnode->value); - pos += strlen(pnode->name) + strlen(pnode->value) + 1; - pnode = pnode->pnext; - } - while (pnode != NULL) { - sprintf(buf + pos, "&%s=%s", pnode->name, pnode->value); - pos += strlen(pnode->name) + strlen(pnode->value) + 2; - pnode = pnode->pnext; - } - return buf; -} -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/libs/signature/src/sha1.c b/libs/signature/src/sha1.c deleted file mode 100755 index 7e7c804fd..000000000 --- a/libs/signature/src/sha1.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sha1.h" - -#include -#include - -#if USE_UNLOCKED_IO -# include "unlocked-io.h" -#endif - -#ifdef __cplusplus -namespace rocketmqSignature { -#endif - -#ifdef WORDS_BIGENDIAN -# define SWAP(n) (n) -#else -# define SWAP(n) \ - (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) -#endif - -#define BLOCKSIZE 4096 -#if BLOCKSIZE % 64 != 0 -# error "invalid BLOCKSIZE" -#endif - -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. (RFC 1321, 3.1: Step 1) */ -static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; - -/*! - * @fn void sha1_init_ctx (struct sha1_ctx *ctx) - * - * @brief initialize a context with start constants - * - * @details Take a pointer to a 160 bit block of data (five 32 bit ints) and - * initialize it to the start constants of the SHA1 algorithm. This - * must be called before using hash in the call to sha1_hash. - * - * @param[out] ctx pointer to a context to be initialized - */ -void -sha1_init_ctx (struct sha1_ctx *ctx) -{ - ctx->A = 0x67452301; - ctx->B = 0xefcdab89; - ctx->C = 0x98badcfe; - ctx->D = 0x10325476; - ctx->E = 0xc3d2e1f0; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - -/*! - * @fn static __inline__ void set_uint32 (char *cp, uint32_t v) - * - * @brief Copy the 4 byte value from v into the memory location pointed to - by *cp - * - * @details Copy the 4 byte value from v into the memory location pointed to by - * *cp, If your architecture allows unaligned access this is equivalent - * to * (uint32_t *) cp = v - * - * @param[out] cp memory location to copy v into - * @param[in] v 4 byte value to be copied - */ -#ifdef WIN32 -static _inline void -#else -static __inline__ void -#endif -set_uint32 (char *cp, uint32_t v) -{ - memcpy (cp, &v, sizeof v); -} - -/*! - * @fn void *sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf) - * - * @brief Put result from CTX in first 20 bytes following RESBUF - * - * @details Put result from CTX in first 20 bytes following RESBUF. The result - * must be in little endian byte order. - * - * @param[in] ctx context whose results will be copied - * @param[out] resbuf result of copies saved in little endian byte order - * @return resbuf - */ -void * -sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf) -{ - char *r = (char*)resbuf; - set_uint32 (r + 0 * sizeof ctx->A, SWAP (ctx->A)); - set_uint32 (r + 1 * sizeof ctx->B, SWAP (ctx->B)); - set_uint32 (r + 2 * sizeof ctx->C, SWAP (ctx->C)); - set_uint32 (r + 3 * sizeof ctx->D, SWAP (ctx->D)); - set_uint32 (r + 4 * sizeof ctx->E, SWAP (ctx->E)); - - return resbuf; -} - -/*! - * @fn void *sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf) - * - * @brief Process the remaining bytes in the internal buffer and write - the result to RESBUF. - * - * @details Process the remaining bytes in the internal buffer and the usual - * prolog according to the standard and write the result to RESBUF. - * - * @param[in] ctx context to be used - * @param[out] resbuf resultant SHA1 hash - * @return resultant SHA1 hash - */ -void * -sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf) -{ - /* Take yet unprocessed bytes into account. */ - uint32_t bytes = ctx->buflen; - size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; - - /* Now count remaining bytes. */ - ctx->total[0] += bytes; - if (ctx->total[0] < bytes) - ++ctx->total[1]; - - /* Put the 64-bit file length in *bits* at the end of the buffer. */ - ctx->buffer[size - 2] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); - ctx->buffer[size - 1] = SWAP (ctx->total[0] << 3); - - memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); - - /* Process last bytes. */ - sha1_process_block (ctx->buffer, size * 4, ctx); - - return sha1_read_ctx (ctx, resbuf); -} - - -/* - * @fn void *sha1_stream (FILE *stream, void *resblock) - * - * @brief Compute SHA1 message digest for A Stream. - * - * @details Compute SHA1 message digest for Stream. The - * result is always in little endian byte order, so that a byte-wise - * output yields to the wanted ASCII representation of the message - * digest. - * - * @param[in] stream message stream to be hashed - * @param[out] resblock resultant hash in little endian byte order - * @return resultant hash in little endian byte order - */ -int -sha1_stream (FILE *stream, void *resblock) -{ - struct sha1_ctx ctx; - char buffer[BLOCKSIZE + 72]; - size_t sum; - - /* Initialize the computation context. */ - sha1_init_ctx (&ctx); - - /* Iterate over full file contents. */ - while (1) - { - /* We read the file in blocks of BLOCKSIZE bytes. One call of the - computation function processes the whole buffer so that with the - next round of the loop another block can be read. */ - size_t n; - sum = 0; - - /* Read block. Take care for partial reads. */ - while (1) - { - n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); - - sum += n; - - if (sum == BLOCKSIZE) - break; - - if (n == 0) - { - /* Check for the error flag IFF N == 0, so that we don't - exit the loop after a partial read due to e.g., EAGAIN - or EWOULDBLOCK. */ - if (ferror (stream)) - return 1; - goto process_partial_block; - } - - /* We've read at least one byte, so ignore errors. But always - check for EOF, since feof may be true even though N > 0. - Otherwise, we could end up calling fread after EOF. */ - if (feof (stream)) - goto process_partial_block; - } - - /* Process buffer with BLOCKSIZE bytes. Note that - BLOCKSIZE % 64 == 0 - */ - sha1_process_block (buffer, BLOCKSIZE, &ctx); - } - - process_partial_block:; - - /* Process any remaining bytes. */ - if (sum > 0) - sha1_process_bytes (buffer, sum, &ctx); - - /* Construct result in desired memory. */ - sha1_finish_ctx (&ctx, resblock); - return 0; -} - - -/* - * @fn void *sha1_buffer (const char *buffer, size_t len, void *resblock) - * - * @brief Compute SHA1 message digest for LEN bytes beginning at BUFFER. - * - * @details Compute SHA1 message digest for LEN bytes beginning at BUFFER. The - * result is always in little endian byte order, so that a byte-wise - * output yields to the wanted ASCII representation of the message - * digest. - * - * @param[in] buffer message to be hashed - * @param[in] len length of buffer - * @param[out] resblock resultant hash in little endian byte order - * @return resultant hash in little endian byte order - */ -void * -sha1_buffer (const char *buffer, size_t len, void *resblock) -{ - struct sha1_ctx ctx; - - /* Initialize the computation context. */ - sha1_init_ctx (&ctx); - - /* Process whole buffer but last len % 64 bytes. */ - sha1_process_bytes (buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha1_finish_ctx (&ctx, resblock); -} - -/*! - * @fn void sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx) - * - * @brief update the context for the next LEN bytes starting at BUFFER. - * - * @details Starting with the result of former calls of this function (or the - * initialization function) update the context for the next LEN bytes - * starting at BUFFER. - * It is NOT required that LEN is a multiple of 64. - * - * @param[in] buffer buffer used to update context values - * @param[in] len length of buffer - * @param[out] ctx context to be updated - */ -void -sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx) -{ - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if (ctx->buflen != 0) - { - size_t left_over = ctx->buflen; - size_t add = 128 - left_over > len ? len : 128 - left_over; - - memcpy (&((char *) ctx->buffer)[left_over], buffer, add); - ctx->buflen += add; - - if (ctx->buflen > 64) - { - sha1_process_block (ctx->buffer, ctx->buflen & ~63, ctx); - - ctx->buflen &= 63; - /* The regions in the following copy operation cannot overlap. */ - memcpy (ctx->buffer, - &((char *) ctx->buffer)[(left_over + add) & ~63], - ctx->buflen); - } - - buffer = (const char *) buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if (len >= 64) - { -#if !_STRING_ARCH_unaligned -# define alignof(type) offsetof (struct { char c; type x; }, x) -# define UNALIGNED_P(p) (((size_t) p) % alignof (uint32_t) != 0) - if (UNALIGNED_P (buffer)) - while (len > 64) - { - sha1_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); - buffer = (const char *) buffer + 64; - len -= 64; - } - else -#endif - { - sha1_process_block (buffer, len & ~63, ctx); - buffer = (const char *) buffer + (len & ~63); - len &= 63; - } - } - - /* Move remaining bytes in internal buffer. */ - if (len > 0) - { - size_t left_over = ctx->buflen; - - memcpy (&((char *) ctx->buffer)[left_over], buffer, len); - left_over += len; - if (left_over >= 64) - { - sha1_process_block (ctx->buffer, 64, ctx); - left_over -= 64; - memcpy (ctx->buffer, &ctx->buffer[16], left_over); - } - ctx->buflen = left_over; - } -} - -/* --- Code below is the primary difference between md5.c and sha1.c --- */ - -/* SHA1 round constants */ -#define K1 0x5a827999 -#define K2 0x6ed9eba1 -#define K3 0x8f1bbcdc -#define K4 0xca62c1d6 - -/* Round functions. Note that F2 is the same as F4. */ -#define F1(B,C,D) ( D ^ ( B & ( C ^ D ) ) ) -#define F2(B,C,D) (B ^ C ^ D) -#define F3(B,C,D) ( ( B & C ) | ( D & ( B | C ) ) ) -#define F4(B,C,D) (B ^ C ^ D) - -/*! - * @fn void sha1_process_block (const void *buffer, size_t len, struct sha1_ctx *ctx) - * - * @brief Process LEN bytes of BUFFER, accumulating context into CTX. - * - * @details Process LEN bytes of BUFFER, accumulating context into CTX. - * It is assumed that LEN % 64 == 0. - * Most of this code comes from GnuPG's cipher/sha1.c. - * - * @param[in] buffer buffer to be processed - * @param[in] len length of buffer - * @param[out] ctx context used to accumulate results - */ - -void -sha1_process_block (const void *buffer, size_t len, struct sha1_ctx *ctx) -{ - const uint32_t *words = (const uint32_t*)buffer; - size_t nwords = len / sizeof (uint32_t); - const uint32_t *endp = words + nwords; - uint32_t x[16]; - uint32_t a = ctx->A; - uint32_t b = ctx->B; - uint32_t c = ctx->C; - uint32_t d = ctx->D; - uint32_t e = ctx->E; - - /* First increment the byte count. RFC 1321 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] += len; - if (ctx->total[0] < len) - ++ctx->total[1]; - -#define rol(x, n) (((x) << (n)) | ((uint32_t) (x) >> (32 - (n)))) - -#define M(I) ( tm = x[I&0x0f] ^ x[(I-14)&0x0f] \ - ^ x[(I-8)&0x0f] ^ x[(I-3)&0x0f] \ - , (x[I&0x0f] = rol(tm, 1)) ) - -#define R(A,B,C,D,E,F,K,M) do { E += rol( A, 5 ) \ - + F( B, C, D ) \ - + K \ - + M; \ - B = rol( B, 30 ); \ - } while(0) - - while (words < endp) - { - uint32_t tm; - int t; - for (t = 0; t < 16; t++) - { - x[t] = SWAP (*words); - words++; - } - - R( a, b, c, d, e, F1, K1, x[ 0] ); - R( e, a, b, c, d, F1, K1, x[ 1] ); - R( d, e, a, b, c, F1, K1, x[ 2] ); - R( c, d, e, a, b, F1, K1, x[ 3] ); - R( b, c, d, e, a, F1, K1, x[ 4] ); - R( a, b, c, d, e, F1, K1, x[ 5] ); - R( e, a, b, c, d, F1, K1, x[ 6] ); - R( d, e, a, b, c, F1, K1, x[ 7] ); - R( c, d, e, a, b, F1, K1, x[ 8] ); - R( b, c, d, e, a, F1, K1, x[ 9] ); - R( a, b, c, d, e, F1, K1, x[10] ); - R( e, a, b, c, d, F1, K1, x[11] ); - R( d, e, a, b, c, F1, K1, x[12] ); - R( c, d, e, a, b, F1, K1, x[13] ); - R( b, c, d, e, a, F1, K1, x[14] ); - R( a, b, c, d, e, F1, K1, x[15] ); - R( e, a, b, c, d, F1, K1, M(16) ); - R( d, e, a, b, c, F1, K1, M(17) ); - R( c, d, e, a, b, F1, K1, M(18) ); - R( b, c, d, e, a, F1, K1, M(19) ); - R( a, b, c, d, e, F2, K2, M(20) ); - R( e, a, b, c, d, F2, K2, M(21) ); - R( d, e, a, b, c, F2, K2, M(22) ); - R( c, d, e, a, b, F2, K2, M(23) ); - R( b, c, d, e, a, F2, K2, M(24) ); - R( a, b, c, d, e, F2, K2, M(25) ); - R( e, a, b, c, d, F2, K2, M(26) ); - R( d, e, a, b, c, F2, K2, M(27) ); - R( c, d, e, a, b, F2, K2, M(28) ); - R( b, c, d, e, a, F2, K2, M(29) ); - R( a, b, c, d, e, F2, K2, M(30) ); - R( e, a, b, c, d, F2, K2, M(31) ); - R( d, e, a, b, c, F2, K2, M(32) ); - R( c, d, e, a, b, F2, K2, M(33) ); - R( b, c, d, e, a, F2, K2, M(34) ); - R( a, b, c, d, e, F2, K2, M(35) ); - R( e, a, b, c, d, F2, K2, M(36) ); - R( d, e, a, b, c, F2, K2, M(37) ); - R( c, d, e, a, b, F2, K2, M(38) ); - R( b, c, d, e, a, F2, K2, M(39) ); - R( a, b, c, d, e, F3, K3, M(40) ); - R( e, a, b, c, d, F3, K3, M(41) ); - R( d, e, a, b, c, F3, K3, M(42) ); - R( c, d, e, a, b, F3, K3, M(43) ); - R( b, c, d, e, a, F3, K3, M(44) ); - R( a, b, c, d, e, F3, K3, M(45) ); - R( e, a, b, c, d, F3, K3, M(46) ); - R( d, e, a, b, c, F3, K3, M(47) ); - R( c, d, e, a, b, F3, K3, M(48) ); - R( b, c, d, e, a, F3, K3, M(49) ); - R( a, b, c, d, e, F3, K3, M(50) ); - R( e, a, b, c, d, F3, K3, M(51) ); - R( d, e, a, b, c, F3, K3, M(52) ); - R( c, d, e, a, b, F3, K3, M(53) ); - R( b, c, d, e, a, F3, K3, M(54) ); - R( a, b, c, d, e, F3, K3, M(55) ); - R( e, a, b, c, d, F3, K3, M(56) ); - R( d, e, a, b, c, F3, K3, M(57) ); - R( c, d, e, a, b, F3, K3, M(58) ); - R( b, c, d, e, a, F3, K3, M(59) ); - R( a, b, c, d, e, F4, K4, M(60) ); - R( e, a, b, c, d, F4, K4, M(61) ); - R( d, e, a, b, c, F4, K4, M(62) ); - R( c, d, e, a, b, F4, K4, M(63) ); - R( b, c, d, e, a, F4, K4, M(64) ); - R( a, b, c, d, e, F4, K4, M(65) ); - R( e, a, b, c, d, F4, K4, M(66) ); - R( d, e, a, b, c, F4, K4, M(67) ); - R( c, d, e, a, b, F4, K4, M(68) ); - R( b, c, d, e, a, F4, K4, M(69) ); - R( a, b, c, d, e, F4, K4, M(70) ); - R( e, a, b, c, d, F4, K4, M(71) ); - R( d, e, a, b, c, F4, K4, M(72) ); - R( c, d, e, a, b, F4, K4, M(73) ); - R( b, c, d, e, a, F4, K4, M(74) ); - R( a, b, c, d, e, F4, K4, M(75) ); - R( e, a, b, c, d, F4, K4, M(76) ); - R( d, e, a, b, c, F4, K4, M(77) ); - R( c, d, e, a, b, F4, K4, M(78) ); - R( b, c, d, e, a, F4, K4, M(79) ); - - a = ctx->A += a; - b = ctx->B += b; - c = ctx->C += c; - d = ctx->D += d; - e = ctx->E += e; - } -} -#ifdef __cplusplus -} -#endif diff --git a/libs/signature/src/sha256.c b/libs/signature/src/sha256.c deleted file mode 100755 index a2ca68242..000000000 --- a/libs/signature/src/sha256.c +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sha256.h" - -#include -#include - -#if USE_UNLOCKED_IO -# include "unlocked-io.h" -#endif - -#ifdef __cplusplus -namespace rocketmqSignature { -#endif - -#ifdef WORDS_BIGENDIAN -# define SWAP(n) (n) -#else -# define SWAP(n) \ - (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) -#endif - -#define BLOCKSIZE 4096 -#if BLOCKSIZE % 64 != 0 -# error "invalid BLOCKSIZE" -#endif - -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. */ -static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; - - -/* - Takes a pointer to a 256 bit block of data (eight 32 bit ints) and - intializes it to the start constants of the SHA256 algorithm. This - must be called before using hash in the call to sha256_hash -*/ -void -sha256_init_ctx (struct sha256_ctx *ctx) -{ - ctx->state[0] = 0x6a09e667UL; - ctx->state[1] = 0xbb67ae85UL; - ctx->state[2] = 0x3c6ef372UL; - ctx->state[3] = 0xa54ff53aUL; - ctx->state[4] = 0x510e527fUL; - ctx->state[5] = 0x9b05688cUL; - ctx->state[6] = 0x1f83d9abUL; - ctx->state[7] = 0x5be0cd19UL; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - -void -sha224_init_ctx (struct sha256_ctx *ctx) -{ - ctx->state[0] = 0xc1059ed8UL; - ctx->state[1] = 0x367cd507UL; - ctx->state[2] = 0x3070dd17UL; - ctx->state[3] = 0xf70e5939UL; - ctx->state[4] = 0xffc00b31UL; - ctx->state[5] = 0x68581511UL; - ctx->state[6] = 0x64f98fa7UL; - ctx->state[7] = 0xbefa4fa4UL; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - -/* Copy the value from v into the memory location pointed to by *cp, - If your architecture allows unaligned access this is equivalent to - * (uint32_t *) cp = v */ -#ifdef WIN32 -static _inline void -#else -static __inline__ void -#endif -set_uint32 (char *cp, uint32_t v) -{ - memcpy (cp, &v, sizeof v); -} - -/* Put result from CTX in first 32 bytes following RESBUF. The result - must be in little endian byte order. */ -void * -sha256_read_ctx (const struct sha256_ctx *ctx, void *resbuf) -{ - int i; - char *r = (char*)resbuf; - - for (i = 0; i < 8; i++) - set_uint32 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i])); - - return resbuf; -} - -void * -sha224_read_ctx (const struct sha256_ctx *ctx, void *resbuf) -{ - int i; - char *r = (char*)resbuf; - - for (i = 0; i < 7; i++) - set_uint32 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i])); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. */ -static void -sha256_conclude_ctx (struct sha256_ctx *ctx) -{ - /* Take yet unprocessed bytes into account. */ - size_t bytes = ctx->buflen; - size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; - - /* Now count remaining bytes. */ - ctx->total[0] += bytes; - if (ctx->total[0] < bytes) - ++ctx->total[1]; - - /* Put the 64-bit file length in *bits* at the end of the buffer. - Use set_uint32 rather than a simple assignment, to avoid risk of - unaligned access. */ - set_uint32 ((char *) &ctx->buffer[size - 2], - SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29))); - set_uint32 ((char *) &ctx->buffer[size - 1], - SWAP (ctx->total[0] << 3)); - - memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); - - /* Process last bytes. */ - sha256_process_block (ctx->buffer, size * 4, ctx); -} - -void * -sha256_finish_ctx (struct sha256_ctx *ctx, void *resbuf) -{ - sha256_conclude_ctx (ctx); - return sha256_read_ctx (ctx, resbuf); -} - -void * -sha224_finish_ctx (struct sha256_ctx *ctx, void *resbuf) -{ - sha256_conclude_ctx (ctx); - return sha224_read_ctx (ctx, resbuf); -} - -/* Compute SHA256 message digest for bytes read from STREAM. The - resulting message digest number will be written into the 32 bytes - beginning at RESBLOCK. */ -int -sha256_stream (FILE *stream, void *resblock) -{ - struct sha256_ctx ctx; - char buffer[BLOCKSIZE + 72]; - size_t sum; - - /* Initialize the computation context. */ - sha256_init_ctx (&ctx); - - /* Iterate over full file contents. */ - while (1) - { - /* We read the file in blocks of BLOCKSIZE bytes. One call of the - computation function processes the whole buffer so that with the - next round of the loop another block can be read. */ - size_t n; - sum = 0; - - /* Read block. Take care for partial reads. */ - while (1) - { - n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); - - sum += n; - - if (sum == BLOCKSIZE) - break; - - if (n == 0) - { - /* Check for the error flag IFF N == 0, so that we don't - exit the loop after a partial read due to e.g., EAGAIN - or EWOULDBLOCK. */ - if (ferror (stream)) - return 1; - goto process_partial_block; - } - - /* We've read at least one byte, so ignore errors. But always - check for EOF, since feof may be true even though N > 0. - Otherwise, we could end up calling fread after EOF. */ - if (feof (stream)) - goto process_partial_block; - } - - /* Process buffer with BLOCKSIZE bytes. Note that - BLOCKSIZE % 64 == 0 - */ - sha256_process_block (buffer, BLOCKSIZE, &ctx); - } - - process_partial_block:; - - /* Process any remaining bytes. */ - if (sum > 0) - sha256_process_bytes (buffer, sum, &ctx); - - /* Construct result in desired memory. */ - sha256_finish_ctx (&ctx, resblock); - return 0; -} - -/* FIXME: Avoid code duplication */ -int -sha224_stream (FILE *stream, void *resblock) -{ - struct sha256_ctx ctx; - char buffer[BLOCKSIZE + 72]; - size_t sum; - - /* Initialize the computation context. */ - sha224_init_ctx (&ctx); - - /* Iterate over full file contents. */ - while (1) - { - /* We read the file in blocks of BLOCKSIZE bytes. One call of the - computation function processes the whole buffer so that with the - next round of the loop another block can be read. */ - size_t n; - sum = 0; - - /* Read block. Take care for partial reads. */ - while (1) - { - n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); - - sum += n; - - if (sum == BLOCKSIZE) - break; - - if (n == 0) - { - /* Check for the error flag IFF N == 0, so that we don't - exit the loop after a partial read due to e.g., EAGAIN - or EWOULDBLOCK. */ - if (ferror (stream)) - return 1; - goto process_partial_block; - } - - /* We've read at least one byte, so ignore errors. But always - check for EOF, since feof may be true even though N > 0. - Otherwise, we could end up calling fread after EOF. */ - if (feof (stream)) - goto process_partial_block; - } - - /* Process buffer with BLOCKSIZE bytes. Note that - BLOCKSIZE % 64 == 0 - */ - sha256_process_block (buffer, BLOCKSIZE, &ctx); - } - - process_partial_block:; - - /* Process any remaining bytes. */ - if (sum > 0) - sha256_process_bytes (buffer, sum, &ctx); - - /* Construct result in desired memory. */ - sha224_finish_ctx (&ctx, resblock); - return 0; -} - -/* Compute SHA512 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -void * -sha256_buffer (const char *buffer, size_t len, void *resblock) -{ - struct sha256_ctx ctx; - - /* Initialize the computation context. */ - sha256_init_ctx (&ctx); - - /* Process whole buffer but last len % 64 bytes. */ - sha256_process_bytes (buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha256_finish_ctx (&ctx, resblock); -} - -void * -sha224_buffer (const char *buffer, size_t len, void *resblock) -{ - struct sha256_ctx ctx; - - /* Initialize the computation context. */ - sha224_init_ctx (&ctx); - - /* Process whole buffer but last len % 64 bytes. */ - sha256_process_bytes (buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha224_finish_ctx (&ctx, resblock); -} - -void -sha256_process_bytes (const void *buffer, size_t len, struct sha256_ctx *ctx) -{ - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if (ctx->buflen != 0) - { - size_t left_over = ctx->buflen; - size_t add = 128 - left_over > len ? len : 128 - left_over; - - memcpy (&((char *) ctx->buffer)[left_over], buffer, add); - ctx->buflen += add; - - if (ctx->buflen > 64) - { - sha256_process_block (ctx->buffer, ctx->buflen & ~63, ctx); - - ctx->buflen &= 63; - /* The regions in the following copy operation cannot overlap. */ - memcpy (ctx->buffer, - &((char *) ctx->buffer)[(left_over + add) & ~63], - ctx->buflen); - } - - buffer = (const char *) buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if (len >= 64) - { -#if !_STRING_ARCH_unaligned -# define alignof(type) offsetof (struct { char c; type x; }, x) -# define UNALIGNED_P(p) (((size_t) p) % alignof (uint32_t) != 0) - if (UNALIGNED_P (buffer)) - while (len > 64) - { - sha256_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); - buffer = (const char *) buffer + 64; - len -= 64; - } - else -#endif - { - sha256_process_block (buffer, len & ~63, ctx); - buffer = (const char *) buffer + (len & ~63); - len &= 63; - } - } - - /* Move remaining bytes in internal buffer. */ - if (len > 0) - { - size_t left_over = ctx->buflen; - - memcpy (&((char *) ctx->buffer)[left_over], buffer, len); - left_over += len; - if (left_over >= 64) - { - sha256_process_block (ctx->buffer, 64, ctx); - left_over -= 64; - memcpy (ctx->buffer, &ctx->buffer[16], left_over); - } - ctx->buflen = left_over; - } -} - -/* --- Code below is the primary difference between sha1.c and sha256.c --- */ - -/* SHA256 round constants */ -#define K(I) sha256_round_constants[I] -static const uint32_t sha256_round_constants[64] = { - 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, - 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, - 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, - 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, - 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, - 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, - 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, - 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, - 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, - 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, - 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, - 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, - 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, - 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, - 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, - 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL, -}; - -/* Round functions. */ -#define F2(A,B,C) ( ( A & B ) | ( C & ( A | B ) ) ) -#define F1(E,F,G) ( G ^ ( E & ( F ^ G ) ) ) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 64 == 0. - Most of this code comes from GnuPG's cipher/sha1.c. */ - -void -sha256_process_block (const void *buffer, size_t len, struct sha256_ctx *ctx) -{ - const uint32_t *words = (const uint32_t *)buffer; - size_t nwords = len / sizeof (uint32_t); - const uint32_t *endp = words + nwords; - uint32_t x[16]; - uint32_t a = ctx->state[0]; - uint32_t b = ctx->state[1]; - uint32_t c = ctx->state[2]; - uint32_t d = ctx->state[3]; - uint32_t e = ctx->state[4]; - uint32_t f = ctx->state[5]; - uint32_t g = ctx->state[6]; - uint32_t h = ctx->state[7]; - - /* First increment the byte count. FIPS PUB 180-2 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] += len; - if (ctx->total[0] < len) - ++ctx->total[1]; - -#define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) -#define S0(x) (rol(x,25)^rol(x,14)^(x>>3)) -#define S1(x) (rol(x,15)^rol(x,13)^(x>>10)) -#define SS0(x) (rol(x,30)^rol(x,19)^rol(x,10)) -#define SS1(x) (rol(x,26)^rol(x,21)^rol(x,7)) - -#define M(I) ( tm = S1(x[(I-2)&0x0f]) + x[(I-7)&0x0f] \ - + S0(x[(I-15)&0x0f]) + x[I&0x0f] \ - , x[I&0x0f] = tm ) - -#define R(A,B,C,D,E,F,G,H,K,M) do { t0 = SS0(A) + F2(A,B,C); \ - t1 = H + SS1(E) \ - + F1(E,F,G) \ - + K \ - + M; \ - D += t1; H = t0 + t1; \ - } while(0) - - while (words < endp) - { - uint32_t tm; - uint32_t t0, t1; - int t; - /* FIXME: see sha1.c for a better implementation. */ - for (t = 0; t < 16; t++) - { - x[t] = SWAP (*words); - words++; - } - - R( a, b, c, d, e, f, g, h, K( 0), x[ 0] ); - R( h, a, b, c, d, e, f, g, K( 1), x[ 1] ); - R( g, h, a, b, c, d, e, f, K( 2), x[ 2] ); - R( f, g, h, a, b, c, d, e, K( 3), x[ 3] ); - R( e, f, g, h, a, b, c, d, K( 4), x[ 4] ); - R( d, e, f, g, h, a, b, c, K( 5), x[ 5] ); - R( c, d, e, f, g, h, a, b, K( 6), x[ 6] ); - R( b, c, d, e, f, g, h, a, K( 7), x[ 7] ); - R( a, b, c, d, e, f, g, h, K( 8), x[ 8] ); - R( h, a, b, c, d, e, f, g, K( 9), x[ 9] ); - R( g, h, a, b, c, d, e, f, K(10), x[10] ); - R( f, g, h, a, b, c, d, e, K(11), x[11] ); - R( e, f, g, h, a, b, c, d, K(12), x[12] ); - R( d, e, f, g, h, a, b, c, K(13), x[13] ); - R( c, d, e, f, g, h, a, b, K(14), x[14] ); - R( b, c, d, e, f, g, h, a, K(15), x[15] ); - R( a, b, c, d, e, f, g, h, K(16), M(16) ); - R( h, a, b, c, d, e, f, g, K(17), M(17) ); - R( g, h, a, b, c, d, e, f, K(18), M(18) ); - R( f, g, h, a, b, c, d, e, K(19), M(19) ); - R( e, f, g, h, a, b, c, d, K(20), M(20) ); - R( d, e, f, g, h, a, b, c, K(21), M(21) ); - R( c, d, e, f, g, h, a, b, K(22), M(22) ); - R( b, c, d, e, f, g, h, a, K(23), M(23) ); - R( a, b, c, d, e, f, g, h, K(24), M(24) ); - R( h, a, b, c, d, e, f, g, K(25), M(25) ); - R( g, h, a, b, c, d, e, f, K(26), M(26) ); - R( f, g, h, a, b, c, d, e, K(27), M(27) ); - R( e, f, g, h, a, b, c, d, K(28), M(28) ); - R( d, e, f, g, h, a, b, c, K(29), M(29) ); - R( c, d, e, f, g, h, a, b, K(30), M(30) ); - R( b, c, d, e, f, g, h, a, K(31), M(31) ); - R( a, b, c, d, e, f, g, h, K(32), M(32) ); - R( h, a, b, c, d, e, f, g, K(33), M(33) ); - R( g, h, a, b, c, d, e, f, K(34), M(34) ); - R( f, g, h, a, b, c, d, e, K(35), M(35) ); - R( e, f, g, h, a, b, c, d, K(36), M(36) ); - R( d, e, f, g, h, a, b, c, K(37), M(37) ); - R( c, d, e, f, g, h, a, b, K(38), M(38) ); - R( b, c, d, e, f, g, h, a, K(39), M(39) ); - R( a, b, c, d, e, f, g, h, K(40), M(40) ); - R( h, a, b, c, d, e, f, g, K(41), M(41) ); - R( g, h, a, b, c, d, e, f, K(42), M(42) ); - R( f, g, h, a, b, c, d, e, K(43), M(43) ); - R( e, f, g, h, a, b, c, d, K(44), M(44) ); - R( d, e, f, g, h, a, b, c, K(45), M(45) ); - R( c, d, e, f, g, h, a, b, K(46), M(46) ); - R( b, c, d, e, f, g, h, a, K(47), M(47) ); - R( a, b, c, d, e, f, g, h, K(48), M(48) ); - R( h, a, b, c, d, e, f, g, K(49), M(49) ); - R( g, h, a, b, c, d, e, f, K(50), M(50) ); - R( f, g, h, a, b, c, d, e, K(51), M(51) ); - R( e, f, g, h, a, b, c, d, K(52), M(52) ); - R( d, e, f, g, h, a, b, c, K(53), M(53) ); - R( c, d, e, f, g, h, a, b, K(54), M(54) ); - R( b, c, d, e, f, g, h, a, K(55), M(55) ); - R( a, b, c, d, e, f, g, h, K(56), M(56) ); - R( h, a, b, c, d, e, f, g, K(57), M(57) ); - R( g, h, a, b, c, d, e, f, K(58), M(58) ); - R( f, g, h, a, b, c, d, e, K(59), M(59) ); - R( e, f, g, h, a, b, c, d, K(60), M(60) ); - R( d, e, f, g, h, a, b, c, K(61), M(61) ); - R( c, d, e, f, g, h, a, b, K(62), M(62) ); - R( b, c, d, e, f, g, h, a, K(63), M(63) ); - - a = ctx->state[0] += a; - b = ctx->state[1] += b; - c = ctx->state[2] += c; - d = ctx->state[3] += d; - e = ctx->state[4] += e; - f = ctx->state[5] += f; - g = ctx->state[6] += g; - h = ctx->state[7] += h; - } -} -#ifdef __cplusplus -} -#endif - diff --git a/libs/signature/src/sha512.c b/libs/signature/src/sha512.c deleted file mode 100755 index 0d9741efb..000000000 --- a/libs/signature/src/sha512.c +++ /dev/null @@ -1,609 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sha512.h" - -#include -#include - -#if USE_UNLOCKED_IO -# include "unlocked-io.h" -#endif - -#ifdef __cplusplus -namespace rocketmqSignature{ -#endif - -#ifdef WORDS_BIGENDIAN -# define SWAP(n) (n) -#else -# define SWAP(n) \ - u64or (u64or (u64or (u64shl (n, 56), \ - u64shl (u64and (n, u64lo (0x0000ff00)), 40)), \ - u64or (u64shl (u64and (n, u64lo (0x00ff0000)), 24), \ - u64shl (u64and (n, u64lo (0xff000000)), 8))), \ - u64or (u64or (u64and (u64shr (n, 8), u64lo (0xff000000)), \ - u64and (u64shr (n, 24), u64lo (0x00ff0000))), \ - u64or (u64and (u64shr (n, 40), u64lo (0x0000ff00)), \ - u64shr (n, 56)))) -#endif - -#define BLOCKSIZE 4096 -#if BLOCKSIZE % 128 != 0 -# error "invalid BLOCKSIZE" -#endif - -/* This array contains the bytes used to pad the buffer to the next - 128-byte boundary. */ -static const unsigned char fillbuf[128] = { 0x80, 0 /* , 0, 0, ... */ }; - - -/* - Takes a pointer to a 512 bit block of data (eight 64 bit ints) and - intializes it to the start constants of the SHA512 algorithm. This - must be called before using hash in the call to sha512_hash -*/ -void -sha512_init_ctx (struct sha512_ctx *ctx) -{ - ctx->state[0] = u64hilo (0x6a09e667, 0xf3bcc908); - ctx->state[1] = u64hilo (0xbb67ae85, 0x84caa73b); - ctx->state[2] = u64hilo (0x3c6ef372, 0xfe94f82b); - ctx->state[3] = u64hilo (0xa54ff53a, 0x5f1d36f1); - ctx->state[4] = u64hilo (0x510e527f, 0xade682d1); - ctx->state[5] = u64hilo (0x9b05688c, 0x2b3e6c1f); - ctx->state[6] = u64hilo (0x1f83d9ab, 0xfb41bd6b); - ctx->state[7] = u64hilo (0x5be0cd19, 0x137e2179); - - ctx->total[0] = ctx->total[1] = u64lo (0); - ctx->buflen = 0; -} - -void -sha384_init_ctx (struct sha512_ctx *ctx) -{ - ctx->state[0] = u64hilo (0xcbbb9d5d, 0xc1059ed8); - ctx->state[1] = u64hilo (0x629a292a, 0x367cd507); - ctx->state[2] = u64hilo (0x9159015a, 0x3070dd17); - ctx->state[3] = u64hilo (0x152fecd8, 0xf70e5939); - ctx->state[4] = u64hilo (0x67332667, 0xffc00b31); - ctx->state[5] = u64hilo (0x8eb44a87, 0x68581511); - ctx->state[6] = u64hilo (0xdb0c2e0d, 0x64f98fa7); - ctx->state[7] = u64hilo (0x47b5481d, 0xbefa4fa4); - - ctx->total[0] = ctx->total[1] = u64lo (0); - ctx->buflen = 0; -} - -/* Copy the value from V into the memory location pointed to by *CP, - If your architecture allows unaligned access, this is equivalent to - * (__typeof__ (v) *) cp = v */ -#ifdef WIN32 -static _inline void -#else -static __inline__ void -#endif -set_uint64 (char *cp, u64 v) -{ - memcpy (cp, &v, sizeof v); -} - -/* Put result from CTX in first 64 bytes following RESBUF. - The result must be in little endian byte order. */ -void * -sha512_read_ctx (const struct sha512_ctx *ctx, void *resbuf) -{ - int i; - char *r = (char*)resbuf; - - for (i = 0; i < 8; i++) - set_uint64 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i])); - - return resbuf; -} - -void * -sha384_read_ctx (const struct sha512_ctx *ctx, void *resbuf) -{ - int i; - char *r = (char*)resbuf; - - for (i = 0; i < 6; i++) - set_uint64 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i])); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. */ -static void -sha512_conclude_ctx (struct sha512_ctx *ctx) -{ - /* Take yet unprocessed bytes into account. */ - size_t bytes = ctx->buflen; - size_t size = (bytes < 112) ? 128 / 8 : 128 * 2 / 8; - - /* Now count remaining bytes. */ - ctx->total[0] = u64plus (ctx->total[0], u64lo (bytes)); - if (u64lt (ctx->total[0], u64lo (bytes))) - ctx->total[1] = u64plus (ctx->total[1], u64lo (1)); - - /* Put the 128-bit file length in *bits* at the end of the buffer. - Use set_uint64 rather than a simple assignment, to avoid risk of - unaligned access. */ - set_uint64 ((char *) &ctx->buffer[size - 2], - SWAP (u64or (u64shl (ctx->total[1], 3), - u64shr (ctx->total[0], 61)))); - set_uint64 ((char *) &ctx->buffer[size - 1], - SWAP (u64shl (ctx->total[0], 3))); - - memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 8 - bytes); - - /* Process last bytes. */ - sha512_process_block (ctx->buffer, size * 8, ctx); -} - -void * -sha512_finish_ctx (struct sha512_ctx *ctx, void *resbuf) -{ - sha512_conclude_ctx (ctx); - return sha512_read_ctx (ctx, resbuf); -} - -void * -sha384_finish_ctx (struct sha512_ctx *ctx, void *resbuf) -{ - sha512_conclude_ctx (ctx); - return sha384_read_ctx (ctx, resbuf); -} - -/* Compute SHA512 message digest for bytes read from STREAM. The - resulting message digest number will be written into the 64 bytes - beginning at RESBLOCK. */ -int -sha512_stream (FILE *stream, void *resblock) -{ - struct sha512_ctx ctx; - char buffer[BLOCKSIZE + 72]; - size_t sum; - - /* Initialize the computation context. */ - sha512_init_ctx (&ctx); - - /* Iterate over full file contents. */ - while (1) - { - /* We read the file in blocks of BLOCKSIZE bytes. One call of the - computation function processes the whole buffer so that with the - next round of the loop another block can be read. */ - size_t n; - sum = 0; - - /* Read block. Take care for partial reads. */ - while (1) - { - n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); - - sum += n; - - if (sum == BLOCKSIZE) - break; - - if (n == 0) - { - /* Check for the error flag IFF N == 0, so that we don't - exit the loop after a partial read due to e.g., EAGAIN - or EWOULDBLOCK. */ - if (ferror (stream)) - return 1; - goto process_partial_block; - } - - /* We've read at least one byte, so ignore errors. But always - check for EOF, since feof may be true even though N > 0. - Otherwise, we could end up calling fread after EOF. */ - if (feof (stream)) - goto process_partial_block; - } - - /* Process buffer with BLOCKSIZE bytes. Note that - BLOCKSIZE % 128 == 0 - */ - sha512_process_block (buffer, BLOCKSIZE, &ctx); - } - - process_partial_block:; - - /* Process any remaining bytes. */ - if (sum > 0) - sha512_process_bytes (buffer, sum, &ctx); - - /* Construct result in desired memory. */ - sha512_finish_ctx (&ctx, resblock); - return 0; -} - -/* FIXME: Avoid code duplication */ -int -sha384_stream (FILE *stream, void *resblock) -{ - struct sha512_ctx ctx; - char buffer[BLOCKSIZE + 72]; - size_t sum; - - /* Initialize the computation context. */ - sha384_init_ctx (&ctx); - - /* Iterate over full file contents. */ - while (1) - { - /* We read the file in blocks of BLOCKSIZE bytes. One call of the - computation function processes the whole buffer so that with the - next round of the loop another block can be read. */ - size_t n; - sum = 0; - - /* Read block. Take care for partial reads. */ - while (1) - { - n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); - - sum += n; - - if (sum == BLOCKSIZE) - break; - - if (n == 0) - { - /* Check for the error flag IFF N == 0, so that we don't - exit the loop after a partial read due to e.g., EAGAIN - or EWOULDBLOCK. */ - if (ferror (stream)) - return 1; - goto process_partial_block; - } - - /* We've read at least one byte, so ignore errors. But always - check for EOF, since feof may be true even though N > 0. - Otherwise, we could end up calling fread after EOF. */ - if (feof (stream)) - goto process_partial_block; - } - - /* Process buffer with BLOCKSIZE bytes. Note that - BLOCKSIZE % 128 == 0 - */ - sha512_process_block (buffer, BLOCKSIZE, &ctx); - } - - process_partial_block:; - - /* Process any remaining bytes. */ - if (sum > 0) - sha512_process_bytes (buffer, sum, &ctx); - - /* Construct result in desired memory. */ - sha384_finish_ctx (&ctx, resblock); - return 0; -} - -/* Compute SHA512 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -void * -sha512_buffer (const char *buffer, size_t len, void *resblock) -{ - struct sha512_ctx ctx; - - /* Initialize the computation context. */ - sha512_init_ctx (&ctx); - - /* Process whole buffer but last len % 128 bytes. */ - sha512_process_bytes (buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha512_finish_ctx (&ctx, resblock); -} - -void * -sha384_buffer (const char *buffer, size_t len, void *resblock) -{ - struct sha512_ctx ctx; - - /* Initialize the computation context. */ - sha384_init_ctx (&ctx); - - /* Process whole buffer but last len % 128 bytes. */ - sha512_process_bytes (buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha384_finish_ctx (&ctx, resblock); -} - -void -sha512_process_bytes (const void *buffer, size_t len, struct sha512_ctx *ctx) -{ - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if (ctx->buflen != 0) - { - size_t left_over = ctx->buflen; - size_t add = 256 - left_over > len ? len : 256 - left_over; - - memcpy (&((char *) ctx->buffer)[left_over], buffer, add); - ctx->buflen += add; - - if (ctx->buflen > 128) - { - sha512_process_block (ctx->buffer, ctx->buflen & ~127, ctx); - - ctx->buflen &= 127; - /* The regions in the following copy operation cannot overlap. */ - memcpy (ctx->buffer, - &((char *) ctx->buffer)[(left_over + add) & ~127], - ctx->buflen); - } - - buffer = (const char *) buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if (len >= 128) - { -#if !_STRING_ARCH_unaligned -# define alignof(type) offsetof (struct { char c; type x; }, x) -# define UNALIGNED_P(p) (((size_t) p) % alignof (u64) != 0) - if (UNALIGNED_P (buffer)) - while (len > 128) - { - sha512_process_block (memcpy (ctx->buffer, buffer, 128), 128, ctx); - buffer = (const char *) buffer + 128; - len -= 128; - } - else -#endif - { - sha512_process_block (buffer, len & ~127, ctx); - buffer = (const char *) buffer + (len & ~127); - len &= 127; - } - } - - /* Move remaining bytes in internal buffer. */ - if (len > 0) - { - size_t left_over = ctx->buflen; - - memcpy (&((char *) ctx->buffer)[left_over], buffer, len); - left_over += len; - if (left_over >= 128) - { - sha512_process_block (ctx->buffer, 128, ctx); - left_over -= 128; - memcpy (ctx->buffer, &ctx->buffer[16], left_over); - } - ctx->buflen = left_over; - } -} - -/* --- Code below is the primary difference between sha1.c and sha512.c --- */ - -/* SHA512 round constants */ -#define K(I) sha512_round_constants[I] -static u64 const sha512_round_constants[80] = { - u64init (0x428a2f98, 0xd728ae22), u64init (0x71374491, 0x23ef65cd), - u64init (0xb5c0fbcf, 0xec4d3b2f), u64init (0xe9b5dba5, 0x8189dbbc), - u64init (0x3956c25b, 0xf348b538), u64init (0x59f111f1, 0xb605d019), - u64init (0x923f82a4, 0xaf194f9b), u64init (0xab1c5ed5, 0xda6d8118), - u64init (0xd807aa98, 0xa3030242), u64init (0x12835b01, 0x45706fbe), - u64init (0x243185be, 0x4ee4b28c), u64init (0x550c7dc3, 0xd5ffb4e2), - u64init (0x72be5d74, 0xf27b896f), u64init (0x80deb1fe, 0x3b1696b1), - u64init (0x9bdc06a7, 0x25c71235), u64init (0xc19bf174, 0xcf692694), - u64init (0xe49b69c1, 0x9ef14ad2), u64init (0xefbe4786, 0x384f25e3), - u64init (0x0fc19dc6, 0x8b8cd5b5), u64init (0x240ca1cc, 0x77ac9c65), - u64init (0x2de92c6f, 0x592b0275), u64init (0x4a7484aa, 0x6ea6e483), - u64init (0x5cb0a9dc, 0xbd41fbd4), u64init (0x76f988da, 0x831153b5), - u64init (0x983e5152, 0xee66dfab), u64init (0xa831c66d, 0x2db43210), - u64init (0xb00327c8, 0x98fb213f), u64init (0xbf597fc7, 0xbeef0ee4), - u64init (0xc6e00bf3, 0x3da88fc2), u64init (0xd5a79147, 0x930aa725), - u64init (0x06ca6351, 0xe003826f), u64init (0x14292967, 0x0a0e6e70), - u64init (0x27b70a85, 0x46d22ffc), u64init (0x2e1b2138, 0x5c26c926), - u64init (0x4d2c6dfc, 0x5ac42aed), u64init (0x53380d13, 0x9d95b3df), - u64init (0x650a7354, 0x8baf63de), u64init (0x766a0abb, 0x3c77b2a8), - u64init (0x81c2c92e, 0x47edaee6), u64init (0x92722c85, 0x1482353b), - u64init (0xa2bfe8a1, 0x4cf10364), u64init (0xa81a664b, 0xbc423001), - u64init (0xc24b8b70, 0xd0f89791), u64init (0xc76c51a3, 0x0654be30), - u64init (0xd192e819, 0xd6ef5218), u64init (0xd6990624, 0x5565a910), - u64init (0xf40e3585, 0x5771202a), u64init (0x106aa070, 0x32bbd1b8), - u64init (0x19a4c116, 0xb8d2d0c8), u64init (0x1e376c08, 0x5141ab53), - u64init (0x2748774c, 0xdf8eeb99), u64init (0x34b0bcb5, 0xe19b48a8), - u64init (0x391c0cb3, 0xc5c95a63), u64init (0x4ed8aa4a, 0xe3418acb), - u64init (0x5b9cca4f, 0x7763e373), u64init (0x682e6ff3, 0xd6b2b8a3), - u64init (0x748f82ee, 0x5defb2fc), u64init (0x78a5636f, 0x43172f60), - u64init (0x84c87814, 0xa1f0ab72), u64init (0x8cc70208, 0x1a6439ec), - u64init (0x90befffa, 0x23631e28), u64init (0xa4506ceb, 0xde82bde9), - u64init (0xbef9a3f7, 0xb2c67915), u64init (0xc67178f2, 0xe372532b), - u64init (0xca273ece, 0xea26619c), u64init (0xd186b8c7, 0x21c0c207), - u64init (0xeada7dd6, 0xcde0eb1e), u64init (0xf57d4f7f, 0xee6ed178), - u64init (0x06f067aa, 0x72176fba), u64init (0x0a637dc5, 0xa2c898a6), - u64init (0x113f9804, 0xbef90dae), u64init (0x1b710b35, 0x131c471b), - u64init (0x28db77f5, 0x23047d84), u64init (0x32caab7b, 0x40c72493), - u64init (0x3c9ebe0a, 0x15c9bebc), u64init (0x431d67c4, 0x9c100d4c), - u64init (0x4cc5d4be, 0xcb3e42b6), u64init (0x597f299c, 0xfc657e2a), - u64init (0x5fcb6fab, 0x3ad6faec), u64init (0x6c44198c, 0x4a475817), -}; - -/* Round functions. */ -#define F2(A, B, C) u64or (u64and (A, B), u64and (C, u64or (A, B))) -#define F1(E, F, G) u64xor (G, u64and (E, u64xor (F, G))) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 128 == 0. - Most of this code comes from GnuPG's cipher/sha1.c. */ - -void -sha512_process_block (const void *buffer, size_t len, struct sha512_ctx *ctx) -{ - u64 const *words = (u64 const *)buffer; - u64 const *endp = words + len / sizeof (u64); - u64 x[16]; - u64 a = ctx->state[0]; - u64 b = ctx->state[1]; - u64 c = ctx->state[2]; - u64 d = ctx->state[3]; - u64 e = ctx->state[4]; - u64 f = ctx->state[5]; - u64 g = ctx->state[6]; - u64 h = ctx->state[7]; - - /* First increment the byte count. FIPS PUB 180-2 specifies the possible - length of the file up to 2^128 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] = u64plus (ctx->total[0], u64lo (len)); - if (u64lt (ctx->total[0], u64lo (len))) - ctx->total[1] = u64plus (ctx->total[1], u64lo (1)); - -#define S0(x) u64xor (u64rol(x, 63), u64xor (u64rol (x, 56), u64shr (x, 7))) -#define S1(x) u64xor (u64rol (x, 45), u64xor (u64rol (x, 3), u64shr (x, 6))) -#define SS0(x) u64xor (u64rol (x, 36), u64xor (u64rol (x, 30), u64rol (x, 25))) -#define SS1(x) u64xor (u64rol(x, 50), u64xor (u64rol (x, 46), u64rol (x, 23))) - -#define M(I) (x[(I) & 15] \ - = u64plus (x[(I) & 15], \ - u64plus (S1 (x[((I) - 2) & 15]), \ - u64plus (x[((I) - 7) & 15], \ - S0 (x[((I) - 15) & 15]))))) - -#define R(A, B, C, D, E, F, G, H, K, M) \ - do \ - { \ - u64 t0 = u64plus (SS0 (A), F2 (A, B, C)); \ - u64 t1 = \ - u64plus (H, u64plus (SS1 (E), \ - u64plus (F1 (E, F, G), u64plus (K, M)))); \ - D = u64plus (D, t1); \ - H = u64plus (t0, t1); \ - } \ - while (0) - - while (words < endp) - { - int t; - /* FIXME: see sha1.c for a better implementation. */ - for (t = 0; t < 16; t++) - { - x[t] = SWAP (*words); - words++; - } - - R( a, b, c, d, e, f, g, h, K( 0), x[ 0] ); - R( h, a, b, c, d, e, f, g, K( 1), x[ 1] ); - R( g, h, a, b, c, d, e, f, K( 2), x[ 2] ); - R( f, g, h, a, b, c, d, e, K( 3), x[ 3] ); - R( e, f, g, h, a, b, c, d, K( 4), x[ 4] ); - R( d, e, f, g, h, a, b, c, K( 5), x[ 5] ); - R( c, d, e, f, g, h, a, b, K( 6), x[ 6] ); - R( b, c, d, e, f, g, h, a, K( 7), x[ 7] ); - R( a, b, c, d, e, f, g, h, K( 8), x[ 8] ); - R( h, a, b, c, d, e, f, g, K( 9), x[ 9] ); - R( g, h, a, b, c, d, e, f, K(10), x[10] ); - R( f, g, h, a, b, c, d, e, K(11), x[11] ); - R( e, f, g, h, a, b, c, d, K(12), x[12] ); - R( d, e, f, g, h, a, b, c, K(13), x[13] ); - R( c, d, e, f, g, h, a, b, K(14), x[14] ); - R( b, c, d, e, f, g, h, a, K(15), x[15] ); - R( a, b, c, d, e, f, g, h, K(16), M(16) ); - R( h, a, b, c, d, e, f, g, K(17), M(17) ); - R( g, h, a, b, c, d, e, f, K(18), M(18) ); - R( f, g, h, a, b, c, d, e, K(19), M(19) ); - R( e, f, g, h, a, b, c, d, K(20), M(20) ); - R( d, e, f, g, h, a, b, c, K(21), M(21) ); - R( c, d, e, f, g, h, a, b, K(22), M(22) ); - R( b, c, d, e, f, g, h, a, K(23), M(23) ); - R( a, b, c, d, e, f, g, h, K(24), M(24) ); - R( h, a, b, c, d, e, f, g, K(25), M(25) ); - R( g, h, a, b, c, d, e, f, K(26), M(26) ); - R( f, g, h, a, b, c, d, e, K(27), M(27) ); - R( e, f, g, h, a, b, c, d, K(28), M(28) ); - R( d, e, f, g, h, a, b, c, K(29), M(29) ); - R( c, d, e, f, g, h, a, b, K(30), M(30) ); - R( b, c, d, e, f, g, h, a, K(31), M(31) ); - R( a, b, c, d, e, f, g, h, K(32), M(32) ); - R( h, a, b, c, d, e, f, g, K(33), M(33) ); - R( g, h, a, b, c, d, e, f, K(34), M(34) ); - R( f, g, h, a, b, c, d, e, K(35), M(35) ); - R( e, f, g, h, a, b, c, d, K(36), M(36) ); - R( d, e, f, g, h, a, b, c, K(37), M(37) ); - R( c, d, e, f, g, h, a, b, K(38), M(38) ); - R( b, c, d, e, f, g, h, a, K(39), M(39) ); - R( a, b, c, d, e, f, g, h, K(40), M(40) ); - R( h, a, b, c, d, e, f, g, K(41), M(41) ); - R( g, h, a, b, c, d, e, f, K(42), M(42) ); - R( f, g, h, a, b, c, d, e, K(43), M(43) ); - R( e, f, g, h, a, b, c, d, K(44), M(44) ); - R( d, e, f, g, h, a, b, c, K(45), M(45) ); - R( c, d, e, f, g, h, a, b, K(46), M(46) ); - R( b, c, d, e, f, g, h, a, K(47), M(47) ); - R( a, b, c, d, e, f, g, h, K(48), M(48) ); - R( h, a, b, c, d, e, f, g, K(49), M(49) ); - R( g, h, a, b, c, d, e, f, K(50), M(50) ); - R( f, g, h, a, b, c, d, e, K(51), M(51) ); - R( e, f, g, h, a, b, c, d, K(52), M(52) ); - R( d, e, f, g, h, a, b, c, K(53), M(53) ); - R( c, d, e, f, g, h, a, b, K(54), M(54) ); - R( b, c, d, e, f, g, h, a, K(55), M(55) ); - R( a, b, c, d, e, f, g, h, K(56), M(56) ); - R( h, a, b, c, d, e, f, g, K(57), M(57) ); - R( g, h, a, b, c, d, e, f, K(58), M(58) ); - R( f, g, h, a, b, c, d, e, K(59), M(59) ); - R( e, f, g, h, a, b, c, d, K(60), M(60) ); - R( d, e, f, g, h, a, b, c, K(61), M(61) ); - R( c, d, e, f, g, h, a, b, K(62), M(62) ); - R( b, c, d, e, f, g, h, a, K(63), M(63) ); - R( a, b, c, d, e, f, g, h, K(64), M(64) ); - R( h, a, b, c, d, e, f, g, K(65), M(65) ); - R( g, h, a, b, c, d, e, f, K(66), M(66) ); - R( f, g, h, a, b, c, d, e, K(67), M(67) ); - R( e, f, g, h, a, b, c, d, K(68), M(68) ); - R( d, e, f, g, h, a, b, c, K(69), M(69) ); - R( c, d, e, f, g, h, a, b, K(70), M(70) ); - R( b, c, d, e, f, g, h, a, K(71), M(71) ); - R( a, b, c, d, e, f, g, h, K(72), M(72) ); - R( h, a, b, c, d, e, f, g, K(73), M(73) ); - R( g, h, a, b, c, d, e, f, K(74), M(74) ); - R( f, g, h, a, b, c, d, e, K(75), M(75) ); - R( e, f, g, h, a, b, c, d, K(76), M(76) ); - R( d, e, f, g, h, a, b, c, K(77), M(77) ); - R( c, d, e, f, g, h, a, b, K(78), M(78) ); - R( b, c, d, e, f, g, h, a, K(79), M(79) ); - - a = ctx->state[0] = u64plus (ctx->state[0], a); - b = ctx->state[1] = u64plus (ctx->state[1], b); - c = ctx->state[2] = u64plus (ctx->state[2], c); - d = ctx->state[3] = u64plus (ctx->state[3], d); - e = ctx->state[4] = u64plus (ctx->state[4], e); - f = ctx->state[5] = u64plus (ctx->state[5], f); - g = ctx->state[6] = u64plus (ctx->state[6], g); - h = ctx->state[7] = u64plus (ctx->state[7], h); - } -} - -#ifdef __cplusplus -} -#endif diff --git a/libs/signature/src/spas_client.c b/libs/signature/src/spas_client.c deleted file mode 100755 index c2885a21f..000000000 --- a/libs/signature/src/spas_client.c +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include - -#include "base64.h" -#include "hmac.h" -#include "sha1.h" -#include "sha256.h" -#include "spas_client.h" - -#ifdef WIN32 -#include -#include -#else -#include -#endif -#ifdef SPAS_MT -#include -#endif - -#ifdef __cplusplus -namespace rocketmqSignature { -#endif - -#define SPAS_VERSION "SPAS_V1_0" - -static SPAS_CREDENTIAL g_credential; -static char g_path[SPAS_MAX_PATH]; -static int g_loaded = 0; -static unsigned int refresh = 10; -static time_t modified = 0; - -#ifdef SPAS_MT - -static pthread_mutex_t cred_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_once_t cred_once = PTHREAD_ONCE_INIT; -static pthread_key_t cred_key; - -#endif - -extern void *_mem_alloc(unsigned int size); -extern void *_mem_realloc(void *ptr, unsigned int old_size, - unsigned int new_size); -extern void _mem_free(void *ptr); -extern void _trim(char *str); - -void *_mem_alloc(unsigned int size) { - void *p = malloc(size); - if (p != NULL) { - memset(p, 0, size); - } - return p; -} - -void *_mem_realloc(void *ptr, unsigned int old_size, unsigned int new_size) { - void *p = realloc(ptr, new_size); - if (p != NULL && new_size > old_size) { - memset((unsigned int *)p + old_size, 0, new_size - old_size); - } - return p; -} - -void _mem_free(void *ptr) { free(ptr); } - -void _trim(char *str) { - int len = strlen(str); - int i; - int done = 0; - for (i = len - 1; i >= 0; i--) { - switch (str[i]) { - case ' ': - case '\t': - case '\r': - case '\n': - str[i] = '\0'; - break; - default: - done = 1; - break; - } - if (done) { - break; - } - } -} - -static int _load_credential(SPAS_CREDENTIAL *pcred, char *path) { - FILE *fp = NULL; - char buf[SPAS_MAX_KEY_LEN * 2]; - if (pcred == NULL || path == NULL) { - return ERROR_INVALID_PARAM; - } - fp = fopen(path, "r"); - if (fp == NULL) { - return ERROR_FILE_OPEN; - } - memset(pcred, 0, sizeof(SPAS_CREDENTIAL)); - while (fgets(buf, sizeof(buf), fp)) { - _trim(buf); - int len = strlen(SPAS_ACCESS_KEY_TAG); - if (strncmp(buf, SPAS_ACCESS_KEY_TAG, len) == 0 && buf[len] == '=') { - strncpy(pcred->access_key, buf + len + 1, SPAS_MAX_KEY_LEN - 1); - } else { - len = strlen(SPAS_SECRET_KEY_TAG); - if (strncmp(buf, SPAS_SECRET_KEY_TAG, len) == 0 && buf[len] == '=') { - strncpy(pcred->secret_key, buf + len + 1, SPAS_MAX_KEY_LEN - 1); - } - } - } - fclose(fp); - if (strlen(pcred->access_key) == 0 || strlen(pcred->secret_key) == 0) { - return ERROR_MISSING_KEY; - } - return SPAS_NO_ERROR; -} - -#ifndef WIN32 -static void _reload_credential(int sig) { - int ret; - SPAS_CREDENTIAL credential; - struct stat status; - struct sigaction act; - - if (sig != SIGALRM) { - return; - } - - memset(&act, 0, sizeof(act)); - act.sa_handler = _reload_credential; - sigaction(SIGALRM, &act, NULL); - alarm(refresh); - if (g_path[0] != '\0') { - ret = stat(g_path, &status); - if (ret != 0) { - return; - } - if (status.st_mtime == modified) { - return; - } - ret = _load_credential(&credential, g_path); - if (ret != SPAS_NO_ERROR) { - return; - } -#ifdef SPAS_MT - pthread_mutex_lock(&cred_mutex); -#endif - memcpy(&g_credential, &credential, sizeof(SPAS_CREDENTIAL)); -#ifdef SPAS_MT - pthread_mutex_unlock(&cred_mutex); -#endif - modified = status.st_mtime; - } -} - -static int _update_credential_by_alarm() { - struct sigaction act; - - memset(&act, 0, sizeof(act)); - act.sa_handler = _reload_credential; - sigaction(SIGALRM, &act, NULL); - alarm(refresh); - return SPAS_NO_ERROR; -} -#endif - -#ifdef SPAS_MT - -static void *_update_credential_entry(void *arg) { - int ret; - SPAS_CREDENTIAL credential; - struct stat status; - struct timeval tv; - while (1) { - tv.tv_sec = refresh; - tv.tv_usec = 0; - select(0, NULL, NULL, NULL, &tv); - if (g_path[0] != '\0') { - ret = stat(g_path, &status); - if (ret != 0) { - continue; - } - if (status.st_mtime == modified) { - continue; - } - ret = _load_credential(&credential, g_path); - if (ret != SPAS_NO_ERROR) { - continue; - } - pthread_mutex_lock(&cred_mutex); - memcpy(&g_credential, &credential, sizeof(SPAS_CREDENTIAL)); - pthread_mutex_unlock(&cred_mutex); - modified = status.st_mtime; - } - } - return NULL; -} - -static int _update_credential_by_thread() { - pthread_t tid; - int ret; - - ret = pthread_create(&tid, NULL, _update_credential_entry, NULL); - if (ret != 0) { - return ERROR_UPDATE_CREDENTIAL; - } - pthread_detach(tid); - return SPAS_NO_ERROR; -} - -int spas_load_credential(char *path, CREDENTIAL_UPDATE_MODE mode) { - int ret = SPAS_NO_ERROR; - SPAS_CREDENTIAL credential; - - if (g_loaded) { - return SPAS_NO_ERROR; - } - if (path == NULL) { - path = getenv(SPAS_CREDENTIAL_ENV); - if (path == NULL) { - return ERROR_NO_CREDENTIAL; - } - } - strncpy(g_path, path, SPAS_MAX_PATH - 1); - ret = _load_credential(&credential, path); - if (ret != SPAS_NO_ERROR) { - return ret; - } -#ifdef SPAS_MT - pthread_mutex_lock(&cred_mutex); -#endif - if (!g_loaded) { - memcpy(&g_credential, &credential, sizeof(SPAS_CREDENTIAL)); - g_loaded = 1; - } -#ifdef SPAS_MT - pthread_mutex_unlock(&cred_mutex); -#endif - switch (mode) { - case UPDATE_BY_ALARM: - ret = _update_credential_by_alarm(); - break; -#ifdef SPAS_MT - case UPDATE_BY_THREAD: - ret = _update_credential_by_thread(); - break; -#endif - case NO_UPDATE: - default: - ret = SPAS_NO_ERROR; - break; - } - return ret; -} - -#endif - -SPAS_CREDENTIAL *spas_get_credential(void) { - SPAS_CREDENTIAL *credential = - (SPAS_CREDENTIAL *)_mem_alloc(sizeof(SPAS_CREDENTIAL)); - if (credential != NULL) { -#ifdef SPAS_MT - pthread_mutex_lock(&cred_mutex); -#endif - memcpy(credential, &g_credential, sizeof(SPAS_CREDENTIAL)); -#ifdef SPAS_MT - pthread_mutex_unlock(&cred_mutex); -#endif - } - return credential; -} - -int spas_set_access_key(char *key) { - int len = 0; - if (key == NULL) { - return ERROR_INVALID_PARAM; - } - len = strlen(key); - if (len == 0 || len >= SPAS_MAX_KEY_LEN) { - return ERROR_KEY_LENGTH; - } -#ifdef SPAS_MT - pthread_mutex_lock(&cred_mutex); -#endif - memcpy(g_credential.access_key, key, len + 1); -#ifdef SPAS_MT - pthread_mutex_unlock(&cred_mutex); -#endif - return SPAS_NO_ERROR; -} - -int spas_set_secret_key(char *key) { - int len = 0; - if (key == NULL) { - return ERROR_INVALID_PARAM; - } - len = strlen(key); - if (len == 0 || len >= SPAS_MAX_KEY_LEN) { - return ERROR_KEY_LENGTH; - } -#ifdef SPAS_MT - pthread_mutex_lock(&cred_mutex); -#endif - memcpy(g_credential.secret_key, key, len + 1); -#ifdef SPAS_MT - pthread_mutex_unlock(&cred_mutex); -#endif - return SPAS_NO_ERROR; -} - -char *spas_get_access_key() { return g_credential.access_key; } - -char *spas_get_secret_key() { return g_credential.secret_key; } - -#ifdef SPAS_MT - -static void _free_thread_credential(void *credential) { - if (credential != NULL) { - _mem_free(credential); - } -} - -static void _init_credential_key(void) { - pthread_key_create(&cred_key, _free_thread_credential); -} - -static SPAS_CREDENTIAL *_get_thread_credential(void) { - int ret = 0; - SPAS_CREDENTIAL *credential = NULL; - ret = pthread_once(&cred_once, _init_credential_key); - if (ret != 0) { - return NULL; - } - credential = pthread_getspecific(cred_key); - if (credential == NULL) { - credential = _mem_alloc(sizeof(SPAS_CREDENTIAL)); - if (credential == NULL) { - return NULL; - } - ret = pthread_setspecific(cred_key, credential); - if (ret != 0) { - _mem_free(credential); - return NULL; - } - } - return credential; -} - -int spas_load_thread_credential(char *path) { - int ret = SPAS_NO_ERROR; - SPAS_CREDENTIAL *credential = NULL; - credential = _get_thread_credential(); - if (credential == NULL) { - return ERROR_MEM_ALLOC; - } - ret = _load_credential(credential, path); - if (ret != SPAS_NO_ERROR) { - memset(credential, 0, sizeof(SPAS_CREDENTIAL)); - return ret; - } - return SPAS_NO_ERROR; -} - -int spas_set_thread_access_key(char *key) { - int len = 0; - SPAS_CREDENTIAL *credential = NULL; - if (key == NULL) { - return ERROR_INVALID_PARAM; - } - len = strlen(key); - if (len == 0 || len >= SPAS_MAX_KEY_LEN) { - return ERROR_KEY_LENGTH; - } - credential = _get_thread_credential(); - if (credential == NULL) { - return ERROR_MEM_ALLOC; - } - memcpy(credential->access_key, key, len + 1); - return SPAS_NO_ERROR; -} - -int spas_set_thread_secret_key(char *key) { - int len = 0; - SPAS_CREDENTIAL *credential = NULL; - if (key == NULL) { - return ERROR_INVALID_PARAM; - } - len = strlen(key); - if (len == 0 || len >= SPAS_MAX_KEY_LEN) { - return ERROR_KEY_LENGTH; - } - credential = _get_thread_credential(); - if (credential == NULL) { - return ERROR_MEM_ALLOC; - } - memcpy(credential->secret_key, key, len + 1); - return SPAS_NO_ERROR; -} - -char *spas_get_thread_access_key(void) { - SPAS_CREDENTIAL *credential = _get_thread_credential(); - if (credential == NULL) { - return NULL; - } - return credential->access_key; -} - -char *spas_get_thread_secret_key(void) { - SPAS_CREDENTIAL *credential = _get_thread_credential(); - if (credential == NULL) { - return NULL; - } - return credential->secret_key; -} - -#endif - -char *spas_get_signature(const SPAS_PARAM_LIST *list, const char *key) { - return spas_get_signature2(list, key, SIGN_HMACSHA1); -} - -char *spas_get_signature2(const SPAS_PARAM_LIST *list, const char *key, - SPAS_SIGN_ALGORITHM algorithm) { - char *sign = NULL; - char *data = NULL; - if (list == NULL || key == NULL) { - return NULL; - } - data = param_list_to_str(list); - if (data == NULL) { - return NULL; - } - sign = spas_sign2(data, strlen(data), key, algorithm); - _mem_free(data); - return sign; -} - -char *spas_sign(const char *data, size_t size, const char *key) { - return spas_sign2(data, size, key, SIGN_HMACSHA1); -} - -char *spas_sign2(const char *data, size_t size, const char *key, - SPAS_SIGN_ALGORITHM algorithm) { - int ret; - int dsize = 0; - char *sha_buf = NULL; - char *base64_ret = NULL; - if (data == NULL || key == NULL) { - return NULL; - } - if (algorithm == SIGN_HMACSHA1) { - dsize = SHA1_DIGEST_SIZE; - sha_buf = (char *)_mem_alloc(dsize + 1); - if (sha_buf == NULL) { - return NULL; - } - ret = hmac_sha1(key, strlen(key), data, size, sha_buf); - if (ret < 0) { - _mem_free(sha_buf); - return NULL; - } - } else if (algorithm == SIGN_HMACSHA256) { - dsize = SHA256_DIGEST_SIZE; - sha_buf = (char *)_mem_alloc(dsize + 1); - if (sha_buf == NULL) { - return NULL; - } - ret = hmac_sha256(key, strlen(key), data, strlen(data), sha_buf); - if (ret < 0) { - _mem_free(sha_buf); - return NULL; - } - } else { - return NULL; - } - ret = base64_encode_alloc(sha_buf, dsize, &base64_ret); - _mem_free(sha_buf); - return base64_ret; -} - -void spas_mem_free(char *pSignature) { _mem_free(pSignature); } - -char *spas_get_version(void) { return SPAS_VERSION; } - -#ifdef __cplusplus -} -#endif diff --git a/package_rocketmq.mri b/package_rocketmq.mri deleted file mode 100644 index f19383ed1..000000000 --- a/package_rocketmq.mri +++ /dev/null @@ -1,25 +0,0 @@ -create librocketmq.a -addlib ../bin/lib/libboost_chrono.a -addlib ../bin/lib/libboost_date_time.a -addlib ../bin/lib/libboost_filesystem.a -addlib ../bin/lib/libboost_iostreams.a -addlib ../bin/lib/libboost_locale.a -addlib ../bin/lib/libboost_log.a -addlib ../bin/lib/libboost_log_setup.a -addlib ../bin/lib/libboost_regex.a -addlib ../bin/lib/libboost_serialization.a -addlib ../bin/lib/libboost_system.a -addlib ../bin/lib/libboost_thread.a -addlib ../bin/lib/libboost_wserialization.a -addlib ../bin/lib/libssl.a -addlib ../bin/lib/libcrypto.a -addlib ../bin/lib/libevent.a -addlib ../bin/lib/libevent_core.a -addlib ../bin/lib/libevent_extra.a -addlib ../bin/lib/libevent_pthreads.a -addlib ../bin/lib/libevent_openssl.a -addlib ../bin/lib/libjsoncpp.a -addlib ../bin/lib/libSignature.a -addlib ../bin/librocketmq.a -save -end diff --git a/project/CMakeLists.txt b/project/CMakeLists.txt deleted file mode 100755 index efd2a815d..000000000 --- a/project/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# source files -project(rocketmq-client) - -file(GLOB_RECURSE SRC_FILES ${CMAKE_SOURCE_DIR}/src/*) -list(REMOVE_ITEM SRC_FILES ${CMAKE_SOURCE_DIR}/src/dllmain.cpp) - -# subdirs -SET(SUB_DIRS) -file(GLOB children ${CMAKE_SOURCE_DIR}/src/*) -FOREACH (child ${children}) - IF (IS_DIRECTORY ${child}) - LIST(APPEND SUB_DIRS ${child}) - ENDIF () -ENDFOREACH () -LIST(APPEND SUB_DIRS ${CMAKE_SOURCE_DIR}/src) - -include_directories(${CMAKE_SOURCE_DIR}/include) -include_directories(${SUB_DIRS}) - -# libs_directories -file(GLOB LIB_DIRS ${CMAKE_SOURCE_DIR}/libs/*) -foreach (dir ${LIB_DIRS}) - if (IS_DIRECTORY ${dir}) - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH};${dir}) - include_directories(${dir}/include) - endif () -endforeach () - -# static -if (BUILD_ROCKETMQ_STATIC) - add_library(rocketmq_static STATIC ${SRC_FILES}) - set_target_properties(rocketmq_static PROPERTIES OUTPUT_NAME "rocketmq") - add_dependencies(rocketmq_static Signature) - target_link_libraries(rocketmq_static Signature) - target_link_libraries(rocketmq_static ${JSONCPP_LIBRARIES}) - target_link_libraries(rocketmq_static ${LIBEVENT_LIBRARIES}) - target_link_libraries(rocketmq_static ${Boost_LIBRARIES}) - target_link_libraries(rocketmq_static ${OPENSSL_LIBRARIES}) - target_link_libraries(rocketmq_static ${deplibs}) -endif () - -# shared -if (BUILD_ROCKETMQ_SHARED) - set(CMAKE_SHARED_LINKER_FLAGS "-DBOOST_ALL_DYN_LINK -shared ") - add_library(rocketmq_shared SHARED ${SRC_FILES}) - set_target_properties(rocketmq_shared PROPERTIES OUTPUT_NAME "rocketmq") - add_dependencies(rocketmq_shared Signature) - target_link_libraries(rocketmq_shared Signature) - target_link_libraries(rocketmq_shared ${JSONCPP_LIBRARIES}) - target_link_libraries(rocketmq_shared ${LIBEVENT_LIBRARIES}) - target_link_libraries(rocketmq_shared ${Boost_LIBRARIES}) - target_link_libraries(rocketmq_shared ${OPENSSL_LIBRARIES}) - target_link_libraries(rocketmq_shared ${deplibs}) -endif () - -# install -if (BUILD_ROCKETMQ_STATIC) - install(TARGETS rocketmq_static DESTINATION lib) -endif () -if (BUILD_ROCKETMQ_SHARED) - install(TARGETS rocketmq_shared DESTINATION lib) -endif () -install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include/rocketmq) -install(DIRECTORY ${CMAKE_SOURCE_DIR}/doc/ DESTINATION doc) diff --git a/proto/BUILD.bazel b/proto/BUILD.bazel new file mode 100644 index 000000000..3416bf41e --- /dev/null +++ b/proto/BUILD.bazel @@ -0,0 +1,79 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@rules_cc//cc:defs.bzl", "cc_proto_library") +load("@rules_proto_grpc//cpp:defs.bzl", "cpp_grpc_library", "cpp_grpc_compile") + +proto_library( + name = "apache_rocketmq_definition", + srcs = [ + "apache/rocketmq/v1/definition.proto", + ], + deps = [ + "@com_google_protobuf//:empty_proto", + "@com_google_protobuf//:field_mask_proto", + "@com_google_protobuf//:duration_proto", + "@com_google_protobuf//:timestamp_proto", + "@com_google_googleapis//google/rpc:code_proto", + "@com_google_googleapis//google/rpc:error_details_proto", + "@com_google_googleapis//google/rpc:status_proto", + ], + strip_import_prefix = "/proto", +) + +proto_library( + name = "apache_rocketmq_admin", + srcs = [ + "apache/rocketmq/v1/admin.proto", + ], + deps = [ + ":apache_rocketmq_definition", + "@com_google_protobuf//:field_mask_proto", + "@com_google_protobuf//:empty_proto", + ], + strip_import_prefix = "/proto", +) + +proto_library( + name = "apache_rocketmq_service", + srcs = [ + "apache/rocketmq/v1/service.proto", + ], + deps = [ + ":apache_rocketmq_definition", + "@com_google_protobuf//:any_proto", + "@com_google_protobuf//:duration_proto", + "@com_google_protobuf//:timestamp_proto", + "@com_google_googleapis//google/rpc:code_proto", + "@com_google_googleapis//google/rpc:error_details_proto", + "@com_google_googleapis//google/rpc:status_proto", + ], + strip_import_prefix = "/proto", +) + +cpp_grpc_library( + name = "rocketmq_grpc_library", + protos = [ + ":apache_rocketmq_definition", + ":apache_rocketmq_admin", + ":apache_rocketmq_service", + "@com_google_googleapis//google/rpc:code_proto", + "@com_google_googleapis//google/rpc:error_details_proto", + "@com_google_googleapis//google/rpc:status_proto", + ], + visibility = ["//visibility:public"] +) \ No newline at end of file diff --git a/proto/CMakeLists.txt b/proto/CMakeLists.txt new file mode 100644 index 000000000..1aced6ccd --- /dev/null +++ b/proto/CMakeLists.txt @@ -0,0 +1,26 @@ +set(PROTO_FILES + google/rpc/code.proto + google/rpc/status.proto + google/rpc/error_details.proto + opentelemetry/proto/resource/v1/resource.proto + opentelemetry/proto/common/v1/common.proto + opentelemetry/proto/collector/trace/v1/trace_service.proto + opentelemetry/proto/trace/v1/trace.proto + apache/rocketmq/v1/admin.proto + apache/rocketmq/v1/definition.proto + apache/rocketmq/v1/service.proto) + +add_library(proto ${PROTO_FILES}) +target_link_libraries(proto + PUBLIC + protobuf::libprotobuf + gRPC::grpc + gRPC::grpc++) +target_include_directories(proto PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +# +# Compile protobuf and grpc files in myproto target to cpp +# +get_target_property(grpc_cpp_plugin_location gRPC::grpc_cpp_plugin LOCATION) +protobuf_generate(TARGET proto LANGUAGE cpp) +protobuf_generate(TARGET proto LANGUAGE grpc GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${grpc_cpp_plugin_location}") \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/admin.proto b/proto/apache/rocketmq/v1/admin.proto new file mode 100644 index 000000000..554207b90 --- /dev/null +++ b/proto/apache/rocketmq/v1/admin.proto @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package apache.rocketmq.v1; + +option cc_enable_arenas = true; +option java_multiple_files = true; +option java_package = "apache.rocketmq.v1"; +option java_generate_equals_and_hash = true; +option java_string_check_utf8 = true; +option java_outer_classname = "MQAdmin"; + +message ChangeLogLevelRequest { + enum Level { + TRACE = 0; + DEBUG = 1; + INFO = 2; + WARN = 3; + ERROR = 4; + } + Level level = 1; +} + +message ChangeLogLevelResponse { + string remark = 1; +} + +service Admin { + rpc ChangeLogLevel(ChangeLogLevelRequest) returns (ChangeLogLevelResponse) { + } +} \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/definition.proto b/proto/apache/rocketmq/v1/definition.proto new file mode 100644 index 000000000..b6cb24beb --- /dev/null +++ b/proto/apache/rocketmq/v1/definition.proto @@ -0,0 +1,349 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; + +package apache.rocketmq.v1; + +option java_multiple_files = true; +option java_package = "apache.rocketmq.v1"; +option java_generate_equals_and_hash = true; +option java_string_check_utf8 = true; +option java_outer_classname = "MQDomain"; + +enum Permission { + NONE = 0; + READ = 1; + WRITE = 2; + READ_WRITE = 3; + + reserved 4 to 64; +} + +enum FilterType { + TAG = 0; + SQL = 1; + + reserved 2 to 64; +} + +message FilterExpression { + FilterType type = 1; + string expression = 2; + + reserved 3 to 64; +} + +// Dead lettering is done on a best effort basis. The same message might be +// dead lettered multiple times. +// +// If validation on any of the fields fails at subscription creation/update, +// the create/update subscription request will fail. +message DeadLetterPolicy { + // The maximum number of delivery attempts for any message. + // + // This field will be honored on a best effort basis. + // + // If this parameter is 0, a default value of 16 is used. + int32 max_delivery_attempts = 1; + + reserved 2 to 64; +} + +message Resource { + string resource_namespace = 1; + + // Resource name identifier, which remains unique within the abstract resource + // namespace. + string name = 2; + + reserved 3 to 64; +} + +enum ConsumeModel { + CLUSTERING = 0; + BROADCASTING = 1; + + reserved 2 to 64; +} + +message ProducerData { + Resource group = 1; + + reserved 2 to 64; +} + +enum ConsumePolicy { + RESUME = 0; + PLAYBACK = 1; + DISCARD = 2; + TARGET_TIMESTAMP = 3; + + reserved 4 to 64; +} + +enum ConsumeMessageType { + ACTIVE = 0; + PASSIVE = 1; + + reserved 2 to 64; +} + +message ConsumerData { + Resource group = 1; + + repeated SubscriptionEntry subscriptions = 2; + + ConsumeModel consume_model = 3; + + ConsumePolicy consume_policy = 4; + + DeadLetterPolicy dead_letter_policy = 5; + + ConsumeMessageType consume_type = 6; + + reserved 7 to 64; +} + +message SubscriptionEntry { + Resource topic = 1; + FilterExpression expression = 2; + + reserved 3 to 64; +} + +enum AddressScheme { + IPv4 = 0; + IPv6 = 1; + DOMAIN_NAME = 2; + + reserved 3 to 64; +} + +message Address { + string host = 1; + int32 port = 2; + + reserved 3 to 64; +} + +message Endpoints { + AddressScheme scheme = 1; + repeated Address addresses = 2; + + reserved 3 to 64; +} + +message Broker { + // Name of the broker + string name = 1; + + // Broker index. Canonically, index = 0 implies that the broker is playing + // leader role while brokers with index > 0 play follower role. + int32 id = 2; + + // Address of the broker, complying with the following scheme + // 1. dns:[//authority/]host[:port] + // 2. ipv4:address[:port][,address[:port],...] – IPv4 addresses + // 3. ipv6:address[:port][,address[:port],...] – IPv6 addresses + Endpoints endpoints = 3; + + reserved 4 to 64; +} + +message Partition { + Resource topic = 1; + int32 id = 2; + Permission permission = 3; + Broker broker = 4; + + reserved 5 to 64; +} + +enum MessageType { + NORMAL = 0; + + // Sequenced message + FIFO = 1; + + // Messages that are delivered after the specified duration. + DELAY = 2; + + // Messages that are transactional. Only committed messages are delivered to + // subscribers. + TRANSACTION = 3; + + reserved 4 to 64; +} + +enum DigestType { + // CRC algorithm achieves goal of detecting random data error with lowest + // computation overhead. + CRC32 = 0; + + // MD5 algorithm achieves good balance between collision rate and computation + // overhead. + MD5 = 1; + + // SHA-family has substantially fewer collision with fair amount of + // computation. + SHA1 = 2; + + reserved 3 to 64; +} + +// When publishing messages to or subscribing messages from brokers, clients +// shall include or validate digests of message body to ensure data integrity. +// +// For message publishment, when an invalid digest were detected, brokers need +// respond client with BAD_REQUEST. +// +// For messags subscription, when an invalid digest were detected, consumers +// need to handle this case according to message type: +// 1) Standard messages should be negatively acknowledged instantly, causing +// immediate re-delivery; 2) FIFO messages require special RPC, to re-fetch +// previously acquired messages batch; +// +// Message consumption model also affects how invalid digest are handled. When +// messages are consumed in broadcasting way, +// TODO: define semantics of invalid-digest-when-broadcasting. +message Digest { + DigestType type = 1; + string checksum = 2; + + reserved 3 to 64; +} + +enum Encoding { + IDENTITY = 0; + GZIP = 1; + + reserved 2 to 64; +} + +message SystemAttribute { + // Tag + string tag = 1; + + // Message keys + repeated string keys = 2; + + // Message identifier, client-side generated, remains unique. + // if message_id is empty, the send message request will be aborted with + // status `INVALID_ARGUMENT` + string message_id = 3; + + // Message body digest + Digest body_digest = 4; + + // Message body encoding. Candidate options are identity, gzip, snappy etc. + Encoding body_encoding = 5; + + // Message type, normal, FIFO or transactional. + MessageType message_type = 6; + + // Message born time-point. + google.protobuf.Timestamp born_timestamp = 7; + + // Message born host. Valid options are IPv4, IPv6 or client host domain name. + string born_host = 8; + + // Time-point at which the message is stored in the broker. + google.protobuf.Timestamp store_timestamp = 9; + + // The broker that stores this message. It may be name, IP or arbitrary + // identifier that uniquely identify the broker. + string store_host = 10; + + oneof timed_delivery { + // Time-point at which broker delivers to clients. + google.protobuf.Timestamp delivery_timestamp = 11; + + // Level-based delay strategy. + int32 delay_level = 12; + } + + // If a message is acquired by way of POP, this field holds the receipt. + // Clients use the receipt to acknowledge or negatively acknowledge the + // message. + string receipt_handle = 13; + + // Partition identifier in which a message is physically stored. + int32 partition_id = 14; + + // Partition offset at which a message is stored. + int64 partition_offset = 15; + + // Period of time servers would remain invisible once a message is acquired. + google.protobuf.Duration invisible_period = 16; + + // Business code may failed to process messages for the moment. Hence, clients + // may request servers to deliver them again using certain back-off strategy, + // the attempt is 1 not 0 if message is delivered first time. + int32 delivery_attempt = 17; + + // Message producer load-balance group if applicable. + Resource producer_group = 18; + + string message_group = 19; + + // Trace context. + string trace_context = 20; + + // Delay time of first recover orphaned transaction request from server. + google.protobuf.Duration orphaned_transaction_recovery_period = 21; + + reserved 22 to 64; +} + +message Message { + + Resource topic = 1; + + // User defined key-value pairs. + // If user_attribute contains the reserved keys by RocketMQ, + // the send message request will be aborted with status `INVALID_ARGUMENT`. + // See below links for the reserved keys + // https://github.com/apache/rocketmq/blob/master/common/src/main/java/org/apache/rocketmq/common/message/MessageConst.java#L58 + map user_attribute = 2; + + SystemAttribute system_attribute = 3; + + bytes body = 4; + + reserved 5 to 64; +} + +message Assignment { + Partition Partition = 1; + + reserved 2 to 64; +} + +enum QueryOffsetPolicy { + // Use this option if client wishes to playback all existing messages. + BEGINNING = 0; + + // Use this option if client wishes to skip all existing messages. + END = 1; + + // Use this option if time-based seek is targeted. + TIME_POINT = 2; + + reserved 3 to 64; +} \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/service.proto b/proto/apache/rocketmq/v1/service.proto new file mode 100644 index 000000000..9d8e3b090 --- /dev/null +++ b/proto/apache/rocketmq/v1/service.proto @@ -0,0 +1,531 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; +import "google/rpc/error_details.proto"; +import "google/rpc/status.proto"; + +import "apache/rocketmq/v1/definition.proto"; + +package apache.rocketmq.v1; + +option java_multiple_files = true; +option java_package = "apache.rocketmq.v1"; +option java_generate_equals_and_hash = true; +option java_string_check_utf8 = true; +option java_outer_classname = "MQService"; + +message ResponseCommon { + google.rpc.Status status = 1; + google.rpc.RequestInfo request_info = 2; + google.rpc.Help help = 3; + google.rpc.RetryInfo retry_info = 4; + google.rpc.DebugInfo debug_info = 5; + google.rpc.ErrorInfo error_info = 6; + + reserved 7 to 64; +} + +// Topics are destination of messages to publish to or subscribe from. Similar +// to domain names, they will be addressable after resolution through the +// provided access point. +// +// Access points are usually the addresses of name servers, which fulfill +// service discovery, load-balancing and other auxillary services. Name servers +// receive periodic heartbeats from affiliate brokers and erase those which +// failed to maintain alive status. +// +// Name servers answer queries of QueryRouteRequest, responding clients with +// addressable partitions, which they may directly publish messages to or +// subscribe messages from. +// +// QueryRouteRequest shall include source endpoints, aka, configured +// access-point, which annotates tenant-id, instance-id or other +// vendor-specific settings. Purpose-built name servers may respond customized +// results based on these particular requirements. +message QueryRouteRequest { + Resource topic = 1; + + Endpoints endpoints = 2; + + reserved 3 to 64; +} + +message QueryRouteResponse { + ResponseCommon common = 1; + + repeated Partition partitions = 2; + + reserved 3 to 64; +} + +message SendMessageRequest { + Message message = 1; + Partition partition = 2; + + reserved 3 to 64; +} + +message SendMessageResponse { + ResponseCommon common = 1; + string message_id = 2; + string transaction_id = 3; + + reserved 4 to 64; +} + +message QueryAssignmentRequest { + Resource topic = 1; + Resource group = 2; + string client_id = 3; + + // Service access point + Endpoints endpoints = 4; + + reserved 5 to 64; +} + +message QueryAssignmentResponse { + ResponseCommon common = 1; + repeated Assignment assignments = 2; + + reserved 3 to 64; +} + +message ReceiveMessageRequest { + Resource group = 1; + string client_id = 2; + Partition partition = 3; + FilterExpression filter_expression = 4; + ConsumePolicy consume_policy = 5; + google.protobuf.Timestamp initialization_timestamp = 6; + int32 batch_size = 7; + google.protobuf.Duration invisible_duration = 8; + google.protobuf.Duration await_time = 9; + bool fifo_flag = 10; + + reserved 11 to 64; +} + +message ReceiveMessageResponse { + ResponseCommon common = 1; + repeated Message messages = 2; + google.protobuf.Timestamp delivery_timestamp = 3; + google.protobuf.Duration invisible_duration = 4; + + reserved 5 to 64; +} + +message AckMessageRequest { + Resource group = 1; + Resource topic = 2; + string client_id = 3; + oneof handle { + string receipt_handle = 4; + int64 offset = 5; + } + string message_id = 6; + + reserved 7 to 64; +} + +message AckMessageResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} + +message NackMessageRequest { + Resource group = 1; + Resource topic = 2; + string client_id = 3; + string receipt_handle = 4; + string message_id = 5; + int32 delivery_attempt = 6; + int32 max_delivery_attempts = 7; + + reserved 8 to 64; +} + +message NackMessageResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} + +message ForwardMessageToDeadLetterQueueRequest { + Resource group = 1; + Resource topic = 2; + string client_id = 3; + string receipt_handle = 4; + string message_id = 5; + int32 delivery_attempt = 6; + int32 max_delivery_attempts = 7; + + reserved 8 to 64; +} + +message ForwardMessageToDeadLetterQueueResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} + +message HeartbeatRequest { + string client_id = 1; + oneof client_data { + ProducerData producer_data = 2; + ConsumerData consumer_data = 3; + } + bool fifo_flag = 4; + + reserved 5 to 64; +} + +message HeartbeatResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} + +message HealthCheckRequest { + Resource group = 1; + string client_host = 2; + + reserved 3 to 64; +} + +message HealthCheckResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} + +message EndTransactionRequest { + Resource group = 1; + string message_id = 2; + string transaction_id = 3; + enum TransactionResolution { + COMMIT = 0; + ROLLBACK = 1; + } + TransactionResolution resolution = 4; + enum Source { + CLIENT = 0; + SERVER_CHECK = 1; + } + Source source = 5; + string trace_context = 6; + + reserved 7 to 64; +} + +message EndTransactionResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} + +message QueryOffsetRequest { + Partition partition = 1; + QueryOffsetPolicy policy = 2; + google.protobuf.Timestamp time_point = 3; + + reserved 4 to 64; +} + +message QueryOffsetResponse { + ResponseCommon common = 1; + int64 offset = 2; + + reserved 3 to 64; +} + +message PullMessageRequest { + Resource group = 1; + Partition partition = 2; + int64 offset = 3; + int32 batch_size = 4; + google.protobuf.Duration await_time = 5; + FilterExpression filter_expression = 6; + string client_id = 7; + + reserved 8 to 64; +} + +message PullMessageResponse { + ResponseCommon common = 1; + int64 min_offset = 2; + int64 next_offset = 3; + int64 max_offset = 4; + repeated Message messages = 5; + + reserved 6 to 64; +} + +message NoopCommand { + reserved 1 to 64; +} + +message PrintThreadStackTraceCommand { + string command_id = 1; + + reserved 2 to 64; +} + +message ReportThreadStackTraceRequest { + string command_id = 1; + string thread_stack_trace = 2; + + reserved 3 to 64; +} + +message ReportThreadStackTraceResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} + +message VerifyMessageConsumptionCommand { + string command_id = 1; + Message message = 2; + + reserved 3 to 64; +} + +message ReportMessageConsumptionResultRequest { + string command_id = 1; + google.rpc.Status status = 2; + + reserved 3 to 64; +} + +message ReportMessageConsumptionResultResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} + +message RecoverOrphanedTransactionCommand { + Message orphaned_transactional_message = 1; + string transaction_id = 2; + + reserved 3 to 64; +} + +message PollCommandRequest { + string client_id = 1; + repeated Resource topics = 2; + oneof group { + Resource producer_group = 3; + Resource consumer_group = 4; + } + + reserved 5 to 64; +} + +message PollCommandResponse { + oneof type { + // Default command when no new command need to be delivered. + NoopCommand noop_command = 1; + // Request client to print thread stack trace. + PrintThreadStackTraceCommand print_thread_stack_trace_command = 2; + // Request client to verify the consumption of the appointed message. + VerifyMessageConsumptionCommand verify_message_consumption_command = 3; + // Request client to recover the orphaned transaction message. + RecoverOrphanedTransactionCommand recover_orphaned_transaction_command = 4; + } + + reserved 5 to 64; +} + +message NotifyClientTerminationRequest { + oneof group { + Resource producer_group = 1; + Resource consumer_group = 2; + } + string client_id = 3; + + reserved 4 to 64; +} + +message NotifyClientTerminationResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} + +// For all the RPCs in MessagingService, the following error handling policies +// apply: +// +// If the request doesn't bear a valid authentication credential, return a +// response with common.status.code == `UNAUTHENTICATED`. If the authenticated +// user is not granted with sufficient permission to execute the requested +// operation, return a response with common.status.code == `PERMISSION_DENIED`. +// If the per-user-resource-based quota is exhausted, return a response with +// common.status.code == `RESOURCE_EXHAUSTED`. If any unexpected server-side +// errors raise, return a response with common.status.code == `INTERNAL`. +service MessagingService { + + // Querys the route entries of the requested topic in the perspective of the + // given endpoints. On success, servers should return a collection of + // addressable partitions. Note servers may return customized route entries + // based on endpoints provided. + // + // If the requested topic doesn't exist, returns `NOT_FOUND`. + // If the specific endpoints is emtpy, returns `INVALID_ARGUMENT`. + rpc QueryRoute(QueryRouteRequest) returns (QueryRouteResponse) { + } + + // Producer or consumer sends HeartbeatRequest to servers periodically to + // keep-alive. Additionally, it also reports client-side configuration, + // including topic subscription, load-balancing group name, etc. + // + // Returns `OK` if success. + // + // If a client specifies a language that is not yet supported by servers, + // returns `INVALID_ARGUMENT` + rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) { + } + + // Checks the health status of message server, returns `OK` if services are + // online and serving. Clients may use this RPC to detect availability of + // messaging service, and take isolation actions when necessary. + rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) { + } + + // Delivers messages to brokers. + // Clients may further: + // 1. Refine a message destination to topic partition which fulfills parts of + // FIFO semantic; + // 2. Flag a message as transactional, which keeps it invisible to consumers + // until it commits; + // 3. Time a message, making it invisible to consumers till specified + // time-point; + // 4. And more... + // + // Returns message-id or transaction-id with status `OK` on success. + // + // If the destination topic doesn't exist, returns `NOT_FOUND`. + rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { + } + + // Querys the assigned partition route info of a topic for current consumer, + // the returned assignment result is descided by server-side load balacner. + // + // If the corresponding topic doesn't exist, returns `NOT_FOUND`. + // If the specific endpoints is emtpy, returns `INVALID_ARGUMENT`. + rpc QueryAssignment(QueryAssignmentRequest) returns (QueryAssignmentResponse) { + } + + // Receives messages from the server in batch manner, returns a set of + // messages if success. The received messages should be acked or uacked after + // processed. + // + // If the pending concurrent receive requests exceed the quota of the given + // consumer group, returns `UNAVAILABLE`. If the upstream store server hangs, + // return `DEADLINE_EXCEEDED` in a timely manner. If the corresponding topic + // or consumer group doesn't exist, returns `NOT_FOUND`. If there is no new + // message in the specific topic, returns `OK` with an empty message set. + // Please note that client may suffer from false empty responses. + rpc ReceiveMessage(ReceiveMessageRequest) returns (ReceiveMessageResponse) { + } + + // Acknowledges the message associated with the `receipt_handle` or `offset` + // in the `AckMessageRequest`, it means the message has been successfully + // processed. Returns `OK` if the message server remove the relevant message + // successfully. + // + // If the given receipt_handle is illegal or out of date, returns + // `INVALID_ARGUMENT`. + rpc AckMessage(AckMessageRequest) returns (AckMessageResponse) { + } + + // Signals that the message has not been successfully processed. The message + // server should resend the message follow the retry policy defined at + // server-side. + // + // If the corresponding topic or consumer group doesn't exist, returns + // `NOT_FOUND`. + rpc NackMessage(NackMessageRequest) returns (NackMessageResponse) { + } + + // Forwards one message to dead letter queue if the DeadLetterPolicy is + // triggered by this message at client-side, return `OK` if success. + rpc ForwardMessageToDeadLetterQueue(ForwardMessageToDeadLetterQueueRequest) + returns (ForwardMessageToDeadLetterQueueResponse) { + } + + // Commits or rollback one transactional message. + rpc EndTransaction(EndTransactionRequest) returns (EndTransactionResponse) { + } + + // Querys the offset of the specific partition, returns the offset with `OK` + // if success. The message server should maintain a numerical offset for each + // message in a parition. + rpc QueryOffset(QueryOffsetRequest) returns (QueryOffsetResponse) { + } + + // Pulls messages from the specific partition, returns a set of messages with + // next pull offset. The pulled messages can't be acked or nacked, while the + // client is responsible for manage offesets for consumer, typically update + // consume offset to local memory or a third-party storage service. + // + // If the pending concurrent receive requests exceed the quota of the given + // consumer group, returns `UNAVAILABLE`. If the upstream store server hangs, + // return `DEADLINE_EXCEEDED` in a timely manner. If the corresponding topic + // or consumer group doesn't exist, returns `NOT_FOUND`. If there is no new + // message in the specific topic, returns `OK` with an empty message set. + // Please note that client may suffer from false empty responses. + rpc PullMessage(PullMessageRequest) returns (PullMessageResponse) { + } + + // Multiplexing RPC(s) for various polling requests, which issue different + // commands to client. + // + // Sometimes client may need to receive and process the command from server. + // To prevent the complexity of streaming RPC(s), a unary RPC using + // long-polling is another solution. + // + // To mark the request-response of corresponding command, `command_id` in + // message is recorded in the subsequent RPC(s). For example, after receiving + // command of printing thread stack trace, client would send + // `ReportMessageConsumptionResultRequest` to server, which contain both of + // the stack trace and `command_id`. + // + // At same time, `NoopCommand` is delivered from server when no new command is + // needed, it is essential for client to maintain the ping-pong. + // + rpc PollCommand(PollCommandRequest) returns (PollCommandResponse) { + } + + // After receiving the corresponding polling command, the thread stack trace + // is reported to the server. + rpc ReportThreadStackTrace(ReportThreadStackTraceRequest) returns (ReportThreadStackTraceResponse) { + } + + // After receiving the corresponding polling command, the consumption result + // of appointed message is reported to the server. + rpc ReportMessageConsumptionResult(ReportMessageConsumptionResultRequest) + returns (ReportMessageConsumptionResultResponse) { + } + + // Notify the server that the client is terminated. + rpc NotifyClientTermination(NotifyClientTerminationRequest) returns (NotifyClientTerminationResponse) { + } +} \ No newline at end of file diff --git a/proto/google/rpc/code.proto b/proto/google/rpc/code.proto new file mode 100644 index 000000000..98ae0ac18 --- /dev/null +++ b/proto/google/rpc/code.proto @@ -0,0 +1,186 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +option go_package = "google.golang.org/genproto/googleapis/rpc/code;code"; +option java_multiple_files = true; +option java_outer_classname = "CodeProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// The canonical error codes for gRPC APIs. +// +// +// Sometimes multiple error codes may apply. Services should return +// the most specific error code that applies. For example, prefer +// `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply. +// Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`. +enum Code { + // Not an error; returned on success + // + // HTTP Mapping: 200 OK + OK = 0; + + // The operation was cancelled, typically by the caller. + // + // HTTP Mapping: 499 Client Closed Request + CANCELLED = 1; + + // Unknown error. For example, this error may be returned when + // a `Status` value received from another address space belongs to + // an error space that is not known in this address space. Also + // errors raised by APIs that do not return enough error information + // may be converted to this error. + // + // HTTP Mapping: 500 Internal Server Error + UNKNOWN = 2; + + // The client specified an invalid argument. Note that this differs + // from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments + // that are problematic regardless of the state of the system + // (e.g., a malformed file name). + // + // HTTP Mapping: 400 Bad Request + INVALID_ARGUMENT = 3; + + // The deadline expired before the operation could complete. For operations + // that change the state of the system, this error may be returned + // even if the operation has completed successfully. For example, a + // successful response from a server could have been delayed long + // enough for the deadline to expire. + // + // HTTP Mapping: 504 Gateway Timeout + DEADLINE_EXCEEDED = 4; + + // Some requested entity (e.g., file or directory) was not found. + // + // Note to server developers: if a request is denied for an entire class + // of users, such as gradual feature rollout or undocumented whitelist, + // `NOT_FOUND` may be used. If a request is denied for some users within + // a class of users, such as user-based access control, `PERMISSION_DENIED` + // must be used. + // + // HTTP Mapping: 404 Not Found + NOT_FOUND = 5; + + // The entity that a client attempted to create (e.g., file or directory) + // already exists. + // + // HTTP Mapping: 409 Conflict + ALREADY_EXISTS = 6; + + // The caller does not have permission to execute the specified + // operation. `PERMISSION_DENIED` must not be used for rejections + // caused by exhausting some resource (use `RESOURCE_EXHAUSTED` + // instead for those errors). `PERMISSION_DENIED` must not be + // used if the caller can not be identified (use `UNAUTHENTICATED` + // instead for those errors). This error code does not imply the + // request is valid or the requested entity exists or satisfies + // other pre-conditions. + // + // HTTP Mapping: 403 Forbidden + PERMISSION_DENIED = 7; + + // The request does not have valid authentication credentials for the + // operation. + // + // HTTP Mapping: 401 Unauthorized + UNAUTHENTICATED = 16; + + // Some resource has been exhausted, perhaps a per-user quota, or + // perhaps the entire file system is out of space. + // + // HTTP Mapping: 429 Too Many Requests + RESOURCE_EXHAUSTED = 8; + + // The operation was rejected because the system is not in a state + // required for the operation's execution. For example, the directory + // to be deleted is non-empty, an rmdir operation is applied to + // a non-directory, etc. + // + // Service implementors can use the following guidelines to decide + // between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`: + // (a) Use `UNAVAILABLE` if the client can retry just the failing call. + // (b) Use `ABORTED` if the client should retry at a higher level + // (e.g., when a client-specified test-and-set fails, indicating the + // client should restart a read-modify-write sequence). + // (c) Use `FAILED_PRECONDITION` if the client should not retry until + // the system state has been explicitly fixed. E.g., if an "rmdir" + // fails because the directory is non-empty, `FAILED_PRECONDITION` + // should be returned since the client should not retry unless + // the files are deleted from the directory. + // + // HTTP Mapping: 400 Bad Request + FAILED_PRECONDITION = 9; + + // The operation was aborted, typically due to a concurrency issue such as + // a sequencer check failure or transaction abort. + // + // See the guidelines above for deciding between `FAILED_PRECONDITION`, + // `ABORTED`, and `UNAVAILABLE`. + // + // HTTP Mapping: 409 Conflict + ABORTED = 10; + + // The operation was attempted past the valid range. E.g., seeking or + // reading past end-of-file. + // + // Unlike `INVALID_ARGUMENT`, this error indicates a problem that may + // be fixed if the system state changes. For example, a 32-bit file + // system will generate `INVALID_ARGUMENT` if asked to read at an + // offset that is not in the range [0,2^32-1], but it will generate + // `OUT_OF_RANGE` if asked to read from an offset past the current + // file size. + // + // There is a fair bit of overlap between `FAILED_PRECONDITION` and + // `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific + // error) when it applies so that callers who are iterating through + // a space can easily look for an `OUT_OF_RANGE` error to detect when + // they are done. + // + // HTTP Mapping: 400 Bad Request + OUT_OF_RANGE = 11; + + // The operation is not implemented or is not supported/enabled in this + // service. + // + // HTTP Mapping: 501 Not Implemented + UNIMPLEMENTED = 12; + + // Internal errors. This means that some invariants expected by the + // underlying system have been broken. This error code is reserved + // for serious errors. + // + // HTTP Mapping: 500 Internal Server Error + INTERNAL = 13; + + // The service is currently unavailable. This is most likely a + // transient condition, which can be corrected by retrying with + // a backoff. Note that it is not always safe to retry + // non-idempotent operations. + // + // See the guidelines above for deciding between `FAILED_PRECONDITION`, + // `ABORTED`, and `UNAVAILABLE`. + // + // HTTP Mapping: 503 Service Unavailable + UNAVAILABLE = 14; + + // Unrecoverable data loss or corruption. + // + // HTTP Mapping: 500 Internal Server Error + DATA_LOSS = 15; +} diff --git a/proto/google/rpc/error_details.proto b/proto/google/rpc/error_details.proto new file mode 100644 index 000000000..c4d6c4b78 --- /dev/null +++ b/proto/google/rpc/error_details.proto @@ -0,0 +1,249 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +import "google/protobuf/duration.proto"; + +option go_package = "google.golang.org/genproto/googleapis/rpc/errdetails;errdetails"; +option java_multiple_files = true; +option java_outer_classname = "ErrorDetailsProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// Describes when the clients can retry a failed request. Clients could ignore +// the recommendation here or retry when this information is missing from error +// responses. +// +// It's always recommended that clients should use exponential backoff when +// retrying. +// +// Clients should wait until `retry_delay` amount of time has passed since +// receiving the error response before retrying. If retrying requests also +// fail, clients should use an exponential backoff scheme to gradually increase +// the delay between retries based on `retry_delay`, until either a maximum +// number of retries have been reached or a maximum retry delay cap has been +// reached. +message RetryInfo { + // Clients should wait at least this long between retrying the same request. + google.protobuf.Duration retry_delay = 1; +} + +// Describes additional debugging info. +message DebugInfo { + // The stack trace entries indicating where the error occurred. + repeated string stack_entries = 1; + + // Additional debugging information provided by the server. + string detail = 2; +} + +// Describes how a quota check failed. +// +// For example if a daily limit was exceeded for the calling project, +// a service could respond with a QuotaFailure detail containing the project +// id and the description of the quota limit that was exceeded. If the +// calling project hasn't enabled the service in the developer console, then +// a service could respond with the project id and set `service_disabled` +// to true. +// +// Also see RetryInfo and Help types for other details about handling a +// quota failure. +message QuotaFailure { + // A message type used to describe a single quota violation. For example, a + // daily quota or a custom quota that was exceeded. + message Violation { + // The subject on which the quota check failed. + // For example, "clientip:" or "project:". + string subject = 1; + + // A description of how the quota check failed. Clients can use this + // description to find more about the quota configuration in the service's + // public documentation, or find the relevant quota limit to adjust through + // developer console. + // + // For example: "Service disabled" or "Daily Limit for read operations + // exceeded". + string description = 2; + } + + // Describes all quota violations. + repeated Violation violations = 1; +} + +// Describes the cause of the error with structured details. +// +// Example of an error when contacting the "pubsub.googleapis.com" API when it +// is not enabled: +// +// { "reason": "API_DISABLED" +// "domain": "googleapis.com" +// "metadata": { +// "resource": "projects/123", +// "service": "pubsub.googleapis.com" +// } +// } +// +// This response indicates that the pubsub.googleapis.com API is not enabled. +// +// Example of an error that is returned when attempting to create a Spanner +// instance in a region that is out of stock: +// +// { "reason": "STOCKOUT" +// "domain": "spanner.googleapis.com", +// "metadata": { +// "availableRegions": "us-central1,us-east2" +// } +// } +message ErrorInfo { + // The reason of the error. This is a constant value that identifies the + // proximate cause of the error. Error reasons are unique within a particular + // domain of errors. This should be at most 63 characters and match + // /[A-Z0-9_]+/. + string reason = 1; + + // The logical grouping to which the "reason" belongs. The error domain + // is typically the registered service name of the tool or product that + // generates the error. Example: "pubsub.googleapis.com". If the error is + // generated by some common infrastructure, the error domain must be a + // globally unique value that identifies the infrastructure. For Google API + // infrastructure, the error domain is "googleapis.com". + string domain = 2; + + // Additional structured details about this error. + // + // Keys should match /[a-zA-Z0-9-_]/ and be limited to 64 characters in + // length. When identifying the current value of an exceeded limit, the units + // should be contained in the key, not the value. For example, rather than + // {"instanceLimit": "100/request"}, should be returned as, + // {"instanceLimitPerRequest": "100"}, if the client exceeds the number of + // instances that can be created in a single (batch) request. + map metadata = 3; +} + +// Describes what preconditions have failed. +// +// For example, if an RPC failed because it required the Terms of Service to be +// acknowledged, it could list the terms of service violation in the +// PreconditionFailure message. +message PreconditionFailure { + // A message type used to describe a single precondition failure. + message Violation { + // The type of PreconditionFailure. We recommend using a service-specific + // enum type to define the supported precondition violation subjects. For + // example, "TOS" for "Terms of Service violation". + string type = 1; + + // The subject, relative to the type, that failed. + // For example, "google.com/cloud" relative to the "TOS" type would indicate + // which terms of service is being referenced. + string subject = 2; + + // A description of how the precondition failed. Developers can use this + // description to understand how to fix the failure. + // + // For example: "Terms of service not accepted". + string description = 3; + } + + // Describes all precondition violations. + repeated Violation violations = 1; +} + +// Describes violations in a client request. This error type focuses on the +// syntactic aspects of the request. +message BadRequest { + // A message type used to describe a single bad request field. + message FieldViolation { + // A path leading to a field in the request body. The value will be a + // sequence of dot-separated identifiers that identify a protocol buffer + // field. E.g., "field_violations.field" would identify this field. + string field = 1; + + // A description of why the request element is bad. + string description = 2; + } + + // Describes all violations in a client request. + repeated FieldViolation field_violations = 1; +} + +// Contains metadata about the request that clients can attach when filing a bug +// or providing other forms of feedback. +message RequestInfo { + // An opaque string that should only be interpreted by the service generating + // it. For example, it can be used to identify requests in the service's logs. + string request_id = 1; + + // Any data that was used to serve this request. For example, an encrypted + // stack trace that can be sent back to the service provider for debugging. + string serving_data = 2; +} + +// Describes the resource that is being accessed. +message ResourceInfo { + // A name for the type of resource being accessed, e.g. "sql table", + // "cloud storage bucket", "file", "Google calendar"; or the type URL + // of the resource: e.g. "type.googleapis.com/google.pubsub.v1.Topic". + string resource_type = 1; + + // The name of the resource being accessed. For example, a shared calendar + // name: "example.com_4fghdhgsrgh@group.calendar.google.com", if the current + // error is [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. + string resource_name = 2; + + // The owner of the resource (optional). + // For example, "user:" or "project:". + string owner = 3; + + // Describes what error is encountered when accessing this resource. + // For example, updating a cloud project may require the `writer` permission + // on the developer console project. + string description = 4; +} + +// Provides links to documentation or for performing an out of band action. +// +// For example, if a quota check failed with an error indicating the calling +// project hasn't enabled the accessed service, this can contain a URL pointing +// directly to the right place in the developer console to flip the bit. +message Help { + // Describes a URL link. + message Link { + // Describes what the link offers. + string description = 1; + + // The URL of the link. + string url = 2; + } + + // URL(s) pointing to additional information on handling the current error. + repeated Link links = 1; +} + +// Provides a localized error message that is safe to return to the user +// which can be attached to an RPC error. +message LocalizedMessage { + // The locale used following the specification defined at + // http://www.rfc-editor.org/rfc/bcp/bcp47.txt. + // Examples are: "en-US", "fr-CH", "es-MX" + string locale = 1; + + // The localized error message in the above locale. + string message = 2; +} diff --git a/proto/google/rpc/status.proto b/proto/google/rpc/status.proto new file mode 100644 index 000000000..3b1f7a932 --- /dev/null +++ b/proto/google/rpc/status.proto @@ -0,0 +1,47 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +import "google/protobuf/any.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; +option java_multiple_files = true; +option java_outer_classname = "StatusProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// The `Status` type defines a logical error model that is suitable for +// different programming environments, including REST APIs and RPC APIs. It is +// used by [gRPC](https://github.com/grpc). Each `Status` message contains +// three pieces of data: error code, error message, and error details. +// +// You can find out more about this error model and how to work with it in the +// [API Design Guide](https://cloud.google.com/apis/design/errors). +message Status { + // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + int32 code = 1; + + // A developer-facing error message, which should be in English. Any + // user-facing error message should be localized and sent in the + // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + string message = 2; + + // A list of messages that carry the error details. There is a common set of + // message types for APIs to use. + repeated google.protobuf.Any details = 3; +} diff --git a/proto/opentelemetry/proto/collector/trace/v1/trace_service.proto b/proto/opentelemetry/proto/collector/trace/v1/trace_service.proto new file mode 100644 index 000000000..41dbeef33 --- /dev/null +++ b/proto/opentelemetry/proto/collector/trace/v1/trace_service.proto @@ -0,0 +1,45 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.collector.trace.v1; + +import "opentelemetry/proto/trace/v1/trace.proto"; + +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.collector.trace.v1"; +option java_outer_classname = "TraceServiceProto"; +option go_package = "go.opentelemetry.io/proto/otlp/collector/trace/v1"; + +// Service that can be used to push spans between one Application instrumented with +// OpenTelemetry and a collector, or between a collector and a central collector (in this +// case spans are sent/received to/from multiple Applications). +service TraceService { + // For performance reasons, it is recommended to keep this RPC + // alive for the entire life of the application. + rpc Export(ExportTraceServiceRequest) returns (ExportTraceServiceResponse) {} +} + +message ExportTraceServiceRequest { + // An array of ResourceSpans. + // For data coming from a single resource this array will typically contain one + // element. Intermediary nodes (such as OpenTelemetry Collector) that receive + // data from multiple origins typically batch the data before forwarding further and + // in that case this array will contain multiple elements. + repeated opentelemetry.proto.trace.v1.ResourceSpans resource_spans = 1; +} + +message ExportTraceServiceResponse { +} diff --git a/proto/opentelemetry/proto/common/v1/common.proto b/proto/opentelemetry/proto/common/v1/common.proto new file mode 100644 index 000000000..afb57eb43 --- /dev/null +++ b/proto/opentelemetry/proto/common/v1/common.proto @@ -0,0 +1,87 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.common.v1; + +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.common.v1"; +option java_outer_classname = "CommonProto"; +option go_package = "go.opentelemetry.io/proto/otlp/common/v1"; + +// AnyValue is used to represent any type of attribute value. AnyValue may contain a +// primitive value such as a string or integer or it may contain an arbitrary nested +// object containing arrays, key-value lists and primitives. +message AnyValue { + // The value is one of the listed fields. It is valid for all values to be unspecified + // in which case this AnyValue is considered to be "empty". + oneof value { + string string_value = 1; + bool bool_value = 2; + int64 int_value = 3; + double double_value = 4; + ArrayValue array_value = 5; + KeyValueList kvlist_value = 6; + bytes bytes_value = 7; + } +} + +// ArrayValue is a list of AnyValue messages. We need ArrayValue as a message +// since oneof in AnyValue does not allow repeated fields. +message ArrayValue { + // Array of values. The array may be empty (contain 0 elements). + repeated AnyValue values = 1; +} + +// KeyValueList is a list of KeyValue messages. We need KeyValueList as a message +// since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need +// a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to +// avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches +// are semantically equivalent. +message KeyValueList { + // A collection of key/value pairs of key-value pairs. The list may be empty (may + // contain 0 elements). + // The keys MUST be unique (it is not allowed to have more than one + // value with the same key). + repeated KeyValue values = 1; +} + +// KeyValue is a key-value pair that is used to store Span attributes, Link +// attributes, etc. +message KeyValue { + string key = 1; + AnyValue value = 2; +} + +// InstrumentationLibrary is a message representing the instrumentation library information +// such as the fully qualified name and version. +// InstrumentationLibrary is wire-compatible with InstrumentationScope for binary +// Protobuf format. +// This message is deprecated and will be removed on June 15, 2022. +message InstrumentationLibrary { + option deprecated = true; + + // An empty instrumentation library name means the name is unknown. + string name = 1; + string version = 2; +} + +// InstrumentationScope is a message representing the instrumentation scope information +// such as the fully qualified name and version. +message InstrumentationScope { + // An empty instrumentation scope name means the name is unknown. + string name = 1; + string version = 2; +} diff --git a/proto/opentelemetry/proto/resource/v1/resource.proto b/proto/opentelemetry/proto/resource/v1/resource.proto new file mode 100644 index 000000000..0a5473455 --- /dev/null +++ b/proto/opentelemetry/proto/resource/v1/resource.proto @@ -0,0 +1,36 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.resource.v1; + +import "opentelemetry/proto/common/v1/common.proto"; + +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.resource.v1"; +option java_outer_classname = "ResourceProto"; +option go_package = "go.opentelemetry.io/proto/otlp/resource/v1"; + +// Resource information. +message Resource { + // Set of attributes that describe the resource. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 1; + + // dropped_attributes_count is the number of dropped attributes. If the value is 0, then + // no attributes were dropped. + uint32 dropped_attributes_count = 2; +} diff --git a/proto/opentelemetry/proto/trace/v1/trace.proto b/proto/opentelemetry/proto/trace/v1/trace.proto new file mode 100644 index 000000000..f2c13712b --- /dev/null +++ b/proto/opentelemetry/proto/trace/v1/trace.proto @@ -0,0 +1,331 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.trace.v1; + +import "opentelemetry/proto/common/v1/common.proto"; +import "opentelemetry/proto/resource/v1/resource.proto"; + +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.trace.v1"; +option java_outer_classname = "TraceProto"; +option go_package = "go.opentelemetry.io/proto/otlp/trace/v1"; + +// TracesData represents the traces data that can be stored in a persistent storage, +// OR can be embedded by other protocols that transfer OTLP traces data but do +// not implement the OTLP protocol. +// +// The main difference between this message and collector protocol is that +// in this message there will not be any "control" or "metadata" specific to +// OTLP protocol. +// +// When new fields are added into this message, the OTLP request MUST be updated +// as well. +message TracesData { + // An array of ResourceSpans. + // For data coming from a single resource this array will typically contain + // one element. Intermediary nodes that receive data from multiple origins + // typically batch the data before forwarding further and in that case this + // array will contain multiple elements. + repeated ResourceSpans resource_spans = 1; +} + +// A collection of ScopeSpans from a Resource. +message ResourceSpans { + // The resource for the spans in this message. + // If this field is not set then no resource info is known. + opentelemetry.proto.resource.v1.Resource resource = 1; + + // A list of ScopeSpans that originate from a resource. + repeated ScopeSpans scope_spans = 2; + + // A list of InstrumentationLibrarySpans that originate from a resource. + // This field is deprecated and will be removed after grace period expires on June 15, 2022. + // + // During the grace period the following rules SHOULD be followed: + // + // For Binary Protobufs + // ==================== + // Binary Protobuf senders SHOULD NOT set instrumentation_library_spans. Instead + // scope_spans SHOULD be set. + // + // Binary Protobuf receivers SHOULD check if instrumentation_library_spans is set + // and scope_spans is not set then the value in instrumentation_library_spans + // SHOULD be used instead by converting InstrumentationLibrarySpans into ScopeSpans. + // If scope_spans is set then instrumentation_library_spans SHOULD be ignored. + // + // For JSON + // ======== + // JSON senders that set instrumentation_library_spans field MAY also set + // scope_spans to carry the same spans, essentially double-publishing the same data. + // Such double-publishing MAY be controlled by a user-settable option. + // If double-publishing is not used then the senders SHOULD set scope_spans and + // SHOULD NOT set instrumentation_library_spans. + // + // JSON receivers SHOULD check if instrumentation_library_spans is set and + // scope_spans is not set then the value in instrumentation_library_spans + // SHOULD be used instead by converting InstrumentationLibrarySpans into ScopeSpans. + // If scope_spans is set then instrumentation_library_spans field SHOULD be ignored. + repeated InstrumentationLibrarySpans instrumentation_library_spans = 1000 [deprecated = true]; + + // This schema_url applies to the data in the "resource" field. It does not apply + // to the data in the "scope_spans" field which have their own schema_url field. + string schema_url = 3; +} + +// A collection of Spans produced by an InstrumentationScope. +message ScopeSpans { + // The instrumentation scope information for the spans in this message. + // Semantically when InstrumentationScope isn't set, it is equivalent with + // an empty instrumentation scope name (unknown). + opentelemetry.proto.common.v1.InstrumentationScope scope = 1; + + // A list of Spans that originate from an instrumentation scope. + repeated Span spans = 2; + + // This schema_url applies to all spans and span events in the "spans" field. + string schema_url = 3; +} + +// A collection of Spans produced by an InstrumentationLibrary. +// InstrumentationLibrarySpans is wire-compatible with ScopeSpans for binary +// Protobuf format. +// This message is deprecated and will be removed on June 15, 2022. +message InstrumentationLibrarySpans { + option deprecated = true; + + // The instrumentation library information for the spans in this message. + // Semantically when InstrumentationLibrary isn't set, it is equivalent with + // an empty instrumentation library name (unknown). + opentelemetry.proto.common.v1.InstrumentationLibrary instrumentation_library = 1; + + // A list of Spans that originate from an instrumentation library. + repeated Span spans = 2; + + // This schema_url applies to all spans and span events in the "spans" field. + string schema_url = 3; +} + +// Span represents a single operation within a trace. Spans can be +// nested to form a trace tree. Spans may also be linked to other spans +// from the same or different trace and form graphs. Often, a trace +// contains a root span that describes the end-to-end latency, and one +// or more subspans for its sub-operations. A trace can also contain +// multiple root spans, or none at all. Spans do not need to be +// contiguous - there may be gaps or overlaps between spans in a trace. +// +// The next available field id is 17. +message Span { + // A unique identifier for a trace. All spans from the same trace share + // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes + // is considered invalid. + // + // This field is semantically required. Receiver should generate new + // random trace_id if empty or invalid trace_id was received. + // + // This field is required. + bytes trace_id = 1; + + // A unique identifier for a span within a trace, assigned when the span + // is created. The ID is an 8-byte array. An ID with all zeroes is considered + // invalid. + // + // This field is semantically required. Receiver should generate new + // random span_id if empty or invalid span_id was received. + // + // This field is required. + bytes span_id = 2; + + // trace_state conveys information about request position in multiple distributed tracing graphs. + // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header + // See also https://github.com/w3c/distributed-tracing for more details about this field. + string trace_state = 3; + + // The `span_id` of this span's parent span. If this is a root span, then this + // field must be empty. The ID is an 8-byte array. + bytes parent_span_id = 4; + + // A description of the span's operation. + // + // For example, the name can be a qualified method name or a file name + // and a line number where the operation is called. A best practice is to use + // the same display name at the same call point in an application. + // This makes it easier to correlate spans in different traces. + // + // This field is semantically required to be set to non-empty string. + // Empty value is equivalent to an unknown span name. + // + // This field is required. + string name = 5; + + // SpanKind is the type of span. Can be used to specify additional relationships between spans + // in addition to a parent/child relationship. + enum SpanKind { + // Unspecified. Do NOT use as default. + // Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. + SPAN_KIND_UNSPECIFIED = 0; + + // Indicates that the span represents an internal operation within an application, + // as opposed to an operation happening at the boundaries. Default value. + SPAN_KIND_INTERNAL = 1; + + // Indicates that the span covers server-side handling of an RPC or other + // remote network request. + SPAN_KIND_SERVER = 2; + + // Indicates that the span describes a request to some remote service. + SPAN_KIND_CLIENT = 3; + + // Indicates that the span describes a producer sending a message to a broker. + // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship + // between producer and consumer spans. A PRODUCER span ends when the message was accepted + // by the broker while the logical processing of the message might span a much longer time. + SPAN_KIND_PRODUCER = 4; + + // Indicates that the span describes consumer receiving a message from a broker. + // Like the PRODUCER kind, there is often no direct critical path latency relationship + // between producer and consumer spans. + SPAN_KIND_CONSUMER = 5; + } + + // Distinguishes between spans generated in a particular context. For example, + // two spans with the same name may be distinguished using `CLIENT` (caller) + // and `SERVER` (callee) to identify queueing latency associated with the span. + SpanKind kind = 6; + + // start_time_unix_nano is the start time of the span. On the client side, this is the time + // kept by the local machine where the span execution starts. On the server side, this + // is the time when the server's application handler starts running. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // + // This field is semantically required and it is expected that end_time >= start_time. + fixed64 start_time_unix_nano = 7; + + // end_time_unix_nano is the end time of the span. On the client side, this is the time + // kept by the local machine where the span execution ends. On the server side, this + // is the time when the server application handler stops running. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // + // This field is semantically required and it is expected that end_time >= start_time. + fixed64 end_time_unix_nano = 8; + + // attributes is a collection of key/value pairs. Note, global attributes + // like server name can be set using the resource API. Examples of attributes: + // + // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" + // "/http/server_latency": 300 + // "abc.com/myattribute": true + // "abc.com/score": 10.239 + // + // The OpenTelemetry API specification further restricts the allowed value types: + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/common.md#attributes + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 9; + + // dropped_attributes_count is the number of attributes that were discarded. Attributes + // can be discarded because their keys are too long or because there are too many + // attributes. If this value is 0, then no attributes were dropped. + uint32 dropped_attributes_count = 10; + + // Event is a time-stamped annotation of the span, consisting of user-supplied + // text description and key-value pairs. + message Event { + // time_unix_nano is the time the event occurred. + fixed64 time_unix_nano = 1; + + // name of the event. + // This field is semantically required to be set to non-empty string. + string name = 2; + + // attributes is a collection of attribute key/value pairs on the event. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 3; + + // dropped_attributes_count is the number of dropped attributes. If the value is 0, + // then no attributes were dropped. + uint32 dropped_attributes_count = 4; + } + + // events is a collection of Event items. + repeated Event events = 11; + + // dropped_events_count is the number of dropped events. If the value is 0, then no + // events were dropped. + uint32 dropped_events_count = 12; + + // A pointer from the current span to another span in the same trace or in a + // different trace. For example, this can be used in batching operations, + // where a single batch handler processes multiple requests from different + // traces or when the handler receives a request from a different project. + message Link { + // A unique identifier of a trace that this linked span is part of. The ID is a + // 16-byte array. + bytes trace_id = 1; + + // A unique identifier for the linked span. The ID is an 8-byte array. + bytes span_id = 2; + + // The trace_state associated with the link. + string trace_state = 3; + + // attributes is a collection of attribute key/value pairs on the link. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 4; + + // dropped_attributes_count is the number of dropped attributes. If the value is 0, + // then no attributes were dropped. + uint32 dropped_attributes_count = 5; + } + + // links is a collection of Links, which are references from this span to a span + // in the same or different trace. + repeated Link links = 13; + + // dropped_links_count is the number of dropped links after the maximum size was + // enforced. If this value is 0, then no links were dropped. + uint32 dropped_links_count = 14; + + // An optional final status for this span. Semantically when Status isn't set, it means + // span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). + Status status = 15; +} + +// The Status type defines a logical error model that is suitable for different +// programming environments, including REST APIs and RPC APIs. +message Status { + reserved 1; + + // A developer-facing human readable error message. + string message = 2; + + // For the semantics of status codes see + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status + enum StatusCode { + // The default status. + STATUS_CODE_UNSET = 0; + // The Span has been validated by an Application developers or Operator to have + // completed successfully. + STATUS_CODE_OK = 1; + // The Span contains an error. + STATUS_CODE_ERROR = 2; + }; + + // The status code. + StatusCode code = 3; +} diff --git a/rpm/build.sh b/rpm/build.sh deleted file mode 100644 index 3fb586983..000000000 --- a/rpm/build.sh +++ /dev/null @@ -1,33 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -BASEDIR=$(dirname "$0") -if [[ ! -d ${BASEDIR}/rocketmq_x64/CENTOS/ ]]; then - echo "Can not find SPEC FILE" - exit 1 -fi -if [[ ! -d /root/rpmbuild/SOURCES/rocketmq/include ]]; then - mkdir -p /root/rpmbuild/SOURCES/rocketmq - mkdir -p /root/rpmbuild/SOURCES/rocketmq/include - mkdir -p /root/rpmbuild/SOURCES/rocketmq/bin - mkdir -p /root/rpmbuild/SPECS/ -fi -cp -R ${BASEDIR}/../include/* /root/rpmbuild/SOURCES/rocketmq/include -cp ${BASEDIR}/../bin/librocketmq.so /root/rpmbuild/SOURCES/rocketmq/bin -cp ${BASEDIR}/../bin/librocketmq.a /root/rpmbuild/SOURCES/rocketmq/bin - -cp ${BASEDIR}/rocketmq_x64/CENTOS/rocketmq-client-cpp.spec /root/rpmbuild/SPECS/ -rpmbuild -bb /root/rpmbuild/SPECS/rocketmq-client-cpp.spec - -cp /root/rpmbuild/RPMS/x86_64/*.rpm ${BASEDIR}/rocketmq_x64 diff --git a/rpm/rocketmq_x64/CENTOS/rocketmq-client-cpp.spec b/rpm/rocketmq_x64/CENTOS/rocketmq-client-cpp.spec deleted file mode 100644 index e24f2d47e..000000000 --- a/rpm/rocketmq_x64/CENTOS/rocketmq-client-cpp.spec +++ /dev/null @@ -1,73 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -Summary: A C++ Client of Apache RocketMQ - -Name: rocketmq-client-cpp -Version: 2.2.0 -Release: centos -Group: Apache -License: APLv2 -Source: https://github.com/apache/rocketmq-client-cpp -URL: http://rocketmq.apache.org/ -Distribution: Linux - -%define _prefix /usr/local - -AutoReqProv: no - -%description -A C++ Client of Apache RocketMQ - -%prep - -pwd - -cat /etc/redhat-release|sed -r 's/.* ([0-9]+)\..*/\1/' - -OS_VERSION=`cat /etc/redhat-release|sed -r 's/.* ([0-9]+)\..*/\1/'` - -echo "OS_VERSION=${OS_VERSION}" - - -%build - -%install -# create dirs -mkdir -p $RPM_BUILD_ROOT%{_prefix} - -# create dirs -mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib -mkdir -p $RPM_BUILD_ROOT%{_prefix}/include/rocketmq - -# copy files -cp -f ${RPM_SOURCE_DIR}/rocketmq/bin/librocketmq.so $RPM_BUILD_ROOT%{_prefix}/lib -cp -f ${RPM_SOURCE_DIR}/rocketmq/bin/librocketmq.a $RPM_BUILD_ROOT%{_prefix}/lib -cp -rf ${RPM_SOURCE_DIR}/rocketmq/include/* $RPM_BUILD_ROOT%{_prefix}/include/rocketmq - -%post -# As '/usr/local/lib' is not included in dynamic libraries on CentOS, it should be made explicit to dlopen() -echo "/usr/local/lib" > /etc/ld.so.conf.d/librocketmq.x86_64.conf -/sbin/ldconfig - -# package information -%files -# set file attribute here -%defattr(-, root, root, 0755) -%{_prefix}/lib -%{_prefix}/include - -%define debug_package %{nil} -%define __os_install_post %{nil} diff --git a/src/MQClientAPIImpl.cpp b/src/MQClientAPIImpl.cpp deleted file mode 100644 index 420a623fe..000000000 --- a/src/MQClientAPIImpl.cpp +++ /dev/null @@ -1,947 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MQClientAPIImpl.h" -#include -#include -#include -#include -#include "CommunicationMode.h" -#include "Logging.h" -#include "MQDecoder.h" -#include "PullResultExt.h" - -namespace rocketmq { -//registerProcessor(CHECK_TRANSACTION_STATE, clientRemotingProcessor); - m_pRemotingClient->registerProcessor(RESET_CONSUMER_CLIENT_OFFSET, clientRemotingProcessor); - m_pRemotingClient->registerProcessor(GET_CONSUMER_STATUS_FROM_CLIENT, clientRemotingProcessor); - m_pRemotingClient->registerProcessor(GET_CONSUMER_RUNNING_INFO, clientRemotingProcessor); - m_pRemotingClient->registerProcessor(NOTIFY_CONSUMER_IDS_CHANGED, clientRemotingProcessor); - m_pRemotingClient->registerProcessor(CONSUME_MESSAGE_DIRECTLY, clientRemotingProcessor); - - m_topAddressing.reset(new TopAddressing(unitName)); -} - -MQClientAPIImpl::~MQClientAPIImpl() { - m_pRemotingClient = NULL; - m_topAddressing = NULL; -} - -void MQClientAPIImpl::stopAllTcpTransportThread() { - m_pRemotingClient->stopAllTcpTransportThread(); -} - -bool MQClientAPIImpl::writeDataToFile(string filename, string data, bool isSync) { - if (data.size() == 0) - return false; - - FILE* pFd = fopen(filename.c_str(), "w+"); - if (NULL == pFd) { - LOG_ERROR("fopen failed, filename:%s", filename.c_str()); - return false; - } - - int byte_write = 0; - int byte_left = data.size(); - const char* pData = data.c_str(); - while (byte_left > 0) { - byte_write = fwrite(pData, sizeof(char), byte_left, pFd); - if (byte_write == byte_left) { - if (ferror(pFd)) { - LOG_ERROR("write data fail, data len:" SIZET_FMT ", file:%s, msg:%s", data.size(), filename.c_str(), - strerror(errno)); - fclose(pFd); - return false; - } - } - byte_left -= byte_write; - pData += byte_write; - } - pData = NULL; - - if (isSync) { - LOG_INFO("fsync with filename:%s", filename.c_str()); - fflush(pFd); - } - fclose(pFd); - - return true; -} - -string MQClientAPIImpl::fetchNameServerAddr(const string& NSDomain) { - try { - string homeDir(UtilAll::getHomeDirectory()); - string storePath = homeDir + "/logs/rocketmq-cpp/snapshot"; - - boost::filesystem::path dir(storePath); - boost::system::error_code ec; - if (!boost::filesystem::exists(dir, ec)) { - if (!boost::filesystem::create_directory(dir, ec)) { - LOG_ERROR("create data dir:%s error", storePath.c_str()); - return ""; - } - } - string file(storePath); - string fileBak(storePath); - vector ret_; - int retSize = UtilAll::Split(ret_, m_mqClientId, "@"); - if (retSize == 2) { - file.append("/nameserver_addr-").append(ret_[retSize - 1]); - } else { - LOG_ERROR("split mqClientId:%s fail", m_mqClientId.c_str()); - file.append("/nameserver_addr-DEFAULT"); - } - boost::filesystem::path snapshot_file(file); - fileBak.append("/nameserver_addr.bak"); - const string addrs = m_topAddressing->fetchNSAddr(NSDomain); - if (addrs.empty()) { - if (m_nameSrvAddr.empty()) { - LOG_INFO("Load the name server snapshot local file:%s", file.c_str()); - if (boost::filesystem::exists(snapshot_file)) { - ifstream snapshot_file(file, ios::binary); - istreambuf_iterator beg(snapshot_file), end; - string filecontent(beg, end); - updateNameServerAddr(filecontent); - m_nameSrvAddr = filecontent; - } else { - LOG_WARN("The name server snapshot local file not exists"); - } - } - } else { - if (m_firstFetchNameSrv == true) { - // it is the first time, so need to create the name server snapshot - // local file - m_firstFetchNameSrv = false; - } - if (addrs.compare(m_nameSrvAddr) != 0) { - LOG_INFO("name server address changed, old: %s, new: %s", m_nameSrvAddr.c_str(), addrs.c_str()); - updateNameServerAddr(addrs); - m_nameSrvAddr = addrs; - } else { - if (!m_firstFetchNameSrv) - return m_nameSrvAddr; - } - // update the snapshot local file if nameSrv changes or - // m_firstFetchNameSrv==true - if (writeDataToFile(fileBak, addrs, true)) { - if (!UtilAll::ReplaceFile(fileBak, file)) - LOG_ERROR("could not rename bak file:%s", strerror(errno)); - } - } - - if (!boost::filesystem::exists(snapshot_file)) { - // the name server snapshot local file maybe deleted by force, create it - if (writeDataToFile(fileBak, m_nameSrvAddr, true)) { - if (!UtilAll::ReplaceFile(fileBak, file)) - LOG_ERROR("could not rename bak file:%s", strerror(errno)); - } - } - } catch (...) { - } - return m_nameSrvAddr; -} - -void MQClientAPIImpl::updateNameServerAddr(const string& addrs) { - if (m_pRemotingClient != NULL) - m_pRemotingClient->updateNameServerAddressList(addrs); -} - -void MQClientAPIImpl::callSignatureBeforeRequest(const string& addr, - RemotingCommand& request, - const SessionCredentials& session_credentials) { - ClientRPCHook rpcHook(session_credentials); - rpcHook.doBeforeRequest(addr, request); -} - -// Note: all request rules: throw exception if got broker error response, -// exclude getTopicRouteInfoFromNameServer and unregisterClient -void MQClientAPIImpl::createTopic(const string& addr, - const string& defaultTopic, - TopicConfig topicConfig, - const SessionCredentials& sessionCredentials) { - string topicWithProjectGroup = topicConfig.getTopicName(); - CreateTopicRequestHeader* requestHeader = new CreateTopicRequestHeader(); - requestHeader->topic = (topicWithProjectGroup); - requestHeader->defaultTopic = (defaultTopic); - requestHeader->readQueueNums = (topicConfig.getReadQueueNums()); - requestHeader->writeQueueNums = (topicConfig.getWriteQueueNums()); - requestHeader->perm = (topicConfig.getPerm()); - requestHeader->topicFilterType = (topicConfig.getTopicFilterType()); - - RemotingCommand request(UPDATE_AND_CREATE_TOPIC, requestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr response(m_pRemotingClient->invokeSync(addr, request)); - - if (response) { - switch (response->getCode()) { - case SUCCESS_VALUE: - return; - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, response->getRemark(), response->getCode()); - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -void MQClientAPIImpl::endTransactionOneway(std::string addr, - EndTransactionRequestHeader* requestHeader, - std::string remark, - const SessionCredentials& sessionCredentials) { - RemotingCommand request(END_TRANSACTION, requestHeader); - request.setRemark(remark); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - m_pRemotingClient->invokeOneway(addr, request); - return; -} - -SendResult MQClientAPIImpl::sendMessage(const string& addr, - const string& brokerName, - const MQMessage& msg, - const SendMessageRequestHeader& requestHeader, - int timeoutMillis, - int maxRetrySendTimes, - int communicationMode, - SendCallback* pSendCallback, - const SessionCredentials& sessionCredentials) { - // RemotingCommand request(SEND_MESSAGE, pRequestHeader); - // Using MQ V2 Protocol to end messages. - SendMessageRequestHeaderV2* pRequestHeaderV2 = new SendMessageRequestHeaderV2(requestHeader); - RemotingCommand request(SEND_MESSAGE_V2, pRequestHeaderV2); - string body = msg.getBody(); - request.SetBody(body.c_str(), body.length()); - request.setMsgBody(body); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - switch (communicationMode) { - case ComMode_ONEWAY: - m_pRemotingClient->invokeOneway(addr, request); - break; - case ComMode_ASYNC: - sendMessageAsync(addr, brokerName, msg, request, pSendCallback, timeoutMillis, maxRetrySendTimes, 1); - break; - case ComMode_SYNC: - return sendMessageSync(addr, brokerName, msg, request, timeoutMillis); - default: - break; - } - return SendResult(); -} - -void MQClientAPIImpl::sendHeartbeat(const string& addr, - HeartbeatData* pHeartbeatData, - const SessionCredentials& sessionCredentials) { - RemotingCommand request(HEART_BEAT, NULL); - - string body; - pHeartbeatData->Encode(body); - request.SetBody(body.data(), body.length()); - request.setMsgBody(body); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - if (m_pRemotingClient->invokeHeartBeat(addr, request)) { - LOG_DEBUG("sendHeartbeat to broker:%s success", addr.c_str()); - } else { - LOG_WARN("sendHeartbeat to broker:%s failed", addr.c_str()); - } -} - -void MQClientAPIImpl::unregisterClient(const string& addr, - const string& clientID, - const string& producerGroup, - const string& consumerGroup, - const SessionCredentials& sessionCredentials) { - LOG_INFO("unregisterClient to broker:%s", addr.c_str()); - RemotingCommand request(UNREGISTER_CLIENT, new UnregisterClientRequestHeader(clientID, producerGroup, consumerGroup)); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr response(m_pRemotingClient->invokeSync(addr, request)); - - if (response) { - switch (response->getCode()) { - case SUCCESS_VALUE: - LOG_INFO("unregisterClient to:%s success", addr.c_str()); - return; - default: - break; - } - LOG_WARN("unregisterClient fail:%s,%d", response->getRemark().c_str(), response->getCode()); - } -} - -// return NULL if got no response or error response -TopicRouteData* MQClientAPIImpl::getTopicRouteInfoFromNameServer(const string& topic, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - RemotingCommand request(GET_ROUTEINTO_BY_TOPIC, new GetRouteInfoRequestHeader(topic)); - callSignatureBeforeRequest("", request, sessionCredentials); - request.Encode(); - - unique_ptr pResponse(m_pRemotingClient->invokeSync("", request, timeoutMillis)); - - if (pResponse != NULL) { - if (((*(pResponse->GetBody())).getSize() == 0) || ((*(pResponse->GetBody())).getData() != NULL)) { - switch (pResponse->getCode()) { - case SUCCESS_VALUE: { - const MemoryBlock* pbody = pResponse->GetBody(); - if (pbody->getSize()) { - TopicRouteData* topicRoute = TopicRouteData::Decode(pbody); - return topicRoute; - } - } - case TOPIC_NOT_EXIST: { - if (DEFAULT_TOPIC.compare(topic) != 0) { - LOG_WARN("Get topic[%s] route failed [TOPIC_NOT_EXIST].", topic.c_str()); - } - return NULL; - } - default: - break; - } - LOG_WARN("%s,%d", pResponse->getRemark().c_str(), pResponse->getCode()); - return NULL; - } - } - if (DEFAULT_TOPIC.compare(topic) != 0) { - LOG_WARN("Get topic[%s] route failed [Null Response].", topic.c_str()); - } - return NULL; -} - -TopicList* MQClientAPIImpl::getTopicListFromNameServer(const SessionCredentials& sessionCredentials) { - RemotingCommand request(GET_ALL_TOPIC_LIST_FROM_NAMESERVER, NULL); - callSignatureBeforeRequest("", request, sessionCredentials); - request.Encode(); - - unique_ptr pResponse(m_pRemotingClient->invokeSync("", request)); - if (pResponse != NULL) { - if (((*(pResponse->GetBody())).getSize() == 0) || ((*(pResponse->GetBody())).getData() != NULL)) { - switch (pResponse->getCode()) { - case SUCCESS_VALUE: { - const MemoryBlock* pbody = pResponse->GetBody(); - if (pbody->getSize()) { - TopicList* topicList = TopicList::Decode(pbody); - return topicList; - } - } - default: - break; - } - - THROW_MQEXCEPTION(MQClientException, pResponse->getRemark(), pResponse->getCode()); - } - } - return NULL; -} - -int MQClientAPIImpl::wipeWritePermOfBroker(const string& namesrvAddr, const string& brokerName, int timeoutMillis) { - return 0; -} - -void MQClientAPIImpl::deleteTopicInBroker(const string& addr, const string& topic, int timeoutMillis) {} - -void MQClientAPIImpl::deleteTopicInNameServer(const string& addr, const string& topic, int timeoutMillis) {} - -void MQClientAPIImpl::deleteSubscriptionGroup(const string& addr, const string& groupName, int timeoutMillis) {} - -string MQClientAPIImpl::getKVConfigByValue(const string& projectNamespace, - const string& projectGroup, - int timeoutMillis) { - return ""; -} - -KVTable MQClientAPIImpl::getKVListByNamespace(const string& projectNamespace, int timeoutMillis) { - return KVTable(); -} - -void MQClientAPIImpl::deleteKVConfigByValue(const string& projectNamespace, - const string& projectGroup, - int timeoutMillis) {} - -SendResult MQClientAPIImpl::sendMessageSync(const string& addr, - const string& brokerName, - const MQMessage& msg, - RemotingCommand& request, - int timeoutMillis) { - // pResponse(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - if (pResponse != NULL) { - try { - SendResult result = processSendResponse(brokerName, msg, pResponse.get()); - LOG_DEBUG("sendMessageSync success:%s to addr:%s,brokername:%s, send status:%d", msg.toString().c_str(), - addr.c_str(), brokerName.c_str(), (int)result.getSendStatus()); - return result; - } catch (std::exception& e) { - LOG_ERROR("send new error, broker:%s, details:%s", brokerName.c_str(), e.what()); - throw e; - } catch (...) { - LOG_ERROR("Unknown error, broker:%s", brokerName.c_str()); - } - } - THROW_MQEXCEPTION(MQClientException, "response is null", -1); -} - -void MQClientAPIImpl::sendMessageAsync(const string& addr, - const string& brokerName, - const MQMessage& msg, - RemotingCommand& request, - SendCallback* pSendCallback, - int64 timeoutMilliseconds, - int maxRetryTimes, - int retrySendTimes) { - int64 begin_time = UtilAll::currentTimeMillis(); - // cbw = std::make_shared(brokerName, msg, pSendCallback, this); - if (m_pRemotingClient->invokeAsync(addr, request, cbw, timeoutMilliseconds, maxRetryTimes, retrySendTimes) == false) { - LOG_WARN("invokeAsync failed to addr:%s,topic:%s, timeout:%lld, maxRetryTimes:%d, retrySendTimes:%d", addr.c_str(), - msg.getTopic().data(), timeoutMilliseconds, maxRetryTimes, retrySendTimes); - // when getTcp return false, need consider retrySendTimes - int retry_time = retrySendTimes + 1; - int64 time_out = timeoutMilliseconds - (UtilAll::currentTimeMillis() - begin_time); - while (retry_time < maxRetryTimes && time_out > 0) { - begin_time = UtilAll::currentTimeMillis(); - if (m_pRemotingClient->invokeAsync(addr, request, cbw, time_out, maxRetryTimes, retry_time) == false) { - retry_time += 1; - time_out = time_out - (UtilAll::currentTimeMillis() - begin_time); - LOG_WARN("invokeAsync retry failed to addr:%s,topic:%s, timeout:%lld, maxRetryTimes:%d, retrySendTimes:%d", - addr.c_str(), msg.getTopic().data(), time_out, maxRetryTimes, retry_time); - continue; - } else { - return; // invokeAsync success - } - } - - LOG_ERROR("sendMessageAsync failed to addr:%s,topic:%s, timeout:%lld, maxRetryTimes:%d, retrySendTimes:%d", - addr.c_str(), msg.getTopic().data(), time_out, maxRetryTimes, retrySendTimes); - - if (cbw && pSendCallback != nullptr) { - cbw->onException(); - // deleteAndZero(cbw); - } else { - THROW_MQEXCEPTION(MQClientException, "sendMessageAsync failed", -1); - } - } -} - -PullResult* MQClientAPIImpl::pullMessage(const string& addr, - PullMessageRequestHeader* pRequestHeader, - int timeoutMillis, - int communicationMode, - PullCallback* pullCallback, - void* pArg, - const SessionCredentials& sessionCredentials) { - RemotingCommand request(PULL_MESSAGE, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - switch (communicationMode) { - case ComMode_ONEWAY: - break; - case ComMode_ASYNC: - pullMessageAsync(addr, request, timeoutMillis, pullCallback, pArg); - break; - case ComMode_SYNC: - return pullMessageSync(addr, request, timeoutMillis); - default: - break; - } - - return NULL; -} - -void MQClientAPIImpl::pullMessageAsync(const string& addr, - RemotingCommand& request, - int timeoutMillis, - PullCallback* pullCallback, - void* pArg) { - // AsyncCallbackWrap* cbw = new PullCallbackWrap(pullCallback, this, pArg); - std::shared_ptr cbw = std::make_shared(pullCallback, this, pArg); - if (m_pRemotingClient->invokeAsync(addr, request, cbw, timeoutMillis) == false) { - LOG_ERROR("pullMessageAsync failed of addr:%s, mq:%s", addr.c_str(), - static_cast(pArg)->mq.toString().data()); - // deleteAndZero(cbw); - THROW_MQEXCEPTION(MQClientException, "pullMessageAsync failed", -1); - } -} - -PullResult* MQClientAPIImpl::pullMessageSync(const string& addr, RemotingCommand& request, int timeoutMillis) { - unique_ptr pResponse(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - if (pResponse != NULL) { - if (((*(pResponse->GetBody())).getSize() == 0) || ((*(pResponse->GetBody())).getData() != NULL)) { - try { - PullResult* pullResult = processPullResponse(pResponse.get()); // pullMessage will handle - // exception from - // processPullResponse - return pullResult; - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - return NULL; - } - } - } - return NULL; -} - -SendResult MQClientAPIImpl::processSendResponse(const string& brokerName, - const MQMessage& msg, - RemotingCommand* pResponse) { - SendStatus sendStatus = SEND_OK; - int res = 0; - switch (pResponse->getCode()) { - case FLUSH_DISK_TIMEOUT: - sendStatus = SEND_FLUSH_DISK_TIMEOUT; - break; - case FLUSH_SLAVE_TIMEOUT: - sendStatus = SEND_FLUSH_SLAVE_TIMEOUT; - break; - case SLAVE_NOT_AVAILABLE: - sendStatus = SEND_SLAVE_NOT_AVAILABLE; - break; - case SUCCESS_VALUE: - sendStatus = SEND_OK; - break; - default: - res = -1; - break; - } - if (res == 0) { - SendMessageResponseHeader* responseHeader = (SendMessageResponseHeader*)pResponse->getCommandHeader(); - auto extFields = pResponse->getExtFields(); - bool traceOn = (extFields->count("TRACE_ON") && extFields->at("TRACE_ON") == "true"); - MQMessageQueue messageQueue(msg.getTopic(), brokerName, responseHeader->queueId); - string unique_msgId = msg.getProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX); - return SendResult(sendStatus, unique_msgId, responseHeader->msgId, messageQueue, responseHeader->queueOffset, - responseHeader->regionId, traceOn); - } - LOG_ERROR("processSendResponse error remark:%s, error code:%d", (pResponse->getRemark()).c_str(), - pResponse->getCode()); - THROW_MQEXCEPTION(MQClientException, pResponse->getRemark(), pResponse->getCode()); -} - -PullResult* MQClientAPIImpl::processPullResponse(RemotingCommand* pResponse) { - PullStatus pullStatus = NO_NEW_MSG; - switch (pResponse->getCode()) { - case SUCCESS_VALUE: - pullStatus = FOUND; - break; - case PULL_NOT_FOUND: - pullStatus = NO_NEW_MSG; - break; - case PULL_RETRY_IMMEDIATELY: - pullStatus = NO_MATCHED_MSG; - break; - case PULL_OFFSET_MOVED: - pullStatus = OFFSET_ILLEGAL; - break; - default: - THROW_MQEXCEPTION(MQBrokerException, pResponse->getRemark(), pResponse->getCode()); - break; - } - - PullMessageResponseHeader* responseHeader = static_cast(pResponse->getCommandHeader()); - - if (!responseHeader) { - LOG_ERROR("processPullResponse:responseHeader is NULL"); - THROW_MQEXCEPTION(MQClientException, "processPullResponse:responseHeader is NULL", -1); - } - //GetBody()); // response data judgement had been done outside - // of processPullResponse - if (bodyFromResponse.getSize() == 0) { - if (pullStatus != FOUND) { - return new PullResultExt(pullStatus, responseHeader->nextBeginOffset, responseHeader->minOffset, - responseHeader->maxOffset, (int)responseHeader->suggestWhichBrokerId); - } else { - THROW_MQEXCEPTION(MQClientException, "memoryBody size is 0, but pullStatus equals found", -1); - } - } else { - return new PullResultExt(pullStatus, responseHeader->nextBeginOffset, responseHeader->minOffset, - responseHeader->maxOffset, (int)responseHeader->suggestWhichBrokerId, bodyFromResponse); - } -} - -//topic = topic; - pRequestHeader->queueId = queueId; - - RemotingCommand request(GET_MIN_OFFSET, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr response(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (response) { - switch (response->getCode()) { - case SUCCESS_VALUE: { - GetMinOffsetResponseHeader* responseHeader = (GetMinOffsetResponseHeader*)response->getCommandHeader(); - - int64 offset = responseHeader->offset; - return offset; - } - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, response->getRemark(), response->getCode()); - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -int64 MQClientAPIImpl::getMaxOffset(const string& addr, - const string& topic, - int queueId, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - GetMaxOffsetRequestHeader* pRequestHeader = new GetMaxOffsetRequestHeader(); - pRequestHeader->topic = topic; - pRequestHeader->queueId = queueId; - - RemotingCommand request(GET_MAX_OFFSET, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr response(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (response) { - switch (response->getCode()) { - case SUCCESS_VALUE: { - GetMaxOffsetResponseHeader* responseHeader = (GetMaxOffsetResponseHeader*)response->getCommandHeader(); - - int64 offset = responseHeader->offset; - return offset; - } - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, response->getRemark(), response->getCode()); - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -int64 MQClientAPIImpl::searchOffset(const string& addr, - const string& topic, - int queueId, - uint64_t timestamp, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - SearchOffsetRequestHeader* pRequestHeader = new SearchOffsetRequestHeader(); - pRequestHeader->topic = topic; - pRequestHeader->queueId = queueId; - pRequestHeader->timestamp = timestamp; - - RemotingCommand request(SEARCH_OFFSET_BY_TIMESTAMP, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr response(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (response) { - switch (response->getCode()) { - case SUCCESS_VALUE: { - SearchOffsetResponseHeader* responseHeader = (SearchOffsetResponseHeader*)response->getCommandHeader(); - - int64 offset = responseHeader->offset; - return offset; - } - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, response->getRemark(), response->getCode()); - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -MQMessageExt* MQClientAPIImpl::viewMessage(const string& addr, - int64 phyoffset, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - ViewMessageRequestHeader* pRequestHeader = new ViewMessageRequestHeader(); - pRequestHeader->offset = phyoffset; - - RemotingCommand request(VIEW_MESSAGE_BY_ID, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr response(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (response) { - switch (response->getCode()) { - case SUCCESS_VALUE: { - } - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, response->getRemark(), response->getCode()); - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -int64 MQClientAPIImpl::getEarliestMsgStoretime(const string& addr, - const string& topic, - int queueId, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - GetEarliestMsgStoretimeRequestHeader* pRequestHeader = new GetEarliestMsgStoretimeRequestHeader(); - pRequestHeader->topic = topic; - pRequestHeader->queueId = queueId; - - RemotingCommand request(GET_EARLIEST_MSG_STORETIME, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr response(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (response) { - switch (response->getCode()) { - case SUCCESS_VALUE: { - GetEarliestMsgStoretimeResponseHeader* responseHeader = - (GetEarliestMsgStoretimeResponseHeader*)response->getCommandHeader(); - - int64 timestamp = responseHeader->timestamp; - return timestamp; - } - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, response->getRemark(), response->getCode()); - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -void MQClientAPIImpl::getConsumerIdListByGroup(const string& addr, - const string& consumerGroup, - vector& cids, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - GetConsumerListByGroupRequestHeader* pRequestHeader = new GetConsumerListByGroupRequestHeader(); - pRequestHeader->consumerGroup = consumerGroup; - - RemotingCommand request(GET_CONSUMER_LIST_BY_GROUP, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr pResponse(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (pResponse != NULL) { - if ((pResponse->GetBody()->getSize() == 0) || (pResponse->GetBody()->getData() != NULL)) { - switch (pResponse->getCode()) { - case SUCCESS_VALUE: { - const MemoryBlock* pbody = pResponse->GetBody(); - if (pbody->getSize()) { - GetConsumerListByGroupResponseBody::Decode(pbody, cids); - return; - } - } - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, pResponse->getRemark(), pResponse->getCode()); - } - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -int64 MQClientAPIImpl::queryConsumerOffset(const string& addr, - QueryConsumerOffsetRequestHeader* pRequestHeader, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - RemotingCommand request(QUERY_CONSUMER_OFFSET, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr response(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (response) { - switch (response->getCode()) { - case SUCCESS_VALUE: { - QueryConsumerOffsetResponseHeader* responseHeader = - (QueryConsumerOffsetResponseHeader*)response->getCommandHeader(); - int64 consumerOffset = responseHeader->offset; - return consumerOffset; - } - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, response->getRemark(), response->getCode()); - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); - return -1; -} - -void MQClientAPIImpl::updateConsumerOffset(const string& addr, - UpdateConsumerOffsetRequestHeader* pRequestHeader, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - RemotingCommand request(UPDATE_CONSUMER_OFFSET, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr response(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (response) { - switch (response->getCode()) { - case SUCCESS_VALUE: { - return; - } - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, response->getRemark(), response->getCode()); - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -void MQClientAPIImpl::updateConsumerOffsetOneway(const string& addr, - UpdateConsumerOffsetRequestHeader* pRequestHeader, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - RemotingCommand request(UPDATE_CONSUMER_OFFSET, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - m_pRemotingClient->invokeOneway(addr, request); -} - -void MQClientAPIImpl::consumerSendMessageBack(const string addr, - MQMessageExt& msg, - const string& consumerGroup, - int delayLevel, - int timeoutMillis, - int maxReconsumeTimes, - const SessionCredentials& sessionCredentials) { - ConsumerSendMsgBackRequestHeader* pRequestHeader = new ConsumerSendMsgBackRequestHeader(); - pRequestHeader->group = consumerGroup; - pRequestHeader->offset = msg.getCommitLogOffset(); - pRequestHeader->delayLevel = delayLevel; - pRequestHeader->unitMode = false; - pRequestHeader->originTopic = msg.getTopic(); - pRequestHeader->originMsgId = msg.getMsgId(); - pRequestHeader->maxReconsumeTimes = maxReconsumeTimes; - - // string addr = socketAddress2IPPort(msg.getStoreHost()); - RemotingCommand request(CONSUMER_SEND_MSG_BACK, pRequestHeader); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr response(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (response) { - switch (response->getCode()) { - case SUCCESS_VALUE: { - return; - } - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, response->getRemark(), response->getCode()); - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -void MQClientAPIImpl::lockBatchMQ(const string& addr, - LockBatchRequestBody* requestBody, - vector& mqs, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - RemotingCommand request(LOCK_BATCH_MQ, NULL); - string body; - requestBody->Encode(body); - request.SetBody(body.data(), body.length()); - request.setMsgBody(body); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr pResponse(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (pResponse != NULL) { - if (((*(pResponse->GetBody())).getSize() == 0) || ((*(pResponse->GetBody())).getData() != NULL)) { - switch (pResponse->getCode()) { - case SUCCESS_VALUE: { - const MemoryBlock* pbody = pResponse->GetBody(); - if (pbody->getSize()) { - LockBatchResponseBody::Decode(pbody, mqs); - } - return; - } break; - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, pResponse->getRemark(), pResponse->getCode()); - } - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -void MQClientAPIImpl::unlockBatchMQ(const string& addr, - UnlockBatchRequestBody* requestBody, - int timeoutMillis, - const SessionCredentials& sessionCredentials) { - RemotingCommand request(UNLOCK_BATCH_MQ, NULL); - string body; - requestBody->Encode(body); - request.SetBody(body.data(), body.length()); - request.setMsgBody(body); - callSignatureBeforeRequest(addr, request, sessionCredentials); - request.Encode(); - - unique_ptr pResponse(m_pRemotingClient->invokeSync(addr, request, timeoutMillis)); - - if (pResponse != NULL) { - switch (pResponse->getCode()) { - case SUCCESS_VALUE: { - return; - } break; - default: - break; - } - THROW_MQEXCEPTION(MQBrokerException, pResponse->getRemark(), pResponse->getCode()); - } - THROW_MQEXCEPTION(MQBrokerException, "response is null", -1); -} - -//& cids, - int timeoutMillis, - const SessionCredentials& sessionCredentials); - - virtual int64 queryConsumerOffset(const string& addr, - QueryConsumerOffsetRequestHeader* pRequestHeader, - int timeoutMillis, - const SessionCredentials& sessionCredentials); - - virtual void updateConsumerOffset(const string& addr, - UpdateConsumerOffsetRequestHeader* pRequestHeader, - int timeoutMillis, - const SessionCredentials& sessionCredentials); - - virtual void updateConsumerOffsetOneway(const string& addr, - UpdateConsumerOffsetRequestHeader* pRequestHeader, - int timeoutMillis, - const SessionCredentials& sessionCredentials); - - virtual void consumerSendMessageBack(const string addr, - MQMessageExt& msg, - const string& consumerGroup, - int delayLevel, - int timeoutMillis, - int maxReconsumeTimes, - const SessionCredentials& sessionCredentials); - - virtual void lockBatchMQ(const string& addr, - LockBatchRequestBody* requestBody, - vector& mqs, - int timeoutMillis, - const SessionCredentials& sessionCredentials); - - virtual void unlockBatchMQ(const string& addr, - UnlockBatchRequestBody* requestBody, - int timeoutMillis, - const SessionCredentials& sessionCredentials); - - virtual void sendMessageAsync(const string& addr, - const string& brokerName, - const MQMessage& msg, - RemotingCommand& request, - SendCallback* pSendCallback, - int64 timeoutMilliseconds, - int maxRetryTimes = 1, - int retrySendTimes = 1); - - private: - SendResult sendMessageSync(const string& addr, - const string& brokerName, - const MQMessage& msg, - RemotingCommand& request, - int timeoutMillis); - /* - void sendMessageAsync(const string& addr, const string& brokerName, - const MQMessage& msg, RemotingCommand& request, - SendCallback* pSendCallback, int64 timeoutMilliseconds); - */ - PullResult* pullMessageSync(const string& addr, RemotingCommand& request, int timeoutMillis); - - void pullMessageAsync(const string& addr, - RemotingCommand& request, - int timeoutMillis, - PullCallback* pullCallback, - void* pArg); - - protected: - unique_ptr m_pRemotingClient; - - private: - unique_ptr m_topAddressing; - string m_nameSrvAddr; - bool m_firstFetchNameSrv; - string m_mqClientId; -}; -} // namespace rocketmq -// pDefaultTopicInfo(new TopicPublishInfo()); - m_topicPublishInfoTable[DEFAULT_TOPIC] = pDefaultTopicInfo; - m_pClientRemotingProcessor.reset(new ClientRemotingProcessor(this)); - m_pClientAPIImpl.reset(new MQClientAPIImpl(m_clientId, m_pClientRemotingProcessor.get(), pullThreadNum, - tcpConnectTimeout, tcpTransportTryLockTimeout, unitName, enableSsl, - sslPropertyFile)); - m_serviceState = CREATE_JUST; - LOG_DEBUG("MQClientFactory construct"); -} - -MQClientFactory::~MQClientFactory() { - LOG_INFO("MQClientFactory:%s destruct", m_clientId.c_str()); - - for (TRDMAP::iterator itp = m_topicRouteTable.begin(); itp != m_topicRouteTable.end(); ++itp) { - delete itp->second; - } - - m_producerTable.clear(); - m_consumerTable.clear(); - m_topicRouteTable.clear(); - m_brokerAddrTable.clear(); - m_topicPublishInfoTable.clear(); -} - -void MQClientFactory::start() { - switch (m_serviceState) { - case CREATE_JUST: - LOG_INFO("MQClientFactory:%s start", m_clientId.c_str()); - m_serviceState = START_FAILED; - // t) { - if ((getConsumerTableSize() == 0) && (getProducerTableSize() == 0)) { - return; - } - - set topicList; - //::iterator it = topicList.begin(); - for (; it != topicList.end(); ++it) { - updateTopicRouteInfoFromNameServer(*it, session_credentials); - } - } - - boost::system::error_code e; - t->expires_from_now(t->expires_from_now() + boost::posix_time::seconds(30), e); - t->async_wait(boost::bind(&MQClientFactory::updateTopicRouteInfo, this, ec, t)); -} - -TopicRouteData* MQClientFactory::getTopicRouteData(const string& topic) { - boost::lock_guard lock(m_topicRouteTableMutex); - if (m_topicRouteTable.find(topic) != m_topicRouteTable.end()) { - return m_topicRouteTable[topic]; - } - return NULL; -} - -void MQClientFactory::addTopicRouteData(const string& topic, TopicRouteData* pTopicRouteData) { - boost::lock_guard lock(m_topicRouteTableMutex); - if (m_topicRouteTable.find(topic) != m_topicRouteTable.end()) { - delete m_topicRouteTable[topic]; - m_topicRouteTable.erase(topic); - } - m_topicRouteTable[topic] = pTopicRouteData; -} - -boost::shared_ptr MQClientFactory::tryToFindTopicPublishInfo( - const string& topic, - const SessionCredentials& session_credentials) { - boost::lock_guard lock(m_topicPublishInfoLock); // add topicPublishInfoLock to avoid con-current - // excuting updateTopicRouteInfoFromNameServer - // when producer send msg before topicRouteInfo - // was got; - if (!isTopicInfoValidInTable(topic)) { - updateTopicRouteInfoFromNameServer(topic, session_credentials); - } - // pTopicPublishInfo; - return pTopicPublishInfo; - } - - return getTopicPublishInfoFromTable(topic); -} - -bool MQClientFactory::updateTopicRouteInfoFromNameServer(const string& topic, - const SessionCredentials& session_credentials, - bool isDefault /* = false */) { - boost::lock_guard lock(m_factoryLock); - unique_ptr pTopicRouteData; - LOG_DEBUG("updateTopicRouteInfoFromNameServer start. Topic:%s", topic.c_str()); - - if (isDefault) { - pTopicRouteData.reset( - m_pClientAPIImpl->getTopicRouteInfoFromNameServer(DEFAULT_TOPIC, 1000 * 5, session_credentials)); - if (pTopicRouteData != NULL) { - vector& queueDatas = pTopicRouteData->getQueueDatas(); - vector::iterator it = queueDatas.begin(); - for (; it != queueDatas.end(); ++it) { - int queueNums = std::min(4, it->readQueueNums); - it->readQueueNums = queueNums; - it->writeQueueNums = queueNums; - } - } - LOG_DEBUG("getTopicRouteInfoFromNameServer is null for topic :%s", topic.c_str()); - } else { - pTopicRouteData.reset(m_pClientAPIImpl->getTopicRouteInfoFromNameServer(topic, 1000 * 5, session_credentials)); - } - - if (pTopicRouteData != NULL) { - LOG_DEBUG("updateTopicRouteInfoFromNameServer has data"); - TopicRouteData* pTemp = getTopicRouteData(topic); - bool changed = true; - if (pTemp != NULL) { - changed = !(*pTemp == *pTopicRouteData); - } - - if (getConsumerTableSize() > 0) { - vector mqs; - topicRouteData2TopicSubscribeInfo(topic, pTopicRouteData.get(), mqs); - updateConsumerSubscribeTopicInfo(topic, mqs); - } - - if (changed) { - // brokerList = pTopicRouteData->getBrokerDatas(); - vector::iterator it = brokerList.begin(); - for (; it != brokerList.end(); ++it) { - LOG_INFO("updateTopicRouteInfoFromNameServer changed with broker name:%s", (*it).brokerName.c_str()); - addBrokerToAddrMap((*it).brokerName, (*it).brokerAddrs); - } - - // publishInfo(topicRouteData2TopicPublishInfo(topic, pTopicRouteData.get())); - addTopicInfoToTable(topic, publishInfo); // erase first, then add - } - - // MQClientFactory::topicRouteData2TopicPublishInfo(const string& topic, - TopicRouteData* pRoute) { - boost::shared_ptr info(new TopicPublishInfo()); - string OrderTopicConf = pRoute->getOrderTopicConf(); - // brokers; - UtilAll::Split(brokers, OrderTopicConf, ';'); - for (size_t i = 0; i < brokers.size(); i++) { - vector item; - UtilAll::Split(item, brokers[i], ':'); - size_t nums = (item.size() > 1) ? atoi(item[1].c_str()) : 0; - for (size_t i = 0; i < nums; i++) { - MQMessageQueue mq(topic, item[0], i); - info->updateMessageQueueList(mq); - } - } - } - //& queueDatas = pRoute->getQueueDatas(); - vector::iterator it = queueDatas.begin(); - for (; it != queueDatas.end(); ++it) { - QueueData& qd = (*it); - if (PermName::isWriteable(qd.perm)) { - string addr = findBrokerAddressInPublish(qd.brokerName); - if (addr.empty()) { - continue; - } - for (int i = 0; i < qd.writeQueueNums; i++) { - MQMessageQueue mq(topic, qd.brokerName, i); - info->updateMessageQueueList(mq); - } - } - } - } - return info; -} - -void MQClientFactory::topicRouteData2TopicSubscribeInfo(const string& topic, - TopicRouteData* pRoute, - vector& mqs) { - mqs.clear(); - vector& queueDatas = pRoute->getQueueDatas(); - vector::iterator it = queueDatas.begin(); - for (; it != queueDatas.end(); ++it) { - QueueData& qd = (*it); - if (PermName::isReadable(qd.perm)) { - for (int i = 0; i < qd.readQueueNums; i++) { - MQMessageQueue mq(topic, qd.brokerName, i); - mqs.push_back(mq); - } - } - } -} - -void MQClientFactory::shutdown() { - if (getConsumerTableSize() != 0) - return; - - if (getProducerTableSize() != 0) - return; - - switch (m_serviceState) { - case CREATE_JUST: - case RUNNING: { - if (m_consumer_async_service_thread) { - m_consumer_async_ioService.stop(); - m_consumer_async_service_thread->interrupt(); - m_consumer_async_service_thread->join(); - m_consumer_async_service_thread.reset(); - } - - if (m_async_service_thread) { - m_async_ioService.stop(); - m_async_service_thread->interrupt(); - m_async_service_thread->join(); - m_async_service_thread.reset(); - } - - if (m_pClientAPIImpl) { - m_pClientAPIImpl->stopAllTcpTransportThread(); // Note: stop all - // TcpTransport Threads - // and release all - // responseFuture - // conditions - m_pClientAPIImpl.reset(); - } - - m_serviceState = SHUTDOWN_ALREADY; - LOG_INFO("MQClientFactory:%s shutdown", m_clientId.c_str()); - break; - } - case SHUTDOWN_ALREADY: - break; - default: - break; - } - - MQClientManager::getInstance()->removeClientFactory(m_clientId); -} - -bool MQClientFactory::registerProducer(MQProducer* pProducer) { - string groupName = pProducer->getGroupName(); - string namesrvaddr = pProducer->getNamesrvAddr(); - if (groupName.empty()) { - return false; - } - - if (!addProducerToTable(groupName, pProducer)) { - return false; - } - - LOG_DEBUG("registerProducer success:%s", groupName.c_str()); - //getNamesrvDomain()); - if (!nameSrvDomain.empty()) - m_nameSrvDomain = nameSrvDomain; - pProducer->setNamesrvAddr(m_pClientAPIImpl->fetchNameServerAddr(m_nameSrvDomain)); - } else { - m_bFetchNSService = false; - m_pClientAPIImpl->updateNameServerAddr(namesrvaddr); - LOG_INFO("user specfied name server address: %s", namesrvaddr.c_str()); - } - return true; -} - -void MQClientFactory::unregisterProducer(MQProducer* pProducer) { - string groupName = pProducer->getGroupName(); - unregisterClient(groupName, "", pProducer->getSessionCredentials()); - - eraseProducerFromTable(groupName); -} - -bool MQClientFactory::registerConsumer(MQConsumer* pConsumer) { - string groupName = pConsumer->getGroupName(); - string namesrvaddr = pConsumer->getNamesrvAddr(); - if (groupName.empty()) { - return false; - } - - if (!addConsumerToTable(groupName, pConsumer)) { - return false; - } - LOG_DEBUG("registerConsumer success:%s", groupName.c_str()); - //getNamesrvDomain()); - if (!nameSrvDomain.empty()) - m_nameSrvDomain = nameSrvDomain; - pConsumer->setNamesrvAddr(m_pClientAPIImpl->fetchNameServerAddr(m_nameSrvDomain)); - } else { - m_bFetchNSService = false; - m_pClientAPIImpl->updateNameServerAddr(namesrvaddr); - LOG_INFO("user specfied name server address: %s", namesrvaddr.c_str()); - } - - return true; -} - -void MQClientFactory::unregisterConsumer(MQConsumer* pConsumer) { - string groupName = pConsumer->getGroupName(); - unregisterClient("", groupName, pConsumer->getSessionCredentials()); - - eraseConsumerFromTable(groupName); -} - -bool MQClientFactory::registerMQAdmin(MQAdmin* pAdmin) { - string groupName = pAdmin->getGroupName(); - string namesrvaddr = pAdmin->getNamesrvAddr(); - if (groupName.empty()) { - return false; - } - if (!addAdminToTable(groupName, pAdmin)) { - return false; - } - LOG_DEBUG("registerAdmin success:%s", groupName.c_str()); - //getNamesrvDomain()); - if (!nameSrvDomain.empty()) - m_nameSrvDomain = nameSrvDomain; - pAdmin->setNamesrvAddr(m_pClientAPIImpl->fetchNameServerAddr(m_nameSrvDomain)); - } else { - m_bFetchNSService = false; - m_pClientAPIImpl->updateNameServerAddr(namesrvaddr); - LOG_INFO("user specfied name server address: %s", namesrvaddr.c_str()); - } - return true; -} - -void MQClientFactory::unregisterMQAdmin(MQAdmin* pAdmin) { - string groupName = pAdmin->getGroupName(); - eraseAdminFromTable(groupName); -} - -MQProducer* MQClientFactory::selectProducer(const string& producerName) { - boost::lock_guard lock(m_producerTableMutex); - if (m_producerTable.find(producerName) != m_producerTable.end()) { - return m_producerTable[producerName]; - } - return NULL; -} - -bool MQClientFactory::getSessionCredentialFromProducerTable(SessionCredentials& sessionCredentials) { - boost::lock_guard lock(m_producerTableMutex); - for (MQPMAP::iterator it = m_producerTable.begin(); it != m_producerTable.end(); ++it) { - if (it->second) - sessionCredentials = it->second->getSessionCredentials(); - } - - if (sessionCredentials.isValid()) - return true; - - return false; -} - -bool MQClientFactory::addProducerToTable(const string& producerName, MQProducer* pMQProducer) { - boost::lock_guard lock(m_producerTableMutex); - if (m_producerTable.find(producerName) != m_producerTable.end()) - return false; - m_producerTable[producerName] = pMQProducer; - return true; -} - -void MQClientFactory::eraseProducerFromTable(const string& producerName) { - boost::lock_guard lock(m_producerTableMutex); - if (m_producerTable.find(producerName) != m_producerTable.end()) - m_producerTable.erase(producerName); -} - -int MQClientFactory::getProducerTableSize() { - boost::lock_guard lock(m_producerTableMutex); - return m_producerTable.size(); -} - -void MQClientFactory::insertProducerInfoToHeartBeatData(HeartbeatData* pHeartbeatData) { - boost::lock_guard lock(m_producerTableMutex); - for (MQPMAP::iterator it = m_producerTable.begin(); it != m_producerTable.end(); ++it) { - ProducerData producerData; - producerData.groupName = it->first; - pHeartbeatData->insertDataToProducerDataSet(producerData); - } -} - -MQConsumer* MQClientFactory::selectConsumer(const string& group) { - boost::lock_guard lock(m_consumerTableMutex); - if (m_consumerTable.find(group) != m_consumerTable.end()) { - return m_consumerTable[group]; - } - return NULL; -} - -bool MQClientFactory::getSessionCredentialFromConsumerTable(SessionCredentials& sessionCredentials) { - boost::lock_guard lock(m_consumerTableMutex); - for (MQCMAP::iterator it = m_consumerTable.begin(); it != m_consumerTable.end(); ++it) { - if (it->second) - sessionCredentials = it->second->getSessionCredentials(); - } - - if (sessionCredentials.isValid()) - return true; - - return false; -} - -bool MQClientFactory::getSessionCredentialFromConsumer(const string& consumerGroup, - SessionCredentials& sessionCredentials) { - boost::lock_guard lock(m_consumerTableMutex); - if (m_consumerTable.find(consumerGroup) != m_consumerTable.end()) { - sessionCredentials = m_consumerTable[consumerGroup]->getSessionCredentials(); - } - - if (sessionCredentials.isValid()) - return true; - - return false; -} - -bool MQClientFactory::addConsumerToTable(const string& consumerName, MQConsumer* pMQConsumer) { - boost::lock_guard lock(m_consumerTableMutex); - if (m_consumerTable.find(consumerName) != m_consumerTable.end()) - return false; - m_consumerTable[consumerName] = pMQConsumer; - return true; -} - -void MQClientFactory::eraseConsumerFromTable(const string& consumerName) { - boost::lock_guard lock(m_consumerTableMutex); - if (m_consumerTable.find(consumerName) != m_consumerTable.end()) - m_consumerTable.erase(consumerName); // do not need freee pConsumer, as it - // was allocated by user - else - LOG_WARN("could not find consumer:%s from table", consumerName.c_str()); -} - -bool MQClientFactory::addAdminToTable(const string& adminName, MQAdmin* pMQAdmin) { - boost::lock_guard lock(m_adminTableMutex); - if (m_adminTable.find(adminName) != m_adminTable.end()) - return false; - m_adminTable[adminName] = pMQAdmin; - return true; -} - -void MQClientFactory::eraseAdminFromTable(const string& adminName) { - boost::lock_guard lock(m_adminTableMutex); - if (m_adminTable.find(adminName) != m_adminTable.end()) - m_adminTable.erase(adminName); - else - LOG_WARN("could not find admin:%s from table", adminName.c_str()); -} - -int MQClientFactory::getConsumerTableSize() { - boost::lock_guard lock(m_consumerTableMutex); - return m_consumerTable.size(); -} - -void MQClientFactory::getTopicListFromConsumerSubscription(set& topicList) { - boost::lock_guard lock(m_consumerTableMutex); - for (MQCMAP::iterator it = m_consumerTable.begin(); it != m_consumerTable.end(); ++it) { - vector result; - it->second->getSubscriptions(result); - - vector::iterator iter = result.begin(); - for (; iter != result.end(); ++iter) { - topicList.insert((*iter).getTopic()); - } - } -} - -void MQClientFactory::updateConsumerSubscribeTopicInfo(const string& topic, vector mqs) { - boost::lock_guard lock(m_consumerTableMutex); - for (MQCMAP::iterator it = m_consumerTable.begin(); it != m_consumerTable.end(); ++it) { - it->second->updateTopicSubscribeInfo(topic, mqs); - } -} - -void MQClientFactory::insertConsumerInfoToHeartBeatData(HeartbeatData* pHeartbeatData) { - boost::lock_guard lock(m_consumerTableMutex); - for (MQCMAP::iterator it = m_consumerTable.begin(); it != m_consumerTable.end(); ++it) { - MQConsumer* pConsumer = it->second; - ConsumerData consumerData; - consumerData.groupName = pConsumer->getGroupName(); - consumerData.consumeType = pConsumer->getConsumeType(); - consumerData.messageModel = pConsumer->getMessageModel(); - consumerData.consumeFromWhere = pConsumer->getConsumeFromWhere(); - - // result; - pConsumer->getSubscriptions(result); - consumerData.subscriptionDataSet.swap(result); - - pHeartbeatData->insertDataToConsumerDataSet(consumerData); - } -} - -void MQClientFactory::addTopicInfoToTable(const string& topic, boost::shared_ptr pTopicPublishInfo) { - boost::lock_guard lock(m_topicPublishInfoTableMutex); - if (m_topicPublishInfoTable.find(topic) != m_topicPublishInfoTable.end()) { - m_topicPublishInfoTable.erase(topic); - } - m_topicPublishInfoTable[topic] = pTopicPublishInfo; -} - -void MQClientFactory::eraseTopicInfoFromTable(const string& topic) { - boost::lock_guard lock(m_topicPublishInfoTableMutex); - if (m_topicPublishInfoTable.find(topic) != m_topicPublishInfoTable.end()) { - m_topicPublishInfoTable.erase(topic); - } -} - -bool MQClientFactory::isTopicInfoValidInTable(const string& topic) { - boost::lock_guard lock(m_topicPublishInfoTableMutex); - if (m_topicPublishInfoTable.find(topic) != m_topicPublishInfoTable.end()) { - if (m_topicPublishInfoTable[topic]->ok()) - return true; - } - return false; -} - -boost::shared_ptr MQClientFactory::getTopicPublishInfoFromTable(const string& topic) { - boost::lock_guard lock(m_topicPublishInfoTableMutex); - if (m_topicPublishInfoTable.find(topic) != m_topicPublishInfoTable.end()) { - return m_topicPublishInfoTable[topic]; - } - boost::shared_ptr pTopicPublishInfo; - return pTopicPublishInfo; -} - -void MQClientFactory::getTopicListFromTopicPublishInfo(set& topicList) { - boost::lock_guard lock(m_topicPublishInfoTableMutex); - for (TPMap::iterator itp = m_topicPublishInfoTable.begin(); itp != m_topicPublishInfoTable.end(); ++itp) { - topicList.insert(itp->first); - } -} - -void MQClientFactory::clearBrokerAddrMap() { - boost::lock_guard lock(m_brokerAddrlock); - m_brokerAddrTable.clear(); -} - -bool MQClientFactory::isBrokerAddressInUse(const std::string& address) { - if (m_topicRouteTableMutex.try_lock()) { - boost::lock_guard lk(m_topicRouteTableMutex, boost::adopt_lock_t()); - for (TRDMAP::iterator it = m_topicRouteTable.begin(); it != m_topicRouteTable.end(); it++) { - TopicRouteData* topicRouteData = it->second; - vector& brokerData = topicRouteData->getBrokerDatas(); - for (vector::iterator next = brokerData.begin(); next != brokerData.end(); next++) { - map& brokerAddresses = next->brokerAddrs; - for (map::iterator entry = brokerAddresses.begin(); entry != brokerAddresses.end(); entry++) { - if (address == entry->second) { - return true; - } - } - } - } - return false; - } else { - LOG_WARN("Cannot lock m_topicRouteTableMutex. Assume %s is still in use", address.c_str()); - return true; - } -} -void MQClientFactory::addBrokerToAddrMap(const string& brokerName, map& brokerAddrs) { - boost::lock_guard lock(m_brokerAddrlock); - if (m_brokerAddrTable.find(brokerName) != m_brokerAddrTable.end()) { - m_brokerAddrTable.erase(brokerName); - } - m_brokerAddrTable[brokerName] = brokerAddrs; -} - -MQClientFactory::BrokerAddrMAP MQClientFactory::getBrokerAddrMap() { - boost::lock_guard lock(m_brokerAddrlock); - return m_brokerAddrTable; -} - -string MQClientFactory::findBrokerAddressInPublish(const string& brokerName) { - /*reslove the concurrent access m_brokerAddrTable by - findBrokerAddressInPublish(called by sendKernlImpl) And - sendHeartbeatToAllBroker, which leads hign RT of sendMsg - 1. change m_brokerAddrTable from hashMap to map; - 2. do not add m_factoryLock here, but copy m_brokerAddrTable, - this is used to avoid con-current access m_factoryLock by - findBrokerAddressInPublish(called by sendKernlImpl) And - updateTopicRouteInfoFromNameServer - - Note: after copying m_brokerAddrTable, updateTopicRouteInfoFromNameServer - modify m_brokerAddrTable imediatly, - after 1st send fail, producer will get topicPushlibshInfo again - before next try, so 2nd try will get correct broker to send ms; - */ - BrokerAddrMAP brokerTable(getBrokerAddrMap()); - string brokerAddr; - bool found = false; - - if (brokerTable.find(brokerName) != brokerTable.end()) { - map brokerMap(brokerTable[brokerName]); - map::iterator it1 = brokerMap.find(MASTER_ID); - if (it1 != brokerMap.end()) { - brokerAddr = it1->second; - found = true; - } - } - - brokerTable.clear(); - if (found) - return brokerAddr; - - return ""; -} - -FindBrokerResult* MQClientFactory::findBrokerAddressInSubscribe(const string& brokerName, - int brokerId, - bool onlyThisBroker) { - string brokerAddr; - bool slave = false; - bool found = false; - BrokerAddrMAP brokerTable(getBrokerAddrMap()); - - if (brokerTable.find(brokerName) != brokerTable.end()) { - map brokerMap(brokerTable[brokerName]); - if (!brokerMap.empty()) { - auto iter = brokerMap.find(brokerId); - if (iter != brokerMap.end()) { - brokerAddr = iter->second; - slave = (brokerId != MASTER_ID); - found = true; - } else if (!onlyThisBroker) { // not only from master - iter = brokerMap.begin(); - brokerAddr = iter->second; - slave = iter->first != MASTER_ID; - found = true; - } - } - } - - brokerTable.clear(); - - if (found) { - return new FindBrokerResult(brokerAddr, slave); - } - - return nullptr; -} - -FindBrokerResult* MQClientFactory::findBrokerAddressInAdmin(const string& brokerName) { - BrokerAddrMAP brokerTable(getBrokerAddrMap()); - bool found = false; - bool slave = false; - string brokerAddr; - - if (brokerTable.find(brokerName) != brokerTable.end()) { - map brokerMap(brokerTable[brokerName]); - map::iterator it1 = brokerMap.begin(); - if (it1 != brokerMap.end()) { - slave = (it1->first != MASTER_ID); - found = true; - brokerAddr = it1->second; - } - } - - brokerTable.clear(); - if (found) - return new FindBrokerResult(brokerAddr, slave); - - return NULL; -} - -void MQClientFactory::checkTransactionState(const std::string& addr, - const MQMessageExt& messageExt, - const CheckTransactionStateRequestHeader& checkRequestHeader) { - string group = messageExt.getProperty(MQMessage::PROPERTY_PRODUCER_GROUP); - if (!group.empty()) { - MQProducer* producer = selectProducer(group); - if (producer != nullptr) { - TransactionMQProducerImpl* transProducer = dynamic_cast(producer); - if (transProducer != nullptr) { - transProducer->checkTransactionState(addr, messageExt, checkRequestHeader.m_tranStateTableOffset, - checkRequestHeader.m_commitLogOffset, checkRequestHeader.m_msgId, - checkRequestHeader.m_transactionId, checkRequestHeader.m_offsetMsgId); - } else { - LOG_ERROR("checkTransactionState, producer not TransactionMQProducer failed, msg:%s", - messageExt.toString().data()); - } - } else { - LOG_ERROR("checkTransactionState, pick producer by group[%s] failed, msg:%s", group.data(), - messageExt.toString().data()); - } - } else { - LOG_ERROR("checkTransactionState, pick producer group failed, msg:%s", messageExt.toString().data()); - } -} - -MQClientAPIImpl* MQClientFactory::getMQClientAPIImpl() { - return m_pClientAPIImpl.get(); -} - -void MQClientFactory::cleanOfflineBrokers() { - LOG_DEBUG("Begin to clean offline brokers"); - boost::lock_guard lock(m_brokerAddrlock); - - for (BrokerAddrMAP::iterator it = m_brokerAddrTable.begin(); it != m_brokerAddrTable.end();) { - std::string brokerName = it->first; - map brokerIdAddressMap = it->second; - - for (map::iterator next = brokerIdAddressMap.begin(); next != brokerIdAddressMap.end();) { - if (!isBrokerAddressInUse(next->second)) { - LOG_INFO("Remove broker address: %s", (next->second).c_str()); - brokerIdAddressMap.erase(next++); - } else { - next++; - } - } - - if (brokerIdAddressMap.empty()) { - m_brokerAddrTable.erase(it++); - LOG_INFO("Broker name: %s is purged from client", brokerName.c_str()); - } else { - LOG_DEBUG("Broker: %s is alive", brokerName.c_str()); - it++; - } - } - - LOG_DEBUG("Exit of cleaning offline brokers"); -} - -void MQClientFactory::sendHeartbeatToAllBroker() { - BrokerAddrMAP brokerTable(getBrokerAddrMap()); - if (brokerTable.size() == 0) { - LOG_WARN("sendheartbeat brokeradd is empty"); - return; - } - - unique_ptr heartbeatData(prepareHeartbeatData()); - bool producerEmpty = heartbeatData->isProducerDataSetEmpty(); - bool consumerEmpty = heartbeatData->isConsumerDataSetEmpty(); - if (producerEmpty && consumerEmpty) { - LOG_WARN("sendheartbeat heartbeatData empty"); - brokerTable.clear(); - return; - } - - SessionCredentials session_credentials; - getSessionCredentialsFromOneOfProducerOrConsumer(session_credentials); - for (BrokerAddrMAP::iterator it = brokerTable.begin(); it != brokerTable.end(); ++it) { - map brokerMap(it->second); - map::iterator it1 = brokerMap.begin(); - for (; it1 != brokerMap.end(); ++it1) { - string& addr = it1->second; - if (consumerEmpty && it1->first != MASTER_ID) - continue; - - try { - m_pClientAPIImpl->sendHeartbeat(addr, heartbeatData.get(), session_credentials); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - } - } - } - brokerTable.clear(); -} - -void MQClientFactory::persistAllConsumerOffset(boost::system::error_code& ec, - boost::shared_ptr t) { - { - boost::lock_guard lock(m_consumerTableMutex); - if (m_consumerTable.size() > 0) { - for (MQCMAP::iterator it = m_consumerTable.begin(); it != m_consumerTable.end(); ++it) { - LOG_DEBUG("Client factory start persistAllConsumerOffset"); - it->second->persistConsumerOffset(); - } - } - } - - boost::system::error_code e; - t->expires_from_now(t->expires_from_now() + boost::posix_time::seconds(5), e); - t->async_wait(boost::bind(&MQClientFactory::persistAllConsumerOffset, this, ec, t)); -} - -HeartbeatData* MQClientFactory::prepareHeartbeatData() { - HeartbeatData* pHeartbeatData = new HeartbeatData(); - // clientID - pHeartbeatData->setClientID(m_clientId); - - // Consumer - insertConsumerInfoToHeartBeatData(pHeartbeatData); - - // Producer - insertProducerInfoToHeartBeatData(pHeartbeatData); - - return pHeartbeatData; -} - -void MQClientFactory::timerCB_sendHeartbeatToAllBroker(boost::system::error_code& ec, - boost::shared_ptr t) { - sendHeartbeatToAllBroker(); - - boost::system::error_code e; - t->expires_from_now(t->expires_from_now() + boost::posix_time::seconds(30), e); - t->async_wait(boost::bind(&MQClientFactory::timerCB_sendHeartbeatToAllBroker, this, ec, t)); -} - -void MQClientFactory::timerCB_cleanOfflineBrokers(boost::system::error_code& ec, - boost::shared_ptr t) { - cleanOfflineBrokers(); - - boost::system::error_code e; - t->expires_from_now(t->expires_from_now() + boost::posix_time::seconds(30), e); - t->async_wait(boost::bind(&MQClientFactory::timerCB_cleanOfflineBrokers, this, ec, t)); -} - -void MQClientFactory::fetchNameServerAddr(boost::system::error_code& ec, - boost::shared_ptr t) { - m_pClientAPIImpl->fetchNameServerAddr(m_nameSrvDomain); - - boost::system::error_code e; - t->expires_from_now(t->expires_from_now() + boost::posix_time::seconds(60 * 2), e); - t->async_wait(boost::bind(&MQClientFactory::fetchNameServerAddr, this, ec, t)); -} - -void MQClientFactory::startScheduledTask(bool startFetchNSService) { - boost::asio::io_service::work work(m_async_ioService); // avoid async io - // service stops after - // first timer timeout - // callback - - boost::system::error_code ec1; - boost::shared_ptr t1 = - boost::make_shared(m_async_ioService, boost::posix_time::seconds(3)); - t1->async_wait(boost::bind(&MQClientFactory::updateTopicRouteInfo, this, ec1, t1)); - - boost::system::error_code ec2; - boost::shared_ptr t2 = - boost::make_shared(m_async_ioService, boost::posix_time::milliseconds(10)); - t2->async_wait(boost::bind(&MQClientFactory::timerCB_sendHeartbeatToAllBroker, this, ec2, t2)); - - boost::system::error_code ec3; - boost::shared_ptr t3 = - boost::make_shared(m_async_ioService, boost::posix_time::seconds(3)); - t3->async_wait(boost::bind(&MQClientFactory::timerCB_cleanOfflineBrokers, this, ec3, t3)); - - if (startFetchNSService) { - boost::system::error_code ec5; - boost::shared_ptr t5 = - boost::make_shared(m_async_ioService, boost::posix_time::seconds(60 * 2)); - t5->async_wait(boost::bind(&MQClientFactory::fetchNameServerAddr, this, ec5, t5)); - } - - LOG_INFO("start scheduled task:%s", m_clientId.c_str()); - boost::system::error_code ec; - m_async_ioService.run(ec); -} - -void MQClientFactory::rebalanceImmediately() { - // m_consumer_async_service_thread will be only started once for all consumer - if (m_consumer_async_service_thread == NULL) { - doRebalance(); - m_consumer_async_service_thread.reset( - new boost::thread(boost::bind(&MQClientFactory::consumer_timerOperation, this))); - } -} - -void MQClientFactory::consumer_timerOperation() { - LOG_INFO("clientFactory:%s start consumer_timerOperation", m_clientId.c_str()); - boost::asio::io_service::work work(m_consumer_async_ioService); // avoid async io - // service stops after - // first timer timeout - // callback - - boost::system::error_code ec1; - boost::shared_ptr t1 = - boost::make_shared(m_consumer_async_ioService, boost::posix_time::seconds(10)); - t1->async_wait(boost::bind(&MQClientFactory::timerCB_doRebalance, this, ec1, t1)); - - boost::system::error_code ec2; - boost::shared_ptr t2 = - boost::make_shared(m_consumer_async_ioService, boost::posix_time::seconds(5)); - t2->async_wait(boost::bind(&MQClientFactory::persistAllConsumerOffset, this, ec2, t2)); - - boost::system::error_code ec; - m_consumer_async_ioService.run(ec); - LOG_INFO("clientFactory:%s stop consumer_timerOperation", m_clientId.c_str()); -} - -void MQClientFactory::timerCB_doRebalance(boost::system::error_code& ec, - boost::shared_ptr t) { - doRebalance(); - - boost::system::error_code e; - t->expires_from_now(t->expires_from_now() + boost::posix_time::seconds(10), e); - t->async_wait(boost::bind(&MQClientFactory::timerCB_doRebalance, this, ec, t)); -} - -void MQClientFactory::doRebalance() { - LOG_DEBUG("Client factory:%s start doRebalance", m_clientId.c_str()); - if (getConsumerTableSize() > 0) { - boost::lock_guard lock(m_consumerTableMutex); - for (MQCMAP::iterator it = m_consumerTable.begin(); it != m_consumerTable.end(); ++it) { - it->second->doRebalance(); - } - } - LOG_DEBUG("Client factory:%s finish doRebalance", m_clientId.c_str()); -} - -void MQClientFactory::doRebalanceByConsumerGroup(const string& consumerGroup) { - boost::lock_guard lock(m_consumerTableMutex); - if (m_consumerTable.find(consumerGroup) != m_consumerTable.end()) { - LOG_INFO("Client factory:%s start dorebalance for consumer:%s", m_clientId.c_str(), consumerGroup.c_str()); - MQConsumer* pMQConsumer = m_consumerTable[consumerGroup]; - pMQConsumer->doRebalance(); - } -} - -void MQClientFactory::endTransactionOneway(const MQMessageQueue& mq, - EndTransactionRequestHeader* requestHeader, - const SessionCredentials& sessionCredentials) { - string brokerAddr = findBrokerAddressInPublish(mq.getBrokerName()); - string remark = ""; - if (!brokerAddr.empty()) { - try { - getMQClientAPIImpl()->endTransactionOneway(brokerAddr, requestHeader, remark, sessionCredentials); - } catch (MQException& e) { - LOG_ERROR("endTransactionOneway exception:%s", e.what()); - throw e; - } - } else { - THROW_MQEXCEPTION(MQClientException, "The broker[" + mq.getBrokerName() + "] not exist", -1); - } -} - -void MQClientFactory::unregisterClient(const string& producerGroup, - const string& consumerGroup, - const SessionCredentials& sessionCredentials) { - BrokerAddrMAP brokerTable(getBrokerAddrMap()); - for (BrokerAddrMAP::iterator it = brokerTable.begin(); it != brokerTable.end(); ++it) { - map brokerMap(it->second); - map::iterator it1 = brokerMap.begin(); - for (; it1 != brokerMap.end(); ++it1) { - string& addr = it1->second; - m_pClientAPIImpl->unregisterClient(addr, m_clientId, producerGroup, consumerGroup, sessionCredentials); - } - } -} - -//& mqs, - const SessionCredentials& sessionCredentials) { - TopicRouteData* pTopicRouteData = getTopicRouteData(topic); - if (pTopicRouteData == NULL) { - updateTopicRouteInfoFromNameServer(topic, sessionCredentials); - pTopicRouteData = getTopicRouteData(topic); - } - if (pTopicRouteData != NULL) { - topicRouteData2TopicSubscribeInfo(topic, pTopicRouteData, mqs); - if (mqs.empty()) { - THROW_MQEXCEPTION(MQClientException, "Can not find Message Queue", -1); - } - return; - } - THROW_MQEXCEPTION(MQClientException, "Can not find Message Queue", -1); -} - -//getMinOffset(brokerAddr, mq.getTopic(), mq.getQueueId(), 1000 * 3, sessionCredentials); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - } - } - THROW_MQEXCEPTION(MQClientException, "The broker is not exist", -1); -} - -int64 MQClientFactory::maxOffset(const MQMessageQueue& mq, const SessionCredentials& sessionCredentials) { - string brokerAddr = findBrokerAddressInPublish(mq.getBrokerName()); - if (brokerAddr.empty()) { - updateTopicRouteInfoFromNameServer(mq.getTopic(), sessionCredentials); - brokerAddr = findBrokerAddressInPublish(mq.getBrokerName()); - } - - if (!brokerAddr.empty()) { - try { - return m_pClientAPIImpl->getMaxOffset(brokerAddr, mq.getTopic(), mq.getQueueId(), 1000 * 3, sessionCredentials); - } catch (MQException& e) { - THROW_MQEXCEPTION(MQClientException, "Invoke Broker exception", -1); - } - } - THROW_MQEXCEPTION(MQClientException, "The broker is not exist", -1); -} - -int64 MQClientFactory::searchOffset(const MQMessageQueue& mq, - int64 timestamp, - const SessionCredentials& sessionCredentials) { - string brokerAddr = findBrokerAddressInPublish(mq.getBrokerName()); - if (brokerAddr.empty()) { - updateTopicRouteInfoFromNameServer(mq.getTopic(), sessionCredentials); - brokerAddr = findBrokerAddressInPublish(mq.getBrokerName()); - } - - if (!brokerAddr.empty()) { - try { - return m_pClientAPIImpl->searchOffset(brokerAddr, mq.getTopic(), mq.getQueueId(), timestamp, 1000 * 3, - sessionCredentials); - } catch (MQException& e) { - THROW_MQEXCEPTION(MQClientException, "Invoke Broker exception", -1); - } - } - THROW_MQEXCEPTION(MQClientException, "The broker is not exist", -1); -} - -MQMessageExt* MQClientFactory::viewMessage(const string& msgId, const SessionCredentials& sessionCredentials) { - try { - return NULL; - } catch (MQException& e) { - THROW_MQEXCEPTION(MQClientException, "message id illegal", -1); - } -} - -int64 MQClientFactory::earliestMsgStoreTime(const MQMessageQueue& mq, const SessionCredentials& sessionCredentials) { - string brokerAddr = findBrokerAddressInPublish(mq.getBrokerName()); - if (brokerAddr.empty()) { - updateTopicRouteInfoFromNameServer(mq.getTopic(), sessionCredentials); - brokerAddr = findBrokerAddressInPublish(mq.getBrokerName()); - } - - if (!brokerAddr.empty()) { - try { - return m_pClientAPIImpl->getEarliestMsgStoretime(brokerAddr, mq.getTopic(), mq.getQueueId(), 1000 * 3, - sessionCredentials); - } catch (MQException& e) { - THROW_MQEXCEPTION(MQClientException, "Invoke Broker exception", -1); - } - } - THROW_MQEXCEPTION(MQClientException, "The broker is not exist", -1); -} - -QueryResult MQClientFactory::queryMessage(const string& topic, - const string& key, - int maxNum, - int64 begin, - int64 end, - const SessionCredentials& sessionCredentials) { - THROW_MQEXCEPTION(MQClientException, "queryMessage", -1); -} - -void MQClientFactory::findConsumerIds(const string& topic, - const string& group, - vector& cids, - const SessionCredentials& sessionCredentials) { - string brokerAddr; - TopicRouteData* pTopicRouteData = getTopicRouteData(topic); - if (pTopicRouteData == NULL) { - updateTopicRouteInfoFromNameServer(topic, sessionCredentials); - pTopicRouteData = getTopicRouteData(topic); - } - if (pTopicRouteData != NULL) { - brokerAddr = pTopicRouteData->selectBrokerAddr(); - } - - if (!brokerAddr.empty()) { - try { - LOG_INFO("getConsumerIdList from broker:%s", brokerAddr.c_str()); - return m_pClientAPIImpl->getConsumerIdListByGroup(brokerAddr, group, cids, 5000, sessionCredentials); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - } - } -} - -void MQClientFactory::resetOffset(const string& group, - const string& topic, - const map& offsetTable) { - MQConsumer* pConsumer = selectConsumer(group); - if (pConsumer) { - map::const_iterator it = offsetTable.begin(); - - for (; it != offsetTable.end(); ++it) { - MQMessageQueue mq = it->first; - boost::weak_ptr pullRequest = pConsumer->getRebalance()->getPullRequest(mq); - boost::shared_ptr pullreq = pullRequest.lock(); - // PullRequest* pullreq = pConsumer->getRebalance()->getPullRequest(mq); - if (pullreq) { - pullreq->setDropped(true); - LOG_INFO("resetOffset setDropped for mq:%s", mq.toString().data()); - pullreq->clearAllMsgs(); - pullreq->updateQueueMaxOffset(it->second); - } else { - LOG_ERROR("no corresponding pullRequest found for topic:%s", topic.c_str()); - } - } - - for (it = offsetTable.begin(); it != offsetTable.end(); ++it) { - MQMessageQueue mq = it->first; - if (topic == mq.getTopic()) { - LOG_INFO("offset sets to:%lld", it->second); - pConsumer->updateConsumeOffset(mq, it->second); - } - } - pConsumer->persistConsumerOffsetByResetOffset(); - - boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); - - for (it = offsetTable.begin(); it != offsetTable.end(); ++it) { - MQMessageQueue mq = it->first; - if (topic == mq.getTopic()) { - LOG_DEBUG("resetOffset sets to:%lld for mq:%s", it->second, mq.toString().c_str()); - pConsumer->updateConsumeOffset(mq, it->second); - } - } - pConsumer->persistConsumerOffsetByResetOffset(); - - for (it = offsetTable.begin(); it != offsetTable.end(); ++it) { - MQMessageQueue mq = it->first; - if (topic == mq.getTopic()) { - pConsumer->removeConsumeOffset(mq); - } - } - - // do call pConsumer->doRebalance directly here, as it is conflict with - // timerCB_doRebalance; - doRebalanceByConsumerGroup(pConsumer->getGroupName()); - } else { - LOG_ERROR("no corresponding consumer found for group:%s", group.c_str()); - } -} - -ConsumerRunningInfo* MQClientFactory::consumerRunningInfo(const string& consumerGroup) { - MQConsumer* pConsumer = selectConsumer(consumerGroup); - if (pConsumer) { - ConsumerRunningInfo* runningInfo = pConsumer->getConsumerRunningInfo(); - if (runningInfo) { - runningInfo->setProperty(ConsumerRunningInfo::PROP_NAMESERVER_ADDR, pConsumer->getNamesrvAddr()); - if (pConsumer->getConsumeType() == CONSUME_PASSIVELY) { - runningInfo->setProperty(ConsumerRunningInfo::PROP_CONSUME_TYPE, "CONSUME_PASSIVELY"); - } else { - runningInfo->setProperty(ConsumerRunningInfo::PROP_CONSUME_TYPE, "CONSUME_ACTIVELY"); - } - runningInfo->setProperty( - ConsumerRunningInfo::PROP_CLIENT_VERSION, - MQVersion::GetVersionDesc(MQVersion::s_CurrentVersion)); // MQVersion::s_CurrentVersion )); - runningInfo->setProperty(ConsumerRunningInfo::PROP_CLIENT_SDK_VERSION, - pConsumer->getClientVersionString()); // in DefaultMQClient.cpp; - - return runningInfo; - } - } - - LOG_ERROR("no corresponding consumer found for group:%s", consumerGroup.c_str()); - return NULL; -} - -void MQClientFactory::getSessionCredentialsFromOneOfProducerOrConsumer(SessionCredentials& session_credentials) { - // Note: on the same MQClientFactory, all producers and consumers used the - // same - // sessionCredentials, - // So only need get sessionCredentials from the first one producer or consumer - // now. - // this function was only used by updateTopicRouteInfo() and - // sendHeartbeatToAllBrokers() now. - // if this strategy was changed in future, need get sessionCredentials for - // each - // producer and consumer. - getSessionCredentialFromProducerTable(session_credentials); - if (!session_credentials.isValid()) - getSessionCredentialFromConsumerTable(session_credentials); - - if (!session_credentials.isValid()) { - LOG_INFO( - "updateTopicRouteInfo: didn't get the session_credentials from any " - "producers and consumers, please re-intialize it if application needs authentication"); - } -} - -// -#include -#include -#include -#include -#include -#include "FindBrokerResult.h" -#include "MQAdmin.h" -#include "MQClientAPIImpl.h" -#include "MQClientException.h" -#include "MQConsumer.h" -#include "MQDecoder.h" -#include "MQMessageQueue.h" -#include "MQProducer.h" -#include "PermName.h" -#include "QueryResult.h" -#include "ServiceState.h" -#include "SocketUtil.h" -#include "TopicConfig.h" -#include "TopicRouteData.h" - -namespace rocketmq { -// topicRouteData2TopicPublishInfo(const string& topic, TopicRouteData* pRoute); - - void topicRouteData2TopicSubscribeInfo(const string& topic, TopicRouteData* pRoute, vector& mqs); - - FindBrokerResult* findBrokerAddressInSubscribe(const string& brokerName, int brokerId, bool onlyThisBroker); - - FindBrokerResult* findBrokerAddressInAdmin(const string& brokerName); - - virtual string findBrokerAddressInPublish(const string& brokerName); - - virtual boost::shared_ptr tryToFindTopicPublishInfo(const string& topic, - const SessionCredentials& session_credentials); - - void fetchSubscribeMessageQueues(const string& topic, - vector& mqs, - const SessionCredentials& session_credentials); - - bool updateTopicRouteInfoFromNameServer(const string& topic, - const SessionCredentials& session_credentials, - bool isDefault = false); - void rebalanceImmediately(); - void doRebalanceByConsumerGroup(const string& consumerGroup); - virtual void sendHeartbeatToAllBroker(); - - void cleanOfflineBrokers(); - - void findConsumerIds(const string& topic, - const string& group, - vector& cids, - const SessionCredentials& session_credentials); - void resetOffset(const string& group, const string& topic, const map& offsetTable); - ConsumerRunningInfo* consumerRunningInfo(const string& consumerGroup); - bool getSessionCredentialFromConsumer(const string& consumerGroup, SessionCredentials& sessionCredentials); - void addBrokerToAddrMap(const string& brokerName, map& brokerAddrs); - map> getBrokerAddrMap(); - void clearBrokerAddrMap(); - - bool isBrokerAddressInUse(const std::string& address); - - private: - void unregisterClient(const string& producerGroup, - const string& consumerGroup, - const SessionCredentials& session_credentials); - TopicRouteData* getTopicRouteData(const string& topic); - void addTopicRouteData(const string& topic, TopicRouteData* pTopicRouteData); - HeartbeatData* prepareHeartbeatData(); - - void startScheduledTask(bool startFetchNSService = true); - // t); - void updateTopicRouteInfo(boost::system::error_code& ec, boost::shared_ptr t); - void timerCB_sendHeartbeatToAllBroker(boost::system::error_code& ec, - boost::shared_ptr t); - - void timerCB_cleanOfflineBrokers(boost::system::error_code& ec, boost::shared_ptr t); - - // consumer related operation - void consumer_timerOperation(); - void persistAllConsumerOffset(boost::system::error_code& ec, boost::shared_ptr t); - void doRebalance(); - void timerCB_doRebalance(boost::system::error_code& ec, boost::shared_ptr t); - bool getSessionCredentialFromConsumerTable(SessionCredentials& sessionCredentials); - void eraseConsumerFromTable(const string& consumerName); - int getConsumerTableSize(); - void getTopicListFromConsumerSubscription(set& topicList); - void updateConsumerSubscribeTopicInfo(const string& topic, vector mqs); - void insertConsumerInfoToHeartBeatData(HeartbeatData* pHeartbeatData); - - // producer related operation - bool getSessionCredentialFromProducerTable(SessionCredentials& sessionCredentials); - void eraseProducerFromTable(const string& producerName); - int getProducerTableSize(); - void insertProducerInfoToHeartBeatData(HeartbeatData* pHeartbeatData); - - // admin related operation - void eraseAdminFromTable(const string& adminName); - - // topicPublishInfo related operation - void addTopicInfoToTable(const string& topic, boost::shared_ptr pTopicPublishInfo); - void eraseTopicInfoFromTable(const string& topic); - bool isTopicInfoValidInTable(const string& topic); - boost::shared_ptr getTopicPublishInfoFromTable(const string& topic); - void getTopicListFromTopicPublishInfo(set& topicList); - - void getSessionCredentialsFromOneOfProducerOrConsumer(SessionCredentials& session_credentials); - - protected: - string m_clientId; - unique_ptr m_pClientAPIImpl; - unique_ptr m_pClientRemotingProcessor; - - bool addProducerToTable(const string& producerName, MQProducer* pMQProducer); - bool addConsumerToTable(const string& consumerName, MQConsumer* pMQConsumer); - bool addAdminToTable(const string& adminName, MQAdmin* pMQAdmin); - - private: - string m_nameSrvDomain; // per clientId - ServiceState m_serviceState; - bool m_bFetchNSService; - - // MQProducer; - typedef map MQPMAP; - boost::mutex m_producerTableMutex; - MQPMAP m_producerTable; - - // MQConsumer; - typedef map MQCMAP; - // Changed to recursive mutex due to avoid deadlock issue: - boost::recursive_mutex m_consumerTableMutex; - MQCMAP m_consumerTable; - - // MQAdmin; - typedef map MQAMAP; - // Changed to recursive mutex due to avoid deadlock issue: - boost::recursive_mutex m_adminTableMutex; - MQAMAP m_adminTable; - - // TopicRouteData - typedef map TRDMAP; - boost::mutex m_topicRouteTableMutex; - TRDMAP m_topicRouteTable; - - //TopicPublishInfo> ; - typedef map> TPMap; - boost::mutex m_topicPublishInfoTableMutex; - TPMap m_topicPublishInfoTable; - boost::mutex m_factoryLock; - boost::mutex m_topicPublishInfoLock; - - boost::asio::io_service m_async_ioService; - unique_ptr m_async_service_thread; - - boost::asio::io_service m_consumer_async_ioService; - unique_ptr m_consumer_async_service_thread; -}; - -} // namespace rocketmq - -#endif diff --git a/src/MQClientManager.cpp b/src/MQClientManager.cpp deleted file mode 100644 index ad821119a..000000000 --- a/src/MQClientManager.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "MQClientManager.h" -#include "Logging.h" -#include -#include - -namespace rocketmq { -//second; - } else { - MQClientFactory* factory = new MQClientFactory(clientId, pullThreadNum, tcpConnectTimeout, - tcpTransportTryLockTimeout, unitName, enableSsl, sslPropertyFile); - m_factoryTable[clientId] = factory; - return factory; - } -} - -void MQClientManager::removeClientFactory(const string& clientId) { - FTMAP::iterator it = m_factoryTable.find(clientId); - if (it != m_factoryTable.end()) { - deleteAndZero(it->second); - m_factoryTable.erase(it); - } -} -// -#include -#include "Logging.h" -#include "MQClientFactory.h" - -namespace rocketmq { -// FTMAP; - FTMAP m_factoryTable; -}; - -//registerMQAdmin(this); - if (!registerOK) { - m_serviceState = CREATE_JUST; - THROW_MQEXCEPTION( - MQClientException, - "The admin group[" + getGroupName() + "] has been created before, specify another name please.", -1); - } - getFactory()->start(); - m_serviceState = RUNNING; - break; - } - case RUNNING: - case START_FAILED: - case SHUTDOWN_ALREADY: - break; - default: - break; - } -} - -void DefaultMQAdmin::shutdown() { - switch (m_serviceState) { - case RUNNING: { - LOG_INFO("DefaultMQAdmin:%s shutdown", m_GroupName.c_str()); - getFactory()->unregisterMQAdmin(this); - getFactory()->shutdown(); - m_serviceState = SHUTDOWN_ALREADY; - break; - } - case SHUTDOWN_ALREADY: - case CREATE_JUST: - break; - default: - break; - } -} -} // namespace rocketmq \ No newline at end of file diff --git a/src/client/DefaultMQAdmin.h b/src/client/DefaultMQAdmin.h deleted file mode 100644 index baa7e0f5b..000000000 --- a/src/client/DefaultMQAdmin.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef DEFAULTMQADMIN_H -#define DEFAULTMQADMIN_H -#include "MQAdmin.h" -#include "MQClientFactory.h" - -namespace rocketmq { -class DefaultMQAdmin : public MQAdmin { - public: - DefaultMQAdmin(const std::string& groupname); - ~DefaultMQAdmin(); - void start(); - void shutdown(); -}; -}; // namespace rocketmq -#endif // DEFAULTMQADMIN_H \ No newline at end of file diff --git a/src/common/Arg_helper.cpp b/src/common/Arg_helper.cpp deleted file mode 100644 index 934954e74..000000000 --- a/src/common/Arg_helper.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Arg_helper.h" -#include "UtilAll.h" - -namespace rocketmq { -// v; - UtilAll::Split(v, arg_str_, " "); - m_args.insert(m_args.end(), v.begin(), v.end()); -} - -string Arg_helper::get_option(int idx_) const { - if ((size_t)idx_ >= m_args.size()) { - return ""; - } - return m_args[idx_]; -} - -bool Arg_helper::is_enable_option(string opt_) const { - for (size_t i = 0; i < m_args.size(); ++i) { - if (opt_ == m_args[i]) { - return true; - } - } - return false; -} - -string Arg_helper::get_option_value(string opt_) const { - string ret = ""; - for (size_t i = 0; i < m_args.size(); ++i) { - if (opt_ == m_args[i]) { - size_t value_idx = ++i; - if (value_idx >= m_args.size()) { - return ret; - } - ret = m_args[value_idx]; - return ret; - } - } - return ret; -} - -//(m_pAsyncCallBack); - if (pCallback) { - unique_ptr exception( - new MQException("send msg failed due to wait response timeout or network error", -1, __FILE__, __LINE__)); - pCallback->onException(*exception); - if (pCallback->getSendCallbackType() == autoDeleteSendCallback) { - deleteAndZero(pCallback); - } - } -} - -void SendCallbackWrap::operationComplete(ResponseFuture* pResponseFuture, bool bProducePullRequest) { - unique_ptr pResponse(pResponseFuture->getCommand()); - - if (m_pAsyncCallBack == NULL) { - return; - } - int opaque = pResponseFuture->getOpaque(); - SendCallback* pCallback = static_cast(m_pAsyncCallBack); - - if (!pResponse) { - string err = "unknow reseaon"; - if (!pResponseFuture->isSendRequestOK()) { - err = "send request failed"; - - } else if (pResponseFuture->isTimeOut()) { - // pResponseFuture->setAsyncResponseFlag(); - err = "wait response timeout"; - } - if (pCallback) { - MQException exception(err, -1, __FILE__, __LINE__); - pCallback->onException(exception); - } - LOG_ERROR("send failed of:%d", pResponseFuture->getOpaque()); - } else { - try { - SendResult ret = m_pClientAPI->processSendResponse(m_brokerName, m_msg, pResponse.get()); - if (pCallback) { - LOG_DEBUG("operationComplete: processSendResponse success, opaque:%d, maxRetryTime:%d, retrySendTimes:%d", - opaque, pResponseFuture->getMaxRetrySendTimes(), pResponseFuture->getRetrySendTimes()); - pCallback->onSuccess(ret); - } - } catch (MQException& e) { - LOG_ERROR("operationComplete: processSendResponse exception: %s", e.what()); - - // broker may return exception, need consider retry send - int maxRetryTimes = pResponseFuture->getMaxRetrySendTimes(); - int retryTimes = pResponseFuture->getRetrySendTimes(); - if (pResponseFuture->getAsyncFlag() && retryTimes < maxRetryTimes && maxRetryTimes > 1) { - int64 left_timeout_ms = pResponseFuture->leftTime(); - string brokerAddr = pResponseFuture->getBrokerAddr(); - const RemotingCommand& requestCommand = pResponseFuture->getRequestCommand(); - retryTimes += 1; - LOG_WARN("retry send, opaque:%d, sendTimes:%d, maxRetryTimes:%d, left_timeout:%lld, brokerAddr:%s, msg:%s", - opaque, retryTimes, maxRetryTimes, left_timeout_ms, brokerAddr.data(), m_msg.toString().data()); - - bool exception_flag = false; - try { - m_pClientAPI->sendMessageAsync(pResponseFuture->getBrokerAddr(), m_brokerName, m_msg, - (RemotingCommand&)requestCommand, pCallback, left_timeout_ms, maxRetryTimes, - retryTimes); - } catch (MQClientException& e) { - LOG_ERROR("retry send exception:%s, opaque:%d, retryTimes:%d, msg:%s, not retry send again", e.what(), opaque, - retryTimes, m_msg.toString().data()); - exception_flag = true; - } - - if (exception_flag == false) { - return; // send retry again, here need return - } - } - - if (pCallback) { - MQException exception("process send response error", -1, __FILE__, __LINE__); - pCallback->onException(exception); - } - } - } - if (pCallback && pCallback->getSendCallbackType() == autoDeleteSendCallback) { - deleteAndZero(pCallback); - } -} - -//(pArg); -} - -PullCallbackWrap::~PullCallbackWrap() {} - -void PullCallbackWrap::onException() { - if (m_pAsyncCallBack == NULL) - return; - - PullCallback* pCallback = static_cast(m_pAsyncCallBack); - if (pCallback) { - MQException exception("wait response timeout", -1, __FILE__, __LINE__); - pCallback->onException(exception); - } else { - LOG_ERROR("PullCallback is NULL, AsyncPull could not continue"); - } -} - -void PullCallbackWrap::operationComplete(ResponseFuture* pResponseFuture, bool bProducePullRequest) { - unique_ptr pResponse(pResponseFuture->getCommand()); - if (m_pAsyncCallBack == NULL) { - LOG_ERROR("m_pAsyncCallBack is NULL, AsyncPull could not continue"); - return; - } - PullCallback* pCallback = static_cast(m_pAsyncCallBack); - if (!pResponse) { - string err = "unknow reseaon"; - if (!pResponseFuture->isSendRequestOK()) { - err = "send request failed"; - - } else if (pResponseFuture->isTimeOut()) { - // pResponseFuture->setAsyncResponseFlag(); - err = "wait response timeout"; - } - MQException exception(err, -1, __FILE__, __LINE__); - LOG_ERROR("Async pull exception of opaque:%d", pResponseFuture->getOpaque()); - if (pCallback && bProducePullRequest) - pCallback->onException(exception); - } else { - try { - if (m_pArg.pPullWrapper) { - unique_ptr pullResult(m_pClientAPI->processPullResponse(pResponse.get())); - PullResult result = m_pArg.pPullWrapper->processPullResult(m_pArg.mq, pullResult.get(), &m_pArg.subData); - if (pCallback) - pCallback->onSuccess(m_pArg.mq, result, bProducePullRequest); - } else { - LOG_ERROR("pPullWrapper had been destroyed with consumer"); - } - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - MQException exception("pullResult error", -1, __FILE__, __LINE__); - if (pCallback && bProducePullRequest) - pCallback->onException(exception); - } - } -} - -// -#include -#include -#include -#include "RocketMQClient.h" -#include "UtilAll.h" -//============================================================================== -/** Contains static methods for converting the byte order between different - endiannesses. -*/ -namespace rocketmq { - -class ROCKETMQCLIENT_API ByteOrder { - public: - //============================================================================== - /** Swaps the upper and lower bytes of a 16-bit integer. */ - static uint16 swap(uint16 value); - - /** Reverses the order of the 4 bytes in a 32-bit integer. */ - static uint32 swap(uint32 value); - - /** Reverses the order of the 8 bytes in a 64-bit integer. */ - static uint64 swap(uint64 value); - - //============================================================================== - /** Swaps the byte order of a 16-bit int if the CPU is big-endian */ - static uint16 swapIfBigEndian(uint16 value); - - /** Swaps the byte order of a 32-bit int if the CPU is big-endian */ - static uint32 swapIfBigEndian(uint32 value); - - /** Swaps the byte order of a 64-bit int if the CPU is big-endian */ - static uint64 swapIfBigEndian(uint64 value); - - /** Swaps the byte order of a 16-bit int if the CPU is little-endian */ - static uint16 swapIfLittleEndian(uint16 value); - - /** Swaps the byte order of a 32-bit int if the CPU is little-endian */ - static uint32 swapIfLittleEndian(uint32 value); - - /** Swaps the byte order of a 64-bit int if the CPU is little-endian */ - static uint64 swapIfLittleEndian(uint64 value); - - //============================================================================== - /** Turns 4 bytes into a little-endian integer. */ - static uint32 littleEndianInt(const void* bytes); - - /** Turns 8 bytes into a little-endian integer. */ - static uint64 littleEndianInt64(const void* bytes); - - /** Turns 2 bytes into a little-endian integer. */ - static uint16 littleEndianShort(const void* bytes); - - /** Turns 4 bytes into a big-endian integer. */ - static uint32 bigEndianInt(const void* bytes); - - /** Turns 8 bytes into a big-endian integer. */ - static uint64 bigEndianInt64(const void* bytes); - - /** Turns 2 bytes into a big-endian integer. */ - static uint16 bigEndianShort(const void* bytes); - - //============================================================================== - /** Converts 3 little-endian bytes into a signed 24-bit value (which is - * sign-extended to 32 bits). */ - static int littleEndian24Bit(const void* bytes); - - /** Converts 3 big-endian bytes into a signed 24-bit value (which is - * sign-extended to 32 bits). */ - static int bigEndian24Bit(const void* bytes); - - /** Copies a 24-bit number to 3 little-endian bytes. */ - static void littleEndian24BitToChars(int value, void* destBytes); - - /** Copies a 24-bit number to 3 big-endian bytes. */ - static void bigEndian24BitToChars(int value, void* destBytes); - - //============================================================================== - /** Returns true if the current CPU is big-endian. */ - static bool isBigEndian(); -}; - -//============================================================================== - -inline uint16 ByteOrder::swap(uint16 n) { - return static_cast((n << 8) | (n >> 8)); -} - -inline uint32 ByteOrder::swap(uint32 n) { - return (n << 24) | (n >> 24) | ((n & 0xff00) << 8) | ((n & 0xff0000) >> 8); -} - -inline uint64 ByteOrder::swap(uint64 value) { - return (((uint64)swap((uint32)value)) << 32) | swap((uint32)(value >> 32)); -} - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ //__BYTE_ORDER__ is defined by GCC -inline uint16 ByteOrder::swapIfBigEndian(const uint16 v) { - return v; -} -inline uint32 ByteOrder::swapIfBigEndian(const uint32 v) { - return v; -} -inline uint64 ByteOrder::swapIfBigEndian(const uint64 v) { - return v; -} -inline uint16 ByteOrder::swapIfLittleEndian(const uint16 v) { - return swap(v); -} -inline uint32 ByteOrder::swapIfLittleEndian(const uint32 v) { - return swap(v); -} -inline uint64 ByteOrder::swapIfLittleEndian(const uint64 v) { - return swap(v); -} -inline uint32 ByteOrder::littleEndianInt(const void* const bytes) { - return *static_cast(bytes); -} -inline uint64 ByteOrder::littleEndianInt64(const void* const bytes) { - return *static_cast(bytes); -} -inline uint16 ByteOrder::littleEndianShort(const void* const bytes) { - return *static_cast(bytes); -} -inline uint32 ByteOrder::bigEndianInt(const void* const bytes) { - return swap(*static_cast(bytes)); -} -inline uint64 ByteOrder::bigEndianInt64(const void* const bytes) { - return swap(*static_cast(bytes)); -} -inline uint16 ByteOrder::bigEndianShort(const void* const bytes) { - return swap(*static_cast(bytes)); -} -inline bool ByteOrder::isBigEndian() { - return false; -} -#else -inline uint16 ByteOrder::swapIfBigEndian(const uint16 v) { - return swap(v); -} -inline uint32 ByteOrder::swapIfBigEndian(const uint32 v) { - return swap(v); -} -inline uint64 ByteOrder::swapIfBigEndian(const uint64 v) { - return swap(v); -} -inline uint16 ByteOrder::swapIfLittleEndian(const uint16 v) { - return v; -} -inline uint32 ByteOrder::swapIfLittleEndian(const uint32 v) { - return v; -} -inline uint64 ByteOrder::swapIfLittleEndian(const uint64 v) { - return v; -} -inline uint32 ByteOrder::littleEndianInt(const void* const bytes) { - return swap(*static_cast(bytes)); -} -inline uint64 ByteOrder::littleEndianInt64(const void* const bytes) { - return swap(*static_cast(bytes)); -} -inline uint16 ByteOrder::littleEndianShort(const void* const bytes) { - return swap(*static_cast(bytes)); -} -inline uint32 ByteOrder::bigEndianInt(const void* const bytes) { - return *static_cast(bytes); -} -inline uint64 ByteOrder::bigEndianInt64(const void* const bytes) { - return *static_cast(bytes); -} -inline uint16 ByteOrder::bigEndianShort(const void* const bytes) { - return *static_cast(bytes); -} -inline bool ByteOrder::isBigEndian() { - return true; -} -#endif - -inline int ByteOrder::littleEndian24Bit(const void* const bytes) { - return (((int)static_cast(bytes)[2]) << 16) | (((int)static_cast(bytes)[1]) << 8) | - ((int)static_cast(bytes)[0]); -} -inline int ByteOrder::bigEndian24Bit(const void* const bytes) { - return (((int)static_cast(bytes)[0]) << 16) | (((int)static_cast(bytes)[1]) << 8) | - ((int)static_cast(bytes)[2]); -} -inline void ByteOrder::littleEndian24BitToChars(const int value, void* const destBytes) { - static_cast(destBytes)[0] = (uint8)value; - static_cast(destBytes)[1] = (uint8)(value >> 8); - static_cast(destBytes)[2] = (uint8)(value >> 16); -} -inline void ByteOrder::bigEndian24BitToChars(const int value, void* const destBytes) { - static_cast(destBytes)[0] = (uint8)(value >> 16); - static_cast(destBytes)[1] = (uint8)(value >> 8); - static_cast(destBytes)[2] = (uint8)value; -} -} // namespace rocketmq -#endif // BYTEORDER_H_INCLUDED diff --git a/src/common/ClientRPCHook.cpp b/src/common/ClientRPCHook.cpp deleted file mode 100644 index a8244cf80..000000000 --- a/src/common/ClientRPCHook.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ClientRPCHook.h" -#include "CommandHeader.h" -#include "Logging.h" -extern "C" { -#include "spas_client.h" -} -#include "string" - -namespace rocketmq { - -const string SessionCredentials::AccessKey = "AccessKey"; -const string SessionCredentials::SecretKey = "SecretKey"; -const string SessionCredentials::Signature = "Signature"; -const string SessionCredentials::SignatureMethod = "SignatureMethod"; -const string SessionCredentials::ONSChannelKey = "OnsChannel"; - -void ClientRPCHook::doBeforeRequest(const string& remoteAddr, RemotingCommand& request) { - CommandHeader* header = request.getCommandHeader(); - - map requestMap; - string totalMsg; - - requestMap.insert(pair(SessionCredentials::AccessKey, sessionCredentials.getAccessKey())); - requestMap.insert(pair(SessionCredentials::ONSChannelKey, sessionCredentials.getAuthChannel())); - - LOG_DEBUG("before insert declared filed,MAP SIZE is:" SIZET_FMT "", requestMap.size()); - if (header != NULL) { - header->SetDeclaredFieldOfCommandHeader(requestMap); - } - LOG_DEBUG("after insert declared filed, MAP SIZE is:" SIZET_FMT "", requestMap.size()); - - map::iterator it = requestMap.begin(); - for (; it != requestMap.end(); ++it) { - totalMsg.append(it->second); - } - const MemoryBlock* pBody = request.GetBody(); - if (pBody && pBody->getSize() > 0) { - const char* msg_body = const_cast(static_cast(pBody->getData())); - LOG_DEBUG("msgBody is:%s, msgBody length is:%d", msg_body, pBody->getSize()); - totalMsg.append(msg_body, pBody->getSize()); - } else if (request.getMsgBody().length() > 0) { - LOG_DEBUG("msgBody is:%s, msgBody length is:" SIZET_FMT "", request.getMsgBody().c_str(), - request.getMsgBody().length()); - totalMsg.append(request.getMsgBody()); - } - LOG_DEBUG("total msg info are:%s, size is:" SIZET_FMT "", totalMsg.c_str(), totalMsg.size()); - char* pSignature = - rocketmqSignature::spas_sign(totalMsg.c_str(), totalMsg.size(), sessionCredentials.getSecretKey().c_str()); - // char *pSignature = spas_sign(totalMsg.c_str(), - // sessionCredentials.getSecretKey().c_str()); - - if (pSignature != NULL) { - string signature(static_cast(pSignature)); - request.addExtField(SessionCredentials::Signature, signature); - request.addExtField(SessionCredentials::AccessKey, sessionCredentials.getAccessKey()); - request.addExtField(SessionCredentials::ONSChannelKey, sessionCredentials.getAuthChannel()); - rocketmqSignature::spas_mem_free(pSignature); - } else { - LOG_ERROR("signature for request failed"); - } -} -} // namespace rocketmq diff --git a/src/common/ClientRPCHook.h b/src/common/ClientRPCHook.h deleted file mode 100644 index 52fef3022..000000000 --- a/src/common/ClientRPCHook.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __CLIENTRPCHOOK_H__ -#define __CLIENTRPCHOOK_H__ - -#include "RemotingCommand.h" -#include "SessionCredentials.h" -namespace rocketmq { -class RPCHook { - public: - RPCHook() {} - virtual ~RPCHook() {} - virtual void doBeforeRequest(const string& remoteAddr, RemotingCommand& request) = 0; - virtual void doAfterResponse(RemotingCommand& request, RemotingCommand& response) = 0; -}; - -class ClientRPCHook : public RPCHook { - private: - SessionCredentials sessionCredentials; - - public: - ClientRPCHook(const SessionCredentials& session_credentials) : sessionCredentials(session_credentials) {} - virtual ~ClientRPCHook() {} - - virtual void doBeforeRequest(const string& remoteAddr, RemotingCommand& request); - - virtual void doAfterResponse(RemotingCommand& request, RemotingCommand& response) {} -}; -} // namespace rocketmq -#endif diff --git a/src/common/CommunicationMode.h b/src/common/CommunicationMode.h deleted file mode 100644 index b2cb60150..000000000 --- a/src/common/CommunicationMode.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __COMMUNICATIONMODE_H__ -#define __COMMUNICATIONMODE_H__ - -namespace rocketmq { -//createTopic(key, newTopic, queueNum, m_SessionCredentials); - } catch (MQException& e) { - LOG_ERROR(e.what()); - } -} - -int64 DefaultMQClient::earliestMsgStoreTime(const MQMessageQueue& mq) { - return getFactory()->earliestMsgStoreTime(mq, m_SessionCredentials); -} - -QueryResult DefaultMQClient::queryMessage(const string& topic, const string& key, int maxNum, int64 begin, int64 end) { - return getFactory()->queryMessage(topic, key, maxNum, begin, end, m_SessionCredentials); -} - -int64 DefaultMQClient::minOffset(const MQMessageQueue& mq) { - return getFactory()->minOffset(mq, m_SessionCredentials); -} - -int64 DefaultMQClient::maxOffset(const MQMessageQueue& mq) { - return getFactory()->maxOffset(mq, m_SessionCredentials); -} - -int64 DefaultMQClient::searchOffset(const MQMessageQueue& mq, uint64_t timestamp) { - return getFactory()->searchOffset(mq, timestamp, m_SessionCredentials); -} - -MQMessageExt* DefaultMQClient::viewMessage(const string& msgId) { - return getFactory()->viewMessage(msgId, m_SessionCredentials); -} - -vector DefaultMQClient::getTopicMessageQueueInfo(const string& topic) { - boost::weak_ptr weak_topicPublishInfo( - getFactory()->tryToFindTopicPublishInfo(topic, m_SessionCredentials)); - boost::shared_ptr topicPublishInfo(weak_topicPublishInfo.lock()); - if (topicPublishInfo) { - return topicPublishInfo->getMessageQueueList(); - } - THROW_MQEXCEPTION(MQClientException, "could not find MessageQueue Info of topic: [" + topic + "].", -1); -} - -void DefaultMQClient::start() { - if (getFactory() == NULL) { - m_clientFactory = MQClientManager::getInstance()->getMQClientFactory( - getMQClientId(), m_pullThreadNum, m_tcpConnectTimeout, m_tcpTransportTryLockTimeout, m_unitName, m_enableSsl, - m_sslPropertyFile); - } - LOG_INFO( - "MQClient " - "start,groupname:%s,clientID:%s,instanceName:%s,nameserveraddr:%s", - getGroupName().c_str(), getMQClientId().c_str(), getInstanceName().c_str(), getNamesrvAddr().c_str()); -} - -void DefaultMQClient::shutdown() { - m_clientFactory->shutdown(); - m_clientFactory = NULL; -} - -MQClientFactory* DefaultMQClient::getFactory() const { - return m_clientFactory; -} -void DefaultMQClient::setFactory(MQClientFactory* factory) { - m_clientFactory = factory; -} - -bool DefaultMQClient::isServiceStateOk() { - return m_serviceState == RUNNING; -} - -void DefaultMQClient::setLogLevel(elogLevel inputLevel) { - ALOG_ADAPTER->setLogLevel(inputLevel); -} - -elogLevel DefaultMQClient::getLogLevel() { - return ALOG_ADAPTER->getLogLevel(); -} - -void DefaultMQClient::setLogFileSizeAndNum(int fileNum, long perFileSize) { - ALOG_ADAPTER->setLogFileNumAndSize(fileNum, perFileSize); -} - -void DefaultMQClient::setTcpTransportPullThreadNum(int num) { - if (num > m_pullThreadNum) { - m_pullThreadNum = num; - } -} - -int DefaultMQClient::getTcpTransportPullThreadNum() const { - return m_pullThreadNum; -} - -void DefaultMQClient::setTcpTransportConnectTimeout(uint64_t timeout) { - m_tcpConnectTimeout = timeout; -} -uint64_t DefaultMQClient::getTcpTransportConnectTimeout() const { - return m_tcpConnectTimeout; -} - -void DefaultMQClient::setTcpTransportTryLockTimeout(uint64_t timeout) { - if (timeout < 1000) { - timeout = 1000; - } - m_tcpTransportTryLockTimeout = timeout / 1000; -} -uint64_t DefaultMQClient::getTcpTransportTryLockTimeout() const { - return m_tcpTransportTryLockTimeout; -} - -void DefaultMQClient::setUnitName(const std::string& unitName) { - m_unitName = unitName; -} -const string& DefaultMQClient::getUnitName() const { - return m_unitName; -} - -bool DefaultMQClient::getMessageTrace() const { - return m_messageTrace; -} - -void DefaultMQClient::setMessageTrace(bool mMessageTrace) { - m_messageTrace = mMessageTrace; -} - -void DefaultMQClient::setSessionCredentials(const string& input_accessKey, - const string& input_secretKey, - const string& input_onsChannel) { - m_SessionCredentials.setAccessKey(input_accessKey); - m_SessionCredentials.setSecretKey(input_secretKey); - m_SessionCredentials.setAuthChannel(input_onsChannel); -} - -const SessionCredentials& DefaultMQClient::getSessionCredentials() const { - return m_SessionCredentials; -} - -void DefaultMQClient::setEnableSsl(bool enableSsl) { - m_enableSsl = enableSsl; -} - -bool DefaultMQClient::getEnableSsl() const { - return m_enableSsl; -} - -void DefaultMQClient::setSslPropertyFile(const std::string& sslPropertyFile) { - m_sslPropertyFile = sslPropertyFile; -} - -const std::string& DefaultMQClient::getSslPropertyFile() const { - return m_sslPropertyFile; -} - -void DefaultMQClient::showClientConfigs() { - // LOG_WARN("*****************************************************************************"); - LOG_WARN("ClientID:%s", getMQClientId().c_str()); - LOG_WARN("GroupName:%s", m_GroupName.c_str()); - LOG_WARN("NameServer:%s", m_namesrvAddr.c_str()); - LOG_WARN("NameServerDomain:%s", m_namesrvDomain.c_str()); - LOG_WARN("NameSpace:%s", m_nameSpace.c_str()); - LOG_WARN("InstanceName:%s", m_instanceName.c_str()); - LOG_WARN("UnitName:%s", m_unitName.c_str()); - LOG_WARN("PullThreadNum:%d", m_pullThreadNum); - LOG_WARN("TcpConnectTimeout:%lld ms", m_tcpConnectTimeout); - LOG_WARN("TcpTransportTryLockTimeout:%lld s", m_tcpTransportTryLockTimeout); - LOG_WARN("EnableSsl:%s", m_enableSsl ? "true" : "false"); - LOG_WARN("SslPropertyFile:%s", m_sslPropertyFile.c_str()); - LOG_WARN("OpenMessageTrace:%s", m_messageTrace ? "true" : "false"); - // LOG_WARN("*****************************************************************************"); -} -// -#include "MQClientException.h" -#include "SubscriptionData.h" -#include "UtilAll.h" -namespace rocketmq { -//setSubString(SUB_ALL); - } else { - vector out; - UtilAll::Split(out, subString, "||"); - - if (out.empty()) { - THROW_MQEXCEPTION(MQClientException, "FilterAPI subString split error", -1); - } - - for (size_t i = 0; i < out.size(); i++) { - string tag = out[i]; - if (!tag.empty()) { - UtilAll::Trim(tag); - if (!tag.empty()) { - subscriptionData->putTagsSet(tag); - subscriptionData->putCodeSet(tag); - } - } - } - } - - return subscriptionData; - } -}; - -// -#include "MemoryOutputStream.h" -#include "big_endian.h" - -namespace rocketmq { -int64 InputStream::getNumBytesRemaining() { - int64 len = getTotalLength(); - - if (len >= 0) - len -= getPosition(); - - return len; -} - -char InputStream::readByte() { - char temp = 0; - read(&temp, 1); - return temp; -} - -bool InputStream::readBool() { - return readByte() != 0; -} - -short InputStream::readShortBigEndian() { - char temp[2]; - - if (read(temp, 2) == 2) { - short int v; - ReadBigEndian(temp, &v); - return v; - } - - return 0; -} - -int InputStream::readIntBigEndian() { - char temp[4]; - - if (read(temp, 4) == 4) { - int v; - ReadBigEndian(temp, &v); - return v; - } - return 0; -} - -int64 InputStream::readInt64BigEndian() { - char asBytes[8]; - uint64 asInt64; - - if (read(asBytes, 8) == 8) { - ReadBigEndian(asBytes, &asInt64); - return asInt64; - } - return 0; -} - -float InputStream::readFloatBigEndian() { - union { - int32 asInt; - float asFloat; - } n; - n.asInt = (int32)readIntBigEndian(); - return n.asFloat; -} - -double InputStream::readDoubleBigEndian() { - union { - int64 asInt; - double asDouble; - } n; - n.asInt = readInt64BigEndian(); - return n.asDouble; -} - -size_t InputStream::readIntoMemoryBlock(MemoryBlock& block, size_t numBytes) { - MemoryOutputStream mo(block, true); - return (size_t)mo.writeFromInputStream(*this, numBytes); -} - -//============================================================================== -void InputStream::skipNextBytes(int64 numBytesToSkip) { - if (numBytesToSkip > 0) { - const int skipBufferSize = (int)std::min(numBytesToSkip, (int64)16384); - char* temp = static_cast(std::malloc(skipBufferSize * sizeof(char))); - - while (numBytesToSkip > 0 && !isExhausted()) - numBytesToSkip -= read(temp, (int)std::min(numBytesToSkip, (int64)skipBufferSize)); - - std::free(temp); - } -} -} // namespace rocketmq diff --git a/src/common/InputStream.h b/src/common/InputStream.h deleted file mode 100644 index 8cf1b8482..000000000 --- a/src/common/InputStream.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef INPUTSTREAM_H_INCLUDED -#define INPUTSTREAM_H_INCLUDED - -#include "dataBlock.h" -//============================================================================== -/** The base class for streams that read data. - - Input and output streams are used throughout the library - subclasses can - override - some or all of the virtual functions to implement their behaviour. - - @see OutputStream, MemoryInputStream, BufferedInputStream, FileInputStream -*/ -namespace rocketmq { -class ROCKETMQCLIENT_API InputStream { - public: - /** Destructor. */ - virtual ~InputStream() {} - - //============================================================================== - /** Returns the total number of bytes available for reading in this stream. - - Note that this is the number of bytes available from the start of the - stream, not from the current position. - - If the size of the stream isn't actually known, this will return -1. - - @see getNumBytesRemaining - */ - virtual int64 getTotalLength() = 0; - - /** Returns the number of bytes available for reading, or a negative value if - the remaining length is not known. - @see getTotalLength - */ - int64 getNumBytesRemaining(); - - /** Returns true if the stream has no more data to read. */ - virtual bool isExhausted() = 0; - - //============================================================================== - /** Reads some data from the stream into a memory buffer. - - This is the only read method that subclasses actually need to implement, - as the - InputStream base class implements the other read methods in terms of this - one (although - it's often more efficient for subclasses to implement them directly). - - @param destBuffer the destination buffer for the data. This must not - be null. - @param maxBytesToRead the maximum number of bytes to read - make sure - the - memory block passed in is big enough to contain - this - many bytes. This value must not be negative. - - @returns the actual number of bytes that were read, which may be less - than - maxBytesToRead if the stream is exhausted before it gets that - far - */ - virtual int read(void* destBuffer, int maxBytesToRead) = 0; - - /** Reads a byte from the stream. - If the stream is exhausted, this will return zero. - @see OutputStream::writeByte - */ - virtual char readByte(); - - /** Reads a boolean from the stream. - The bool is encoded as a single byte - non-zero for true, 0 for false. - If the stream is exhausted, this will return false. - @see OutputStream::writeBool - */ - virtual bool readBool(); - - /** Reads two bytes from the stream as a little-endian 16-bit value. - If the next two bytes read are byte1 and byte2, this returns (byte2 | - (byte1 << 8)). - If the stream is exhausted partway through reading the bytes, this will - return zero. - @see OutputStream::writeShortBigEndian, readShort - */ - virtual short readShortBigEndian(); - - /** Reads four bytes from the stream as a big-endian 32-bit value. - - If the next four bytes are byte1 to byte4, this returns - (byte4 | (byte3 << 8) | (byte2 << 16) | (byte1 << 24)). - - If the stream is exhausted partway through reading the bytes, this will - return zero. - - @see OutputStream::writeIntBigEndian, readInt - */ - virtual int readIntBigEndian(); - - /** Reads eight bytes from the stream as a big-endian 64-bit value. - - If the next eight bytes are byte1 to byte8, this returns - (byte8 | (byte7 << 8) | (byte6 << 16) | (byte5 << 24) | (byte4 << 32) | - (byte3 << 40) | (byte2 << 48) | (byte1 << 56)). - - If the stream is exhausted partway through reading the bytes, this will - return zero. - - @see OutputStream::writeInt64BigEndian, readInt64 - */ - virtual int64 readInt64BigEndian(); - - /** Reads four bytes as a 32-bit floating point value. - The raw 32-bit encoding of the float is read from the stream as a - big-endian int. - If the stream is exhausted partway through reading the bytes, this will - return zero. - @see OutputStream::writeFloatBigEndian, readDoubleBigEndian - */ - virtual float readFloatBigEndian(); - - /** Reads eight bytes as a 64-bit floating point value. - The raw 64-bit encoding of the double is read from the stream as a - big-endian int64. - If the stream is exhausted partway through reading the bytes, this will - return zero. - @see OutputStream::writeDoubleBigEndian, readFloatBigEndian - */ - virtual double readDoubleBigEndian(); - - //==============================================================================whole - // stream and turn it into a string. - /** Reads from the stream and appends the data to a MemoryBlock. - - @param destBlock the block to append the data onto - @param maxNumBytesToRead if this is a positive value, it sets a limit - to the number - of bytes that will be read - if it's negative, - data - will be read until the stream is exhausted. - @returns the number of bytes that were added to the memory block - */ - virtual size_t readIntoMemoryBlock(MemoryBlock& destBlock, size_t maxNumBytesToRead = -1); - - //============================================================================== - /** Returns the offset of the next byte that will be read from the stream. - @see setPosition - */ - virtual int64 getPosition() = 0; - - /** Tries to move the current read position of the stream. - - The position is an absolute number of bytes from the stream's start. - - Some streams might not be able to do this, in which case they should do - nothing and return false. Others might be able to manage it by resetting - themselves and skipping to the correct position, although this is - obviously a bit slow. - - @returns true if the stream manages to reposition itself correctly - @see getPosition - */ - virtual bool setPosition(int64 newPosition) = 0; - - /** Reads and discards a number of bytes from the stream. - - Some input streams might implement this efficiently, but the base - class will just keep reading data until the requisite number of bytes - have been done. - */ - virtual void skipNextBytes(int64 numBytesToSkip); - - protected: - //============================================================================== - InputStream() {} -}; -} // namespace rocketmq -#endif // INPUTSTREAM_H_INCLUDED diff --git a/src/common/MQClientErrorContainer.cpp b/src/common/MQClientErrorContainer.cpp deleted file mode 100644 index a9e0e5d31..000000000 --- a/src/common/MQClientErrorContainer.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "MQClientErrorContainer.h" - -namespace rocketmq { - -thread_local std::string MQClientErrorContainer::t_err; - -void MQClientErrorContainer::setErr(const std::string& str) { - t_err = str; -} - -const std::string& MQClientErrorContainer::getErr() { - return t_err; -} - -} // namespace rocketmq diff --git a/src/common/MQClientErrorContainer.h b/src/common/MQClientErrorContainer.h deleted file mode 100644 index 3e09f0a71..000000000 --- a/src/common/MQClientErrorContainer.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __MQ_CLIENT_ERROR_CONTAINER_H__ -#define __MQ_CLIENT_ERROR_CONTAINER_H__ - -#include -#include - -namespace rocketmq { - -class MQClientErrorContainer { - public: - static void setErr(const std::string& str); - static const std::string& getErr(); - - private: - static thread_local std::string t_err; -}; - -} // namespace rocketmq - -#endif // __MQ_CLIENT_ERROR_CONTAINER_H__ diff --git a/src/common/MQVersion.cpp b/src/common/MQVersion.cpp deleted file mode 100644 index 5c0c78963..000000000 --- a/src/common/MQVersion.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "MQVersion.h" - -namespace rocketmq { -int MQVersion::s_CurrentVersion = MQVersion::V4_6_0; -std::string MQVersion::s_CurrentLanguage = "CPP"; - -//= HIGHER_VERSION) { - currentVersion = HIGHER_VERSION; - } - return RocketMQCPPClientVersion[currentVersion]; -} -// - -namespace rocketmq { -//(std::malloc(dataSize)); - memcpy(internalCopy, data, dataSize); - data = internalCopy; -} - -MemoryInputStream::~MemoryInputStream() { - std::free(internalCopy); -} - -int64 MemoryInputStream::getTotalLength() { - return (int64)dataSize; -} - -int MemoryInputStream::read(void* const buffer, const int howMany) { - const int num = std::min(howMany, (int)(dataSize - position)); - if (num <= 0) - return 0; - - memcpy((char*)buffer, (char*)data + position, (size_t)num); - position += (unsigned int)num; - return num; -} - -bool MemoryInputStream::isExhausted() { - return position >= dataSize; -} - -bool MemoryInputStream::setPosition(const int64 pos) { - if (pos < 0) - position = 0; - else - position = (int64)dataSize < pos ? (int64)dataSize : pos; - - return true; -} - -int64 MemoryInputStream::getPosition() { - return (int64)position; -} -} // namespace rocketmq diff --git a/src/common/MemoryInputStream.h b/src/common/MemoryInputStream.h deleted file mode 100644 index 2d3a365b3..000000000 --- a/src/common/MemoryInputStream.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MEMORYINPUTSTREAM_H_INCLUDED -#define MEMORYINPUTSTREAM_H_INCLUDED - -#include "InputStream.h" - -namespace rocketmq { -//============================================================================== -/** - Allows a block of data to be accessed as a stream. - - This can either be used to refer to a shared block of memory, or can make - its - own internal copy of the data when the MemoryInputStream is created. -*/ -class ROCKETMQCLIENT_API MemoryInputStream : public InputStream { - public: - //============================================================================== - /** Creates a MemoryInputStream. - - @param sourceData the block of data to use as the stream's - source - @param sourceDataSize the number of bytes in the source data - block - @param keepInternalCopyOfData if false, the stream will just keep a - pointer to - the source data, so this data shouldn't be - changed - for the lifetime of the stream; if this - parameter is - true, the stream will make its own copy of - the - data and use that. - */ - MemoryInputStream(const void* sourceData, size_t sourceDataSize, bool keepInternalCopyOfData); - - /** Creates a MemoryInputStream. - - @param data a block of data to use as the stream's - source - @param keepInternalCopyOfData if false, the stream will just keep a - reference to - the source data, so this data shouldn't be - changed - for the lifetime of the stream; if this - parameter is - true, the stream will make its own copy of - the - data and use that. - */ - MemoryInputStream(const MemoryBlock& data, bool keepInternalCopyOfData); - - /** Destructor. */ - ~MemoryInputStream(); - - /** Returns a pointer to the source data block from which this stream is - * reading. */ - const void* getData() const { return data; } - - /** Returns the number of bytes of source data in the block from which this - * stream is reading. */ - size_t getDataSize() const { return dataSize; } - - //============================================================================== - int64 getPosition(); - bool setPosition(int64 pos); - int64 getTotalLength(); - bool isExhausted(); - int read(void* destBuffer, int maxBytesToRead); - - private: - //============================================================================== - const void* data; - size_t dataSize, position; - char* internalCopy; - - void createInternalCopy(); -}; -} // namespace rocketmq -#endif diff --git a/src/common/MemoryOutputStream.cpp b/src/common/MemoryOutputStream.cpp deleted file mode 100644 index d7366f534..000000000 --- a/src/common/MemoryOutputStream.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "MemoryOutputStream.h" - -namespace rocketmq { -MemoryOutputStream::MemoryOutputStream(const size_t initialSize) - : blockToUse(&internalBlock), externalData(NULL), position(0), size(0), availableSize(0) { - internalBlock.setSize(initialSize, false); -} - -MemoryOutputStream::MemoryOutputStream(MemoryBlock& memoryBlockToWriteTo, const bool appendToExistingBlockContent) - : blockToUse(&memoryBlockToWriteTo), externalData(NULL), position(0), size(0), availableSize(0) { - if (appendToExistingBlockContent) - position = size = memoryBlockToWriteTo.getSize(); -} - -MemoryOutputStream::MemoryOutputStream(void* destBuffer, size_t destBufferSize) - : blockToUse(NULL), externalData(destBuffer), position(0), size(0), availableSize(destBufferSize) {} - -MemoryOutputStream::~MemoryOutputStream() { - trimExternalBlockSize(); -} - -void MemoryOutputStream::flush() { - trimExternalBlockSize(); -} - -void MemoryOutputStream::trimExternalBlockSize() { - if (blockToUse != &internalBlock && blockToUse != NULL) - blockToUse->setSize(size, false); -} - -void MemoryOutputStream::preallocate(const size_t bytesToPreallocate) { - if (blockToUse != NULL) - blockToUse->ensureSize(bytesToPreallocate + 1); -} - -void MemoryOutputStream::reset() { - position = 0; - size = 0; -} - -char* MemoryOutputStream::prepareToWrite(size_t numBytes) { - size_t storageNeeded = position + numBytes; - - char* data; - - if (blockToUse != NULL) { - if (storageNeeded >= (unsigned int)(blockToUse->getSize())) - blockToUse->ensureSize((storageNeeded + std::min(storageNeeded / 2, (size_t)(1024 * 1024)) + 32) & ~31u); - - data = static_cast(blockToUse->getData()); - } else { - if (storageNeeded > availableSize) - return NULL; - - data = static_cast(externalData); - } - - char* const writePointer = data + position; - position += numBytes; - size = std::max(size, position); - return writePointer; -} - -bool MemoryOutputStream::write(const void* const buffer, size_t howMany) { - if (howMany == 0) - return true; - - if (char* dest = prepareToWrite(howMany)) { - memcpy(dest, buffer, howMany); - return true; - } - - return false; -} - -bool MemoryOutputStream::writeRepeatedByte(uint8 byte, size_t howMany) { - if (howMany == 0) - return true; - - if (char* dest = prepareToWrite(howMany)) { - memset(dest, byte, howMany); - return true; - } - - return false; -} - -MemoryBlock MemoryOutputStream::getMemoryBlock() const { - return MemoryBlock(getData(), getDataSize()); -} - -const void* MemoryOutputStream::getData() const { - if (blockToUse == NULL) - return externalData; - - if ((unsigned int)blockToUse->getSize() > size) - static_cast(blockToUse->getData())[size] = 0; - - return blockToUse->getData(); -} - -bool MemoryOutputStream::setPosition(int64 newPosition) { - if (newPosition <= (int64)size) { - // ok to seek backwards - if (newPosition < 0) - position = 0; - else - position = (int64)size < newPosition ? size : newPosition; - return true; - } - - // can't move beyond the end of the stream.. - return false; -} - -int64 MemoryOutputStream::writeFromInputStream(InputStream& source, int64 maxNumBytesToWrite) { - // before writing from an input, see if we can preallocate to make it more - // efficient.. - int64 availableData = source.getTotalLength() - source.getPosition(); - - if (availableData > 0) { - if (maxNumBytesToWrite > availableData || maxNumBytesToWrite < 0) - maxNumBytesToWrite = availableData; - - if (blockToUse != NULL) - preallocate(blockToUse->getSize() + (size_t)maxNumBytesToWrite); - } - - return OutputStream::writeFromInputStream(source, maxNumBytesToWrite); -} - -OutputStream& operator<<(OutputStream& stream, const MemoryOutputStream& streamToRead) { - const size_t dataSize = streamToRead.getDataSize(); - - if (dataSize > 0) - stream.write(streamToRead.getData(), dataSize); - - return stream; -} -} // namespace rocketmq diff --git a/src/common/MemoryOutputStream.h b/src/common/MemoryOutputStream.h deleted file mode 100644 index cc3245a5e..000000000 --- a/src/common/MemoryOutputStream.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MEMORYOUTPUTSTREAM_H_INCLUDED -#define MEMORYOUTPUTSTREAM_H_INCLUDED - -#include "OutputStream.h" - -namespace rocketmq { -//============================================================================== -/** - Writes data to an internal memory buffer, which grows as required. - - The data that was written into the stream can then be accessed later as - a contiguous block of memory. -*/ -class ROCKETMQCLIENT_API MemoryOutputStream : public OutputStream { - public: - //============================================================================== - /** Creates an empty memory stream, ready to be written into. - @param initialSize the intial amount of capacity to allocate for writing - into - */ - MemoryOutputStream(size_t initialSize = 256); - - /** Creates a memory stream for writing into into a pre-existing MemoryBlock - object. - - Note that the destination block will always be larger than the amount of - data - that has been written to the stream, because the MemoryOutputStream keeps - some - spare capactity at its end. To trim the block's size down to fit the - actual - data, call flush(), or delete the MemoryOutputStream. - - @param memoryBlockToWriteTo the block into which new data will - be written. - @param appendToExistingBlockContent if this is true, the contents of - the block will be - kept, and new data will be - appended to it. If false, - the block will be cleared before - use - */ - MemoryOutputStream(MemoryBlock& memoryBlockToWriteTo, bool appendToExistingBlockContent); - - /** Creates a MemoryOutputStream that will write into a user-supplied, - fixed-size - block of memory. - When using this mode, the stream will write directly into this memory area - until - it's full, at which point write operations will fail. - */ - MemoryOutputStream(void* destBuffer, size_t destBufferSize); - - /** Destructor. - This will free any data that was written to it. - */ - ~MemoryOutputStream(); - - //============================================================================== - /** Returns a pointer to the data that has been written to the stream. - @see getDataSize - */ - const void* getData() const; - - /** Returns the number of bytes of data that have been written to the stream. - @see getData - */ - size_t getDataSize() const { return size; } - - /** Resets the stream, clearing any data that has been written to it so far. - */ - void reset(); - - /** Increases the internal storage capacity to be able to contain at least the - specified - amount of data without needing to be resized. - */ - void preallocate(size_t bytesToPreallocate); - - /** Returns a copy of the stream's data as a memory block. */ - MemoryBlock getMemoryBlock() const; - - //============================================================================== - /** If the stream is writing to a user-supplied MemoryBlock, this will trim - any excess - capacity off the block, so that its length matches the amount of actual - data that - has been written so far. - */ - void flush(); - - bool write(const void*, size_t); - int64 getPosition() { return (int64)position; } - bool setPosition(int64); - int64 writeFromInputStream(InputStream&, int64 maxNumBytesToWrite); - bool writeRepeatedByte(uint8 byte, size_t numTimesToRepeat); - - private: - //============================================================================== - MemoryBlock* const blockToUse; - MemoryBlock internalBlock; - void* externalData; - size_t position, size, availableSize; - - void trimExternalBlockSize(); - char* prepareToWrite(size_t); -}; - -/** Copies all the data that has been written to a MemoryOutputStream into - * another stream. */ -OutputStream& operator<<(OutputStream& stream, const MemoryOutputStream& streamToRead); -} // namespace rocketmq -#endif // MEMORYOUTPUTSTREAM_H_INCLUDED diff --git a/src/common/MessageAccessor.cpp b/src/common/MessageAccessor.cpp deleted file mode 100644 index 7b8714a9a..000000000 --- a/src/common/MessageAccessor.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "MessageAccessor.h" -#include -#include -#include "Logging.h" -#include "NameSpaceUtil.h" - -using namespace std; -namespace rocketmq { - -void MessageAccessor::withNameSpace(MQMessage& msg, const string& nameSpace) { - if (!nameSpace.empty()) { - string originTopic = msg.getTopic(); - string newTopic = nameSpace + NAMESPACE_SPLIT_FLAG + originTopic; - msg.setTopic(newTopic); - } -} - -void MessageAccessor::withoutNameSpaceSingle(MQMessageExt& msg, const string& nameSpace) { - if (!nameSpace.empty()) { - string originTopic = msg.getTopic(); - auto index = originTopic.find(nameSpace); - if (index != string::npos) { - string newTopic = - originTopic.substr(index + nameSpace.length() + NAMESPACE_SPLIT_FLAG.length(), originTopic.length()); - msg.setTopic(newTopic); - LOG_DEBUG("Find Name Space Prefix in MessageID[%s], OriginTopic[%s], NewTopic[%s]", msg.getMsgId().c_str(), - originTopic.c_str(), newTopic.c_str()); - } - } -} -void MessageAccessor::withoutNameSpace(vector& msgs, const string& nameSpace) { - if (!nameSpace.empty()) { - // for_each(msgs.cbegin(), msgs.cend(), bind2nd(&MessageAccessor::withoutNameSpaceSingle, nameSpace)); - for (auto iter = msgs.begin(); iter != msgs.end(); iter++) { - withoutNameSpaceSingle(*iter, nameSpace); - } - } -} -// -#include -#include "MQMessage.h" -#include "MQMessageExt.h" -namespace rocketmq { -//& msgs, const std::string& nameSpace); -}; - -//= ENDPOINT_PREFIX_LENGTH && nameServerAddr.find(ENDPOINT_PREFIX) != string::npos) { - return true; - } - return false; -} - -string NameSpaceUtil::formatNameServerURL(const string& nameServerAddr) { - auto index = nameServerAddr.find(ENDPOINT_PREFIX); - if (index != string::npos) { - LOG_DEBUG("Get Name Server from endpoint [%s]", - nameServerAddr.substr(ENDPOINT_PREFIX_LENGTH, nameServerAddr.length() - ENDPOINT_PREFIX_LENGTH).c_str()); - return nameServerAddr.substr(ENDPOINT_PREFIX_LENGTH, nameServerAddr.length() - ENDPOINT_PREFIX_LENGTH); - } - return nameServerAddr; -} - -string NameSpaceUtil::getNameSpaceFromNsURL(const string& nameServerAddr) { - LOG_DEBUG("Try to get Name Space from nameServerAddr [%s]", nameServerAddr.c_str()); - string nsAddr = formatNameServerURL(nameServerAddr); - string nameSpace; - auto index = nsAddr.find(NAMESPACE_PREFIX); - if (index != string::npos) { - auto indexDot = nsAddr.find('.'); - if (indexDot != string::npos && indexDot > index) { - nameSpace = nsAddr.substr(index, indexDot - index); - LOG_INFO("Get Name Space [%s] from nameServerAddr [%s]", nameSpace.c_str(), nameServerAddr.c_str()); - return nameSpace; - } - } - return ""; -} - -bool NameSpaceUtil::checkNameSpaceExistInNsURL(const string& nameServerAddr) { - if (!isEndPointURL(nameServerAddr)) { - LOG_DEBUG("This nameServerAddr [%s] is not a endpoint. should not get Name Space.", nameServerAddr.c_str()); - return false; - } - auto index = nameServerAddr.find(NAMESPACE_PREFIX); - if (index != string::npos) { - LOG_INFO("Find Name Space Prefix in nameServerAddr [%s]", nameServerAddr.c_str()); - return true; - } - return false; -} - -bool NameSpaceUtil::checkNameSpaceExistInNameServer(const string& nameServerAddr) { - auto index = nameServerAddr.find(NAMESPACE_PREFIX); - if (index != string::npos) { - LOG_INFO("Find Name Space Prefix in nameServerAddr [%s]", nameServerAddr.c_str()); - return true; - } - return false; -} - -string NameSpaceUtil::withoutNameSpace(const string& source, const string& nameSpace) { - if (!nameSpace.empty()) { - auto index = source.find(nameSpace); - if (index != string::npos) { - return source.substr(index + nameSpace.length() + NAMESPACE_SPLIT_FLAG.length(), source.length()); - } - } - return source; -} -string NameSpaceUtil::withNameSpace(const string& source, const string& ns) { - if (!ns.empty()) { - return ns + NAMESPACE_SPLIT_FLAG + source; - } - return source; -} - -bool NameSpaceUtil::hasNameSpace(const string& source, const string& ns) { - if (source.find(TraceConstant::TRACE_TOPIC) != string::npos) { - LOG_DEBUG("Find Trace Topic [%s]", source.c_str()); - return true; - } - if (!ns.empty() && source.length() >= ns.length() && source.find(ns) != string::npos) { - return true; - } - return false; -} -} // namespace rocketmq diff --git a/src/common/NameSpaceUtil.h b/src/common/NameSpaceUtil.h deleted file mode 100644 index 094fb49ca..000000000 --- a/src/common/NameSpaceUtil.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __NAMESPACEUTIL_H__ -#define __NAMESPACEUTIL_H__ - -#include - -using namespace std; - -static const string ENDPOINT_PREFIX = "http://"; -static const unsigned int ENDPOINT_PREFIX_LENGTH = ENDPOINT_PREFIX.length(); -static const string NAMESPACE_PREFIX = "MQ_INST_"; -static const int NAMESPACE_PREFIX_LENGTH = NAMESPACE_PREFIX.length(); -static const string NAMESPACE_SPLIT_FLAG = "%"; - -namespace rocketmq { -class NameSpaceUtil { - public: - static bool isEndPointURL(const string& nameServerAddr); - - static string formatNameServerURL(const string& nameServerAddr); - - static string getNameSpaceFromNsURL(const string& nameServerAddr); - - static bool checkNameSpaceExistInNsURL(const string& nameServerAddr); - - static bool checkNameSpaceExistInNameServer(const string& nameServerAddr); - - static string withNameSpace(const string& source, const string& ns); - - static string withoutNameSpace(const string& source, const string& ns); - - static bool hasNameSpace(const string& source, const string& ns); -}; - -} // namespace rocketmq -#endif //__NAMESPACEUTIL_H__ diff --git a/src/common/NamesrvConfig.h b/src/common/NamesrvConfig.h deleted file mode 100644 index 30bbb0e49..000000000 --- a/src/common/NamesrvConfig.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __NAMESRVCONFIG_H__ -#define __NAMESRVCONFIG_H__ - -#include -#include -#include "UtilAll.h" - -namespace rocketmq { -// -#include "big_endian.h" - -namespace rocketmq { -//============================================================================== -OutputStream::OutputStream() {} - -OutputStream::~OutputStream() {} - -//============================================================================== -bool OutputStream::writeBool(const bool b) { - return writeByte(b ? (char)1 : (char)0); -} - -bool OutputStream::writeByte(char byte) { - return write(&byte, 1); -} - -bool OutputStream::writeRepeatedByte(uint8 byte, size_t numTimesToRepeat) { - for (size_t i = 0; i < numTimesToRepeat; ++i) - if (!writeByte((char)byte)) - return false; - - return true; -} - -bool OutputStream::writeShortBigEndian(short value) { - unsigned short v; - char pShort[sizeof(v)]; - WriteBigEndian(pShort, (unsigned short)value); - return write(pShort, 2); -} - -bool OutputStream::writeIntBigEndian(int value) { - unsigned int v; - char pInt[sizeof(v)]; - WriteBigEndian(pInt, (unsigned int)value); - return write(pInt, 4); -} - -bool OutputStream::writeInt64BigEndian(int64 value) { - uint64 v; - char pUint64[sizeof(v)]; - WriteBigEndian(pUint64, (uint64)value); - return write(pUint64, 8); -} - -bool OutputStream::writeFloatBigEndian(float value) { - union { - int asInt; - float asFloat; - } n; - n.asFloat = value; - return writeIntBigEndian(n.asInt); -} - -bool OutputStream::writeDoubleBigEndian(double value) { - union { - int64 asInt; - double asDouble; - } n; - n.asDouble = value; - return writeInt64BigEndian(n.asInt); -} - -int64 OutputStream::writeFromInputStream(InputStream& source, int64 numBytesToWrite) { - if (numBytesToWrite < 0) - numBytesToWrite = std::numeric_limits::max(); - - int64 numWritten = 0; - - while (numBytesToWrite > 0) { - char buffer[8192]; - const int num = source.read(buffer, (int)std::min(numBytesToWrite, (int64)sizeof(buffer))); - - if (num <= 0) - break; - - write(buffer, (size_t)num); - - numBytesToWrite -= num; - numWritten += num; - } - - return numWritten; -} -} // namespace rocketmq diff --git a/src/common/OutputStream.h b/src/common/OutputStream.h deleted file mode 100644 index 6dd459ddb..000000000 --- a/src/common/OutputStream.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OUTPUTSTREAM_H_INCLUDED -#define OUTPUTSTREAM_H_INCLUDED - -#include "InputStream.h" -namespace rocketmq { -//============================================================================== -/** - The base class for streams that write data to some kind of destination. - - Input and output streams are used throughout the library - subclasses can - override - some or all of the virtual functions to implement their behaviour. - - @see InputStream, MemoryOutputStream, FileOutputStream -*/ -class ROCKETMQCLIENT_API OutputStream { - protected: - //============================================================================== - OutputStream(); - - public: - /** Destructor. - - Some subclasses might want to do things like call flush() during their - destructors. - */ - virtual ~OutputStream(); - - //============================================================================== - /** If the stream is using a buffer, this will ensure it gets written - out to the destination. */ - virtual void flush() = 0; - - /** Tries to move the stream's output position. - - Not all streams will be able to seek to a new position - this will return - false if it fails to work. - - @see getPosition - */ - virtual bool setPosition(int64 newPosition) = 0; - - /** Returns the stream's current position. - - @see setPosition - */ - virtual int64 getPosition() = 0; - - //============================================================================== - /** Writes a block of data to the stream. - - When creating a subclass of OutputStream, this is the only write method - that needs to be overloaded - the base class has methods for writing other - types of data which use this to do the work. - - @param dataToWrite the target buffer to receive the data. This must - not be null. - @param numberOfBytes the number of bytes to write. - @returns false if the write operation fails for some reason - */ - virtual bool write(const void* dataToWrite, size_t numberOfBytes) = 0; - - //============================================================================== - /** Writes a single byte to the stream. - @returns false if the write operation fails for some reason - @see InputStream::readByte - */ - virtual bool writeByte(char byte); - - /** Writes a boolean to the stream as a single byte. - This is encoded as a binary byte (not as text) with a value of 1 or 0. - @returns false if the write operation fails for some reason - @see InputStream::readBool - */ - virtual bool writeBool(bool boolValue); - - /** Writes a 16-bit integer to the stream in a big-endian byte order. - This will write two bytes to the stream: (value >> 8), then (value & - 0xff). - @returns false if the write operation fails for some reason - @see InputStream::readShortBigEndian - */ - virtual bool writeShortBigEndian(short value); - - /** Writes a 32-bit integer to the stream in a big-endian byte order. - @returns false if the write operation fails for some reason - @see InputStream::readIntBigEndian - */ - virtual bool writeIntBigEndian(int value); - - /** Writes a 64-bit integer to the stream in a big-endian byte order. - @returns false if the write operation fails for some reason - @see InputStream::readInt64BigEndian - */ - virtual bool writeInt64BigEndian(int64 value); - - /** Writes a 32-bit floating point value to the stream in a binary format. - The binary 32-bit encoding of the float is written as a big-endian int. - @returns false if the write operation fails for some reason - @see InputStream::readFloatBigEndian - */ - virtual bool writeFloatBigEndian(float value); - - /** Writes a 64-bit floating point value to the stream in a binary format. - The eight raw bytes of the double value are written out as a big-endian - 64-bit int. - @see InputStream::readDoubleBigEndian - @returns false if the write operation fails for some reason - */ - virtual bool writeDoubleBigEndian(double value); - - /** Writes a byte to the output stream a given number of times. - @returns false if the write operation fails for some reason - */ - virtual bool writeRepeatedByte(uint8 byte, size_t numTimesToRepeat); - - /** Reads data from an input stream and writes it to this stream. - - @param source the stream to read from - @param maxNumBytesToWrite the number of bytes to read from the stream - (if this is - less than zero, it will keep reading until the - input - is exhausted) - @returns the number of bytes written - */ - virtual int64 writeFromInputStream(InputStream& source, int64 maxNumBytesToWrite); -}; -} // namespace rocketmq - -#endif // OUTPUTSTREAM_H_INCLUDED diff --git a/src/common/PermName.cpp b/src/common/PermName.cpp deleted file mode 100644 index 7484cc675..000000000 --- a/src/common/PermName.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "PermName.h" -#include "UtilAll.h" - -namespace rocketmq { -// - -namespace rocketmq { -// - -namespace rocketmq { -//groupName = groupName; - consumeEnable = true; - consumeFromMinEnable = true; - consumeBroadcastEnable = true; - retryQueueNums = 1; - retryMaxTimes = 5; - brokerId = MASTER_ID; - whichBrokerWhenConsumeSlowly = 1; - } - - string groupName; - bool consumeEnable; - bool consumeFromMinEnable; - bool consumeBroadcastEnable; - int retryQueueNums; - int retryMaxTimes; - int brokerId; - int whichBrokerWhenConsumeSlowly; -}; - -// -#include "UtilAll.h" -#include "sync_http_client.h" -#include "url.h" - -namespace rocketmq { -TopAddressing::TopAddressing(const std::string& unitName) : m_unitName(unitName) {} - -TopAddressing::~TopAddressing() {} - -int TopAddressing::IsIPAddr(const char* sValue) { - if (NULL == sValue) - return -1; - - while (*sValue != '\0') { - if ((*sValue < '0' || *sValue > '9') && (*sValue != '.')) - return -1; - sValue++; - } - return 0; -} - -void TopAddressing::updateNameServerAddressList(const string& adds) { - boost::lock_guard lock(m_addrLock); - vector out; - UtilAll::Split(out, adds, ";"); - if (out.size() > 0) - m_addrs.clear(); - for (size_t i = 0; i < out.size(); i++) { - string addr = out[i]; - UtilAll::Trim(addr); - - list::iterator findit = find(m_addrs.begin(), m_addrs.end(), addr); - if (findit == m_addrs.end()) { - string hostName; - short portNumber; - if (UtilAll::SplitURL(addr, hostName, portNumber)) { - LOG_INFO("updateNameServerAddressList:%s", addr.c_str()); - m_addrs.push_back(addr); - } - } - } -} - -string TopAddressing::fetchNSAddr(const string& NSDomain) { - LOG_DEBUG("fetchNSAddr begin"); - string nsAddr = NSDomain.empty() ? WS_ADDR : NSDomain; - if (!m_unitName.empty()) { - nsAddr = nsAddr + "-" + m_unitName + "?nofix=1"; - LOG_INFO("NSAddr is:%s", nsAddr.c_str()); - } - - std::string tmp_nameservers; - std::string nameservers; - Url url_s(nsAddr); - LOG_INFO("fetchNSAddr protocol: %s, port: %s, host:%s, path:%s, ", url_s.protocol_.c_str(), url_s.port_.c_str(), - url_s.host_.c_str(), url_s.path_.c_str()); - - bool ret = SyncfetchNsAddr(url_s, tmp_nameservers); - if (ret) { - nameservers = clearNewLine(tmp_nameservers); - if (nameservers.empty()) { - LOG_ERROR("fetchNSAddr with domain is empty"); - } else { - updateNameServerAddressList(nameservers); - } - } else { - LOG_ERROR("fetchNSAddr with domain failed, connect failure or wrong response"); - } - - return nameservers; -} - -string TopAddressing::clearNewLine(const string& str) { - string newString = str; - size_t index = newString.find("\r"); - if (index != string::npos) { - return newString.substr(0, index); - } - - index = newString.find("\n"); - if (index != string::npos) { - return newString.substr(0, index); - } - - return newString; -} -} // namespace rocketmq diff --git a/src/common/TopAddressing.h b/src/common/TopAddressing.h deleted file mode 100644 index 07297dbad..000000000 --- a/src/common/TopAddressing.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __TOPADDRESSING_H__ -#define __TOPADDRESSING_H__ - -#include -#include -#include -#include -#include "Logging.h" -#include "UtilAll.h" - -namespace rocketmq { -class TopAddressing { - public: - TopAddressing(const std::string& unitName); - virtual ~TopAddressing(); - - public: - virtual string fetchNSAddr(const string& NSDomain); - - private: - string clearNewLine(const string& str); - void updateNameServerAddressList(const string& adds); - int IsIPAddr(const char* sValue); - - private: - boost::mutex m_addrLock; - list m_addrs; - string m_unitName; -}; -} // namespace rocketmq -#endif diff --git a/src/common/TopicConfig.cpp b/src/common/TopicConfig.cpp deleted file mode 100644 index 38ea138bd..000000000 --- a/src/common/TopicConfig.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "TopicConfig.h" -#include -#include -#include "PermName.h" - -namespace rocketmq { -//> m_topicName; - ss >> m_readQueueNums; - ss >> m_writeQueueNums; - ss >> m_perm; - - int type; - ss >> type; - m_topicFilterType = (TopicFilterType)type; - - return true; -} - -const string& TopicConfig::getTopicName() { - return m_topicName; -} - -void TopicConfig::setTopicName(const string& topicName) { - m_topicName = topicName; -} - -int TopicConfig::getReadQueueNums() { - return m_readQueueNums; -} - -void TopicConfig::setReadQueueNums(int readQueueNums) { - m_readQueueNums = readQueueNums; -} - -int TopicConfig::getWriteQueueNums() { - return m_writeQueueNums; -} - -void TopicConfig::setWriteQueueNums(int writeQueueNums) { - m_writeQueueNums = writeQueueNums; -} - -int TopicConfig::getPerm() { - return m_perm; -} - -void TopicConfig::setPerm(int perm) { - m_perm = perm; -} - -TopicFilterType TopicConfig::getTopicFilterType() { - return m_topicFilterType; -} - -void TopicConfig::setTopicFilterType(TopicFilterType topicFilterType) { - m_topicFilterType = topicFilterType; -} -// -#include "TopicFilterType.h" -#include "UtilAll.h" -namespace rocketmq { -// - -namespace rocketmq { -//(str); -} - -string UtilAll::bytes2string(const char* bytes, int len) { - if (bytes == NULL || len <= 0) { - return string(); - } - -#ifdef WIN32 - string buffer; - for (int i = 0; i < len; i++) { - char tmp[3]; - sprintf(tmp, "%02X", (unsigned char)bytes[i]); - buffer.append(tmp); - } - - return buffer; -#else - static const char hex_str[] = "0123456789ABCDEF"; - - char result[len * 2 + 1]; - - result[len * 2] = 0; - for (int i = 0; i < len; i++) { - result[i * 2 + 0] = hex_str[(bytes[i] >> 4) & 0x0F]; - result[i * 2 + 1] = hex_str[(bytes[i]) & 0x0F]; - } - - string buffer(result); - return buffer; -#endif -} - -bool UtilAll::SplitURL(const string& serverURL, string& addr, short& nPort) { - size_t pos = serverURL.find(':'); - if (pos == string::npos) { - return false; - } - - addr = serverURL.substr(0, pos); - if (0 == addr.compare("localhost")) { - addr = "127.0.0.1"; - } - - pos++; - string port = serverURL.substr(pos, serverURL.length() - pos); - nPort = atoi(port.c_str()); - if (nPort == 0) { - return false; - } - return true; -} - -int UtilAll::Split(vector& ret_, const string& strIn, const char sep) { - if (strIn.empty()) - return 0; - - string tmp; - string::size_type pos_begin = strIn.find_first_not_of(sep); - string::size_type comma_pos = 0; - - while (pos_begin != string::npos) { - comma_pos = strIn.find(sep, pos_begin); - if (comma_pos != string::npos) { - tmp = strIn.substr(pos_begin, comma_pos - pos_begin); - pos_begin = comma_pos + 1; - } else { - tmp = strIn.substr(pos_begin); - pos_begin = comma_pos; - } - - if (!tmp.empty()) { - ret_.push_back(tmp); - tmp.clear(); - } - } - return ret_.size(); -} -int UtilAll::Split(vector& ret_, const string& strIn, const string& sep) { - if (strIn.empty()) - return 0; - - string tmp; - string::size_type pos_begin = strIn.find_first_not_of(sep); - string::size_type comma_pos = 0; - - while (pos_begin != string::npos) { - comma_pos = strIn.find(sep, pos_begin); - if (comma_pos != string::npos) { - tmp = strIn.substr(pos_begin, comma_pos - pos_begin); - pos_begin = comma_pos + sep.length(); - } else { - tmp = strIn.substr(pos_begin); - pos_begin = comma_pos; - } - - if (!tmp.empty()) { - ret_.push_back(tmp); - tmp.clear(); - } - } - return ret_.size(); -} - -bool UtilAll::StringToInt32(const std::string& str, int32_t& out) { - out = 0; - if (str.empty()) { - return false; - } - - char* end = NULL; - errno = 0; - long l = strtol(str.c_str(), &end, 10); - /* Both checks are needed because INT_MAX == LONG_MAX is possible. */ - if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) - return false; - if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN)) - return false; - if (*end != '\0') - return false; - out = l; - return true; -} - -bool UtilAll::StringToInt64(const std::string& str, int64_t& val) { - char* endptr = NULL; - errno = 0; /* To distinguish success/failure after call */ - val = strtoll(str.c_str(), &endptr, 10); - - /* Check for various possible errors */ - if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) { - return false; - } - /*no digit was found Or Further characters after number*/ - if (endptr == str.c_str()) { - return false; - } - /*no digit was found Or Further characters after number*/ - if (*endptr != '\0') { - return false; - } - /* If we got here, strtol() successfully parsed a number */ - return true; -} - -string UtilAll::getLocalHostName() { - if (s_localHostName.empty()) { - // boost::system::error_code error; - // s_localHostName = boost::asio::ip::host_name(error); - - char name[1024]; - boost::system::error_code ec; - if (boost::asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) { - return std::string(); - } - s_localHostName.append(name, strlen(name)); - } - return s_localHostName; -} - -string UtilAll::getLocalAddress() { - if (s_localIpAddress.empty()) { - boost::asio::io_service io_service; - boost::asio::ip::tcp::resolver resolver(io_service); - boost::asio::ip::tcp::resolver::query query(getLocalHostName(), ""); - boost::system::error_code error; - boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query, error); - if (error) { - return ""; - } - boost::asio::ip::tcp::resolver::iterator end; // End marker. - boost::asio::ip::tcp::endpoint ep; - while (iter != end) { - ep = *iter++; - } - s_localIpAddress = ep.address().to_string(); - } - return s_localIpAddress; -} - -string UtilAll::getHomeDirectory() { -#ifndef WIN32 - char* homeEnv = getenv("HOME"); - string homeDir; - if (homeEnv == NULL) { - homeDir.append(getpwuid(getuid())->pw_dir); - } else { - homeDir.append(homeEnv); - } -#else - string homeDir(getenv("USERPROFILE")); -#endif - return homeDir; -} - -string UtilAll::getProcessName() { -#ifndef WIN32 - char buf[PATH_MAX + 1] = {0}; - int count = PATH_MAX + 1; - char procpath[PATH_MAX + 1] = {0}; - sprintf(procpath, "/proc/%d/exe", getpid()); - - if (access(procpath, F_OK) == -1) { - return ""; - } - - int retval = readlink(procpath, buf, count - 1); - if ((retval < 0 || retval >= count - 1)) { - return ""; - } - if (!strcmp(buf + retval - 10, " (deleted)")) - buf[retval - 10] = '\0'; // remove last " (deleted)" - else - buf[retval] = '\0'; - - char* process_name = strrchr(buf, '/'); - if (process_name) { - return std::string(process_name + 1); - } else { - return ""; - } -#else - TCHAR szFileName[MAX_PATH + 1]; - GetModuleFileName(NULL, szFileName, MAX_PATH + 1); - return std::string(szFileName); -#endif -} - -uint64_t UtilAll::currentTimeMillis() { - auto since_epoch = - std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); - return static_cast(since_epoch.count()); -} - -uint64_t UtilAll::currentTimeSeconds() { - auto since_epoch = - std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); - return static_cast(since_epoch.count()); -} - -bool UtilAll::deflate(std::string& input, std::string& out, int level) { - boost::iostreams::zlib_params zlibParams(level, boost::iostreams::zlib::deflated); - boost::iostreams::filtering_ostream compressingStream; - compressingStream.push(boost::iostreams::zlib_compressor(zlibParams)); - compressingStream.push(boost::iostreams::back_inserter(out)); - compressingStream << input; - boost::iostreams::close(compressingStream); - - return true; -} - -bool UtilAll::inflate(std::string& input, std::string& out) { - boost::iostreams::filtering_ostream decompressingStream; - decompressingStream.push(boost::iostreams::zlib_decompressor()); - decompressingStream.push(boost::iostreams::back_inserter(out)); - decompressingStream << input; - boost::iostreams::close(decompressingStream); - - return true; -} - -bool UtilAll::ReplaceFile(const std::string& from_path, const std::string& to_path) { -#ifdef WIN32 - // Try a simple move first. It will only succeed when |to_path| doesn't - // already exist. - if (::MoveFile(from_path.c_str(), to_path.c_str())) - return true; - // Try the full-blown replace if the move fails, as ReplaceFile will only - // succeed when |to_path| does exist. When writing to a network share, we may - // not be able to change the ACLs. Ignore ACL errors then - // (REPLACEFILE_IGNORE_MERGE_ERRORS). - if (::ReplaceFile(to_path.c_str(), from_path.c_str(), NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) { - return true; - } - return false; -#else - if (rename(from_path.c_str(), to_path.c_str()) == 0) - return true; - return false; -#endif -} - -std::map UtilAll::ReadProperties(const std::string& path) { - std::map property_map; - std::ifstream property_file; - property_file.open(path); - std::string line_buffer; - - if (property_file.is_open()) { - while (!property_file.eof()) { - std::getline(property_file, line_buffer); - std::size_t pos{0}; - pos = line_buffer.find('#'); - if (pos != string::npos) { - line_buffer = line_buffer.substr(0, pos); - } - if (line_buffer.empty()) { - continue; - } - pos = line_buffer.find('='); - if (pos != string::npos) { - std::string key = boost::trim_copy(line_buffer.substr(0, pos)); - std::string value = boost::trim_copy(line_buffer.substr(pos + 1)); - property_map[key] = value; - } - } - } - - return property_map; -} - -} // namespace rocketmq diff --git a/src/common/UtilAll.h b/src/common/UtilAll.h deleted file mode 100644 index b2bc1a397..000000000 --- a/src/common/UtilAll.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __UTILALL_H__ -#define __UTILALL_H__ - -#include -#include -#include -#include -#include -#ifndef WIN32 -#include -#include -#include -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "RocketMQClient.h" - -using namespace std; -namespace rocketmq { -// -inline void deleteAndZero(Type& pointer) { - delete pointer; - pointer = NULL; -} -#define EMPTY_STR_PTR(ptr) (ptr == NULL || ptr[0] == '\0') -#ifdef WIN32 -#define SIZET_FMT "%lu" -#else -#define SIZET_FMT "%zu" -#endif - -namespace detail { - -template -struct UseStdToString { - typedef std::false_type type; -}; - -template -struct UseStdToString { - typedef std::true_type type; -}; - -template -inline std::string to_string(const T& v, std::false_type) { - std::ostringstream stm; - stm << v; - return stm.str(); -} - -template -inline std::string to_string(const T& v, std::true_type) { - return std::to_string(v); -} - -template -inline std::string to_string(const T& v) { - return to_string(v, typename UseStdToString < T, - std::is_arithmetic::value && !std::is_same::type>::value > ::type{}); -} - -template <> -inline std::string to_string(const bool& v) { - return v ? "true" : "false"; -} - -} // namespace detail - -// - static string to_string(const T& n) { - return detail::to_string(n); - } - - static bool to_bool(std::string const& s) { return atoi(s.c_str()); } - - static bool SplitURL(const string& serverURL, string& addr, short& nPort); - static int Split(vector& ret_, const string& strIn, const char sep); - static int Split(vector& ret_, const string& strIn, const string& sep); - - static bool StringToInt32(const std::string& str, int32_t& out); - static bool StringToInt64(const std::string& str, int64_t& val); - - static string getLocalHostName(); - static string getLocalAddress(); - static string getHomeDirectory(); - - static string getProcessName(); - - static uint64_t currentTimeMillis(); - static uint64_t currentTimeSeconds(); - - static bool deflate(std::string& input, std::string& out, int level); - static bool inflate(std::string& input, std::string& out); - // Renames file |from_path| to |to_path|. Both paths must be on the same - // volume, or the function will fail. Destination file will be created - // if it doesn't exist. Prefer this function over Move when dealing with - // temporary files. On Windows it preserves attributes of the target file. - // Returns true on success. - // Returns false on failure.. - static bool ReplaceFile(const std::string& from_path, const std::string& to_path); - - static std::map ReadProperties(const std::string& path); - - private: - static std::string s_localHostName; - static std::string s_localIpAddress; -}; -// -#include -namespace rocketmq { - -const string Validators::validPatternStr = "^[a-zA-Z0-9_-]+$"; -const int Validators::CHARACTER_MAX_LENGTH = 255; -// CHARACTER_MAX_LENGTH) { - THROW_MQEXCEPTION(MQClientException, "the specified topic is longer than topic max length 255.", -1); - } - - if (topic == DEFAULT_TOPIC) { - THROW_MQEXCEPTION(MQClientException, "the topic[" + topic + "] is conflict with default topic.", -1); - } - - if (!regularExpressionMatcher(topic, validPatternStr)) { - string str; - str = "the specified topic[" + topic + "] contains illegal characters, allowing only" + validPatternStr; - - THROW_MQEXCEPTION(MQClientException, str.c_str(), -1); - } -} - -void Validators::checkGroup(const string& group) { - if (UtilAll::isBlank(group)) { - THROW_MQEXCEPTION(MQClientException, "the specified group is blank", -1); - } - - if (!regularExpressionMatcher(group, validPatternStr)) { - string str; - str = "the specified group[" + group + "] contains illegal characters, allowing only" + validPatternStr; - - THROW_MQEXCEPTION(MQClientException, str.c_str(), -1); - } - if ((int)group.length() > CHARACTER_MAX_LENGTH) { - THROW_MQEXCEPTION(MQClientException, "the specified group is longer than group max length 255.", -1); - } -} - -void Validators::checkMessage(const MQMessage& msg, int maxMessageSize) { - checkTopic(msg.getTopic()); - - string body = msg.getBody(); - // maxMessageSize) { - char info[256]; - sprintf(info, "the message body size over max value, MAX: %d", maxMessageSize); - THROW_MQEXCEPTION(MQClientException, info, -1); - } -} - -// -#include "MQClientException.h" -#include "MQMessage.h" -#include "UtilAll.h" -namespace rocketmq { -// -#include -#include "UtilAll.h" - -namespace rocketmq { -const char* VirtualEnvUtil::VIRTUAL_APPGROUP_PREFIX = "%%PROJECT_%s%%"; - -// -namespace rocketmq { -// -#include - -namespace rocketmq { - -BigEndianReader::BigEndianReader(const char* buf, size_t len) : ptr_(buf), end_(ptr_ + len) {} - -bool BigEndianReader::Skip(size_t len) { - if (ptr_ + len > end_) - return false; - ptr_ += len; - return true; -} - -bool BigEndianReader::ReadBytes(void* out, size_t len) { - if (ptr_ + len > end_) - return false; - memcpy(out, ptr_, len); - ptr_ += len; - return true; -} - -template -bool BigEndianReader::Read(T* value) { - if (ptr_ + sizeof(T) > end_) - return false; - ReadBigEndian(ptr_, value); - ptr_ += sizeof(T); - return true; -} - -bool BigEndianReader::ReadU8(uint8_t* value) { - return Read(value); -} - -bool BigEndianReader::ReadU16(uint16_t* value) { - return Read(value); -} - -bool BigEndianReader::ReadU32(uint32_t* value) { - return Read(value); -} - -bool BigEndianReader::ReadU64(uint64_t* value) { - return Read(value); -} - -BigEndianWriter::BigEndianWriter(char* buf, size_t len) : ptr_(buf), end_(ptr_ + len) {} - -bool BigEndianWriter::Skip(size_t len) { - if (ptr_ + len > end_) - return false; - ptr_ += len; - return true; -} - -bool BigEndianWriter::WriteBytes(const void* buf, size_t len) { - if (ptr_ + len > end_) - return false; - memcpy(ptr_, buf, len); - ptr_ += len; - return true; -} - -template -bool BigEndianWriter::Write(T value) { - if (ptr_ + sizeof(T) > end_) - return false; - WriteBigEndian(ptr_, value); - ptr_ += sizeof(T); - return true; -} - -bool BigEndianWriter::WriteU8(uint8_t value) { - return Write(value); -} - -bool BigEndianWriter::WriteU16(uint16_t value) { - return Write(value); -} - -bool BigEndianWriter::WriteU32(uint32_t value) { - return Write(value); -} - -bool BigEndianWriter::WriteU64(uint64_t value) { - return Write(value); -} - -} // namespace rocketmq diff --git a/src/common/big_endian.h b/src/common/big_endian.h deleted file mode 100644 index 57077ee65..000000000 --- a/src/common/big_endian.h +++ /dev/null @@ -1,102 +0,0 @@ - -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_BIG_ENDIAN_H_ -#define BASE_BIG_ENDIAN_H_ - -#include -#include - -namespace rocketmq { - -// Read an integer (signed or unsigned) from |buf| in Big Endian order. -// Note: this loop is unrolled with -O1 and above. -// NOTE(szym): glibc dns-canon.c use ntohs(*(uint16_t*)ptr) which is -// potentially unaligned. -// This would cause SIGBUS on ARMv5 or earlier and ARMv6-M. -template -inline void ReadBigEndian(const char buf[], T* out) { - *out = buf[0]; - for (size_t i = 1; i < sizeof(T); ++i) { - *out <<= 8; - // Must cast to uint8_t to avoid clobbering by sign extension. - *out |= static_cast(buf[i]); - } -} - -// Write an integer (signed or unsigned) |val| to |buf| in Big Endian order. -// Note: this loop is unrolled with -O1 and above. -template -inline void WriteBigEndian(char buf[], T val) { - for (size_t i = 0; i < sizeof(T); ++i) { - buf[sizeof(T) - i - 1] = static_cast(val & 0xFF); - val >>= 8; - } -} - -// Specializations to make clang happy about the (dead code) shifts above. -template <> -inline void ReadBigEndian(const char buf[], uint8_t* out) { - *out = buf[0]; -} - -template <> -inline void WriteBigEndian(char buf[], uint8_t val) { - buf[0] = static_cast(val); -} - -// Allows reading integers in network order (big endian) while iterating over -// an underlying buffer. All the reading functions advance the internal pointer. -class BigEndianReader { - public: - BigEndianReader(const char* buf, size_t len); - - const char* ptr() const { return ptr_; } - int remaining() const { return end_ - ptr_; } - - bool Skip(size_t len); - bool ReadBytes(void* out, size_t len); - bool ReadU8(uint8_t* value); - bool ReadU16(uint16_t* value); - bool ReadU32(uint32_t* value); - bool ReadU64(uint64_t* value); - - private: - // Hidden to promote type safety. - template - bool Read(T* v); - - const char* ptr_; - const char* end_; -}; - -// Allows writing integers in network order (big endian) while iterating over -// an underlying buffer. All the writing functions advance the internal pointer. -class BigEndianWriter { - public: - BigEndianWriter(char* buf, size_t len); - - char* ptr() const { return ptr_; } - int remaining() const { return end_ - ptr_; } - - bool Skip(size_t len); - bool WriteBytes(const void* buf, size_t len); - bool WriteU8(uint8_t value); - bool WriteU16(uint16_t value); - bool WriteU32(uint32_t value); - bool WriteU64(uint64_t value); - - private: - // Hidden to promote type safety. - template - bool Write(T v); - - char* ptr_; - char* end_; -}; - -} // namespace rocketmq - -#endif // BASE_BIG_ENDIAN_H_ diff --git a/src/common/dataBlock.cpp b/src/common/dataBlock.cpp deleted file mode 100644 index 87e98af49..000000000 --- a/src/common/dataBlock.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "dataBlock.h" -#include - -namespace rocketmq { - -MemoryBlock::MemoryBlock() : size(0), data(NULL) {} - -MemoryBlock::MemoryBlock(const int initialSize, const bool initialiseToZero) : size(0), data(NULL) { - if (initialSize > 0) { - size = initialSize; - data = static_cast(initialiseToZero ? std::calloc(initialSize, sizeof(char)) - : std::malloc(initialSize * sizeof(char))); - } -} - -MemoryBlock::MemoryBlock(const void* const dataToInitialiseFrom, const size_t sizeInBytes) - : size(sizeInBytes), data(NULL) { - if (size > 0) { - data = static_cast(std::malloc(size * sizeof(char))); - - if (dataToInitialiseFrom != NULL) - memcpy(data, dataToInitialiseFrom, size); - } -} - -MemoryBlock::MemoryBlock(const MemoryBlock& other) : size(other.size), data(NULL) { - if (size > 0) { - data = static_cast(std::malloc(size * sizeof(char))); - memcpy(data, other.data, size); - } -} - -MemoryBlock::MemoryBlock(MemoryBlock&& other) : size(other.size), data(other.data) { - other.size = 0; - other.data = NULL; -} - -MemoryBlock::~MemoryBlock() { - std::free(data); -} - -MemoryBlock& MemoryBlock::operator=(const MemoryBlock& other) { - if (this != &other) { - setSize(other.size, false); - memcpy(data, other.data, size); - } - - return *this; -} - -MemoryBlock& MemoryBlock::operator=(MemoryBlock&& other) { - if (this != &other) { - std::free(data); - - size = other.size; - data = other.data; - - other.size = 0; - other.data = NULL; - } - - return *this; -} - -//============================================================================== -bool MemoryBlock::operator==(const MemoryBlock& other) const { - return matches(other.data, other.size); -} - -bool MemoryBlock::operator!=(const MemoryBlock& other) const { - return !operator==(other); -} - -bool MemoryBlock::matches(const void* dataToCompare, int dataSize) const { - return size == dataSize && memcmp(data, dataToCompare, size) == 0; -} - -//============================================================================== -// this will resize the block to this size -void MemoryBlock::setSize(const int newSize, const bool initialiseToZero) { - if (size != newSize) { - if (newSize <= 0) { - reset(); - } else { - if (data != NULL) { - data = static_cast(data == NULL ? std::malloc(newSize * sizeof(char)) - : std::realloc(data, newSize * sizeof(char))); - - if (initialiseToZero && (newSize > size)) - memset(data + size, 0, newSize - size); - } else { - std::free(data); - data = static_cast(initialiseToZero ? std::calloc(newSize, sizeof(char)) - : std::malloc(newSize * sizeof(char))); - } - - size = newSize; - } - } -} - -void MemoryBlock::reset() { - std::free(data); - data = NULL; - size = 0; -} - -void MemoryBlock::ensureSize(const int minimumSize, const bool initialiseToZero) { - if (size < minimumSize) - setSize(minimumSize, initialiseToZero); -} - -//============================================================================== -void MemoryBlock::fillWith(const int value) { - memset(data, (int)value, size); -} - -void MemoryBlock::append(const void* const srcData, const int numBytes) { - if (numBytes > 0) { - const int oldSize = size; - setSize(size + numBytes); - memcpy(data + oldSize, srcData, numBytes); - } -} - -void MemoryBlock::replaceWith(const void* const srcData, const int numBytes) { - if (numBytes > 0) { - setSize(numBytes); - memcpy(data, srcData, numBytes); - } -} - -void MemoryBlock::insert(const void* const srcData, const int numBytes, int insertPosition) { - if (numBytes > 0) { - insertPosition = std::min(insertPosition, size); - const int trailingDataSize = size - insertPosition; - setSize(size + numBytes, false); - - if (trailingDataSize > 0) - memmove(data + insertPosition + numBytes, data + insertPosition, trailingDataSize); - - memcpy(data + insertPosition, srcData, numBytes); - } -} - -void MemoryBlock::removeSection(const int startByte, const int numBytesToRemove) { - if (startByte + numBytesToRemove >= size) { - setSize(startByte); - } else if (numBytesToRemove > 0) { - memmove(data + startByte, data + startByte + numBytesToRemove, size - (startByte + numBytesToRemove)); - - setSize(size - numBytesToRemove); - } -} - -void MemoryBlock::copyFrom(const void* const src, int offset, int num) { - const char* d = static_cast(src); - - if (offset < 0) { - d -= offset; - num += (size_t)-offset; - offset = 0; - } - - if ((size_t)offset + num > (unsigned int)size) - num = size - (size_t)offset; - - if (num > 0) - memcpy(data + offset, d, num); -} - -void MemoryBlock::copyTo(void* const dst, int offset, int num) const { - char* d = static_cast(dst); - - if (offset < 0) { - memset(d, 0, (size_t)-offset); - d -= offset; - num -= (size_t)-offset; - offset = 0; - } - - if ((size_t)offset + num > (unsigned int)size) { - const int newNum = (size_t)size - (size_t)offset; - memset(d + newNum, 0, num - newNum); - num = newNum; - } - - if (num > 0) - memcpy(d, data + offset, num); -} -} // namespace rocketmq diff --git a/src/common/dataBlock.h b/src/common/dataBlock.h deleted file mode 100644 index 7472927cd..000000000 --- a/src/common/dataBlock.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __DATABLOCK_H__ -#define __DATABLOCK_H__ - -#include -#include -#include -#include -#include - -#include "RocketMQClient.h" - -namespace rocketmq { - -class ROCKETMQCLIENT_API MemoryBlock { - public: - //============================================================================== - /** Create an uninitialised block with 0 size. */ - MemoryBlock(); - - /** Creates a memory block with a given initial size. - - @param initialSize the size of block to create - @param initialiseToZero whether to clear the memory or just leave it - uninitialised - */ - MemoryBlock(const int initialSize, bool initialiseToZero = false); - - /** Creates a memory block using a copy of a block of data. - - @param dataToInitialiseFrom some data to copy into this block - @param sizeInBytes how much space to use - */ - MemoryBlock(const void* dataToInitialiseFrom, size_t sizeInBytes); - - /** Creates a copy of another memory block. */ - MemoryBlock(const MemoryBlock&); - - MemoryBlock(MemoryBlock&&); - - /** Destructor. */ - ~MemoryBlock(); - - /** Copies another memory block onto this one. - This block will be resized and copied to exactly match the other one. - */ - MemoryBlock& operator=(const MemoryBlock&); - - MemoryBlock& operator=(MemoryBlock&&); - - //============================================================================== - /** Compares two memory blocks. - @returns true only if the two blocks are the same size and have identical - contents. - */ - bool operator==(const MemoryBlock& other) const; - - /** Compares two memory blocks. - @returns true if the two blocks are different sizes or have different - contents. - */ - bool operator!=(const MemoryBlock& other) const; - - //============================================================================== - /** Returns a void pointer to the data. - - Note that the pointer returned will probably become invalid when the - block is resized. - */ - char* getData() const { return data; } - - /** Returns a byte from the memory block. - This returns a reference, so you can also use it to set a byte. - */ - template - char& operator[](const Type offset) const { - return data[offset]; - } - - /** Returns true if the data in this MemoryBlock matches the raw bytes - * passed-in. */ - bool matches(const void* data, int dataSize) const; - - //============================================================================== - /** Returns the block's current allocated size, in bytes. */ - int getSize() const { return size; } - - /** Resizes the memory block. - - Any data that is present in both the old and new sizes will be retained. - When enlarging the block, the new space that is allocated at the end can - either be - cleared, or left uninitialised. - - @param newSize the new desired size for the block - @param initialiseNewSpaceToZero if the block gets enlarged, this - determines - whether to clear the new section or - just leave it - uninitialised - @see ensureSize - */ - void setSize(const int newSize, bool initialiseNewSpaceToZero = false); - - /** Increases the block's size only if it's smaller than a given size. - - @param minimumSize if the block is already bigger than - this size, no action - will be taken; otherwise it will be - increased to this size - @param initialiseNewSpaceToZero if the block gets enlarged, this - determines - whether to clear the new section or - just leave it - uninitialised - @see setSize - */ - void ensureSize(const int minimumSize, bool initialiseNewSpaceToZero = false); - - /** Frees all the blocks data, setting its size to 0. */ - void reset(); - - //============================================================================== - /** Fills the entire memory block with a repeated byte value. - This is handy for clearing a block of memory to zero. - */ - void fillWith(int valueToUse); - - /** Adds another block of data to the end of this one. - The data pointer must not be null. This block's size will be increased - accordingly. - */ - void append(const void* data, int numBytes); - - /** Resizes this block to the given size and fills its contents from the - supplied buffer. - The data pointer must not be null. - */ - void replaceWith(const void* data, int numBytes); - - /** Inserts some data into the block. - The dataToInsert pointer must not be null. This block's size will be - increased accordingly. - If the insert position lies outside the valid range of the block, it will - be clipped to - within the range before being used. - */ - void insert(const void* dataToInsert, int numBytesToInsert, int insertPosition); - - /** Chops out a section of the block. - - This will remove a section of the memory block and close the gap around - it, - shifting any subsequent data downwards and reducing the size of the block. - - If the range specified goes beyond the size of the block, it will be - clipped. - */ - void removeSection(int startByte, int numBytesToRemove); - - //============================================================================== - /** Copies data into this MemoryBlock from a memory address. - - @param srcData the memory location of the data to copy into - this block - @param destinationOffset the offset in this block at which the data - being copied should begin - @param numBytes how much to copy in (if this goes beyond the - size of the memory block, - it will be clipped so not to do anything - nasty) - */ - void copyFrom(const void* srcData, int destinationOffset, int numBytes); - - /** Copies data from this MemoryBlock to a memory address. - - @param destData the memory location to write to - @param sourceOffset the offset within this block from which the copied - data will be read - @param numBytes how much to copy (if this extends beyond the - limits of the memory block, - zeros will be used for that portion of the data) - */ - void copyTo(void* destData, int sourceOffset, int numBytes) const; - - private: - //============================================================================== - int size; - char* data; -}; -} // namespace rocketmq - -#endif diff --git a/src/common/noncopyable.h b/src/common/noncopyable.h deleted file mode 100644 index f52f98806..000000000 --- a/src/common/noncopyable.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __NONCOPYABLE_H__ -#define __NONCOPYABLE_H__ - -namespace rocketmq { - -class noncopyable { - protected: - noncopyable() = default; - ~noncopyable() = default; - - noncopyable(const noncopyable&) = delete; - noncopyable& operator=(const noncopyable&) = delete; -}; - -} // namespace rocketmq - -#endif //__NONCOPYABLE_H__ diff --git a/src/common/sync_http_client.cpp b/src/common/sync_http_client.cpp deleted file mode 100644 index 52137b395..000000000 --- a/src/common/sync_http_client.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Logging.h" -#include "url.h" - -using boost::asio::deadline_timer; -using boost::asio::ip::tcp; -using boost::lambda::var; - -namespace { -void check_deadline(deadline_timer* deadline, tcp::socket* socket, const boost::system::error_code& ec) { - // Check whether the deadline has passed. We compare the deadline against - // the current time since a new asynchronous operation may have moved the - // deadline before this actor had a chance to run. - if (deadline->expires_at() <= deadline_timer::traits_type::now()) { - // The deadline has passed. The socket is closed so that any outstanding - // asynchronous operations are cancelled. This allows the blocked - // connect(), read_line() or write_line() functions to return. - boost::system::error_code ignored_ec; - socket->close(ignored_ec); - - // There is no longer an active deadline. The expiry is set to positive - // infinity so that the actor takes no action until a new deadline is set. - deadline->expires_at(boost::posix_time::pos_infin); - } - - // Put the actor back to sleep. - deadline->async_wait(boost::bind(&check_deadline, deadline, socket, boost::asio::placeholders::error)); -} -} // namespace - -namespace rocketmq { -bool SyncfetchNsAddr(const Url& url_s, std::string& body) { - bool ret = true; - try { - boost::asio::io_service io_service; - // Get a list of endpoints corresponding to the server name. - tcp::resolver resolver(io_service); - tcp::resolver::query query(url_s.host_, url_s.port_); - tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); - boost::system::error_code ec = boost::asio::error::would_block; - deadline_timer deadline(io_service); - // TODO hardcode - boost::posix_time::seconds timeout(3); - deadline.expires_from_now(timeout); - // Try each endpoint until we successfully establish a connection. - tcp::socket socket(io_service); - boost::system::error_code deadline_ec; - check_deadline(&deadline, &socket, deadline_ec); - - boost::asio::async_connect(socket, endpoint_iterator, boost::lambda::var(ec) = boost::lambda::_1); - - do { - io_service.run_one(); - } while (ec == boost::asio::error::would_block); - - if (ec || !socket.is_open()) { - LOG_ERROR("socket connect failure, connect timeout or connect failure"); - return false; - } - - // Form the request. We specify the "Connection: close" header so that the - // server will close the socket after transmitting the response. This will - // allow us to treat all data up until the EOF as the content. - boost::asio::streambuf request; - std::ostream request_stream(&request); - request_stream << "GET " << url_s.path_ << " HTTP/1.0\r\n"; - request_stream << "Host: " << url_s.host_ << "\r\n"; - request_stream << "Accept: */*\r\n"; - request_stream << "Connection: close\r\n\r\n"; - - // Send the request. - boost::asio::write(socket, request); - - // Read the response status line. The response streambuf will automatically - // grow to accommodate the entire line. The growth may be limited by passing - // a maximum size to the streambuf constructor. - boost::asio::streambuf response; - boost::asio::read_until(socket, response, "\r\n"); - - // Check that response is OK. - std::istream response_stream(&response); - std::string http_version; - response_stream >> http_version; - unsigned int status_code; - response_stream >> status_code; - std::string status_message; - std::getline(response_stream, status_message); - if (!response_stream || http_version.substr(0, 5) != "HTTP/") { - LOG_INFO("Invalid response %s\n", status_message.c_str()); - return false; - } - - if (status_code != 200) { - LOG_INFO("Response returned with status code %d ", status_code); - return false; - } - - // Read the response headers, which are terminated by a blank line. - boost::asio::read_until(socket, response, "\r\n\r\n"); - - // Process the response headers. - std::string header; - while (std::getline(response_stream, header) && header != "\r") - ; - - // Write whatever content we already have to output. - if (response.size() > 0) { - boost::asio::streambuf::const_buffers_type cbt = response.data(); - body.clear(); - body.insert(body.begin(), boost::asio::buffers_begin(cbt), boost::asio::buffers_end(cbt)); - } - - // Read until EOF, writing data to output as we go. - boost::system::error_code error; - while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error)) - std::cout << &response; - if (error != boost::asio::error::eof) - throw boost::system::system_error(error); - - } catch (std::exception& e) { - LOG_ERROR("Exception: %s", e.what()); - ret = false; - } - - return ret; -} -} // namespace rocketmq diff --git a/src/common/sync_http_client.h b/src/common/sync_http_client.h deleted file mode 100644 index 1bf55fa18..000000000 --- a/src/common/sync_http_client.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef ROCKETMQ_CLIENT4CPP__SYNC_HTTP_CLIENT_H_ -#define ROCKETMQ_CLIENT4CPP__SYNC_HTTP_CLIENT_H_ - -#include - -namespace rocketmq { -class Url; - -extern bool SyncfetchNsAddr(const Url& url_s, std::string& body); - -} // namespace rocketmq - -#endif // ROCKETMQ_CLIENT4CPP__SYNC_HTTP_CLIENT_H_ diff --git a/src/common/url.cpp b/src/common/url.cpp deleted file mode 100644 index d63203721..000000000 --- a/src/common/url.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "url.h" -#include -#include -#include -#include -#include - -namespace rocketmq { - -Url::Url(const std::string& url_s) { - parse(url_s); -} - -void Url::parse(const std::string& url_s) { - const std::string prot_end("://"); - auto prot_i = std::search(url_s.begin(), url_s.end(), prot_end.begin(), prot_end.end()); - protocol_.reserve(std::distance(url_s.begin(), prot_i)); - std::transform(url_s.begin(), prot_i, std::back_inserter(protocol_), - std::ptr_fun(tolower)); // protocol is icase - - if (prot_i == url_s.end()) - return; - - std::advance(prot_i, prot_end.length()); - - auto path_i = find(prot_i, url_s.end(), ':'); - std::string::const_iterator path_end_i; - if (path_i == url_s.end()) { - // not include port, use default port - port_ = "80"; - path_i = std::find(prot_i, url_s.end(), '/'); - path_end_i = path_i; - } else { - auto port_i = find(path_i + 1, url_s.end(), '/'); - port_.insert(port_.begin(), path_i + 1, port_i); - path_end_i = path_i + port_.length() + 1; - } - - host_.reserve(distance(prot_i, path_i)); - std::transform(prot_i, path_i, std::back_inserter(host_), std::ptr_fun(tolower)); // host is icase} - - auto query_i = find(path_end_i, url_s.end(), '?'); - path_.assign(path_end_i, query_i); - if (query_i != url_s.end()) - ++query_i; - query_.assign(query_i, url_s.end()); -} - -} // namespace rocketmq diff --git a/src/common/url.h b/src/common/url.h deleted file mode 100644 index 62d86a232..000000000 --- a/src/common/url.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef ROCKETMQ_CLIENT4CPP_URL_HH_ -#define ROCKETMQ_CLIENT4CPP_URL_HH_ - -#include - -namespace rocketmq { -class Url { - public: - Url(const std::string& url_s); // omitted copy, ==, accessors, ... - - private: - void parse(const std::string& url_s); - - public: - std::string protocol_; - std::string host_; - std::string port_; - std::string path_; - std::string query_; -}; -} // namespace rocketmq -#endif // ROCKETMQ_CLIENT4CPP_URL_HH_ diff --git a/src/consumer/AllocateMQStrategy.h b/src/consumer/AllocateMQStrategy.h deleted file mode 100644 index e24966c84..000000000 --- a/src/consumer/AllocateMQStrategy.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __ALLOCATEMESSAGEQUEUESTRATEGY_H__ -#define __ALLOCATEMESSAGEQUEUESTRATEGY_H__ - -#include "Logging.h" -#include "MQClientException.h" -#include "MQMessageQueue.h" - -namespace rocketmq { -//& mqAll, - std::vector& cidAll, - std::vector& outReuslt) = 0; -}; - -//& mqAll, - std::vector& cidAll, - std::vector& outReuslt) { - outReuslt.clear(); - if (currentCID.empty()) { - THROW_MQEXCEPTION(MQClientException, "currentCID is empty", -1); - } - - if (mqAll.empty()) { - THROW_MQEXCEPTION(MQClientException, "mqAll is empty", -1); - } - - if (cidAll.empty()) { - THROW_MQEXCEPTION(MQClientException, "cidAll is empty", -1); - } - - int index = -1; - int cidAllSize = cidAll.size(); - for (int i = 0; i < cidAllSize; i++) { - if (cidAll[i] == currentCID) { - index = i; - break; - } - } - - if (index == -1) { - LOG_ERROR("could not find clientId from Broker"); - return; - } - - int mqAllSize = mqAll.size(); - int mod = mqAllSize % cidAllSize; - int averageSize = - mqAllSize <= cidAllSize ? 1 : (mod > 0 && index < mod ? mqAllSize / cidAllSize + 1 : mqAllSize / cidAllSize); - int startIndex = (mod > 0 && index < mod) ? index * averageSize : index * averageSize + mod; - int range = (std::min)(averageSize, mqAllSize - startIndex); - LOG_INFO( - "range is:%d, index is:%d, mqAllSize is:%d, averageSize is:%d, " - "startIndex is:%d", - range, index, mqAllSize, averageSize, startIndex); - //= 0) // example: range is:-1, index is:1, mqAllSize is:1, - // averageSize is:1, startIndex is:2 - { - for (int i = 0; i < range; i++) { - if ((startIndex + i) >= 0) { - outReuslt.push_back(mqAll.at((startIndex + i) % mqAllSize)); - } - } - } - } -}; - -// -#endif - -#include "ConsumeMsgService.h" -#include "DefaultMQPushConsumer.h" -#include "Logging.h" -#include "MessageAccessor.h" -#include "StatsServerManager.h" -#include "UtilAll.h" - -namespace rocketmq { - -//getMessageListenerType(); -} - -void ConsumeMessageConcurrentlyService::submitConsumeRequest(boost::weak_ptr pullRequest, - vector& msgs) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released"); - return; - } - if (request->isDropped()) { - LOG_INFO("Pull request for %s is dropped, which will be released in next re-balance.", - request->m_messageQueue.toString().c_str()); - return; - } - if (!request->isDropped() && !m_ioService.stopped()) { - m_ioService.post(boost::bind(&ConsumeMessageConcurrentlyService::ConsumeRequest, this, request, msgs)); - } else { - LOG_INFO("IOService stopped or Pull request for %s is dropped, will not post ConsumeRequest.", - request->m_messageQueue.toString().c_str()); - } -} - -void ConsumeMessageConcurrentlyService::submitConsumeRequestLater(boost::weak_ptr pullRequest, - vector& msgs, - int millis) { - if (msgs.empty()) { - return; - } - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released"); - return; - } - if (request->isDropped()) { - LOG_INFO("Pull request is set as dropped with mq:%s, need release in next rebalance.", - (request->m_messageQueue).toString().c_str()); - return; - } - if (!request->isDropped() && !m_ioService.stopped()) { - boost::asio::deadline_timer* t = - new boost::asio::deadline_timer(m_ioService, boost::posix_time::milliseconds(millis)); - t->async_wait( - boost::bind(&(ConsumeMessageConcurrentlyService::static_submitConsumeRequest), this, t, request, msgs)); - LOG_INFO("Submit Message to Consumer [%s] Later and Sleep [%d]ms.", (request->m_messageQueue).toString().c_str(), - millis); - } else { - LOG_INFO("IOService stopped or Pull request for %s is dropped, will not post delay ConsumeRequest.", - request->m_messageQueue.toString().c_str()); - } -} - -void ConsumeMessageConcurrentlyService::static_submitConsumeRequest(void* context, - boost::asio::deadline_timer* t, - boost::weak_ptr pullRequest, - vector& msgs) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released"); - return; - } - ConsumeMessageConcurrentlyService* pService = (ConsumeMessageConcurrentlyService*)context; - if (pService) { - pService->triggersubmitConsumeRequestLater(t, request, msgs); - } -} - -void ConsumeMessageConcurrentlyService::triggersubmitConsumeRequestLater(boost::asio::deadline_timer* t, - boost::weak_ptr pullRequest, - vector& msgs) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released"); - return; - } - submitConsumeRequest(request, msgs); - deleteAndZero(t); -} - -void ConsumeMessageConcurrentlyService::ConsumeRequest(boost::weak_ptr pullRequest, - vector& msgs) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released"); - return; - } - if (request->isDropped()) { - LOG_WARN("the pull request for %s Had been dropped before", request->m_messageQueue.toString().c_str()); - request->clearAllMsgs(); // add clear operation to avoid bad state when - // dropped pullRequest returns normal - return; - } - if (msgs.empty()) { - LOG_WARN("the msg of pull result is NULL,its mq:%s", (request->m_messageQueue).toString().c_str()); - return; - } - ConsumeMessageContext consumeMessageContext; - DefaultMQPushConsumerImpl* pConsumer = dynamic_cast(m_pConsumer); - std::string groupName = pConsumer->getGroupName(); - if (pConsumer) { - if (pConsumer->getMessageTrace() && pConsumer->hasConsumeMessageHook()) { - consumeMessageContext.setDefaultMQPushConsumer(pConsumer); - consumeMessageContext.setConsumerGroup(groupName); - consumeMessageContext.setMessageQueue(request->m_messageQueue); - consumeMessageContext.setMsgList(msgs); - consumeMessageContext.setSuccess(false); - consumeMessageContext.setNameSpace(pConsumer->getNameSpace()); - pConsumer->executeConsumeMessageHookBefore(&consumeMessageContext); - } - } - ConsumeStatus status = CONSUME_SUCCESS; - if (m_pMessageListener != NULL) { - resetRetryTopic(msgs); - request->setLastConsumeTimestamp(UtilAll::currentTimeMillis()); - LOG_DEBUG("=====Receive Messages,Topic[%s], MsgId[%s],Body[%s],RetryTimes[%d]", msgs[0].getTopic().c_str(), - msgs[0].getMsgId().c_str(), msgs[0].getBody().c_str(), msgs[0].getReconsumeTimes()); - if (m_pConsumer->isUseNameSpaceMode()) { - MessageAccessor::withoutNameSpace(msgs, m_pConsumer->getNameSpace()); - } - - if (pConsumer->getMessageTrace() && pConsumer->hasConsumeMessageHook()) { - // For open trace message, consume message one by one. - for (size_t i = 0; i < msgs.size(); ++i) { - LOG_DEBUG("=====Trace Receive Messages,Topic[%s], MsgId[%s],Body[%s],RetryTimes[%d]", - msgs[i].getTopic().c_str(), msgs[i].getMsgId().c_str(), msgs[i].getBody().c_str(), - msgs[i].getReconsumeTimes()); - std::vector msgInner; - msgInner.push_back(msgs[i]); - if (status != CONSUME_SUCCESS) { - // all the Messages behind should be set to failed. - status = RECONSUME_LATER; - consumeMessageContext.setMsgIndex(i); - consumeMessageContext.setStatus("RECONSUME_LATER"); - consumeMessageContext.setSuccess(false); - pConsumer->executeConsumeMessageHookAfter(&consumeMessageContext); - continue; - } - uint64 startTimeStamp = UtilAll::currentTimeMillis(); - try { - status = m_pMessageListener->consumeMessage(msgInner); - } catch (...) { - status = RECONSUME_LATER; - LOG_ERROR("Consumer's code is buggy. Un-caught exception raised"); - } - uint64 consumerRT = UtilAll::currentTimeMillis() - startTimeStamp; - StatsServerManager::getInstance()->getConsumeStatServer()->incConsumeRT(request->m_messageQueue.getTopic(), - groupName, consumerRT); - consumeMessageContext.setMsgIndex(i); // indicate message position,not support batch consumer - if (status == CONSUME_SUCCESS) { - consumeMessageContext.setStatus("CONSUME_SUCCESS"); - consumeMessageContext.setSuccess(true); - } else { - status = RECONSUME_LATER; - consumeMessageContext.setStatus("RECONSUME_LATER"); - consumeMessageContext.setSuccess(false); - } - pConsumer->executeConsumeMessageHookAfter(&consumeMessageContext); - } - } else { - uint64 startTimeStamp = UtilAll::currentTimeMillis(); - try { - status = m_pMessageListener->consumeMessage(msgs); - } catch (...) { - status = RECONSUME_LATER; - LOG_ERROR("Consumer's code is buggy. Un-caught exception raised"); - } - uint64 consumerRT = UtilAll::currentTimeMillis() - startTimeStamp; - StatsServerManager::getInstance()->getConsumeStatServer()->incConsumeRT(request->m_messageQueue.getTopic(), - groupName, consumerRT, msgs.size()); - } - } - - int ackIndex = -1; - switch (status) { - case CONSUME_SUCCESS: - ackIndex = msgs.size(); - break; - case RECONSUME_LATER: - ackIndex = -1; - break; - default: - break; - } - - std::vector localRetryMsgs; - switch (m_pConsumer->getMessageModel()) { - case BROADCASTING: { - // Note: broadcasting reconsume should do by application, as it has big - // affect to broker cluster - if (ackIndex != (int)msgs.size()) - LOG_WARN("BROADCASTING, the message consume failed, drop it:%s", (request->m_messageQueue).toString().c_str()); - break; - } - case CLUSTERING: { - // status consumer tps - if (ackIndex == -1) { - StatsServerManager::getInstance()->getConsumeStatServer()->incConsumeFailedTPS( - request->m_messageQueue.getTopic(), groupName, msgs.size()); - } else { - StatsServerManager::getInstance()->getConsumeStatServer()->incConsumeOKTPS(request->m_messageQueue.getTopic(), - groupName, msgs.size()); - } - - // send back msg to broker; - for (size_t i = ackIndex + 1; i < msgs.size(); i++) { - LOG_DEBUG("consume fail, MQ is:%s, its msgId is:%s, index is:" SIZET_FMT ", reconsume times is:%d", - (request->m_messageQueue).toString().c_str(), msgs[i].getMsgId().c_str(), i, - msgs[i].getReconsumeTimes()); - if (m_pConsumer->getConsumeType() == CONSUME_PASSIVELY) { - string brokerName = request->m_messageQueue.getBrokerName(); - if (m_pConsumer->isUseNameSpaceMode()) { - MessageAccessor::withNameSpace(msgs[i], m_pConsumer->getNameSpace()); - } - if (!m_pConsumer->sendMessageBack(msgs[i], 0, brokerName)) { - LOG_WARN("Send message back fail, MQ is:%s, its msgId is:%s, index is:%d, re-consume times is:%d", - (request->m_messageQueue).toString().c_str(), msgs[i].getMsgId().c_str(), i, - msgs[i].getReconsumeTimes()); - msgs[i].setReconsumeTimes(msgs[i].getReconsumeTimes() + 1); - localRetryMsgs.push_back(msgs[i]); - } - } - } - break; - } - default: - break; - } - - if (!localRetryMsgs.empty()) { - LOG_ERROR("Client side re-consume launched due to both message consuming and SDK send-back retry failure"); - for (std::vector::iterator itOrigin = msgs.begin(); itOrigin != msgs.end();) { - bool remove = false; - for (std::vector::iterator itRetry = localRetryMsgs.begin(); itRetry != localRetryMsgs.end(); - itRetry++) { - if (itRetry->getQueueOffset() == itOrigin->getQueueOffset()) { - remove = true; - break; - } - } - if (remove) { - itOrigin = msgs.erase(itOrigin); - } else { - itOrigin++; - } - } - } - // update offset - int64 offset = request->removeMessage(msgs); - if (offset >= 0) { - m_pConsumer->updateConsumeOffset(request->m_messageQueue, offset); - } else { - LOG_WARN("Note: Get local offset for mq:%s failed, may be it is updated before. skip..", - (request->m_messageQueue).toString().c_str()); - } - if (!localRetryMsgs.empty()) { - // submitConsumeRequest(request, localTryMsgs); - LOG_INFO("Send [%d ]messages back to mq:%s failed, call reconsume again after 1s.", localRetryMsgs.size(), - (request->m_messageQueue).toString().c_str()); - submitConsumeRequestLater(request, localRetryMsgs, 1000); - } -} // namespace rocketmq - -void ConsumeMessageConcurrentlyService::resetRetryTopic(vector& msgs) { - string groupTopic = UtilAll::getRetryTopic(m_pConsumer->getGroupName()); - for (size_t i = 0; i < msgs.size(); i++) { - MQMessageExt& msg = msgs[i]; - string retryTopic = msg.getProperty(MQMessage::PROPERTY_RETRY_TOPIC); - if (!retryTopic.empty() && groupTopic.compare(msg.getTopic()) == 0) { - msg.setTopic(retryTopic); - } - } -} - -// -#include -#include "ConsumeMessageContext.h" -#include "DefaultMQPushConsumerImpl.h" -#include "Logging.h" -#include "MQClientException.h" -#include "NameSpaceUtil.h" -#include "TraceConstant.h" -#include "TraceContext.h" -#include "TraceTransferBean.h" -#include "TraceUtil.h" -#include "UtilAll.h" -namespace rocketmq { - -class TraceMessageConsumeCallback : public SendCallback { - virtual void onSuccess(SendResult& sendResult) { - LOG_DEBUG("TraceMessageConsumeCallback, MsgId:[%s],OffsetMsgId[%s]", sendResult.getMsgId().c_str(), - sendResult.getOffsetMsgId().c_str()); - } - virtual void onException(MQException& e) {} -}; -static TraceMessageConsumeCallback* consumeTraceCallback = new TraceMessageConsumeCallback(); -std::string ConsumeMessageHookImpl::getHookName() { - return "RocketMQConsumeMessageHookImpl"; -} - -void ConsumeMessageHookImpl::executeHookBefore(ConsumeMessageContext* context) { - if (context == NULL || context->getMsgList().empty()) { - return; - } - TraceContext* traceContext = new TraceContext(); - context->setTraceContext(traceContext); - traceContext->setTraceType(SubBefore); - traceContext->setGroupName(NameSpaceUtil::withoutNameSpace(context->getConsumerGroup(), context->getNameSpace())); - std::vector beans; - - std::vector msgs = context->getMsgList(); - std::vector::iterator it = msgs.begin(); - for (; it != msgs.end(); ++it) { - std::string traceOn = it->getProperty(MQMessage::PROPERTY_TRACE_SWITCH); - if (traceOn != "" && traceOn == "false") { - continue; - } - TraceBean bean; - bean.setTopic((*it).getTopic()); - bean.setMsgId((*it).getMsgId()); - bean.setTags((*it).getTags()); - bean.setKeys((*it).getKeys()); - bean.setStoreHost((*it).getStoreHostString()); - bean.setStoreTime((*it).getStoreTimestamp()); - bean.setBodyLength((*it).getStoreSize()); - bean.setRetryTimes((*it).getReconsumeTimes()); - std::string regionId = (*it).getProperty(MQMessage::PROPERTY_MSG_REGION); - if (regionId.empty()) { - regionId = TraceConstant::DEFAULT_REDION; - } - traceContext->setRegionId(regionId); - traceContext->setTraceBean(bean); - } - traceContext->setTimeStamp(UtilAll::currentTimeMillis()); - - std::string topic = TraceConstant::TRACE_TOPIC + traceContext->getRegionId(); - - TraceTransferBean ben = TraceUtil::CovertTraceContextToTransferBean(traceContext); - MQMessage message(topic, ben.getTransData()); - message.setKeys(ben.getTransKey()); - - // send trace message async. - context->getDefaultMQPushConsumer()->submitSendTraceRequest(message, consumeTraceCallback); - return; -} - -void ConsumeMessageHookImpl::executeHookAfter(ConsumeMessageContext* context) { - if (context == NULL || context->getMsgList().empty()) { - return; - } - - std::shared_ptr subBeforeContext = context->getTraceContext(); - TraceContext subAfterContext; - subAfterContext.setTraceType(SubAfter); - subAfterContext.setRegionId(subBeforeContext->getRegionId()); - subAfterContext.setGroupName(subBeforeContext->getGroupName()); - subAfterContext.setRequestId(subBeforeContext->getRequestId()); - subAfterContext.setStatus(context->getSuccess()); - int costTime = static_cast(UtilAll::currentTimeMillis() - subBeforeContext->getTimeStamp()); - subAfterContext.setCostTime(costTime); - subAfterContext.setTraceBeanIndex(context->getMsgIndex()); - TraceBean bean = subBeforeContext->getTraceBeans()[subAfterContext.getTraceBeanIndex()]; - subAfterContext.setTraceBean(bean); - - std::string topic = TraceConstant::TRACE_TOPIC + subAfterContext.getRegionId(); - TraceTransferBean ben = TraceUtil::CovertTraceContextToTransferBean(&subAfterContext); - MQMessage message(topic, ben.getTransData()); - message.setKeys(ben.getTransKey()); - - // send trace message async. - context->getDefaultMQPushConsumer()->submitSendTraceRequest(message, consumeTraceCallback); - return; -} -} // namespace rocketmq diff --git a/src/consumer/ConsumeMessageHookImpl.h b/src/consumer/ConsumeMessageHookImpl.h deleted file mode 100644 index 30852bac0..000000000 --- a/src/consumer/ConsumeMessageHookImpl.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __ROCKETMQ_CONSUME_MESSAGE_RPC_HOOK_IMPL_H__ -#define __ROCKETMQ_CONSUME_MESSAGE_RPC_HOOK_IMPL_H__ - -#include -#include "ConsumeMessageContext.h" -#include "ConsumeMessageHook.h" -namespace rocketmq { -class ConsumeMessageHookImpl : public ConsumeMessageHook { - public: - virtual ~ConsumeMessageHookImpl() {} - virtual std::string getHookName(); - virtual void executeHookBefore(ConsumeMessageContext* context); - virtual void executeHookAfter(ConsumeMessageContext* context); -}; -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/src/consumer/ConsumeMessageOrderlyService.cpp b/src/consumer/ConsumeMessageOrderlyService.cpp deleted file mode 100644 index 9ee8b82c6..000000000 --- a/src/consumer/ConsumeMessageOrderlyService.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#if !defined(WIN32) && !defined(__APPLE__) -#include -#endif - -#include -#include "ConsumeMsgService.h" -#include "DefaultMQPushConsumer.h" -#include "Logging.h" -#include "Rebalance.h" -#include "StatsServerManager.h" -#include "UtilAll.h" - -namespace rocketmq { - -//getRebalance()->lockAll(); - - boost::system::error_code e; - t->expires_at(t->expires_at() + boost::posix_time::milliseconds(PullRequest::RebalanceLockInterval), e); - t->async_wait(boost::bind(&ConsumeMessageOrderlyService::lockMQPeriodically, this, ec, t)); -} - -void ConsumeMessageOrderlyService::unlockAllMQ() { - m_pConsumer->getRebalance()->unlockAll(false); -} - -bool ConsumeMessageOrderlyService::lockOneMQ(const MQMessageQueue& mq) { - return m_pConsumer->getRebalance()->lock(mq); -} - -void ConsumeMessageOrderlyService::stopThreadPool() { - m_shutdownInprogress = true; - m_ioService.stop(); - m_async_ioService.stop(); - m_async_service_thread->interrupt(); - m_async_service_thread->join(); - m_threadpool.join_all(); -} - -MessageListenerType ConsumeMessageOrderlyService::getConsumeMsgSerivceListenerType() { - return m_pMessageListener->getMessageListenerType(); -} - -void ConsumeMessageOrderlyService::submitConsumeRequest(boost::weak_ptr pullRequest, - vector& msgs) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released"); - return; - } - m_ioService.post(boost::bind(&ConsumeMessageOrderlyService::ConsumeRequest, this, request)); -} - -void ConsumeMessageOrderlyService::static_submitConsumeRequestLater(void* context, - boost::weak_ptr pullRequest, - bool tryLockMQ, - boost::asio::deadline_timer* t) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released"); - return; - } - LOG_INFO("submit consumeRequest later for mq:%s", request->m_messageQueue.toString().c_str()); - vector msgs; - ConsumeMessageOrderlyService* orderlyService = (ConsumeMessageOrderlyService*)context; - orderlyService->submitConsumeRequest(request, msgs); - if (tryLockMQ) { - orderlyService->lockOneMQ(request->m_messageQueue); - } - if (t) - deleteAndZero(t); -} - -void ConsumeMessageOrderlyService::ConsumeRequest(boost::weak_ptr pullRequest) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released"); - return; - } - bool bGetMutex = false; - boost::unique_lock lock(request->getPullRequestCriticalSection(), boost::try_to_lock); - if (!lock.owns_lock()) { - if (!lock.timed_lock(boost::get_system_time() + boost::posix_time::seconds(1))) { - LOG_ERROR("ConsumeRequest of:%s get timed_mutex timeout", request->m_messageQueue.toString().c_str()); - return; - } else { - bGetMutex = true; - } - } else { - bGetMutex = true; - } - if (!bGetMutex) { - // LOG_INFO("pullrequest of mq:%s consume inprogress", - // request->m_messageQueue.toString().c_str()); - return; - } - if (!request || request->isDropped()) { - LOG_WARN("the pull result is NULL or Had been dropped"); - request->clearAllMsgs(); // add clear operation to avoid bad state when - // dropped pullRequest returns normal - return; - } - - if (m_pMessageListener) { - if ((request->isLocked() && !request->isLockExpired()) || m_pConsumer->getMessageModel() == BROADCASTING) { - // DefaultMQPushConsumer* pConsumer = (DefaultMQPushConsumer*)m_pConsumer; - uint64_t beginTime = UtilAll::currentTimeMillis(); - bool continueConsume = true; - while (continueConsume) { - if ((UtilAll::currentTimeMillis() - beginTime) > m_MaxTimeConsumeContinuously) { - LOG_INFO("Continuely consume %s more than 60s, consume it 1s later", - request->m_messageQueue.toString().c_str()); - tryLockLaterAndReconsumeDelay(request, false, 1000); - break; - } - vector msgs; - // request->takeMessages(msgs, pConsumer->getConsumeMessageBatchMaxSize()); - request->takeMessages(msgs, 1); - if (!msgs.empty()) { - request->setLastConsumeTimestamp(UtilAll::currentTimeMillis()); - if (m_pConsumer->isUseNameSpaceMode()) { - MessageAccessor::withoutNameSpace(msgs, m_pConsumer->getNameSpace()); - } - ConsumeMessageContext consumeMessageContext; - DefaultMQPushConsumerImpl* pConsumer = dynamic_cast(m_pConsumer); - std::string groupName = pConsumer->getGroupName(); - if (pConsumer) { - if (pConsumer->getMessageTrace() && pConsumer->hasConsumeMessageHook()) { - consumeMessageContext.setDefaultMQPushConsumer(pConsumer); - consumeMessageContext.setConsumerGroup(pConsumer->getGroupName()); - consumeMessageContext.setMessageQueue(request->m_messageQueue); - consumeMessageContext.setMsgList(msgs); - consumeMessageContext.setSuccess(false); - consumeMessageContext.setNameSpace(pConsumer->getNameSpace()); - pConsumer->executeConsumeMessageHookBefore(&consumeMessageContext); - } - } - uint64 startTimeStamp = UtilAll::currentTimeMillis(); - ConsumeStatus consumeStatus = m_pMessageListener->consumeMessage(msgs); - - uint64 consumerRT = UtilAll::currentTimeMillis() - startTimeStamp; - StatsServerManager::getInstance()->getConsumeStatServer()->incConsumeRT(request->m_messageQueue.getTopic(), - groupName, consumerRT); - if (consumeStatus == RECONSUME_LATER) { - StatsServerManager::getInstance()->getConsumeStatServer()->incConsumeFailedTPS( - request->m_messageQueue.getTopic(), groupName, 1); - - if (pConsumer) { - consumeMessageContext.setMsgIndex(0); - consumeMessageContext.setStatus("RECONSUME_LATER"); - consumeMessageContext.setSuccess(false); - pConsumer->executeConsumeMessageHookAfter(&consumeMessageContext); - } - if (msgs[0].getReconsumeTimes() <= 15) { - msgs[0].setReconsumeTimes(msgs[0].getReconsumeTimes() + 1); - request->makeMessageToCosumeAgain(msgs); - continueConsume = false; - tryLockLaterAndReconsumeDelay(request, false, 1000); - } else { - // need change to reconsumer delay level and print log. - LOG_INFO("Local Consume failed [%d] times, change [%s] delay to 5s.", msgs[0].getReconsumeTimes(), - msgs[0].getMsgId().c_str()); - msgs[0].setReconsumeTimes(msgs[0].getReconsumeTimes() + 1); - continueConsume = false; - request->makeMessageToCosumeAgain(msgs); - tryLockLaterAndReconsumeDelay(request, false, 5000); - } - } else { - StatsServerManager::getInstance()->getConsumeStatServer()->incConsumeOKTPS( - request->m_messageQueue.getTopic(), groupName, 1); - if (pConsumer) { - consumeMessageContext.setMsgIndex(0); - consumeMessageContext.setStatus("CONSUME_SUCCESS"); - consumeMessageContext.setSuccess(true); - pConsumer->executeConsumeMessageHookAfter(&consumeMessageContext); - } - m_pConsumer->updateConsumeOffset(request->m_messageQueue, request->commit()); - } - } else { - continueConsume = false; - } - msgs.clear(); - if (m_shutdownInprogress) { - LOG_INFO("shutdown inprogress, break the consuming"); - return; - } - } - LOG_DEBUG("consume once exit of mq:%s", request->m_messageQueue.toString().c_str()); - } else { - LOG_ERROR("message queue:%s was not locked", request->m_messageQueue.toString().c_str()); - tryLockLaterAndReconsumeDelay(request, true, 1000); - } - } -} -void ConsumeMessageOrderlyService::tryLockLaterAndReconsumeDelay(boost::weak_ptr pullRequest, - bool tryLockMQ, - int millisDelay) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released"); - return; - } - int retryTimer = millisDelay; - if (millisDelay >= 30000 || millisDelay <= 1000) { - retryTimer = 1000; - } - boost::asio::deadline_timer* t = - new boost::asio::deadline_timer(m_async_ioService, boost::posix_time::milliseconds(retryTimer)); - t->async_wait( - boost::bind(&ConsumeMessageOrderlyService::static_submitConsumeRequestLater, this, request, tryLockMQ, t)); -} - -} // namespace rocketmq diff --git a/src/consumer/ConsumeMsgService.h b/src/consumer/ConsumeMsgService.h deleted file mode 100644 index 8745bae1d..000000000 --- a/src/consumer/ConsumeMsgService.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _CONSUMEMESSAGESERVICE_H_ -#define _CONSUMEMESSAGESERVICE_H_ - -#include -#include -#include -#include -#include -#include -#include "DefaultMQPushConsumerImpl.h" -#include "Logging.h" -#include "MQConsumer.h" -#include "MQMessageListener.h" -#include "PullRequest.h" -namespace rocketmq { -// class MQConsumer; -// request, vector& msgs) {} - virtual MessageListenerType getConsumeMsgSerivceListenerType() { return messageListenerDefaultly; } -}; - -class ConsumeMessageConcurrentlyService : public ConsumeMsgService { - public: - ConsumeMessageConcurrentlyService(MQConsumer*, int threadCount, MQMessageListener* msgListener); - virtual ~ConsumeMessageConcurrentlyService(); - virtual void start(); - virtual void shutdown(); - virtual void submitConsumeRequest(boost::weak_ptr request, vector& msgs); - virtual MessageListenerType getConsumeMsgSerivceListenerType(); - virtual void stopThreadPool(); - - void ConsumeRequest(boost::weak_ptr request, vector& msgs); - void submitConsumeRequestLater(boost::weak_ptr request, vector& msgs, int millis); - - void triggersubmitConsumeRequestLater(boost::asio::deadline_timer* t, - boost::weak_ptr pullRequest, - vector& msgs); - static void static_submitConsumeRequest(void* context, - boost::asio::deadline_timer* t, - boost::weak_ptr pullRequest, - vector& msgs); - - private: - void resetRetryTopic(vector& msgs); - - private: - MQConsumer* m_pConsumer; - MQMessageListener* m_pMessageListener; - boost::asio::io_service m_ioService; - boost::thread_group m_threadpool; - boost::asio::io_service::work m_ioServiceWork; -}; - -class ConsumeMessageOrderlyService : public ConsumeMsgService { - public: - ConsumeMessageOrderlyService(MQConsumer*, int threadCount, MQMessageListener* msgListener); - virtual ~ConsumeMessageOrderlyService(); - virtual void start(); - virtual void shutdown(); - virtual void submitConsumeRequest(boost::weak_ptr request, vector& msgs); - virtual void stopThreadPool(); - virtual MessageListenerType getConsumeMsgSerivceListenerType(); - - void boost_asio_work(); - // void tryLockLaterAndReconsume(boost::weak_ptr request, bool tryLockMQ); - void tryLockLaterAndReconsumeDelay(boost::weak_ptr request, bool tryLockMQ, int millisDelay); - static void static_submitConsumeRequestLater(void* context, - boost::weak_ptr request, - bool tryLockMQ, - boost::asio::deadline_timer* t); - void ConsumeRequest(boost::weak_ptr request); - void lockMQPeriodically(boost::system::error_code& ec, boost::asio::deadline_timer* t); - void unlockAllMQ(); - bool lockOneMQ(const MQMessageQueue& mq); - - private: - MQConsumer* m_pConsumer; - bool m_shutdownInprogress; - MQMessageListener* m_pMessageListener; - uint64_t m_MaxTimeConsumeContinuously; - boost::asio::io_service m_ioService; - boost::thread_group m_threadpool; - boost::asio::io_service::work m_ioServiceWork; - boost::asio::io_service m_async_ioService; - boost::scoped_ptr m_async_service_thread; -}; - -//start(); -} - -void DefaultMQPullConsumer::shutdown() { - impl->shutdown(); -} -std::string DefaultMQPullConsumer::version() { - std::string versions = impl->getClientVersionString(); - /*versions.append(", PROTOCOL VERSION: ") - .append(MQVersion::GetVersionDesc(MQVersion::s_CurrentVersion)) - .append(", LANGUAGE: ") - .append(MQVersion::s_CurrentLanguage);*/ - return versions; -} -// start mqclient set -const std::string& DefaultMQPullConsumer::getNamesrvAddr() const { - return impl->getNamesrvAddr(); -} - -void DefaultMQPullConsumer::setNamesrvAddr(const std::string& namesrvAddr) { - impl->setNamesrvAddr(namesrvAddr); -} - -const std::string& DefaultMQPullConsumer::getNamesrvDomain() const { - return impl->getNamesrvDomain(); -} - -void DefaultMQPullConsumer::setNamesrvDomain(const std::string& namesrvDomain) { - impl->setNamesrvDomain(namesrvDomain); -} -void DefaultMQPullConsumer::setSessionCredentials(const std::string& accessKey, - const std::string& secretKey, - const std::string& accessChannel) { - impl->setSessionCredentials(accessKey, secretKey, accessChannel); -} - -const SessionCredentials& DefaultMQPullConsumer::getSessionCredentials() const { - return impl->getSessionCredentials(); -} -const std::string& DefaultMQPullConsumer::getInstanceName() const { - return impl->getInstanceName(); -} - -void DefaultMQPullConsumer::setInstanceName(const std::string& instanceName) { - impl->setInstanceName(instanceName); -} - -const std::string& DefaultMQPullConsumer::getNameSpace() const { - return impl->getNameSpace(); -} - -void DefaultMQPullConsumer::setNameSpace(const std::string& nameSpace) { - impl->setNameSpace(nameSpace); -} -const std::string& DefaultMQPullConsumer::getGroupName() const { - return impl->getGroupName(); -} - -void DefaultMQPullConsumer::setGroupName(const std::string& groupName) { - impl->setGroupName(groupName); -} - -void DefaultMQPullConsumer::setEnableSsl(bool enableSsl) { - impl->setEnableSsl(enableSsl); -} - -bool DefaultMQPullConsumer::getEnableSsl() const { - return impl->getEnableSsl(); -} - -void DefaultMQPullConsumer::setSslPropertyFile(const std::string& sslPropertyFile) { - impl->setSslPropertyFile(sslPropertyFile); -} - -const std::string& DefaultMQPullConsumer::getSslPropertyFile() const { - return impl->getSslPropertyFile(); -} - -void DefaultMQPullConsumer::setLogLevel(elogLevel inputLevel) { - impl->setLogLevel(inputLevel); -} - -elogLevel DefaultMQPullConsumer::getLogLevel() { - return impl->getLogLevel(); -} -void DefaultMQPullConsumer::setLogFileSizeAndNum(int fileNum, long perFileSize) { - impl->setLogFileSizeAndNum(fileNum, perFileSize); -} - -// void DefaultMQPullConsumer::setUnitName(std::string unitName) { -// impl->setUnitName(unitName); -// } -// const std::string& DefaultMQPullConsumer::getUnitName() const { -// return impl->getUnitName(); -// } - -void DefaultMQPullConsumer::fetchSubscribeMessageQueues(const std::string& topic, std::vector& mqs) { - impl->fetchSubscribeMessageQueues(topic, mqs); -} - -void DefaultMQPullConsumer::persistConsumerOffset() { - impl->persistConsumerOffset(); -} -void DefaultMQPullConsumer::persistConsumerOffsetByResetOffset() { - impl->persistConsumerOffsetByResetOffset(); -} -void DefaultMQPullConsumer::updateTopicSubscribeInfo(const std::string& topic, std::vector& info) { - impl->updateTopicSubscribeInfo(topic, info); -} - -ConsumeFromWhere DefaultMQPullConsumer::getConsumeFromWhere() { - return impl->getConsumeFromWhere(); -} -void DefaultMQPullConsumer::getSubscriptions(std::vector& subData) { - impl->getSubscriptions(subData); -} -void DefaultMQPullConsumer::updateConsumeOffset(const MQMessageQueue& mq, int64 offset) { - impl->updateConsumeOffset(mq, offset); -} -void DefaultMQPullConsumer::removeConsumeOffset(const MQMessageQueue& mq) { - impl->removeConsumeOffset(mq); -} - -void DefaultMQPullConsumer::registerMessageQueueListener(const std::string& topic, MQueueListener* pListener) { - impl->registerMessageQueueListener(topic, pListener); -} -PullResult DefaultMQPullConsumer::pull(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums) { - return impl->pull(mq, subExpression, offset, maxNums); -} -void DefaultMQPullConsumer::pull(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums, - PullCallback* pPullCallback) { - impl->pull(mq, subExpression, offset, maxNums, pPullCallback); -} - -PullResult DefaultMQPullConsumer::pullBlockIfNotFound(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums) { - return impl->pullBlockIfNotFound(mq, subExpression, offset, maxNums); -} -void DefaultMQPullConsumer::pullBlockIfNotFound(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums, - PullCallback* pPullCallback) { - impl->pullBlockIfNotFound(mq, subExpression, offset, maxNums, pPullCallback); -} - -int64 DefaultMQPullConsumer::fetchConsumeOffset(const MQMessageQueue& mq, bool fromStore) { - return impl->fetchConsumeOffset(mq, fromStore); -} - -void DefaultMQPullConsumer::fetchMessageQueuesInBalance(const std::string& topic, std::vector mqs) { - impl->fetchMessageQueuesInBalance(topic, mqs); -} -void DefaultMQPullConsumer::persistConsumerOffset4PullConsumer(const MQMessageQueue& mq) { - // impl->persistConsumerOffsetByResetOffset(mq); -} - -//registerConsumer(this); - if (!registerOK) { - m_serviceState = CREATE_JUST; - THROW_MQEXCEPTION( - MQClientException, - "The cousumer group[" + getGroupName() + "] has been created before, specify another name please.", -1); - } - - //load(); - } catch (MQClientException& e) { - bStartFailed = true; - errorMsg = std::string(e.what()); - } - - getFactory()->start(); - m_serviceState = RUNNING; - if (bStartFailed) { - shutdown(); - THROW_MQEXCEPTION(MQClientException, errorMsg, -1); - } - break; - } - case RUNNING: - case START_FAILED: - case SHUTDOWN_ALREADY: - break; - default: - break; - } -} - -void DefaultMQPullConsumerImpl::shutdown() { - switch (m_serviceState) { - case RUNNING: { - LOG_INFO("DefaultMQPullConsumerImpl:%s shutdown", m_GroupName.c_str()); - persistConsumerOffset(); - getFactory()->unregisterConsumer(this); - getFactory()->shutdown(); - m_serviceState = SHUTDOWN_ALREADY; - break; - } - case SHUTDOWN_ALREADY: - case CREATE_JUST: - break; - default: - break; - } -} - -bool DefaultMQPullConsumerImpl::sendMessageBack(MQMessageExt& msg, int delayLevel, string& brokerName) { - return true; -} - -void DefaultMQPullConsumerImpl::fetchSubscribeMessageQueues(const string& topic, vector& mqs) { - mqs.clear(); - try { - const string localTopic = NameSpaceUtil::withNameSpace(topic, getNameSpace()); - getFactory()->fetchSubscribeMessageQueues(localTopic, mqs, getSessionCredentials()); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - } -} - -void DefaultMQPullConsumerImpl::updateTopicSubscribeInfo(const string& topic, vector& info) {} - -void DefaultMQPullConsumerImpl::registerMessageQueueListener(const string& topic, MQueueListener* pListener) { - m_registerTopics.insert(topic); - if (pListener) { - m_pMessageQueueListener = pListener; - } -} - -PullResult DefaultMQPullConsumerImpl::pull(const MQMessageQueue& mq, - const string& subExpression, - int64 offset, - int maxNums) { - return pullSyncImpl(mq, subExpression, offset, maxNums, false); -} - -void DefaultMQPullConsumerImpl::pull(const MQMessageQueue& mq, - const string& subExpression, - int64 offset, - int maxNums, - PullCallback* pPullCallback) { - pullAsyncImpl(mq, subExpression, offset, maxNums, false, pPullCallback); -} - -PullResult DefaultMQPullConsumerImpl::pullBlockIfNotFound(const MQMessageQueue& mq, - const string& subExpression, - int64 offset, - int maxNums) { - return pullSyncImpl(mq, subExpression, offset, maxNums, true); -} - -void DefaultMQPullConsumerImpl::pullBlockIfNotFound(const MQMessageQueue& mq, - const string& subExpression, - int64 offset, - int maxNums, - PullCallback* pPullCallback) { - pullAsyncImpl(mq, subExpression, offset, maxNums, true, pPullCallback); -} - -PullResult DefaultMQPullConsumerImpl::pullSyncImpl(const MQMessageQueue& mq, - const string& subExpression, - int64 offset, - int maxNums, - bool block) { - if (offset < 0) - THROW_MQEXCEPTION(MQClientException, "offset < 0", -1); - - if (maxNums <= 0) - THROW_MQEXCEPTION(MQClientException, "maxNums <= 0", -1); - - // pSData(FilterAPI::buildSubscriptionData(mq.getTopic(), subExpression)); - - int timeoutMillis = block ? 1000 * 30 : 1000 * 10; - - try { - unique_ptr pullResult(m_pPullAPIWrapper->pullKernelImpl(mq, // 1 - pSData->getSubString(), // 2 - 0L, // 3 - offset, // 4 - maxNums, // 5 - sysFlag, // 6 - 0, // 7 - 1000 * 20, // 8 - timeoutMillis, // 9 - ComMode_SYNC, // 10 - NULL, //processPullResult(mq, pullResult.get(), pSData.get()); - if (m_useNameSpaceMode) { - MessageAccessor::withoutNameSpace(pr.msgFoundList, m_nameSpace); - } - return pr; - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - } - return PullResult(BROKER_TIMEOUT); -} - -void DefaultMQPullConsumerImpl::pullAsyncImpl(const MQMessageQueue& mq, - const string& subExpression, - int64 offset, - int maxNums, - bool block, - PullCallback* pPullCallback) { - if (offset < 0) - THROW_MQEXCEPTION(MQClientException, "offset < 0", -1); - - if (maxNums <= 0) - THROW_MQEXCEPTION(MQClientException, "maxNums <= 0", -1); - - if (!pPullCallback) - THROW_MQEXCEPTION(MQClientException, "pPullCallback is null", -1); - - // pSData(FilterAPI::buildSubscriptionData(mq.getTopic(), subExpression)); - - int timeoutMillis = block ? 1000 * 30 : 1000 * 10; - - // pullResult(m_pPullAPIWrapper->pullKernelImpl(mq, // 1 - pSData->getSubString(), // 2 - 0L, // 3 - offset, // 4 - maxNums, // 5 - sysFlag, // 6 - 0, // 7 - 1000 * 20, // 8 - timeoutMillis, // 9 - ComMode_ASYNC, // 10 - pPullCallback, getSessionCredentials(), &arg)); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - } -} - -void DefaultMQPullConsumerImpl::subscriptionAutomatically(const string& topic) { - SubscriptionData* pSdata = m_pRebalance->getSubscriptionData(topic); - if (pSdata == NULL) { - unique_ptr subscriptionData(FilterAPI::buildSubscriptionData(topic, SUB_ALL)); - m_pRebalance->setSubscriptionData(topic, subscriptionData.release()); - } -} - -void DefaultMQPullConsumerImpl::updateConsumeOffset(const MQMessageQueue& mq, int64 offset) { - m_pOffsetStore->updateOffset(mq, offset); -} - -void DefaultMQPullConsumerImpl::removeConsumeOffset(const MQMessageQueue& mq) { - m_pOffsetStore->removeOffset(mq); -} - -int64 DefaultMQPullConsumerImpl::fetchConsumeOffset(const MQMessageQueue& mq, bool fromStore) { - return m_pOffsetStore->readOffset(mq, fromStore ? READ_FROM_STORE : MEMORY_FIRST_THEN_STORE, getSessionCredentials()); -} - -void DefaultMQPullConsumerImpl::persistConsumerOffset() { - /*As do not execute rebalance for pullConsumer now, requestTable is always - empty - map requestTable = - m_pRebalance->getPullRequestTable(); - map::iterator it = requestTable.begin(); - vector mqs; - for (; it != requestTable.end(); ++it) - { - if (it->second) - { - mqs.push_back(it->first); - } - } - m_pOffsetStore->persistAll(mqs);*/ -} - -void DefaultMQPullConsumerImpl::persistConsumerOffsetByResetOffset() {} - -void DefaultMQPullConsumerImpl::persistConsumerOffset4PullConsumer(const MQMessageQueue& mq) { - if (isServiceStateOk()) { - m_pOffsetStore->persist(mq, getSessionCredentials()); - } -} - -void DefaultMQPullConsumerImpl::fetchMessageQueuesInBalance(const string& topic, vector mqs) {} - -void DefaultMQPullConsumerImpl::checkConfig() { - string groupname = getGroupName(); - // check consumerGroup - Validators::checkGroup(groupname); - - // consumerGroup - if (!groupname.compare(DEFAULT_CONSUMER_GROUP)) { - THROW_MQEXCEPTION(MQClientException, "consumerGroup can not equal DEFAULT_CONSUMER", -1); - } - - if (getMessageModel() != BROADCASTING && getMessageModel() != CLUSTERING) { - THROW_MQEXCEPTION(MQClientException, "messageModel is valid ", -1); - } -} - -void DefaultMQPullConsumerImpl::doRebalance() {} - -void DefaultMQPullConsumerImpl::copySubscription() { - set::iterator it = m_registerTopics.begin(); - for (; it != m_registerTopics.end(); ++it) { - unique_ptr subscriptionData(FilterAPI::buildSubscriptionData((*it), SUB_ALL)); - m_pRebalance->setSubscriptionData((*it), subscriptionData.release()); - } -} - -ConsumeType DefaultMQPullConsumerImpl::getConsumeType() { - return CONSUME_ACTIVELY; -} - -ConsumeFromWhere DefaultMQPullConsumerImpl::getConsumeFromWhere() { - return CONSUME_FROM_LAST_OFFSET; -} - -void DefaultMQPullConsumerImpl::getSubscriptions(vector& result) { - set::iterator it = m_registerTopics.begin(); - for (; it != m_registerTopics.end(); ++it) { - SubscriptionData ms(*it, SUB_ALL); - result.push_back(ms); - } -} - -bool DefaultMQPullConsumerImpl::producePullMsgTask(boost::weak_ptr pullRequest) { - return true; -} - -Rebalance* DefaultMQPullConsumerImpl::getRebalance() const { - return NULL; -} -// we should deal with name space before producer start. -bool DefaultMQPullConsumerImpl::dealWithNameSpace() { - string ns = getNameSpace(); - if (ns.empty()) { - string nsAddr = getNamesrvAddr(); - if (!NameSpaceUtil::checkNameSpaceExistInNameServer(nsAddr)) { - return true; - } - ns = NameSpaceUtil::getNameSpaceFromNsURL(nsAddr); - // reset namespace - setNameSpace(ns); - } - // reset group name - if (!NameSpaceUtil::hasNameSpace(getGroupName(), ns)) { - string fullGID = NameSpaceUtil::withNameSpace(getGroupName(), ns); - setGroupName(fullGID); - } - set tmpTopics; - for (auto iter = m_registerTopics.begin(); iter != m_registerTopics.end(); iter++) { - string topic = *iter; - if (!NameSpaceUtil::hasNameSpace(topic, ns)) { - LOG_INFO("Update Subscribe Topic[%s] with NameSpace:%s", topic.c_str(), ns.c_str()); - topic = NameSpaceUtil::withNameSpace(topic, ns); - // let other mode to known, the name space model opened. - m_useNameSpaceMode = true; - } - tmpTopics.insert(topic); - } - m_registerTopics.swap(tmpTopics); - return true; -} -// -#include -#include "MQConsumer.h" -#include "MQMessageQueue.h" -#include "MQueueListener.h" -#include "RocketMQClient.h" - -namespace rocketmq { -class Rebalance; -class SubscriptionData; -class OffsetStore; -class PullAPIWrapper; -class ConsumerRunningInfo; -//& mqs); - virtual void doRebalance(); - virtual void persistConsumerOffset(); - virtual void persistConsumerOffsetByResetOffset(); - virtual void updateTopicSubscribeInfo(const std::string& topic, std::vector& info); - virtual ConsumeType getConsumeType(); - virtual ConsumeFromWhere getConsumeFromWhere(); - virtual void getSubscriptions(std::vector&); - virtual void updateConsumeOffset(const MQMessageQueue& mq, int64 offset); - virtual void removeConsumeOffset(const MQMessageQueue& mq); - virtual bool producePullMsgTask(boost::weak_ptr pullRequest); - virtual Rebalance* getRebalance() const; - // mqs); - - // temp persist consumer offset interface, only valid with - // RemoteBrokerOffsetStore, updateConsumeOffset should be called before. - void persistConsumerOffset4PullConsumer(const MQMessageQueue& mq); - - private: - void checkConfig(); - void copySubscription(); - bool dealWithNameSpace(); - - PullResult pullSyncImpl(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums, - bool block); - - void pullAsyncImpl(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums, - bool block, - PullCallback* pPullCallback); - - void subscriptionAutomatically(const std::string& topic); - - private: - std::set m_registerTopics; - - MQueueListener* m_pMessageQueueListener; - OffsetStore* m_pOffsetStore; - Rebalance* m_pRebalance; - PullAPIWrapper* m_pPullAPIWrapper; -}; -// -#include "DefaultMQPushConsumerImpl.h" - -namespace rocketmq { - -DefaultMQPushConsumer::DefaultMQPushConsumer(const std::string& groupName) { - impl = new DefaultMQPushConsumerImpl(groupName); -} - -DefaultMQPushConsumer::~DefaultMQPushConsumer() { - delete impl; -} -void DefaultMQPushConsumer::start() { - impl->start(); -} - -void DefaultMQPushConsumer::shutdown() { - impl->shutdown(); -} -std::string DefaultMQPushConsumer::version() { - std::string versions = impl->getClientVersionString(); - /*versions.append(", PROTOCOL VERSION: ") - .append(MQVersion::GetVersionDesc(MQVersion::s_CurrentVersion)) - .append(", LANGUAGE: ") - .append(MQVersion::s_CurrentLanguage);*/ - return versions; -} -// ConsumeType DefaultMQPushConsumer::getConsumeType() { -// return impl->getConsumeType(); -//} - -ConsumeFromWhere DefaultMQPushConsumer::getConsumeFromWhere() { - return impl->getConsumeFromWhere(); -} -void DefaultMQPushConsumer::setConsumeFromWhere(ConsumeFromWhere consumeFromWhere) { - impl->setConsumeFromWhere(consumeFromWhere); -} - -void DefaultMQPushConsumer::registerMessageListener(MQMessageListener* pMessageListener) { - impl->registerMessageListener(pMessageListener); -} -MessageListenerType DefaultMQPushConsumer::getMessageListenerType() { - return impl->getMessageListenerType(); -} -void DefaultMQPushConsumer::subscribe(const std::string& topic, const std::string& subExpression) { - impl->subscribe(topic, subExpression); -} - -void DefaultMQPushConsumer::setConsumeMessageBatchMaxSize(int consumeMessageBatchMaxSize) { - impl->setConsumeMessageBatchMaxSize(consumeMessageBatchMaxSize); -} -int DefaultMQPushConsumer::getConsumeMessageBatchMaxSize() const { - return impl->getConsumeMessageBatchMaxSize(); -} - -/* - set consuming thread count, default value is cpu cores -*/ -void DefaultMQPushConsumer::setConsumeThreadCount(int threadCount) { - impl->setConsumeThreadCount(threadCount); -} -int DefaultMQPushConsumer::getConsumeThreadCount() const { - return impl->getConsumeThreadCount(); -} -void DefaultMQPushConsumer::setMaxReconsumeTimes(int maxReconsumeTimes) { - impl->setMaxReconsumeTimes(maxReconsumeTimes); -} -int DefaultMQPushConsumer::getMaxReconsumeTimes() const { - return impl->getMaxReconsumeTimes(); -} - -/* - set pullMsg thread count, default value is cpu cores -*/ -void DefaultMQPushConsumer::setPullMsgThreadPoolCount(int threadCount) { - impl->setPullMsgThreadPoolCount(threadCount); -} -int DefaultMQPushConsumer::getPullMsgThreadPoolCount() const { - return impl->getPullMsgThreadPoolCount(); -} - -/* - set max cache msg size perQueue in memory if consumer could not consume msgs - immediately - default maxCacheMsgSize perQueue is 1000, set range is:1~65535 -*/ -void DefaultMQPushConsumer::setMaxCacheMsgSizePerQueue(int maxCacheSize) { - impl->setMaxCacheMsgSizePerQueue(maxCacheSize); -} -int DefaultMQPushConsumer::getMaxCacheMsgSizePerQueue() const { - return impl->getMaxCacheMsgSizePerQueue(); -} - -MessageModel DefaultMQPushConsumer::getMessageModel() const { - return impl->getMessageModel(); -} -void DefaultMQPushConsumer::setMessageModel(MessageModel messageModel) { - impl->setMessageModel(messageModel); -} - -const std::string& DefaultMQPushConsumer::getNamesrvAddr() const { - return impl->getNamesrvAddr(); -} - -void DefaultMQPushConsumer::setNamesrvAddr(const std::string& namesrvAddr) { - impl->setNamesrvAddr(namesrvAddr); -} - -const std::string& DefaultMQPushConsumer::getNamesrvDomain() const { - return impl->getNamesrvDomain(); -} - -void DefaultMQPushConsumer::setNamesrvDomain(const std::string& namesrvDomain) { - impl->setNamesrvDomain(namesrvDomain); -} -void DefaultMQPushConsumer::setSessionCredentials(const std::string& accessKey, - const std::string& secretKey, - const std::string& accessChannel) { - impl->setSessionCredentials(accessKey, secretKey, accessChannel); -} - -const SessionCredentials& DefaultMQPushConsumer::getSessionCredentials() const { - return impl->getSessionCredentials(); -} -const std::string& DefaultMQPushConsumer::getInstanceName() const { - return impl->getInstanceName(); -} - -void DefaultMQPushConsumer::setInstanceName(const std::string& instanceName) { - impl->setInstanceName(instanceName); -} - -const std::string& DefaultMQPushConsumer::getNameSpace() const { - return impl->getNameSpace(); -} - -void DefaultMQPushConsumer::setNameSpace(const std::string& nameSpace) { - impl->setNameSpace(nameSpace); -} -const std::string& DefaultMQPushConsumer::getGroupName() const { - return impl->getGroupName(); -} - -void DefaultMQPushConsumer::setGroupName(const std::string& groupName) { - impl->setGroupName(groupName); -} - -void DefaultMQPushConsumer::setEnableSsl(bool enableSsl) { - impl->setEnableSsl(enableSsl); -} - -bool DefaultMQPushConsumer::getEnableSsl() const { - return impl->getEnableSsl(); -} - -void DefaultMQPushConsumer::setSslPropertyFile(const std::string& sslPropertyFile) { - impl->setSslPropertyFile(sslPropertyFile); -} - -const std::string& DefaultMQPushConsumer::getSslPropertyFile() const { - return impl->getSslPropertyFile(); -} - -void DefaultMQPushConsumer::setLogLevel(elogLevel inputLevel) { - impl->setLogLevel(inputLevel); -} - -elogLevel DefaultMQPushConsumer::getLogLevel() { - return impl->getLogLevel(); -} -void DefaultMQPushConsumer::setLogFileSizeAndNum(int fileNum, long perFileSize) { - impl->setLogFileSizeAndNum(fileNum, perFileSize); -} - -void DefaultMQPushConsumer::setUnitName(std::string unitName) { - impl->setUnitName(unitName); -} -const std::string& DefaultMQPushConsumer::getUnitName() const { - return impl->getUnitName(); -} - -void DefaultMQPushConsumer::setTcpTransportPullThreadNum(int num) { - impl->setTcpTransportPullThreadNum(num); -} -int DefaultMQPushConsumer::getTcpTransportPullThreadNum() const { - return impl->getTcpTransportPullThreadNum(); -} - -void DefaultMQPushConsumer::setTcpTransportConnectTimeout(uint64_t timeout) { - impl->setTcpTransportConnectTimeout(timeout); -} -uint64_t DefaultMQPushConsumer::getTcpTransportConnectTimeout() const { - return impl->getTcpTransportConnectTimeout(); -} -void DefaultMQPushConsumer::setTcpTransportTryLockTimeout(uint64_t timeout) { - impl->setTcpTransportTryLockTimeout(timeout); -} -uint64_t DefaultMQPushConsumer::getTcpTransportTryLockTimeout() const { - return impl->getTcpTransportTryLockTimeout(); -} -void DefaultMQPushConsumer::setAsyncPull(bool asyncFlag) { - impl->setAsyncPull(asyncFlag); -} -void DefaultMQPushConsumer::setMessageTrace(bool messageTrace) { - impl->setMessageTrace(messageTrace); -} -bool DefaultMQPushConsumer::getMessageTrace() const { - return impl->getMessageTrace(); -} -} // namespace rocketmq diff --git a/src/consumer/DefaultMQPushConsumerImpl.cpp b/src/consumer/DefaultMQPushConsumerImpl.cpp deleted file mode 100644 index c7ee30e60..000000000 --- a/src/consumer/DefaultMQPushConsumerImpl.cpp +++ /dev/null @@ -1,1193 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "DefaultMQPushConsumerImpl.h" -#include "CommunicationMode.h" -#include "ConsumeMessageHookImpl.h" -#include "ConsumeMsgService.h" -#include "ConsumerRunningInfo.h" -#include "FilterAPI.h" -#include "Logging.h" -#include "MQClientAPIImpl.h" -#include "MQClientFactory.h" -#include "NameSpaceUtil.h" -#include "OffsetStore.h" -#include "PullAPIWrapper.h" -#include "PullSysFlag.h" -#include "Rebalance.h" -#include "StatsServerManager.h" -#include "UtilAll.h" -#include "Validators.h" -#include "task_queue.h" - -namespace rocketmq { - -class AsyncPullCallback : public PullCallback { - public: - AsyncPullCallback(DefaultMQPushConsumerImpl* pushConsumer, boost::weak_ptr request) - : m_callbackOwner(pushConsumer), m_pullRequest(request), m_bShutdown(false) {} - - virtual ~AsyncPullCallback() { m_callbackOwner = NULL; } - - virtual void onSuccess(MQMessageQueue& mq, PullResult& result, bool bProducePullRequest) { - boost::shared_ptr pullRequest = m_pullRequest.lock(); - if (!pullRequest) { - LOG_WARN("Pull request for[%s] has been released", mq.toString().c_str()); - return; - } - - if (m_bShutdown) { - LOG_INFO("pullrequest for:%s in shutdown, return", (pullRequest->m_messageQueue).toString().c_str()); - return; - } - if (pullRequest->isDropped()) { - LOG_INFO("Pull request for queue[%s] has been set as dropped. Will NOT pull this queue any more", - pullRequest->m_messageQueue.toString().c_str()); - return; - } - switch (result.pullStatus) { - case FOUND: { - if (pullRequest->isDropped()) { - LOG_INFO("[Dropped]Remove pullmsg event of mq:%s", (pullRequest->m_messageQueue).toString().c_str()); - break; - } - - uint64 pullRT = UtilAll::currentTimeMillis() - pullRequest->getLastPullTimestamp(); - StatsServerManager::getInstance()->getConsumeStatServer()->incPullRT( - pullRequest->m_messageQueue.getTopic(), m_callbackOwner->getGroupName(), pullRT); - pullRequest->setNextOffset(result.nextBeginOffset); - pullRequest->putMessage(result.msgFoundList); - if (!result.msgFoundList.empty()) { - StatsServerManager::getInstance()->getConsumeStatServer()->incPullTPS( - pullRequest->m_messageQueue.getTopic(), m_callbackOwner->getGroupName(), result.msgFoundList.size()); - } - m_callbackOwner->getConsumerMsgService()->submitConsumeRequest(pullRequest, result.msgFoundList); - - if (bProducePullRequest) { - m_callbackOwner->producePullMsgTask(pullRequest); - } else { - LOG_INFO("[bProducePullRequest = false]Stop pullmsg event of mq:%s", - (pullRequest->m_messageQueue).toString().c_str()); - } - - LOG_DEBUG("FOUND:%s with size:" SIZET_FMT ", nextBeginOffset:%lld", - (pullRequest->m_messageQueue).toString().c_str(), result.msgFoundList.size(), result.nextBeginOffset); - - break; - } - case NO_NEW_MSG: { - if (pullRequest->isDropped()) { - LOG_INFO("[Dropped]Remove pullmsg event of mq:%s", (pullRequest->m_messageQueue).toString().c_str()); - break; - } - pullRequest->setNextOffset(result.nextBeginOffset); - - if ((pullRequest->getCacheMsgCount() == 0) && (result.nextBeginOffset >= 0)) { - m_callbackOwner->updateConsumeOffset(pullRequest->m_messageQueue, result.nextBeginOffset); - } - if (bProducePullRequest) { - m_callbackOwner->producePullMsgTask(pullRequest); - } else { - LOG_INFO("[bProducePullRequest = false]Stop pullmsg event of mq:%s", - (pullRequest->m_messageQueue).toString().c_str()); - } - LOG_DEBUG("NO_NEW_MSG:%s,nextBeginOffset:%lld", pullRequest->m_messageQueue.toString().c_str(), - result.nextBeginOffset); - break; - } - case NO_MATCHED_MSG: { - if (pullRequest->isDropped()) { - LOG_INFO("[Dropped]Remove pullmsg event of mq:%s", (pullRequest->m_messageQueue).toString().c_str()); - break; - } - pullRequest->setNextOffset(result.nextBeginOffset); - - if ((pullRequest->getCacheMsgCount() == 0) && (result.nextBeginOffset >= 0)) { - m_callbackOwner->updateConsumeOffset(pullRequest->m_messageQueue, result.nextBeginOffset); - } - if (bProducePullRequest) { - m_callbackOwner->producePullMsgTask(pullRequest); - } else { - LOG_INFO("[bProducePullRequest = false]Stop pullmsg event of mq:%s", - (pullRequest->m_messageQueue).toString().c_str()); - } - LOG_DEBUG("NO_MATCHED_MSG:%s,nextBeginOffset:%lld", pullRequest->m_messageQueue.toString().c_str(), - result.nextBeginOffset); - break; - } - case OFFSET_ILLEGAL: { - if (pullRequest->isDropped()) { - LOG_INFO("[Dropped]Remove pullmsg event of mq:%s", (pullRequest->m_messageQueue).toString().c_str()); - break; - } - pullRequest->setNextOffset(result.nextBeginOffset); - if (bProducePullRequest) { - m_callbackOwner->producePullMsgTask(pullRequest); - } else { - LOG_INFO("[bProducePullRequest = false]Stop pullmsg event of mq:%s", - (pullRequest->m_messageQueue).toString().c_str()); - } - - LOG_DEBUG("OFFSET_ILLEGAL:%s,nextBeginOffset:%lld", pullRequest->m_messageQueue.toString().c_str(), - result.nextBeginOffset); - break; - } - case BROKER_TIMEOUT: { - if (pullRequest->isDropped()) { - LOG_INFO("[Dropped]Remove pullmsg event of mq:%s", (pullRequest->m_messageQueue).toString().c_str()); - break; - } - LOG_ERROR("impossible BROKER_TIMEOUT Occurs"); - pullRequest->setNextOffset(result.nextBeginOffset); - if (bProducePullRequest) { - m_callbackOwner->producePullMsgTask(pullRequest); - } else { - LOG_INFO("[bProducePullRequest = false]Stop pullmsg event of mq:%s", - (pullRequest->m_messageQueue).toString().c_str()); - } - break; - } - } - } - - virtual void onException(MQException& e) { - boost::shared_ptr pullRequest = m_pullRequest.lock(); - if (!pullRequest) { - LOG_WARN("Pull request has been released."); - return; - } - std::string queueName = pullRequest->m_messageQueue.toString(); - if (m_bShutdown) { - LOG_INFO("pullrequest for:%s in shutdown, return", queueName.c_str()); - return; - } - if (pullRequest->isDropped()) { - LOG_INFO("[Dropped]Remove pullmsg event of mq:%s", queueName.c_str()); - return; - } - LOG_WARN("Pullrequest for:%s occurs exception, reproduce it after 1s.", queueName.c_str()); - m_callbackOwner->producePullMsgTaskLater(pullRequest, 1000); - } - - void setShutdownStatus() { m_bShutdown = true; } - - const boost::weak_ptr& getPullRequest() const { return m_pullRequest; } - - void setPullRequest(boost::weak_ptr& pullRequest) { m_pullRequest = pullRequest; } - - private: - DefaultMQPushConsumerImpl* m_callbackOwner; - boost::weak_ptr m_pullRequest; - bool m_bShutdown; -}; - -static boost::mutex m_asyncCallbackLock; - -DefaultMQPushConsumerImpl::DefaultMQPushConsumerImpl() {} -DefaultMQPushConsumerImpl::DefaultMQPushConsumerImpl(const string& groupname) - : m_consumeFromWhere(CONSUME_FROM_LAST_OFFSET), - m_pOffsetStore(NULL), - m_pRebalance(NULL), - m_pPullAPIWrapper(NULL), - m_consumerService(NULL), - m_pMessageListener(NULL), - m_consumeMessageBatchMaxSize(1), - m_maxMsgCacheSize(1000), - m_pullmsgQueue(NULL) { - //second); - } - m_PullCallback.clear(); - m_subTopics.clear(); -} - -bool DefaultMQPushConsumerImpl::sendMessageBack(MQMessageExt& msg, int delayLevel, string& brokerName) { - string brokerAddr; - if (!brokerName.empty()) - brokerAddr = getFactory()->findBrokerAddressInPublish(brokerName); - else - brokerAddr = socketAddress2IPPort(msg.getStoreHost()); - try { - getFactory()->getMQClientAPIImpl()->consumerSendMessageBack(brokerAddr, msg, getGroupName(), delayLevel, 3000, - getMaxReconsumeTimes(), getSessionCredentials()); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - return false; - } - return true; -} - -void DefaultMQPushConsumerImpl::fetchSubscribeMessageQueues(const string& topic, vector& mqs) { - mqs.clear(); - try { - getFactory()->fetchSubscribeMessageQueues(topic, mqs, getSessionCredentials()); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - } -} - -void DefaultMQPushConsumerImpl::doRebalance() { - if (isServiceStateOk()) { - try { - m_pRebalance->doRebalance(); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - } - } -} - -void DefaultMQPushConsumerImpl::persistConsumerOffset() { - if (isServiceStateOk()) { - m_pRebalance->persistConsumerOffset(); - } -} - -void DefaultMQPushConsumerImpl::persistConsumerOffsetByResetOffset() { - if (isServiceStateOk()) { - m_pRebalance->persistConsumerOffsetByResetOffset(); - } -} - -void DefaultMQPushConsumerImpl::start() { -#ifndef WIN32 - /* Ignore the SIGPIPE */ - struct sigaction sa; - memset(&sa, 0, sizeof(struct sigaction)); - sa.sa_handler = SIG_IGN; - sa.sa_flags = 0; - sigaction(SIGPIPE, &sa, 0); -#endif - LOG_WARN("###Current Push Consumer@%s", getClientVersionString().c_str()); - // deal with name space before start - dealWithNameSpace(); - logConfigs(); - switch (m_serviceState) { - case CREATE_JUST: { - m_serviceState = START_FAILED; - // Start status server - StatsServerManager::getInstance()->getConsumeStatServer()->start(); - DefaultMQClient::start(); - dealWithMessageTrace(); - LOG_INFO("DefaultMQPushConsumerImpl:%s start", m_GroupName.c_str()); - - //getMessageListenerType() == messageListenerOrderly) { - LOG_INFO("start orderly consume service:%s", getGroupName().c_str()); - m_consumerService = new ConsumeMessageOrderlyService(this, m_consumeThreadCount, m_pMessageListener); - } else // for backward compatible, defaultly and concurrently listeners - // are allocating ConsumeMessageConcurrentlyService - { - LOG_INFO("start concurrently consume service:%s", getGroupName().c_str()); - m_consumerService = new ConsumeMessageConcurrentlyService(this, m_consumeThreadCount, m_pMessageListener); - } - } - - m_pullmsgQueue = new TaskQueue(m_pullMsgThreadPoolNum); - m_pullmsgThread.reset( - new boost::thread(boost::bind(&DefaultMQPushConsumerImpl::runPullMsgQueue, this, m_pullmsgQueue))); - - copySubscription(); - - //registerConsumer(this); - if (!registerOK) { - m_serviceState = CREATE_JUST; - THROW_MQEXCEPTION( - MQClientException, - "The cousumer group[" + getGroupName() + "] has been created before, specify another name please.", -1); - } - - //load(); - } catch (MQClientException& e) { - bStartFailed = true; - errorMsg = std::string(e.what()); - } - m_consumerService->start(); - - getFactory()->start(); - - updateTopicSubscribeInfoWhenSubscriptionChanged(); - getFactory()->sendHeartbeatToAllBroker(); - - m_serviceState = RUNNING; - if (bStartFailed) { - shutdown(); - THROW_MQEXCEPTION(MQClientException, errorMsg, -1); - } - break; - } - case RUNNING: - case START_FAILED: - case SHUTDOWN_ALREADY: - break; - default: - break; - } - - getFactory()->rebalanceImmediately(); -} - -void DefaultMQPushConsumerImpl::shutdown() { - switch (m_serviceState) { - case RUNNING: { - LOG_INFO("DefaultMQPushConsumerImpl shutdown"); - - // Shutdown status server - StatsServerManager::getInstance()->getConsumeStatServer()->shutdown(); - shutdownMessageTraceInnerProducer(); - m_async_ioService.stop(); - m_async_service_thread->interrupt(); - m_async_service_thread->join(); - m_pullmsgQueue->close(); - m_pullmsgThread->interrupt(); - m_pullmsgThread->join(); - m_consumerService->shutdown(); - persistConsumerOffset(); - shutdownAsyncPullCallBack(); // delete aync pullMsg resources - getFactory()->unregisterConsumer(this); - getFactory()->shutdown(); - m_serviceState = SHUTDOWN_ALREADY; - break; - } - case CREATE_JUST: - case SHUTDOWN_ALREADY: - break; - default: - break; - } -} - -void DefaultMQPushConsumerImpl::registerMessageListener(MQMessageListener* pMessageListener) { - if (NULL != pMessageListener) { - m_pMessageListener = pMessageListener; - } -} - -MessageListenerType DefaultMQPushConsumerImpl::getMessageListenerType() { - if (NULL != m_pMessageListener) { - return m_pMessageListener->getMessageListenerType(); - } - return messageListenerDefaultly; -} - -ConsumeMsgService* DefaultMQPushConsumerImpl::getConsumerMsgService() const { - return m_consumerService; -} - -OffsetStore* DefaultMQPushConsumerImpl::getOffsetStore() const { - return m_pOffsetStore; -} - -Rebalance* DefaultMQPushConsumerImpl::getRebalance() const { - return m_pRebalance; -} - -void DefaultMQPushConsumerImpl::subscribe(const string& topic, const string& subExpression) { - m_subTopics[topic] = subExpression; -} - -void DefaultMQPushConsumerImpl::checkConfig() { - string groupname = getGroupName(); - // check consumerGroup - Validators::checkGroup(groupname); - - // consumerGroup - if (!groupname.compare(DEFAULT_CONSUMER_GROUP)) { - THROW_MQEXCEPTION(MQClientException, "consumerGroup can not equal DEFAULT_CONSUMER", -1); - } - - if (getMessageModel() != BROADCASTING && getMessageModel() != CLUSTERING) { - THROW_MQEXCEPTION(MQClientException, "messageModel is valid ", -1); - } - - if (m_pMessageListener == NULL) { - THROW_MQEXCEPTION(MQClientException, "messageListener is null ", -1); - } -} - -void DefaultMQPushConsumerImpl::copySubscription() { - map::iterator it = m_subTopics.begin(); - for (; it != m_subTopics.end(); ++it) { - LOG_INFO("buildSubscriptionData,:%s,%s", it->first.c_str(), it->second.c_str()); - unique_ptr pSData(FilterAPI::buildSubscriptionData(it->first, it->second)); - - m_pRebalance->setSubscriptionData(it->first, pSData.release()); - } - - switch (getMessageModel()) { - case BROADCASTING: - break; - case CLUSTERING: { - string retryTopic = UtilAll::getRetryTopic(getGroupName()); - - // pSData(FilterAPI::buildSubscriptionData(retryTopic, SUB_ALL)); - - m_pRebalance->setSubscriptionData(retryTopic, pSData.release()); - break; - } - default: - break; - } -} - -void DefaultMQPushConsumerImpl::updateTopicSubscribeInfo(const string& topic, vector& info) { - m_pRebalance->setTopicSubscribeInfo(topic, info); -} - -void DefaultMQPushConsumerImpl::updateTopicSubscribeInfoWhenSubscriptionChanged() { - map& subTable = m_pRebalance->getSubscriptionInner(); - map::iterator it = subTable.begin(); - for (; it != subTable.end(); ++it) { - bool btopic = getFactory()->updateTopicRouteInfoFromNameServer(it->first, getSessionCredentials()); - if (btopic == false) { - LOG_WARN("The topic:[%s] not exist", it->first.c_str()); - } - } -} - -ConsumeType DefaultMQPushConsumerImpl::getConsumeType() { - return CONSUME_PASSIVELY; -} - -ConsumeFromWhere DefaultMQPushConsumerImpl::getConsumeFromWhere() { - return m_consumeFromWhere; -} - -void DefaultMQPushConsumerImpl::setConsumeFromWhere(ConsumeFromWhere consumeFromWhere) { - m_consumeFromWhere = consumeFromWhere; -} - -void DefaultMQPushConsumerImpl::getSubscriptions(vector& result) { - map& subTable = m_pRebalance->getSubscriptionInner(); - map::iterator it = subTable.begin(); - for (; it != subTable.end(); ++it) { - result.push_back(*(it->second)); - } -} - -void DefaultMQPushConsumerImpl::updateConsumeOffset(const MQMessageQueue& mq, int64 offset) { - if (offset >= 0) { - m_pOffsetStore->updateOffset(mq, offset); - } else { - LOG_ERROR("updateConsumeOffset of mq:%s error", mq.toString().c_str()); - } -} - -void DefaultMQPushConsumerImpl::removeConsumeOffset(const MQMessageQueue& mq) { - m_pOffsetStore->removeOffset(mq); -} - -void DefaultMQPushConsumerImpl::static_triggerNextPullRequest(void* context, - boost::asio::deadline_timer* t, - boost::weak_ptr pullRequest) { - if (pullRequest.expired()) { - LOG_WARN("Pull request has been released before."); - return; - } - DefaultMQPushConsumerImpl* pDefaultMQPushConsumerImpl = (DefaultMQPushConsumerImpl*)context; - if (pDefaultMQPushConsumerImpl) { - pDefaultMQPushConsumerImpl->triggerNextPullRequest(t, pullRequest); - } -} - -void DefaultMQPushConsumerImpl::triggerNextPullRequest(boost::asio::deadline_timer* t, - boost::weak_ptr pullRequest) { - // delete first to avoild memleak - deleteAndZero(t); - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released before."); - return; - } - producePullMsgTask(request); -} - -bool DefaultMQPushConsumerImpl::producePullMsgTaskLater(boost::weak_ptr pullRequest, int millis) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_INFO("Pull request is invalid. Maybe it is dropped before."); - return false; - } - if (request->isDropped()) { - LOG_INFO("[Dropped]Remove pullmsg event of mq:%s", request->m_messageQueue.toString().c_str()); - return false; - } - if (m_pullmsgQueue->bTaskQueueStatusOK() && isServiceStateOk()) { - boost::asio::deadline_timer* t = - new boost::asio::deadline_timer(m_async_ioService, boost::posix_time::milliseconds(millis)); - t->async_wait(boost::bind(&(DefaultMQPushConsumerImpl::static_triggerNextPullRequest), this, t, request)); - LOG_INFO("Produce Pull request [%s] Later and Sleep [%d]ms.", (request->m_messageQueue).toString().c_str(), millis); - return true; - } else { - LOG_WARN("Service or TaskQueue shutdown, produce PullRequest of mq:%s failed", - request->m_messageQueue.toString().c_str()); - return false; - } -} - -bool DefaultMQPushConsumerImpl::producePullMsgTask(boost::weak_ptr pullRequest) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_WARN("Pull request has been released."); - return false; - } - if (request->isDropped()) { - LOG_INFO("[Dropped]Remove pullmsg event of mq:%s", request->m_messageQueue.toString().c_str()); - return false; - } - if (m_pullmsgQueue->bTaskQueueStatusOK() && isServiceStateOk()) { - if (m_asyncPull) { - m_pullmsgQueue->produce(TaskBinder::gen(&DefaultMQPushConsumerImpl::pullMessageAsync, this, request)); - } else { - m_pullmsgQueue->produce(TaskBinder::gen(&DefaultMQPushConsumerImpl::pullMessage, this, request)); - } - } else { - LOG_WARN("produce PullRequest of mq:%s failed", request->m_messageQueue.toString().c_str()); - return false; - } - return true; -} - -void DefaultMQPushConsumerImpl::runPullMsgQueue(TaskQueue* pTaskQueue) { - pTaskQueue->run(); -} - -void DefaultMQPushConsumerImpl::pullMessage(boost::weak_ptr pullRequest) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_ERROR("Pull request is released, return"); - return; - } - if (request->isDropped()) { - LOG_WARN("Pull request is set drop with mq:%s, return", (request->m_messageQueue).toString().c_str()); - // request->removePullMsgEvent(); - return; - } - - MQMessageQueue& messageQueue = request->m_messageQueue; - if (m_consumerService->getConsumeMsgSerivceListenerType() == messageListenerOrderly) { - if (!request->isLocked() || request->isLockExpired()) { - if (!m_pRebalance->lock(messageQueue)) { - request->setLastPullTimestamp(UtilAll::currentTimeMillis()); - producePullMsgTaskLater(request, 1000); - return; - } - } - } - - if (request->getCacheMsgCount() > m_maxMsgCacheSize) { - LOG_INFO("Sync Pull request for %s has Cached with %d Messages and The Max size is %d, Sleep 1s.", - (request->m_messageQueue).toString().c_str(), request->getCacheMsgCount(), m_maxMsgCacheSize); - request->setLastPullTimestamp(UtilAll::currentTimeMillis()); - // Retry 1s, - producePullMsgTaskLater(request, 1000); - return; - } - - bool commitOffsetEnable = false; - int64 commitOffsetValue = 0; - if (CLUSTERING == getMessageModel()) { - commitOffsetValue = m_pOffsetStore->readOffset(messageQueue, READ_FROM_MEMORY, getSessionCredentials()); - if (commitOffsetValue > 0) { - commitOffsetEnable = true; - } - } - - string subExpression; - SubscriptionData* pSdata = m_pRebalance->getSubscriptionData(messageQueue.getTopic()); - if (pSdata == NULL) { - LOG_INFO("Can not get SubscriptionData of Pull request for [%s], Sleep 1s.", - (request->m_messageQueue).toString().c_str()); - producePullMsgTaskLater(request, 1000); - return; - } - subExpression = pSdata->getSubString(); - - int sysFlag = PullSysFlag::buildSysFlag(commitOffsetEnable, // commitOffset - false, // suspend - !subExpression.empty(), // subscription - false); // class filter - if (request->isDropped()) { - LOG_WARN("Pull request is set as dropped with mq:%s, return", (request->m_messageQueue).toString().c_str()); - return; - } - try { - uint64 startTimeStamp = UtilAll::currentTimeMillis(); - request->setLastPullTimestamp(startTimeStamp); - unique_ptr result(m_pPullAPIWrapper->pullKernelImpl(messageQueue, // 1 - subExpression, // 2 - pSdata->getSubVersion(), // 3 - request->getNextOffset(), // 4 - 32, // 5 - sysFlag, // 6 - commitOffsetValue, // 7 - 1000 * 15, // 8 - 1000 * 30, // 9 - ComMode_SYNC, // 10 - NULL, getSessionCredentials())); - - PullResult pullResult = m_pPullAPIWrapper->processPullResult(messageQueue, result.get(), pSdata); - switch (pullResult.pullStatus) { - case FOUND: { - uint64 pullRT = UtilAll::currentTimeMillis() - startTimeStamp; - StatsServerManager::getInstance()->getConsumeStatServer()->incPullRT(messageQueue.getTopic(), getGroupName(), - pullRT); - if (request->isDropped()) { - LOG_INFO("Get pull result but the queue has been marked as dropped. Queue: %s", - messageQueue.toString().c_str()); - break; - } - // and this request is dropped, and then received pulled msgs. - request->setNextOffset(pullResult.nextBeginOffset); - request->putMessage(pullResult.msgFoundList); - if (!pullResult.msgFoundList.empty()) { - StatsServerManager::getInstance()->getConsumeStatServer()->incPullTPS(messageQueue.getTopic(), getGroupName(), - pullResult.msgFoundList.size()); - } - m_consumerService->submitConsumeRequest(request, pullResult.msgFoundList); - producePullMsgTask(request); - - LOG_DEBUG("FOUND:%s with size:" SIZET_FMT ",nextBeginOffset:%lld", messageQueue.toString().c_str(), - pullResult.msgFoundList.size(), pullResult.nextBeginOffset); - - break; - } - case NO_NEW_MSG: { - if (request->isDropped()) { - LOG_INFO("Get pull result but the queue has been marked as dropped. Queue: %s", - messageQueue.toString().c_str()); - break; - } - request->setNextOffset(pullResult.nextBeginOffset); - if ((request->getCacheMsgCount() == 0) && (pullResult.nextBeginOffset >= 0)) { - updateConsumeOffset(messageQueue, pullResult.nextBeginOffset); - } - producePullMsgTask(request); - LOG_DEBUG("NO_NEW_MSG:%s,nextBeginOffset:%lld", messageQueue.toString().c_str(), pullResult.nextBeginOffset); - break; - } - case NO_MATCHED_MSG: { - if (request->isDropped()) { - LOG_INFO("Get pull result but the queue has been marked as dropped. Queue: %s", - messageQueue.toString().c_str()); - break; - } - request->setNextOffset(pullResult.nextBeginOffset); - if ((request->getCacheMsgCount() == 0) && (pullResult.nextBeginOffset >= 0)) { - updateConsumeOffset(messageQueue, pullResult.nextBeginOffset); - } - producePullMsgTask(request); - - LOG_DEBUG("NO_MATCHED_MSG:%s,nextBeginOffset:%lld", messageQueue.toString().c_str(), - pullResult.nextBeginOffset); - break; - } - case OFFSET_ILLEGAL: { - if (request->isDropped()) { - LOG_INFO("Get pull result but the queue has been marked as dropped. Queue: %s", - messageQueue.toString().c_str()); - break; - } - request->setNextOffset(pullResult.nextBeginOffset); - producePullMsgTask(request); - - LOG_DEBUG("OFFSET_ILLEGAL:%s,nextBeginOffset:%lld", messageQueue.toString().c_str(), - pullResult.nextBeginOffset); - break; - } - case BROKER_TIMEOUT: { // as BROKER_TIMEOUT is defined by client, broker - // will not returns this status, so this case - // could not be entered. - LOG_ERROR("impossible BROKER_TIMEOUT Occurs"); - request->setNextOffset(pullResult.nextBeginOffset); - producePullMsgTask(request); - break; - } - } - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - LOG_WARN("Pull %s occur exception, restart 1s later.", messageQueue.toString().c_str()); - producePullMsgTaskLater(request, 1000); - } -} - -AsyncPullCallback* DefaultMQPushConsumerImpl::getAsyncPullCallBack(boost::weak_ptr pullRequest, - MQMessageQueue msgQueue) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - return NULL; - } - boost::lock_guard lock(m_asyncCallbackLock); - if (m_asyncPull && request) { - PullMAP::iterator it = m_PullCallback.find(msgQueue); - if (it == m_PullCallback.end()) { - LOG_INFO("new pull callback for mq:%s", msgQueue.toString().c_str()); - m_PullCallback[msgQueue] = new AsyncPullCallback(this, request); - } - AsyncPullCallback* asyncPullCallback = m_PullCallback[msgQueue]; - if (asyncPullCallback) { - // maybe the pull request has dropped before, replace event time. - asyncPullCallback->setPullRequest(pullRequest); - } - return asyncPullCallback; - } - - return NULL; -} - -void DefaultMQPushConsumerImpl::shutdownAsyncPullCallBack() { - boost::lock_guard lock(m_asyncCallbackLock); - if (m_asyncPull) { - PullMAP::iterator it = m_PullCallback.begin(); - for (; it != m_PullCallback.end(); ++it) { - if (it->second) { - it->second->setShutdownStatus(); - } else { - LOG_ERROR("could not find asyncPullCallback for:%s", it->first.toString().c_str()); - } - } - } -} - -void DefaultMQPushConsumerImpl::pullMessageAsync(boost::weak_ptr pullRequest) { - boost::shared_ptr request = pullRequest.lock(); - if (!request) { - LOG_ERROR("Pull request is released, return"); - return; - } - if (request->isDropped()) { - LOG_WARN("Pull request is set drop with mq:%s, return", (request->m_messageQueue).toString().c_str()); - return; - } - MQMessageQueue& messageQueue = request->m_messageQueue; - if (m_consumerService->getConsumeMsgSerivceListenerType() == messageListenerOrderly) { - if (!request->isLocked() || request->isLockExpired()) { - if (!m_pRebalance->lock(messageQueue)) { - request->setLastPullTimestamp(UtilAll::currentTimeMillis()); - // Retry later. - producePullMsgTaskLater(request, 1000); - return; - } - } - } - - if (request->getCacheMsgCount() > m_maxMsgCacheSize) { - LOG_INFO("Pull request for [%s] has Cached with %d Messages and The Max size is %d, Sleep 3s.", - (request->m_messageQueue).toString().c_str(), request->getCacheMsgCount(), m_maxMsgCacheSize); - request->setLastPullTimestamp(UtilAll::currentTimeMillis()); - // Retry 3s, - producePullMsgTaskLater(request, 3000); - return; - } - - bool commitOffsetEnable = false; - int64 commitOffsetValue = 0; - if (CLUSTERING == getMessageModel()) { - commitOffsetValue = m_pOffsetStore->readOffset(messageQueue, READ_FROM_MEMORY, getSessionCredentials()); - if (commitOffsetValue > 0) { - commitOffsetEnable = true; - } - } - - string subExpression; - SubscriptionData* pSdata = (m_pRebalance->getSubscriptionData(messageQueue.getTopic())); - if (pSdata == NULL) { - LOG_INFO("Can not get SubscriptionData of Pull request for [%s], Sleep 1s.", - (request->m_messageQueue).toString().c_str()); - // Subscribe data error, retry later. - producePullMsgTaskLater(request, 1000); - return; - } - subExpression = pSdata->getSubString(); - - int sysFlag = PullSysFlag::buildSysFlag(commitOffsetEnable, // commitOffset - true, // suspend - !subExpression.empty(), // subscription - false); // class filter - - AsyncArg arg; - arg.mq = messageQueue; - arg.subData = *pSdata; - arg.pPullWrapper = m_pPullAPIWrapper; - if (request->isDropped()) { - LOG_WARN("Pull request is set as dropped with mq:%s, return", request->m_messageQueue.toString().c_str()); - return; - } - try { - request->setLastPullTimestamp(UtilAll::currentTimeMillis()); - AsyncPullCallback* pullCallback = getAsyncPullCallBack(request, messageQueue); - if (pullCallback == NULL) { - LOG_WARN("Can not get pull callback for:%s, Maybe this pull request has been released.", - request->m_messageQueue.toString().c_str()); - return; - } - m_pPullAPIWrapper->pullKernelImpl(messageQueue, // 1 - subExpression, // 2 - pSdata->getSubVersion(), // 3 - request->getNextOffset(), // 4 - 32, // 5 - sysFlag, // 6 - commitOffsetValue, // 7 - 1000 * 15, // 8 - m_asyncPullTimeout, // 9 - ComMode_ASYNC, // 10 - pullCallback, // 11 - getSessionCredentials(), // 12 - &arg); // 13 - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - if (request->isDropped()) { - LOG_WARN("Pull request is set as dropped with mq:%s, return", (request->m_messageQueue).toString().c_str()); - return; - } - LOG_INFO("Pull %s occur exception, restart 1s later.", (request->m_messageQueue).toString().c_str()); - producePullMsgTaskLater(request, 1000); - } -} - -void DefaultMQPushConsumerImpl::setAsyncPull(bool asyncFlag) { - if (asyncFlag) { - LOG_INFO("set pushConsumer:%s to async default pull mode", getGroupName().c_str()); - } else { - LOG_INFO("set pushConsumer:%s to sync pull mode", getGroupName().c_str()); - } - m_asyncPull = asyncFlag; -} - -void DefaultMQPushConsumerImpl::setConsumeThreadCount(int threadCount) { - if (threadCount > 0) { - m_consumeThreadCount = threadCount; - } else { - LOG_ERROR("setConsumeThreadCount with invalid value"); - } -} - -int DefaultMQPushConsumerImpl::getConsumeThreadCount() const { - return m_consumeThreadCount; -} -void DefaultMQPushConsumerImpl::setMaxReconsumeTimes(int maxReconsumeTimes) { - if (maxReconsumeTimes > 0) { - m_maxReconsumeTimes = maxReconsumeTimes; - } else { - LOG_ERROR("set maxReconsumeTimes with invalid value"); - } -} - -int DefaultMQPushConsumerImpl::getMaxReconsumeTimes() const { - if (m_maxReconsumeTimes >= 0) { - return m_maxReconsumeTimes; - } - // return 16 as default; - return 16; -} - -void DefaultMQPushConsumerImpl::setPullMsgThreadPoolCount(int threadCount) { - m_pullMsgThreadPoolNum = threadCount; -} - -int DefaultMQPushConsumerImpl::getPullMsgThreadPoolCount() const { - return m_pullMsgThreadPoolNum; -} - -int DefaultMQPushConsumerImpl::getConsumeMessageBatchMaxSize() const { - return m_consumeMessageBatchMaxSize; -} - -void DefaultMQPushConsumerImpl::setConsumeMessageBatchMaxSize(int consumeMessageBatchMaxSize) { - if (consumeMessageBatchMaxSize >= 1) - m_consumeMessageBatchMaxSize = consumeMessageBatchMaxSize; -} - -void DefaultMQPushConsumerImpl::setMaxCacheMsgSizePerQueue(int maxCacheSize) { - if (maxCacheSize > 0 && maxCacheSize < 65535) { - LOG_INFO("set maxCacheSize to:%d for consumer:%s", maxCacheSize, getGroupName().c_str()); - m_maxMsgCacheSize = maxCacheSize; - } -} - -int DefaultMQPushConsumerImpl::getMaxCacheMsgSizePerQueue() const { - return m_maxMsgCacheSize; -} - -ConsumerRunningInfo* DefaultMQPushConsumerImpl::getConsumerRunningInfo() { - auto* info = new ConsumerRunningInfo(); - if (m_consumerService->getConsumeMsgSerivceListenerType() == messageListenerOrderly) { - info->setProperty(ConsumerRunningInfo::PROP_CONSUME_ORDERLY, "true"); - } else { - info->setProperty(ConsumerRunningInfo::PROP_CONSUME_ORDERLY, "false"); - } - info->setProperty(ConsumerRunningInfo::PROP_THREADPOOL_CORE_SIZE, UtilAll::to_string(m_consumeThreadCount)); - info->setProperty(ConsumerRunningInfo::PROP_CONSUMER_START_TIMESTAMP, UtilAll::to_string(m_startTime)); - - std::vector result; - getSubscriptions(result); - info->setSubscriptionSet(result); - - for (const auto& it : result) { - ConsumeStats consumeStat; - // we should get it from status service. - consumeStat = - StatsServerManager::getInstance()->getConsumeStatServer()->getConsumeStats(it.getTopic(), this->getGroupName()); - info->setStatusTable(it.getTopic(), consumeStat); - } - - std::map> requestTable = m_pRebalance->getPullRequestTable(); - - for (const auto& it : requestTable) { - if (!it.second->isDropped()) { - MessageQueue queue((it.first).getTopic(), (it.first).getBrokerName(), (it.first).getQueueId()); - ProcessQueueInfo processQueue; - processQueue.cachedMsgMinOffset = it.second->getCacheMinOffset(); - processQueue.cachedMsgMaxOffset = it.second->getCacheMaxOffset(); - processQueue.cachedMsgCount = it.second->getCacheMsgCount(); - processQueue.setCommitOffset( - m_pOffsetStore->readOffset(it.first, MEMORY_FIRST_THEN_STORE, getSessionCredentials())); - processQueue.setDroped(it.second->isDropped()); - processQueue.setLocked(it.second->isLocked()); - processQueue.lastLockTimestamp = it.second->getLastLockTimestamp(); - processQueue.lastPullTimestamp = it.second->getLastPullTimestamp(); - processQueue.lastConsumeTimestamp = it.second->getLastConsumeTimestamp(); - info->setMqTable(queue, processQueue); - } - } - - return info; -} -// we should deal with name space before producer start. -bool DefaultMQPushConsumerImpl::dealWithNameSpace() { - string ns = getNameSpace(); - if (ns.empty()) { - string nsAddr = getNamesrvAddr(); - if (!NameSpaceUtil::checkNameSpaceExistInNameServer(nsAddr)) { - return true; - } - ns = NameSpaceUtil::getNameSpaceFromNsURL(nsAddr); - // reset namespace - setNameSpace(ns); - } - // reset group name - if (!NameSpaceUtil::hasNameSpace(getGroupName(), ns)) { - string fullGID = NameSpaceUtil::withNameSpace(getGroupName(), ns); - setGroupName(fullGID); - } - map subTmp; - map::iterator it = m_subTopics.begin(); - for (; it != m_subTopics.end(); ++it) { - string topic = it->first; - string subs = it->second; - if (!NameSpaceUtil::hasNameSpace(topic, ns)) { - LOG_INFO("Update Subscribe[%s:%s] with NameSpace:%s", it->first.c_str(), it->second.c_str(), ns.c_str()); - topic = NameSpaceUtil::withNameSpace(topic, ns); - // let other mode to known, the name space model opened. - m_useNameSpaceMode = true; - } - subTmp[topic] = subs; - } - m_subTopics.swap(subTmp); - - return true; -} -void DefaultMQPushConsumerImpl::logConfigs() { - showClientConfigs(); - - LOG_WARN("MessageModel:%d", m_messageModel); - LOG_WARN("MessageModel:%s", m_messageModel == BROADCASTING ? "BROADCASTING" : "CLUSTERING"); - - LOG_WARN("ConsumeFromWhere:%d", m_consumeFromWhere); - switch (m_consumeFromWhere) { - case CONSUME_FROM_FIRST_OFFSET: - LOG_WARN("ConsumeFromWhere:%s", "CONSUME_FROM_FIRST_OFFSET"); - break; - case CONSUME_FROM_LAST_OFFSET: - LOG_WARN("ConsumeFromWhere:%s", "CONSUME_FROM_LAST_OFFSET"); - break; - - case CONSUME_FROM_TIMESTAMP: - LOG_WARN("ConsumeFromWhere:%s", "CONSUME_FROM_TIMESTAMP"); - break; - case CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRST: - LOG_WARN("ConsumeFromWhere:%s", "CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRST"); - break; - case CONSUME_FROM_MAX_OFFSET: - LOG_WARN("ConsumeFromWhere:%s", "CONSUME_FROM_MAX_OFFSET"); - break; - case CONSUME_FROM_MIN_OFFSET: - LOG_WARN("ConsumeFromWhere:%s", "CONSUME_FROM_MAX_OFFSET"); - break; - default: - LOG_WARN("ConsumeFromWhere:%s", "UnKnown."); - break; - } - LOG_WARN("ConsumeThreadCount:%d", m_consumeThreadCount); - LOG_WARN("ConsumeMessageBatchMaxSize:%d", m_consumeMessageBatchMaxSize); - LOG_WARN("MaxMsgCacheSizePerQueue:%d", m_maxMsgCacheSize); - LOG_WARN("MaxReconsumeTimes:%d", m_maxReconsumeTimes); - LOG_WARN("PullMsgThreadPoolNum:%d", m_pullMsgThreadPoolNum); - LOG_WARN("AsyncPullMode:%s", m_asyncPull ? "true" : "false"); - LOG_WARN("AsyncPullTimeout:%d ms", m_asyncPullTimeout); -} -// we should create trace message poll before producer send messages. -bool DefaultMQPushConsumerImpl::dealWithMessageTrace() { - if (!getMessageTrace()) { - LOG_INFO("Message Trace set to false, Will not send trace messages."); - return false; - } - // Try to create default producer inner. - LOG_INFO("DefaultMQPushConsumer Open message trace.."); - - createMessageTraceInnerProducer(); - std::shared_ptr hook(new ConsumeMessageHookImpl()); - registerConsumeMessageHook(hook); - return true; -} - -void DefaultMQPushConsumerImpl::createMessageTraceInnerProducer() { - m_DefaultMQProducerImpl = std::make_shared(getGroupName()); - m_DefaultMQProducerImpl->setMessageTrace(false); - m_DefaultMQProducerImpl->setInstanceName("MESSAGE_TRACE_" + getInstanceName()); - const SessionCredentials& session = getSessionCredentials(); - m_DefaultMQProducerImpl->setSessionCredentials(session.getAccessKey(), session.getSecretKey(), - session.getAuthChannel()); - if (!getNamesrvAddr().empty()) { - m_DefaultMQProducerImpl->setNamesrvAddr(getNamesrvAddr()); - } - m_DefaultMQProducerImpl->setNameSpace(getNameSpace()); - // m_DefaultMQProducerImpl->setNamesrvDomain(getNamesrvDomain()); - m_DefaultMQProducerImpl->start(); -} -void DefaultMQPushConsumerImpl::shutdownMessageTraceInnerProducer() { - if (!getMessageTrace()) { - return; - } - if (m_DefaultMQProducerImpl) { - LOG_INFO("Shutdown Message Trace Inner Producer In Consumer."); - m_DefaultMQProducerImpl->shutdown(); - } -} -bool DefaultMQPushConsumerImpl::hasConsumeMessageHook() { - return !m_consumeMessageHookList.empty(); -} - -void DefaultMQPushConsumerImpl::registerConsumeMessageHook(std::shared_ptr& hook) { - m_consumeMessageHookList.push_back(hook); - LOG_INFO("Register ConsumeMessageHook success,hookname is %s", hook->getHookName().c_str()); -} - -void DefaultMQPushConsumerImpl::executeConsumeMessageHookBefore(ConsumeMessageContext* context) { - if (!m_consumeMessageHookList.empty()) { - std::vector>::iterator it = m_consumeMessageHookList.begin(); - for (; it != m_consumeMessageHookList.end(); ++it) { - try { - (*it)->executeHookBefore(context); - } catch (exception e) { - } - } - } -} - -void DefaultMQPushConsumerImpl::executeConsumeMessageHookAfter(ConsumeMessageContext* context) { - if (!m_consumeMessageHookList.empty()) { - std::vector>::iterator it = m_consumeMessageHookList.begin(); - for (; it != m_consumeMessageHookList.end(); ++it) { - try { - (*it)->executeHookAfter(context); - } catch (exception e) { - } - } - } -} - -void DefaultMQPushConsumerImpl::submitSendTraceRequest(MQMessage& msg, SendCallback* pSendCallback) { - if (getMessageTrace()) { - try { - LOG_DEBUG("=====Send Trace Messages,Topic[%s],Key[%s],Body[%s]", msg.getTopic().c_str(), msg.getKeys().c_str(), - msg.getBody().c_str()); - // m_DefaultMQProducerImpl->submitSendTraceRequest(msg, pSendCallback); - m_DefaultMQProducerImpl->send(msg, pSendCallback, false); - } catch (exception e) { - LOG_INFO(e.what()); - } - } -} - -void DefaultMQPushConsumerImpl::setDefaultMqProducerImpl(DefaultMQProducerImpl* DefaultMqProducerImpl) { - m_DefaultMQProducerImpl.reset(DefaultMqProducerImpl); -} -} // namespace rocketmq diff --git a/src/consumer/DefaultMQPushConsumerImpl.h b/src/consumer/DefaultMQPushConsumerImpl.h deleted file mode 100644 index 34fb30739..000000000 --- a/src/consumer/DefaultMQPushConsumerImpl.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEFAULTMQPUSHCONSUMERIMPL_H__ -#define __DEFAULTMQPUSHCONSUMERIMPL_H__ - -#include -#include -#include -#include -#include -#include -#include -#include "AsyncCallback.h" -#include "ConsumeMessageContext.h" -#include "ConsumeMessageHook.h" -#include "DefaultMQProducerImpl.h" -#include "MQConsumer.h" -#include "MQMessageListener.h" -#include "MQMessageQueue.h" - -namespace rocketmq { - -class Rebalance; -class SubscriptionData; -class OffsetStore; -class PullAPIWrapper; -class PullRequest; -class ConsumeMsgService; -class TaskQueue; -class TaskThread; -class AsyncPullCallback; -class ConsumerRunningInfo; -//& mqs); - virtual void doRebalance(); - virtual void persistConsumerOffset(); - virtual void persistConsumerOffsetByResetOffset(); - virtual void updateTopicSubscribeInfo(const std::string& topic, std::vector& info); - virtual ConsumeType getConsumeType(); - virtual ConsumeFromWhere getConsumeFromWhere(); - void setConsumeFromWhere(ConsumeFromWhere consumeFromWhere); - virtual void getSubscriptions(std::vector&); - virtual void updateConsumeOffset(const MQMessageQueue& mq, int64 offset); - virtual void removeConsumeOffset(const MQMessageQueue& mq); - virtual PullResult pull(const MQMessageQueue& mq, const std::string& subExpression, int64 offset, int maxNums) { - return PullResult(); - } - virtual void pull(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums, - PullCallback* pPullCallback) {} - virtual ConsumerRunningInfo* getConsumerRunningInfo(); - //); - virtual bool producePullMsgTaskLater(boost::weak_ptr, int millis); - static void static_triggerNextPullRequest(void* context, - boost::asio::deadline_timer* t, - boost::weak_ptr); - void triggerNextPullRequest(boost::asio::deadline_timer* t, boost::weak_ptr); - void runPullMsgQueue(TaskQueue* pTaskQueue); - void pullMessage(boost::weak_ptr pullrequest); - void pullMessageAsync(boost::weak_ptr pullrequest); - void setAsyncPull(bool asyncFlag); - AsyncPullCallback* getAsyncPullCallBack(boost::weak_ptr, MQMessageQueue msgQueue); - void shutdownAsyncPullCallBack(); - - /* - for orderly consume, set the pull num of message size by each pullMsg, - default value is 1; - */ - void setConsumeMessageBatchMaxSize(int consumeMessageBatchMaxSize); - int getConsumeMessageBatchMaxSize() const; - - /* - set consuming thread count, default value is cpu cores - */ - void setConsumeThreadCount(int threadCount); - int getConsumeThreadCount() const; - void setMaxReconsumeTimes(int maxReconsumeTimes); - int getMaxReconsumeTimes() const; - - /* - set pullMsg thread count, default value is cpu cores - */ - void setPullMsgThreadPoolCount(int threadCount); - int getPullMsgThreadPoolCount() const; - - /* - set max cache msg size perQueue in memory if consumer could not consume msgs - immediately - default maxCacheMsgSize perQueue is 1000, set range is:1~65535 - */ - void setMaxCacheMsgSizePerQueue(int maxCacheSize); - int getMaxCacheMsgSizePerQueue() const; - void submitSendTraceRequest(MQMessage& msg, SendCallback* pSendCallback); - bool hasConsumeMessageHook(); - - void registerConsumeMessageHook(std::shared_ptr& hook); - void setDefaultMqProducerImpl(DefaultMQProducerImpl* DefaultMqProducerImpl); - void executeConsumeMessageHookBefore(ConsumeMessageContext* context); - void executeConsumeMessageHookAfter(ConsumeMessageContext* context); - - private: - void checkConfig(); - void copySubscription(); - void updateTopicSubscribeInfoWhenSubscriptionChanged(); - bool dealWithNameSpace(); - void logConfigs(); - - bool dealWithMessageTrace(); - void createMessageTraceInnerProducer(); - void shutdownMessageTraceInnerProducer(); - - private: - uint64_t m_startTime; - ConsumeFromWhere m_consumeFromWhere; - std::map m_subTopics; - int m_consumeThreadCount; - OffsetStore* m_pOffsetStore; - Rebalance* m_pRebalance; - PullAPIWrapper* m_pPullAPIWrapper; - ConsumeMsgService* m_consumerService; - MQMessageListener* m_pMessageListener; - int m_consumeMessageBatchMaxSize; - int m_maxMsgCacheSize; - int m_maxReconsumeTimes = -1; - boost::asio::io_service m_async_ioService; - boost::scoped_ptr m_async_service_thread; - - typedef std::map PullMAP; - PullMAP m_PullCallback; - bool m_asyncPull; - int m_asyncPullTimeout; - int m_pullMsgThreadPoolNum; - - private: - TaskQueue* m_pullmsgQueue; - std::unique_ptr m_pullmsgThread; - - // used for trace - std::vector > m_consumeMessageHookList; - std::shared_ptr m_DefaultMQProducerImpl; -}; -// -#include - -#include -#include -#include -#include -#include -#include -namespace rocketmq { - -//selectConsumer(groupName); - if (pConsumer) { - LOG_INFO("new LocalFileOffsetStore"); - string directoryName = UtilAll::getLocalAddress() + "@" + pConsumer->getInstanceName(); - m_storePath = ".rocketmq_offsets/" + directoryName + "/" + groupName; - string homeDir(UtilAll::getHomeDirectory()); - m_storeFile = homeDir + "/" + m_storePath + "/offsets.Json"; - - string storePath(homeDir); - storePath.append("/").append(m_storePath); - boost::filesystem::path dir(storePath); - boost::system::error_code ec; - if (!boost::filesystem::exists(dir, ec)) { - if (!boost::filesystem::create_directories(dir, ec)) { - LOG_ERROR("create offset store dir:%s error", storePath.c_str()); - string errorMsg("create offset store dir fail: "); - errorMsg.append(storePath); - THROW_MQEXCEPTION(MQClientException, errorMsg, -1); - } - } - } -} - -LocalFileOffsetStore::~LocalFileOffsetStore() {} - -void LocalFileOffsetStore::load() { - std::ifstream ifs(m_storeFile.c_str(), std::ios::in); - if (ifs.good()) { - if (ifs.is_open()) { - if (ifs.peek() != std::ifstream::traits_type::eof()) { - map m_offsetTable_tmp; - boost::system::error_code e; - try { - boost::archive::text_iarchive ia(ifs); - ia >> m_offsetTable_tmp; - } catch (...) { - LOG_ERROR( - "load offset store file failed, please check whether file: %s is " - "cleared by operator, if so, delete this offsets.Json file and " - "then restart consumer", - m_storeFile.c_str()); - ifs.close(); - string errorMsg("load offset store file: "); - errorMsg.append(m_storeFile) - .append( - " failed, please check whether offsets.Json is cleared by " - "operator, if so, delete this offsets.Json file and then " - "restart consumer"); - THROW_MQEXCEPTION(MQClientException, errorMsg, -1); - } - ifs.close(); - - for (map::iterator it = m_offsetTable_tmp.begin(); it != m_offsetTable_tmp.end(); ++it) { - // LOG_INFO("it->first:%s, it->second:%lld", it->first.c_str(), - // it->second); - Json::Reader reader; - Json::Value object; - reader.parse(it->first.c_str(), object); - MQMessageQueue mq(object["topic"].asString(), object["brokerName"].asString(), object["queueId"].asInt()); - updateOffset(mq, it->second); - } - m_offsetTable_tmp.clear(); - } else { - LOG_ERROR( - "open offset store file failed, please check whether file: %s is " - "cleared by operator, if so, delete this offsets.Json file and " - "then restart consumer", - m_storeFile.c_str()); - THROW_MQEXCEPTION(MQClientException, - "open offset store file failed, please check whether " - "offsets.Json is cleared by operator, if so, delete " - "this offsets.Json file and then restart consumer", - -1); - } - } else { - LOG_ERROR( - "open offset store file failed, please check whether file:%s is " - "deleted by operator and then restart consumer", - m_storeFile.c_str()); - THROW_MQEXCEPTION(MQClientException, - "open offset store file failed, please check " - "directory:%s is deleted by operator or offset.Json " - "file is cleared by operator, and then restart " - "consumer", - -1); - } - } else { - LOG_WARN( - "offsets.Json file not exist, maybe this is the first time " - "consumation"); - } -} - -void LocalFileOffsetStore::updateOffset(const MQMessageQueue& mq, int64 offset) { - boost::lock_guard lock(m_lock); - m_offsetTable[mq] = offset; -} - -int64 LocalFileOffsetStore::readOffset(const MQMessageQueue& mq, - ReadOffsetType type, - const SessionCredentials& session_credentials) { - switch (type) { - case MEMORY_FIRST_THEN_STORE: - case READ_FROM_MEMORY: { - boost::lock_guard lock(m_lock); - MQ2OFFSET::iterator it = m_offsetTable.find(mq); - if (it != m_offsetTable.end()) { - return it->second; - } else if (READ_FROM_MEMORY == type) { - return -1; - } - } - case READ_FROM_STORE: { - try { - load(); - } catch (MQException& e) { - LOG_ERROR("catch exception when load local file"); - return -1; - } - boost::lock_guard lock(m_lock); - MQ2OFFSET::iterator it = m_offsetTable.find(mq); - if (it != m_offsetTable.end()) { - return it->second; - } - } - default: - break; - } - LOG_ERROR("can not readOffset from offsetStore.json, maybe first time consumation"); - return -1; -} - -void LocalFileOffsetStore::persist(const MQMessageQueue& mq, const SessionCredentials& session_credentials) {} - -void LocalFileOffsetStore::persistAll(const std::vector& mqs) { - boost::lock_guard lock(m_lock); - - map m_offsetTable_tmp; - vector::const_iterator it = mqs.begin(); - for (; it != mqs.end(); ++it) { - MessageQueue mq_tmp((*it).getTopic(), (*it).getBrokerName(), (*it).getQueueId()); - string mqKey = mq_tmp.toJson().toStyledString(); - m_offsetTable_tmp[mqKey] = m_offsetTable[*it]; - } - - std::ofstream s; - string storefile_bak(m_storeFile); - storefile_bak.append(".bak"); - s.open(storefile_bak.c_str(), std::ios::out); - if (s.is_open()) { - try { - boost::archive::text_oarchive oa(s); - // Boost is nervous that archiving non-const class instances which might - // cause a problem with object tracking if different tracked objects use - // the same address. - oa << const_cast&>(m_offsetTable_tmp); - } catch (...) { - LOG_ERROR("persist offset store file:%s failed", m_storeFile.c_str()); - s.close(); - THROW_MQEXCEPTION(MQClientException, "persistAll:open offset store file failed", -1); - } - s.close(); - if (!UtilAll::ReplaceFile(storefile_bak, m_storeFile)) - LOG_ERROR("could not rename bak file:%s", strerror(errno)); - m_offsetTable_tmp.clear(); - } else { - LOG_ERROR("open offset store file:%s failed", m_storeFile.c_str()); - m_offsetTable_tmp.clear(); - THROW_MQEXCEPTION(MQClientException, "persistAll:open offset store file failed", -1); - } -} - -void LocalFileOffsetStore::removeOffset(const MQMessageQueue& mq) {} - -// lock(m_lock); - m_offsetTable[mq] = offset; -} - -int64 RemoteBrokerOffsetStore::readOffset(const MQMessageQueue& mq, - ReadOffsetType type, - const SessionCredentials& session_credentials) { - switch (type) { - case MEMORY_FIRST_THEN_STORE: - case READ_FROM_MEMORY: { - boost::lock_guard lock(m_lock); - - MQ2OFFSET::iterator it = m_offsetTable.find(mq); - if (it != m_offsetTable.end()) { - return it->second; - } else if (READ_FROM_MEMORY == type) { - return -1; - } - } - case READ_FROM_STORE: { - try { - int64 brokerOffset = fetchConsumeOffsetFromBroker(mq, session_credentials); - // lock(m_lock); - offsetTable = m_offsetTable; - } - - MQ2OFFSET::iterator it = offsetTable.find(mq); - if (it != offsetTable.end()) { - try { - updateConsumeOffsetToBroker(mq, it->second, session_credentials); - } catch (MQException& e) { - LOG_ERROR("updateConsumeOffsetToBroker %s ,offset:[%lld] error", mq.toString().c_str(), it->second); - } - } -} - -void RemoteBrokerOffsetStore::persistAll(const std::vector& mq) {} - -void RemoteBrokerOffsetStore::removeOffset(const MQMessageQueue& mq) { - boost::lock_guard lock(m_lock); - if (m_offsetTable.find(mq) != m_offsetTable.end()) - m_offsetTable.erase(mq); -} - -void RemoteBrokerOffsetStore::updateConsumeOffsetToBroker(const MQMessageQueue& mq, - int64 offset, - const SessionCredentials& session_credentials) { - unique_ptr pFindBrokerResult(m_pClientFactory->findBrokerAddressInAdmin(mq.getBrokerName())); - - if (pFindBrokerResult == NULL) { - m_pClientFactory->updateTopicRouteInfoFromNameServer(mq.getTopic(), session_credentials); - pFindBrokerResult.reset(m_pClientFactory->findBrokerAddressInAdmin(mq.getBrokerName())); - } - - if (pFindBrokerResult != NULL) { - UpdateConsumerOffsetRequestHeader* pRequestHeader = new UpdateConsumerOffsetRequestHeader(); - pRequestHeader->topic = mq.getTopic(); - pRequestHeader->consumerGroup = m_groupName; - pRequestHeader->queueId = mq.getQueueId(); - pRequestHeader->commitOffset = offset; - - try { - LOG_INFO("oneway updateConsumeOffsetToBroker of mq:%s, its offset is:%lld", mq.toString().c_str(), offset); - return m_pClientFactory->getMQClientAPIImpl()->updateConsumerOffsetOneway( - pFindBrokerResult->brokerAddr, pRequestHeader, 1000 * 5, session_credentials); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - } - } - LOG_WARN("The broker not exist"); -} - -int64 RemoteBrokerOffsetStore::fetchConsumeOffsetFromBroker(const MQMessageQueue& mq, - const SessionCredentials& session_credentials) { - unique_ptr pFindBrokerResult(m_pClientFactory->findBrokerAddressInAdmin(mq.getBrokerName())); - - if (pFindBrokerResult == NULL) { - m_pClientFactory->updateTopicRouteInfoFromNameServer(mq.getTopic(), session_credentials); - pFindBrokerResult.reset(m_pClientFactory->findBrokerAddressInAdmin(mq.getBrokerName())); - } - - if (pFindBrokerResult != NULL) { - QueryConsumerOffsetRequestHeader* pRequestHeader = new QueryConsumerOffsetRequestHeader(); - pRequestHeader->topic = mq.getTopic(); - pRequestHeader->consumerGroup = m_groupName; - pRequestHeader->queueId = mq.getQueueId(); - - return m_pClientFactory->getMQClientAPIImpl()->queryConsumerOffset(pFindBrokerResult->brokerAddr, pRequestHeader, - 1000 * 5, session_credentials); - } else { - LOG_ERROR("The broker not exist when fetchConsumeOffsetFromBroker"); - THROW_MQEXCEPTION(MQClientException, "The broker not exist", -1); - } -} -// -#include -#include -#include -#include "MQMessageQueue.h" -#include "SessionCredentials.h" - -namespace rocketmq { -class MQClientFactory; -//& mq) = 0; - virtual void removeOffset(const MQMessageQueue& mq) = 0; - - protected: - std::string m_groupName; - typedef std::map MQ2OFFSET; - MQ2OFFSET m_offsetTable; - MQClientFactory* m_pClientFactory; - boost::mutex m_lock; -}; - -//& mq); - virtual void removeOffset(const MQMessageQueue& mq); - - private: - std::string m_storePath; - std::string m_storeFile; -}; - -//& mq); - virtual void removeOffset(const MQMessageQueue& mq); - - private: - void updateConsumeOffsetToBroker(const MQMessageQueue& mq, - int64 offset, - const SessionCredentials& session_credentials); - int64 fetchConsumeOffsetFromBroker(const MQMessageQueue& mq, const SessionCredentials& session_credentials); -}; -// lock(m_lock); - m_pullFromWhichNodeTable[mq] = brokerId; -} - -int PullAPIWrapper::recalculatePullFromWhichNode(const MQMessageQueue& mq) { - boost::lock_guard lock(m_lock); - if (m_pullFromWhichNodeTable.find(mq) != m_pullFromWhichNodeTable.end()) { - return m_pullFromWhichNodeTable[mq]; - } - return MASTER_ID; -} - -PullResult PullAPIWrapper::processPullResult(const MQMessageQueue& mq, - PullResult* pullResult, - SubscriptionData* subscriptionData) { - PullResultExt* pResultExt = static_cast(pullResult); - if (pResultExt == NULL) { - string errMsg("The pullResult NULL of"); - errMsg.append(mq.toString()); - THROW_MQEXCEPTION(MQClientException, errMsg, -1); - } - - //suggestWhichBrokerId); - - vector msgFilterList; - if (pResultExt->pullStatus == FOUND) { - // msgAllList; - MQDecoder::decodes(&pResultExt->msgMemBlock, msgAllList); - - //getTagsSet().empty()) { - msgFilterList.reserve(msgAllList.size()); - vector::iterator it = msgAllList.begin(); - for (; it != msgAllList.end(); ++it) { - string msgTag = (*it).getTags(); - if (subscriptionData->containTag(msgTag)) { - msgFilterList.push_back(*it); - } - } - } else { - msgFilterList.swap(msgAllList); - } - } - - return PullResult(pResultExt->pullStatus, pResultExt->nextBeginOffset, pResultExt->minOffset, pResultExt->maxOffset, - msgFilterList); -} - -PullResult* PullAPIWrapper::pullKernelImpl(const MQMessageQueue& mq, // 1 - const string& subExpression, // 2 - int64 subVersion, // 3 - int64 offset, // 4 - int maxNums, // 5 - int sysFlag, // 6 - int64 commitOffset, // 7 - int brokerSuspendMaxTimeMillis, // 8 - int timeoutMillis, // 9 - int communicationMode, // 10 - PullCallback* pullCallback, - const SessionCredentials& session_credentials, - void* pArg /*= NULL*/) { - unique_ptr pFindBrokerResult( - m_MQClientFactory->findBrokerAddressInSubscribe(mq.getBrokerName(), recalculatePullFromWhichNode(mq), false)); - //updateTopicRouteInfoFromNameServer(mq.getTopic(), session_credentials); - pFindBrokerResult.reset( - m_MQClientFactory->findBrokerAddressInSubscribe(mq.getBrokerName(), recalculatePullFromWhichNode(mq), false)); - } - - if (pFindBrokerResult != NULL) { - int sysFlagInner = sysFlag; - - if (pFindBrokerResult->slave) { - sysFlagInner = PullSysFlag::clearCommitOffsetFlag(sysFlagInner); - } - - PullMessageRequestHeader* pRequestHeader = new PullMessageRequestHeader(); - pRequestHeader->consumerGroup = m_consumerGroup; - pRequestHeader->topic = mq.getTopic(); - pRequestHeader->queueId = mq.getQueueId(); - pRequestHeader->queueOffset = offset; - pRequestHeader->maxMsgNums = maxNums; - pRequestHeader->sysFlag = sysFlagInner; - pRequestHeader->commitOffset = commitOffset; - pRequestHeader->suspendTimeoutMillis = brokerSuspendMaxTimeMillis; - pRequestHeader->subscription = subExpression; - pRequestHeader->subVersion = subVersion; - - return m_MQClientFactory->getMQClientAPIImpl()->pullMessage(pFindBrokerResult->brokerAddr, pRequestHeader, - timeoutMillis, communicationMode, pullCallback, pArg, - session_credentials); - } - THROW_MQEXCEPTION(MQClientException, "The broker not exist", -1); -} - -} // namespace rocketmq diff --git a/src/consumer/PullAPIWrapper.h b/src/consumer/PullAPIWrapper.h deleted file mode 100644 index dca370a36..000000000 --- a/src/consumer/PullAPIWrapper.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef _PULLAPIWRAPPER_H_ -#define _PULLAPIWRAPPER_H_ - -#include -#include -#include "AsyncCallback.h" -#include "MQMessageQueue.h" -#include "SessionCredentials.h" -#include "SubscriptionData.h" - -namespace rocketmq { -class MQClientFactory; -// m_pullFromWhichNodeTable; -}; - -// lock(m_pullRequestLock); - if (this != &other) { - m_groupname = other.m_groupname; - m_nextOffset = other.m_nextOffset; - m_bDropped.store(other.m_bDropped.load()); - m_queueOffsetMax = other.m_queueOffsetMax; - m_messageQueue = other.m_messageQueue; - m_msgTreeMap = other.m_msgTreeMap; - m_msgTreeMapTemp = other.m_msgTreeMapTemp; - m_lastPullTimestamp = other.m_lastPullTimestamp; - m_lastConsumeTimestamp = other.m_lastConsumeTimestamp; - } - return *this; -} - -void PullRequest::putMessage(vector& msgs) { - boost::lock_guard lock(m_pullRequestLock); - - vector::iterator it = msgs.begin(); - for (; it != msgs.end(); it++) { - m_msgTreeMap[it->getQueueOffset()] = *it; - m_queueOffsetMax = (std::max)(m_queueOffsetMax, it->getQueueOffset()); - } - LOG_DEBUG("PullRequest: putMessage m_queueOffsetMax:%lld ", m_queueOffsetMax); -} - -void PullRequest::getMessage(vector& msgs) { - boost::lock_guard lock(m_pullRequestLock); - - map::iterator it = m_msgTreeMap.begin(); - for (; it != m_msgTreeMap.end(); it++) { - msgs.push_back(it->second); - } -} - -int64 PullRequest::getCacheMinOffset() { - boost::lock_guard lock(m_pullRequestLock); - if (m_msgTreeMap.empty()) { - return 0; - } else { - map::iterator it = m_msgTreeMap.begin(); - MQMessageExt msg = it->second; - return msg.getQueueOffset(); - } -} - -int64 PullRequest::getCacheMaxOffset() { - return m_queueOffsetMax; -} - -int PullRequest::getCacheMsgCount() { - boost::lock_guard lock(m_pullRequestLock); - return m_msgTreeMap.size() + m_msgTreeMapTemp.size(); -} - -void PullRequest::getMessageByQueueOffset(vector& msgs, int64 minQueueOffset, int64 maxQueueOffset) { - boost::lock_guard lock(m_pullRequestLock); - - int64 it = minQueueOffset; - for (; it <= maxQueueOffset; it++) { - msgs.push_back(m_msgTreeMap[it]); - } -} - -int64 PullRequest::removeMessage(vector& msgs) { - boost::lock_guard lock(m_pullRequestLock); - - int64 result = -1; - LOG_DEBUG("m_queueOffsetMax is:%lld", m_queueOffsetMax); - if (!m_msgTreeMap.empty()) { - result = m_queueOffsetMax + 1; - LOG_DEBUG(" offset result is:%lld, m_queueOffsetMax is:%lld, msgs size:" SIZET_FMT "", result, m_queueOffsetMax, - msgs.size()); - vector::iterator it = msgs.begin(); - for (; it != msgs.end(); it++) { - LOG_DEBUG("remove these msg from m_msgTreeMap, its offset:%lld", it->getQueueOffset()); - m_msgTreeMap.erase(it->getQueueOffset()); - } - - if (!m_msgTreeMap.empty()) { - map::iterator it = m_msgTreeMap.begin(); - result = it->first; - LOG_INFO("cache msg size:" SIZET_FMT " of pullRequest:%s, return offset result is:%lld", m_msgTreeMap.size(), - m_messageQueue.toString().c_str(), result); - } - } - - return result; -} - -void PullRequest::clearAllMsgs() { - boost::lock_guard lock(m_pullRequestLock); - - if (isDropped()) { - LOG_DEBUG("clear m_msgTreeMap as PullRequest had been dropped."); - m_msgTreeMap.clear(); - m_msgTreeMapTemp.clear(); - } -} - -void PullRequest::updateQueueMaxOffset(int64 queueOffset) { - // following 2 cases which may set queueOffset smaller than m_queueOffsetMax: - // 1. resetOffset cmd - // 2. during rebalance, if configured with CONSUMER_FROM_FIRST_OFFSET, when - // readOffset called by computePullFromWhere was failed, m_nextOffset will be - // setted to 0 - m_queueOffsetMax = queueOffset; -} - -void PullRequest::setDropped(bool dropped) { - int temp = (dropped == true ? 1 : 0); - m_bDropped.store(temp); - /* - m_queueOffsetMax = 0; - m_nextOffset = 0; - //the reason why not clear m_queueOffsetMax and m_nextOffset is due to - ConsumeMsgService and drop mq are concurrent running. - consider following situation: - 1>. ConsumeMsgService running - 2>. dorebalance, drop mq, reset m_nextOffset and m_queueOffsetMax - 3>. ConsumeMsgService calls removeMessages, if no other msgs in - m_msgTreeMap, m_queueOffsetMax(0)+1 will return; - 4>. updateOffset with 1, which is more smaller than correct offset. - */ -} - -bool PullRequest::isDropped() const { - return m_bDropped.load() == 1; -} - -int64 PullRequest::getNextOffset() { - boost::lock_guard lock(m_pullRequestLock); - return m_nextOffset; -} - -void PullRequest::setLocked(bool Locked) { - int temp = (Locked == true ? 1 : 0); - m_bLocked.store(temp); -} - -bool PullRequest::isLocked() const { - return m_bLocked.load() == 1; -} - -bool PullRequest::isLockExpired() const { - return (UtilAll::currentTimeMillis() - m_lastLockTimestamp) > RebalanceLockMaxLiveTime; -} - -void PullRequest::setLastLockTimestamp(int64 time) { - m_lastLockTimestamp = time; -} - -int64 PullRequest::getLastLockTimestamp() const { - return m_lastLockTimestamp; -} - -void PullRequest::setLastPullTimestamp(uint64 time) { - m_lastPullTimestamp = time; -} - -uint64 PullRequest::getLastPullTimestamp() const { - return m_lastPullTimestamp; -} - -bool PullRequest::isPullRequestExpired() const { - uint64 interval = m_lastPullTimestamp + MAX_PULL_IDLE_TIME; - if (interval <= UtilAll::currentTimeMillis()) { - LOG_WARN("PullRequest for [%s] has been expired %lld ms,m_lastPullTimestamp = %lld ms", - m_messageQueue.toString().c_str(), UtilAll::currentTimeMillis() - m_lastPullTimestamp, - m_lastPullTimestamp); - return true; - } - return false; -} - -void PullRequest::setLastConsumeTimestamp(uint64 time) { - m_lastConsumeTimestamp = time; -} - -uint64 PullRequest::getLastConsumeTimestamp() const { - return m_lastConsumeTimestamp; -} - -void PullRequest::setTryUnlockTimes(int time) { - m_lastLockTimestamp = time; -} - -int PullRequest::getTryUnlockTimes() const { - return m_lastLockTimestamp; -} - -void PullRequest::setNextOffset(int64 nextoffset) { - boost::lock_guard lock(m_pullRequestLock); - m_nextOffset = nextoffset; -} - -string PullRequest::getGroupName() const { - return m_groupname; -} - -boost::timed_mutex& PullRequest::getPullRequestCriticalSection() { - return m_consumeLock; -} - -void PullRequest::takeMessages(vector& msgs, int batchSize) { - boost::lock_guard lock(m_pullRequestLock); - for (int i = 0; i != batchSize; i++) { - map::iterator it = m_msgTreeMap.begin(); - if (it != m_msgTreeMap.end()) { - msgs.push_back(it->second); - m_msgTreeMapTemp[it->first] = it->second; - m_msgTreeMap.erase(it); - } - } -} - -void PullRequest::makeMessageToCosumeAgain(vector& msgs) { - boost::lock_guard lock(m_pullRequestLock); - for (unsigned int it = 0; it != msgs.size(); ++it) { - m_msgTreeMap[msgs[it].getQueueOffset()] = msgs[it]; - m_msgTreeMapTemp.erase(msgs[it].getQueueOffset()); - } -} - -int64 PullRequest::commit() { - boost::lock_guard lock(m_pullRequestLock); - if (!m_msgTreeMapTemp.empty()) { - int64 offset = (--m_msgTreeMapTemp.end())->first; - m_msgTreeMapTemp.clear(); - return offset + 1; - } else { - return -1; - } -} - -// -#include -#include -#include "MQMessageExt.h" -#include "MQMessageQueue.h" -#include "UtilAll.h" -namespace rocketmq { -//& msgs); - void getMessage(vector& msgs); - int64 getCacheMinOffset(); - int64 getCacheMaxOffset(); - int getCacheMsgCount(); - void getMessageByQueueOffset(vector& msgs, int64 minQueueOffset, int64 maxQueueOffset); - int64 removeMessage(vector& msgs); - void clearAllMsgs(); - - PullRequest& operator=(const PullRequest& other); - - void setDropped(bool dropped); - bool isDropped() const; - - int64 getNextOffset(); - void setNextOffset(int64 nextoffset); - - string getGroupName() const; - - void updateQueueMaxOffset(int64 queueOffset); - - void setLocked(bool Locked); - bool isLocked() const; - bool isLockExpired() const; - void setLastLockTimestamp(int64 time); - int64 getLastLockTimestamp() const; - void setLastPullTimestamp(uint64 time); - uint64 getLastPullTimestamp() const; - bool isPullRequestExpired() const; - void setLastConsumeTimestamp(uint64 time); - uint64 getLastConsumeTimestamp() const; - void setTryUnlockTimes(int time); - int getTryUnlockTimes() const; - void takeMessages(vector& msgs, int batchSize); - int64 commit(); - void makeMessageToCosumeAgain(vector& msgs); - boost::timed_mutex& getPullRequestCriticalSection(); - bool removePullMsgEvent(bool force = false); - bool addPullMsgEvent(); - /** - * Check if there is an in-flight pull request. - */ - bool hasInFlightPullRequest() const; - - public: - MQMessageQueue m_messageQueue; - static const uint64 RebalanceLockInterval; // ms - static const uint64 RebalanceLockMaxLiveTime; // ms - static const uint64 MAX_PULL_IDLE_TIME; // ms - - private: - string m_groupname; - int64 m_nextOffset; - int64 m_queueOffsetMax; - boost::atomic m_bDropped; - boost::atomic m_bLocked; - map m_msgTreeMap; - map m_msgTreeMapTemp; - boost::mutex m_pullRequestLock; - uint64 m_lastLockTimestamp; // ms - // uint64 m_tryUnlockTimes; - uint64 m_lastPullTimestamp; - uint64 m_lastConsumeTimestamp; - boost::timed_mutex m_consumeLock; -}; -//& src) - : pullStatus(pullStatus), nextBeginOffset(nextBeginOffset), minOffset(minOffset), maxOffset(maxOffset) { - msgFoundList.reserve(src.size()); - for (size_t i = 0; i < src.size(); i++) { - msgFoundList.push_back(src[i]); - } -} - -PullResult::~PullResult() { - msgFoundList.clear(); -} - -//(messageBinary)) {} - - PullResultExt(PullStatus pullStatus, - int64 nextBeginOffset, - int64 minOffset, - int64 maxOffset, - int suggestWhichBrokerId) - : PullResult(pullStatus, nextBeginOffset, minOffset, maxOffset), suggestWhichBrokerId(suggestWhichBrokerId) {} - - virtual ~PullResultExt() {} - - public: - int suggestWhichBrokerId; - MemoryBlock msgMemBlock; -}; - -} // namespace rocketmq diff --git a/src/consumer/Rebalance.cpp b/src/consumer/Rebalance.cpp deleted file mode 100644 index 18c8b2f8d..000000000 --- a/src/consumer/Rebalance.cpp +++ /dev/null @@ -1,659 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "Rebalance.h" -#include "DefaultMQPushConsumerImpl.h" -#include "LockBatchBody.h" -#include "Logging.h" -#include "MQClientAPIImpl.h" -#include "MQClientFactory.h" -#include "OffsetStore.h" - -namespace rocketmq { -//::iterator it = m_subscriptionData.begin(); - for (; it != m_subscriptionData.end(); ++it) { - deleteAndZero(it->second); - } - m_subscriptionData.clear(); - } - { - /* - MQ2PULLREQ::iterator it = m_requestQueueTable.begin(); - for (; it != m_requestQueueTable.end(); ++it) { - delete it->second; - it->second = NULL; - } - m_requestQueueTable.clear();*/ - } - m_topicSubscribeInfoTable.clear(); - m_pConsumer = NULL; - m_pClientFactory = NULL; - deleteAndZero(m_pAllocateMQStrategy); -} - -void Rebalance::doRebalance() { - LOG_DEBUG("start doRebalance"); - try { - map::iterator it = m_subscriptionData.begin(); - for (; it != m_subscriptionData.end(); ++it) { - string topic = (it->first); - LOG_DEBUG("current topic is:%s", topic.c_str()); - // mqs - vector mqAll; - if (!getTopicSubscribeInfo(topic, mqAll)) { - continue; - } - if (mqAll.empty()) { - if (!UtilAll::startsWith_retry(topic)) { - std::string msg("#doRebalance. mqAll for topic:"); - msg.append(topic); - msg.append(" is empty"); - LOG_ERROR("Queues to allocate are empty. Msg: %s", msg.c_str()); - // to check, return error or throw exception - THROW_MQEXCEPTION(MQClientException, msg, -1); - } - } - - //getMessageModel()) { - case BROADCASTING: { - bool changed = updateRequestTableInRebalance(topic, mqAll); - if (changed) { - messageQueueChanged(topic, mqAll, mqAll); - } - break; - } - case CLUSTERING: { - vector cidAll; - m_pClientFactory->findConsumerIds(topic, m_pConsumer->getGroupName(), cidAll, - m_pConsumer->getSessionCredentials()); - - if (cidAll.empty()) { - LOG_ERROR("[ERROR] Get empty consumer IDs. Consumer Group: %s, Topic: %s", - m_pConsumer->getGroupName().c_str(), topic.c_str()); - // Should skip this round of re-balance immediately if consumer ID set is empty. - THROW_MQEXCEPTION(MQClientException, "doRebalance the cidAll is empty", -1); - } - // log - for (int i = 0; i < (int)cidAll.size(); ++i) { - LOG_DEBUG("client id:%s of topic:%s", cidAll[i].c_str(), topic.c_str()); - } - // allocateResult; - try { - m_pAllocateMQStrategy->allocate(m_pConsumer->getMQClientId(), mqAll, cidAll, allocateResult); - } catch (MQException& e) { - std::string errMsg("Allocate message queue for ConsumerGroup["); - errMsg.append(m_pConsumer->getGroupName()); - errMsg.append("],Topic["); - errMsg.append(topic); - errMsg.append("] failed. "); - LOG_ERROR("%s", errMsg.c_str()); - THROW_MQEXCEPTION(MQClientException, errMsg, -1); - } - - // log - for (int i = 0; i < (int)allocateResult.size(); ++i) { - LOG_DEBUG("allocate mq:%s", allocateResult[i].toString().c_str()); - } - - //getGroupName() << ", Topic: " << topic - << ", Current Consumer ID: " << m_pConsumer->getMQClientId() << "] is changed.\n" - << "Total Queue :#" << mqAll.size() << ", Total Consumer :#" << cidAll.size() - << ", Allocated Queues are: \n"; - - for (vector::size_type i = 0; i < allocateResult.size(); ++i) { - ss << allocateResult[i].toString() << "\n"; - } - // Log allocation result. - LOG_INFO("%s", ss.str().c_str()); - - messageQueueChanged(topic, mqAll, allocateResult); - break; - } - } - default: - break; - } - } - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - } -} - -void Rebalance::persistConsumerOffset() { - DefaultMQPushConsumerImpl* pConsumer = static_cast(m_pConsumer); - OffsetStore* pOffsetStore = pConsumer->getOffsetStore(); - vector mqs; - { - boost::lock_guard lock(m_requestTableMutex); - MQ2PULLREQ::iterator it = m_requestQueueTable.begin(); - for (; it != m_requestQueueTable.end(); ++it) { - if (it->second && (!it->second->isDropped())) { - mqs.push_back(it->first); - } - } - } - - if (pConsumer->getMessageModel() == BROADCASTING) { - pOffsetStore->persistAll(mqs); - } else { - vector::iterator it2 = mqs.begin(); - for (; it2 != mqs.end(); ++it2) { - pOffsetStore->persist(*it2, m_pConsumer->getSessionCredentials()); - } - } -} - -void Rebalance::persistConsumerOffsetByResetOffset() { - DefaultMQPushConsumerImpl* pConsumer = static_cast(m_pConsumer); - OffsetStore* pOffsetStore = pConsumer->getOffsetStore(); - vector mqs; - { - boost::lock_guard lock(m_requestTableMutex); - MQ2PULLREQ::iterator it = m_requestQueueTable.begin(); - for (; it != m_requestQueueTable.end(); ++it) { - if (it->second) { // even if it was dropped, also need update offset when - // rcv resetOffset cmd - mqs.push_back(it->first); - } - } - } - vector::iterator it2 = mqs.begin(); - for (; it2 != mqs.end(); ++it2) { - pOffsetStore->persist(*it2, m_pConsumer->getSessionCredentials()); - } -} - -SubscriptionData* Rebalance::getSubscriptionData(const string& topic) { - if (m_subscriptionData.find(topic) != m_subscriptionData.end()) { - return m_subscriptionData[topic]; - } - return NULL; -} - -map& Rebalance::getSubscriptionInner() { - return m_subscriptionData; -} - -void Rebalance::setSubscriptionData(const string& topic, SubscriptionData* pdata) { - if (pdata != NULL && m_subscriptionData.find(topic) == m_subscriptionData.end()) - m_subscriptionData[topic] = pdata; -} - -void Rebalance::setTopicSubscribeInfo(const string& topic, vector& mqs) { - if (m_subscriptionData.find(topic) != m_subscriptionData.end()) { - { - boost::lock_guard lock(m_topicSubscribeInfoTableMutex); - if (m_topicSubscribeInfoTable.find(topic) != m_topicSubscribeInfoTable.end()) - m_topicSubscribeInfoTable.erase(topic); - m_topicSubscribeInfoTable[topic] = mqs; - } - // log - vector::iterator it = mqs.begin(); - for (; it != mqs.end(); ++it) { - LOG_DEBUG("topic [%s] has :%s", topic.c_str(), (*it).toString().c_str()); - } - } -} - -bool Rebalance::getTopicSubscribeInfo(const string& topic, vector& mqs) { - boost::lock_guard lock(m_topicSubscribeInfoTableMutex); - if (m_topicSubscribeInfoTable.find(topic) != m_topicSubscribeInfoTable.end()) { - mqs = m_topicSubscribeInfoTable[topic]; - return true; - } - return false; -} - -void Rebalance::addPullRequest(MQMessageQueue mq, boost::shared_ptr pPullRequest) { - boost::lock_guard lock(m_requestTableMutex); - m_requestQueueTable[mq] = pPullRequest; -} - -void Rebalance::removePullRequest(MQMessageQueue mq) { - boost::lock_guard lock(m_requestTableMutex); - if (m_requestQueueTable.find(mq) != m_requestQueueTable.end()) { - m_requestQueueTable.erase(mq); - } -} -bool Rebalance::isPullRequestExist(MQMessageQueue mq) { - boost::lock_guard lock(m_requestTableMutex); - if (m_requestQueueTable.find(mq) != m_requestQueueTable.end()) { - return true; - } - return false; -} -boost::weak_ptr Rebalance::getPullRequest(MQMessageQueue mq) { - boost::lock_guard lock(m_requestTableMutex); - if (m_requestQueueTable.find(mq) != m_requestQueueTable.end()) { - return m_requestQueueTable[mq]; - } - return boost::weak_ptr(); -} - -map> Rebalance::getPullRequestTable() { - boost::lock_guard lock(m_requestTableMutex); - return m_requestQueueTable; -} - -void Rebalance::unlockAll(bool oneWay) { - map*> brokerMqs; - MQ2PULLREQ requestQueueTable = getPullRequestTable(); - for (MQ2PULLREQ::iterator it = requestQueueTable.begin(); it != requestQueueTable.end(); ++it) { - if (!(it->second->isDropped())) { - if (brokerMqs.find(it->first.getBrokerName()) == brokerMqs.end()) { - vector* mqs = new vector; - brokerMqs[it->first.getBrokerName()] = mqs; - } else { - brokerMqs[it->first.getBrokerName()]->push_back(it->first); - } - } - } - LOG_INFO("unLockAll " SIZET_FMT " broker mqs", brokerMqs.size()); - for (map*>::iterator itb = brokerMqs.begin(); itb != brokerMqs.end(); ++itb) { - unique_ptr pFindBrokerResult( - m_pClientFactory->findBrokerAddressInSubscribe(itb->first, MASTER_ID, true)); - if (!pFindBrokerResult) { - LOG_ERROR("unlockAll findBrokerAddressInSubscribe ret null for broker:%s", itb->first.data()); - continue; - } - unique_ptr unlockBatchRequest(new UnlockBatchRequestBody()); - vector mqs(*(itb->second)); - unlockBatchRequest->setClientId(m_pConsumer->getMQClientId()); - unlockBatchRequest->setConsumerGroup(m_pConsumer->getGroupName()); - unlockBatchRequest->setMqSet(mqs); - - try { - m_pClientFactory->getMQClientAPIImpl()->unlockBatchMQ(pFindBrokerResult->brokerAddr, unlockBatchRequest.get(), - 1000, m_pConsumer->getSessionCredentials()); - for (unsigned int i = 0; i != mqs.size(); ++i) { - boost::weak_ptr pullreq = getPullRequest(mqs[i]); - if (!pullreq.expired()) { - LOG_INFO("unlockBatchMQ success of mq:%s", mqs[i].toString().c_str()); - pullreq.lock()->setLocked(false); - } else { - LOG_ERROR("unlockBatchMQ fails of mq:%s", mqs[i].toString().c_str()); - } - } - } catch (MQException& e) { - LOG_ERROR("unlockBatchMQ fails"); - } - deleteAndZero(itb->second); - } - brokerMqs.clear(); -} - -void Rebalance::unlock(MQMessageQueue mq) { - unique_ptr pFindBrokerResult( - m_pClientFactory->findBrokerAddressInSubscribe(mq.getBrokerName(), MASTER_ID, true)); - if (!pFindBrokerResult) { - LOG_ERROR("unlock findBrokerAddressInSubscribe ret null for broker:%s", mq.getBrokerName().data()); - return; - } - unique_ptr unlockBatchRequest(new UnlockBatchRequestBody()); - vector mqs; - mqs.push_back(mq); - unlockBatchRequest->setClientId(m_pConsumer->getMQClientId()); - unlockBatchRequest->setConsumerGroup(m_pConsumer->getGroupName()); - unlockBatchRequest->setMqSet(mqs); - - try { - m_pClientFactory->getMQClientAPIImpl()->unlockBatchMQ(pFindBrokerResult->brokerAddr, unlockBatchRequest.get(), 1000, - m_pConsumer->getSessionCredentials()); - for (unsigned int i = 0; i != mqs.size(); ++i) { - boost::weak_ptr pullreq = getPullRequest(mqs[i]); - if (!pullreq.expired()) { - LOG_INFO("unlock success of mq:%s", mqs[i].toString().c_str()); - pullreq.lock()->setLocked(false); - } else { - LOG_ERROR("unlock fails of mq:%s", mqs[i].toString().c_str()); - } - } - } catch (MQException& e) { - LOG_ERROR("unlock fails of mq:%s", mq.toString().c_str()); - } -} - -void Rebalance::lockAll() { - map*> brokerMqs; - MQ2PULLREQ requestQueueTable = getPullRequestTable(); - for (MQ2PULLREQ::iterator it = requestQueueTable.begin(); it != requestQueueTable.end(); ++it) { - if (!(it->second->isDropped())) { - string brokerKey = it->first.getBrokerName() + it->first.getTopic(); - if (brokerMqs.find(brokerKey) == brokerMqs.end()) { - vector* mqs = new vector; - brokerMqs[brokerKey] = mqs; - brokerMqs[brokerKey]->push_back(it->first); - } else { - brokerMqs[brokerKey]->push_back(it->first); - } - } - } - LOG_INFO("LockAll " SIZET_FMT " broker mqs", brokerMqs.size()); - for (map*>::iterator itb = brokerMqs.begin(); itb != brokerMqs.end(); ++itb) { - string brokerName = (*(itb->second))[0].getBrokerName(); - unique_ptr pFindBrokerResult( - m_pClientFactory->findBrokerAddressInSubscribe(brokerName, MASTER_ID, true)); - if (!pFindBrokerResult) { - LOG_ERROR("lockAll findBrokerAddressInSubscribe ret null for broker:%s", brokerName.data()); - continue; - } - unique_ptr lockBatchRequest(new LockBatchRequestBody()); - lockBatchRequest->setClientId(m_pConsumer->getMQClientId()); - lockBatchRequest->setConsumerGroup(m_pConsumer->getGroupName()); - lockBatchRequest->setMqSet(*(itb->second)); - LOG_INFO("try to lock:" SIZET_FMT " mqs of broker:%s", itb->second->size(), itb->first.c_str()); - try { - vector messageQueues; - m_pClientFactory->getMQClientAPIImpl()->lockBatchMQ(pFindBrokerResult->brokerAddr, lockBatchRequest.get(), - messageQueues, 1000, m_pConsumer->getSessionCredentials()); - for (unsigned int i = 0; i != messageQueues.size(); ++i) { - boost::weak_ptr pullreq = getPullRequest(messageQueues[i]); - if (!pullreq.expired()) { - LOG_INFO("lockBatchMQ success of mq:%s", messageQueues[i].toString().c_str()); - pullreq.lock()->setLocked(true); - pullreq.lock()->setLastLockTimestamp(UtilAll::currentTimeMillis()); - } else { - LOG_ERROR("lockBatchMQ fails of mq:%s", messageQueues[i].toString().c_str()); - } - } - messageQueues.clear(); - } catch (MQException& e) { - LOG_ERROR("lockBatchMQ fails"); - } - deleteAndZero(itb->second); - } - brokerMqs.clear(); -} - -bool Rebalance::lock(MQMessageQueue mq) { - unique_ptr pFindBrokerResult( - m_pClientFactory->findBrokerAddressInSubscribe(mq.getBrokerName(), MASTER_ID, true)); - if (!pFindBrokerResult) { - LOG_ERROR("lock findBrokerAddressInSubscribe ret null for broker:%s", mq.getBrokerName().data()); - return false; - } - unique_ptr lockBatchRequest(new LockBatchRequestBody()); - lockBatchRequest->setClientId(m_pConsumer->getMQClientId()); - lockBatchRequest->setConsumerGroup(m_pConsumer->getGroupName()); - vector in_mqSet; - in_mqSet.push_back(mq); - lockBatchRequest->setMqSet(in_mqSet); - bool lockResult = false; - - try { - vector messageQueues; - LOG_DEBUG("try to lock mq:%s", mq.toString().c_str()); - m_pClientFactory->getMQClientAPIImpl()->lockBatchMQ(pFindBrokerResult->brokerAddr, lockBatchRequest.get(), - messageQueues, 1000, m_pConsumer->getSessionCredentials()); - if (messageQueues.size() == 0) { - LOG_ERROR("lock mq on broker:%s failed", pFindBrokerResult->brokerAddr.c_str()); - return false; - } - for (unsigned int i = 0; i != messageQueues.size(); ++i) { - boost::weak_ptr pullreq = getPullRequest(messageQueues[i]); - if (!pullreq.expired()) { - LOG_INFO("lock success of mq:%s", messageQueues[i].toString().c_str()); - pullreq.lock()->setLocked(true); - pullreq.lock()->setLastLockTimestamp(UtilAll::currentTimeMillis()); - lockResult = true; - } else { - LOG_ERROR("lock fails of mq:%s", messageQueues[i].toString().c_str()); - } - } - messageQueues.clear(); - return lockResult; - } catch (MQException& e) { - LOG_ERROR("lock fails of mq:%s", mq.toString().c_str()); - return false; - } -} - -//& mqsSelf) { - return false; -} - -int64 RebalancePull::computePullFromWhere(const MQMessageQueue& mq) { - return 0; -} - -void RebalancePull::messageQueueChanged(const string& topic, - vector& mqAll, - vector& mqDivided) {} - -void RebalancePull::removeUnnecessaryMessageQueue(const MQMessageQueue& mq) {} - -//& mqsSelf) { - LOG_DEBUG("updateRequestTableInRebalance for Topic[%s] Enter", topic.c_str()); - - // 1. Clear no in charge of - // 1. set dropped - // 2. clear local message - // 3. clear offset - // 4. remove request table - // 5. set flag for route changed - // 2. Check and clear dropped/invalid pullrequest(timeout and so on) - // 3. Add new mq in charge of - // 1. new pullrequest - // 2. init next pull offset - // 3. int offset - // 4. add request table - // 5. set flag for route changed - // 4. Start long pull for request - if (mqsSelf.empty()) { - LOG_WARN("allocated queue is empty for topic:%s", topic.c_str()); - } - - bool changed = false; - - //first; - if (mqtemp.getTopic().compare(topic) == 0) { - if (mqsSelf.empty() || (std::find(mqsSelf.begin(), mqsSelf.end(), mqtemp) == mqsSelf.end())) { - // if not response , set to dropped - LOG_INFO("Drop mq:%s,because not responsive", mqtemp.toString().c_str()); - itDel->second->setDropped(true); - // remove offset table to avoid offset backup - removeUnnecessaryMessageQueue(mqtemp); - itDel->second->clearAllMsgs(); - removePullRequest(mqtemp); - changed = true; - } else if (itDel->second->isPullRequestExpired()) { - // if pull expired , set to dropped, eg: if add pull task error, the pull request will be expired. - LOG_INFO("Drop mq:%s according Pull timeout.", mqtemp.toString().c_str()); - itDel->second->setDropped(true); - removeUnnecessaryMessageQueue(mqtemp); - itDel->second->clearAllMsgs(); - removePullRequest(mqtemp); - changed = true; - } - } - } - - //> pullRequestsToAdd; - vector::iterator itAdd = mqsSelf.begin(); - for (; itAdd != mqsSelf.end(); ++itAdd) { - if (isPullRequestExist(*itAdd)) { - // have check the expired pull request, re-add it. - continue; - } - boost::shared_ptr pullRequest = boost::make_shared(m_pConsumer->getGroupName()); - pullRequest->m_messageQueue = *itAdd; - int64 nextOffset = computePullFromWhere(*itAdd); - if (nextOffset >= 0) { - pullRequest->setNextOffset(nextOffset); - pullRequest->setDropped(false); - changed = true; - addPullRequest(*itAdd, pullRequest); - pullRequestsToAdd.push_back(pullRequest); - LOG_INFO("Add mq:%s, request initial offset:%ld", (*itAdd).toString().c_str(), nextOffset); - } else { - LOG_WARN( - "Failed to add pull request for %s due to failure of querying consume offset, request initial offset:%ld", - (*itAdd).toString().c_str(), nextOffset); - } - } - - for (vector>::iterator itAdded = pullRequestsToAdd.begin(); - itAdded != pullRequestsToAdd.end(); ++itAdded) { - LOG_INFO("Start to pull %s, offset:%ld, GroupName %s", (*itAdded)->m_messageQueue.toString().c_str(), - (*itAdded)->getNextOffset(), (*itAdded)->getGroupName().c_str()); - if (!m_pConsumer->producePullMsgTask(*itAdded)) { - LOG_WARN( - "Failed to producer pull message task for %s, Remove it from Request table and wait for next #Rebalance.", - (*itAdded)->m_messageQueue.toString().c_str()); - // remove from request table, and wait for next rebalance. - (*itAdded)->setDropped(true); - removePullRequest((*itAdded)->m_messageQueue); - } - } - - LOG_DEBUG("updateRequestTableInRebalance Topic[%s] exit", topic.c_str()); - return changed; -} - -int64 RebalancePush::computePullFromWhere(const MQMessageQueue& mq) { - int64 result = -1; - DefaultMQPushConsumerImpl* pConsumer = dynamic_cast(m_pConsumer); - if (!pConsumer) { - LOG_ERROR("Cast consumer pointer to DefaultMQPushConsumer pointer failed when computePullFromWhere %s", - mq.toString().c_str()); - return result; - } - ConsumeFromWhere consumeFromWhere = pConsumer->getConsumeFromWhere(); - OffsetStore* pOffsetStore = pConsumer->getOffsetStore(); - switch (consumeFromWhere) { - case CONSUME_FROM_LAST_OFFSET: { - int64 lastOffset = pOffsetStore->readOffset(mq, READ_FROM_STORE, m_pConsumer->getSessionCredentials()); - if (lastOffset >= 0) { - LOG_INFO("CONSUME_FROM_LAST_OFFSET, lastOffset of mq:%s is:%lld", mq.toString().c_str(), lastOffset); - result = lastOffset; - } else if (-1 == lastOffset) { - LOG_WARN("CONSUME_FROM_LAST_OFFSET, lastOffset of mq:%s is -1", mq.toString().c_str()); - if (UtilAll::startsWith_retry(mq.getTopic())) { - LOG_INFO("CONSUME_FROM_LAST_OFFSET, lastOffset of mq:%s is 0", mq.toString().c_str()); - result = 0; - } else { - try { - result = pConsumer->maxOffset(mq); - LOG_INFO("CONSUME_FROM_LAST_OFFSET, maxOffset of mq:%s is:%lld", mq.toString().c_str(), result); - } catch (MQException& e) { - LOG_ERROR("CONSUME_FROM_LAST_OFFSET error, lastOffset of mq:%s is -1", mq.toString().c_str()); - result = -1; - } - } - } else { - LOG_ERROR("CONSUME_FROM_LAST_OFFSET error, lastOffset of mq:%s is -1", mq.toString().c_str()); - result = -1; - } - break; - } - case CONSUME_FROM_FIRST_OFFSET: { - int64 lastOffset = pOffsetStore->readOffset(mq, READ_FROM_STORE, m_pConsumer->getSessionCredentials()); - if (lastOffset >= 0) { - LOG_INFO("CONSUME_FROM_FIRST_OFFSET, lastOffset of mq:%s is:%lld", mq.toString().c_str(), lastOffset); - result = lastOffset; - } else if (-1 == lastOffset) { - LOG_INFO("CONSUME_FROM_FIRST_OFFSET, lastOffset of mq:%s, return 0", mq.toString().c_str()); - result = 0; - } else { - LOG_ERROR("CONSUME_FROM_FIRST_OFFSET, lastOffset of mq:%s, return -1", mq.toString().c_str()); - result = -1; - } - break; - } - case CONSUME_FROM_TIMESTAMP: { - int64 lastOffset = pOffsetStore->readOffset(mq, READ_FROM_STORE, m_pConsumer->getSessionCredentials()); - if (lastOffset >= 0) { - LOG_INFO("CONSUME_FROM_TIMESTAMP, lastOffset of mq:%s is:%lld", mq.toString().c_str(), lastOffset); - result = lastOffset; - } else if (-1 == lastOffset) { - if (UtilAll::startsWith_retry(mq.getTopic())) { - try { - result = pConsumer->maxOffset(mq); - LOG_INFO("CONSUME_FROM_TIMESTAMP, maxOffset of mq:%s is:%lld", mq.toString().c_str(), result); - } catch (MQException& e) { - LOG_ERROR("CONSUME_FROM_TIMESTAMP error, lastOffset of mq:%s is -1", mq.toString().c_str()); - result = -1; - } - } else { - try { - } catch (MQException& e) { - LOG_ERROR("CONSUME_FROM_TIMESTAMP error, lastOffset of mq:%s, return 0", mq.toString().c_str()); - result = -1; - } - } - } else { - LOG_ERROR("CONSUME_FROM_TIMESTAMP error, lastOffset of mq:%s, return -1", mq.toString().c_str()); - result = -1; - } - break; - } - default: - break; - } - return result; -} - -void RebalancePush::messageQueueChanged(const string& topic, - vector& mqAll, - vector& mqDivided) {} - -void RebalancePush::removeUnnecessaryMessageQueue(const MQMessageQueue& mq) { - // DefaultMQPushConsumer *pConsumer = static_cast(m_pConsumer); - DefaultMQPushConsumerImpl* pConsumer = dynamic_cast(m_pConsumer); - if (!pConsumer) { - LOG_ERROR("Cast MQConsumer* to DefaultMQPushConsumer* failed when remove %s", mq.toString().c_str()); - return; - } - OffsetStore* pOffsetStore = pConsumer->getOffsetStore(); - - pOffsetStore->persist(mq, m_pConsumer->getSessionCredentials()); - pOffsetStore->removeOffset(mq); - if (pConsumer->getMessageListenerType() == messageListenerOrderly) { - unlock(mq); - } -} - -// -#include - -namespace rocketmq { -class MQClientFactory; - -//& mqAll, - vector& mqDivided) = 0; - - virtual void removeUnnecessaryMessageQueue(const MQMessageQueue& mq) = 0; - - virtual int64 computePullFromWhere(const MQMessageQueue& mq) = 0; - - virtual bool updateRequestTableInRebalance(const string& topic, vector& mqsSelf) = 0; - - public: - void doRebalance(); - - void persistConsumerOffset(); - - void persistConsumerOffsetByResetOffset(); - - //& getSubscriptionInner(); - - //& mqs); - - bool getTopicSubscribeInfo(const string& topic, vector& mqs); - - void addPullRequest(MQMessageQueue mq, boost::shared_ptr pPullRequest); - void removePullRequest(MQMessageQueue mq); - bool isPullRequestExist(MQMessageQueue mq); - boost::weak_ptr getPullRequest(MQMessageQueue mq); - - map> getPullRequestTable(); - - void lockAll(); - - bool lock(MQMessageQueue mq); - - void unlockAll(bool oneWay = false); - - void unlock(MQMessageQueue mq); - - protected: - map m_subscriptionData; - - boost::mutex m_topicSubscribeInfoTableMutex; - map> m_topicSubscribeInfoTable; - typedef map> MQ2PULLREQ; - MQ2PULLREQ m_requestQueueTable; - boost::mutex m_requestTableMutex; - - AllocateMQStrategy* m_pAllocateMQStrategy; - MQConsumer* m_pConsumer; - MQClientFactory* m_pClientFactory; -}; - -//& mqAll, - vector& mqDivided); - - virtual void removeUnnecessaryMessageQueue(const MQMessageQueue& mq); - - virtual int64 computePullFromWhere(const MQMessageQueue& mq); - - virtual bool updateRequestTableInRebalance(const string& topic, vector& mqsSelf); -}; - -//& mqAll, - vector& mqDivided); - - virtual void removeUnnecessaryMessageQueue(const MQMessageQueue& mq); - - virtual int64 computePullFromWhere(const MQMessageQueue& mq); - - virtual bool updateRequestTableInRebalance(const string& topic, vector& mqsSelf); -}; - -// -#include -#include -#include "Logging.h" -#include "UtilAll.h" -namespace rocketmq { -//& SubscriptionData::getTagsSet() { - return m_tagSet; -} - -bool SubscriptionData::operator==(const SubscriptionData& other) const { - if (!m_subString.compare(other.m_subString)) { - return false; - } - if (m_subVersion != other.m_subVersion) { - return false; - } - if (m_tagSet.size() != other.m_tagSet.size()) { - return false; - } - if (!m_topic.compare(other.m_topic)) { - return false; - } - return true; -} - -bool SubscriptionData::operator<(const SubscriptionData& other) const { - int ret = m_topic.compare(other.m_topic); - if (ret < 0) { - return true; - } else if (ret == 0) { - ret = m_subString.compare(other.m_subString); - if (ret < 0) { - return true; - } else { - return false; - } - } else { - return false; - } -} - -void SubscriptionData::putCodeSet(const string& tag) { - int value = atoi(tag.c_str()); - m_codeSet.push_back(value); -} - -Json::Value SubscriptionData::toJson() const { - Json::Value outJson; - outJson["subString"] = m_subString; - outJson["subVersion"] = UtilAll::to_string(m_subVersion); - outJson["topic"] = m_topic; - - { - vector::const_iterator it = m_tagSet.begin(); - for (; it != m_tagSet.end(); it++) { - outJson["tagsSet"].append(*it); - } - } - - { - vector::const_iterator it = m_codeSet.begin(); - for (; it != m_codeSet.end(); it++) { - outJson["codeSet"].append(*it); - } - } - return outJson; -} - -// -#include "UtilAll.h" -#include "json/json.h" - -namespace rocketmq { -//& getTagsSet(); - - void putCodeSet(const string& tag); - - bool operator==(const SubscriptionData& other) const; - bool operator<(const SubscriptionData& other) const; - - Json::Value toJson() const; - - private: - string m_topic; - string m_subString; - int64 m_subVersion; - vector m_tagSet; - vector m_codeSet; -}; -// -#include "windows.h" - -BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { - switch (ul_reason_for_call) { - case DLL_PROCESS_ATTACH: - break; - case DLL_THREAD_ATTACH: - break; - case DLL_THREAD_DETACH: - break; - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} diff --git a/src/extern/CBatchMessage.cpp b/src/extern/CBatchMessage.cpp deleted file mode 100644 index 3fb2a977f..000000000 --- a/src/extern/CBatchMessage.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "CBatchMessage.h" -#include "CCommon.h" -#include "CMessage.h" -#include "MQMessage.h" - -using std::vector; - -#ifdef __cplusplus -extern "C" { -#endif - -using namespace rocketmq; - -CBatchMessage* CreateBatchMessage() { - vector* msgs = new vector(); - return (CBatchMessage*)msgs; -} - -int AddMessage(CBatchMessage* batchMsg, CMessage* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - if (batchMsg == NULL) { - return NULL_POINTER; - } - MQMessage* message = (MQMessage*)msg; - ((vector*)batchMsg)->push_back(*message); - return OK; -} -int DestroyBatchMessage(CBatchMessage* batchMsg) { - if (batchMsg == NULL) { - return NULL_POINTER; - } - delete (vector*)batchMsg; - return OK; -} - -#ifdef __cplusplus -}; -#endif diff --git a/src/extern/CErrorMessage.cpp b/src/extern/CErrorMessage.cpp deleted file mode 100644 index 420fbe261..000000000 --- a/src/extern/CErrorMessage.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CErrorMessage.h" -#include "CCommon.h" -#include "MQClientErrorContainer.h" - -#ifdef __cplusplus -extern "C" { -#endif -using namespace rocketmq; - -const char* GetLatestErrorMessage() { - return MQClientErrorContainer::getErr().c_str(); -} - -#ifdef __cplusplus -}; -#endif \ No newline at end of file diff --git a/src/extern/CMessage.cpp b/src/extern/CMessage.cpp deleted file mode 100644 index d55b391f9..000000000 --- a/src/extern/CMessage.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CMessage.h" -#include "CCommon.h" -#include "MQMessage.h" - -#ifdef __cplusplus -extern "C" { -#endif - -using namespace rocketmq; - -CMessage* CreateMessage(const char* topic) { - MQMessage* mqMessage = new MQMessage(); - if (topic != NULL) { - mqMessage->setTopic(topic); - } - return (CMessage*)mqMessage; -} -int DestroyMessage(CMessage* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - delete (MQMessage*)msg; - return OK; -} -int SetMessageTopic(CMessage* msg, const char* topic) { - if (msg == NULL) { - return NULL_POINTER; - } - ((MQMessage*)msg)->setTopic(topic); - return OK; -} -int SetMessageTags(CMessage* msg, const char* tags) { - if (msg == NULL) { - return NULL_POINTER; - } - ((MQMessage*)msg)->setTags(tags); - return OK; -} -int SetMessageKeys(CMessage* msg, const char* keys) { - if (msg == NULL) { - return NULL_POINTER; - } - ((MQMessage*)msg)->setKeys(keys); - return OK; -} - -/** - * DO NOT USE THIS FUNCTION, IT IS ERROR-PRONE. - */ -int SetMessageBody(CMessage* msg, const char* body) { - if (msg == NULL) { - return NULL_POINTER; - } - ((MQMessage*)msg)->setBody(body); - return OK; -} -int SetByteMessageBody(CMessage* msg, const char* body, int len) { - if (msg == NULL) { - return NULL_POINTER; - } - ((MQMessage*)msg)->setBody(body, len); - return OK; -} -int SetMessageProperty(CMessage* msg, const char* key, const char* value) { - if (msg == NULL) { - return NULL_POINTER; - } - ((MQMessage*)msg)->setProperty(key, value); - return OK; -} -int SetDelayTimeLevel(CMessage* msg, int level) { - if (msg == NULL) { - return NULL_POINTER; - } - ((MQMessage*)msg)->setDelayTimeLevel(level); - return OK; -} -const char* GetOriginMessageTopic(CMessage* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessage*)msg)->getTopic().c_str(); -} -const char* GetOriginMessageTags(CMessage* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessage*)msg)->getTags().c_str(); -} -const char* GetOriginMessageKeys(CMessage* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessage*)msg)->getKeys().c_str(); -} -const char* GetOriginMessageBody(CMessage* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessage*)msg)->getBody().c_str(); -} -int GetOriginMessageBodyLength(CMessage* msg) { - if (NULL == msg) { - return 0; - } - return reinterpret_cast(msg)->getBody().length(); -} -const char* GetOriginMessageProperty(CMessage* msg, const char* key) { - if (msg == NULL) { - return NULL; - } - return ((MQMessage*)msg)->getProperty(key).c_str(); -} -int GetOriginDelayTimeLevel(CMessage* msg) { - if (msg == NULL) { - return -1; - } - return ((MQMessage*)msg)->getDelayTimeLevel(); -} -#ifdef __cplusplus -}; -#endif diff --git a/src/extern/CMessageExt.cpp b/src/extern/CMessageExt.cpp deleted file mode 100644 index a85a27d12..000000000 --- a/src/extern/CMessageExt.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CMessageExt.h" -#include "CCommon.h" -#include "MQMessageExt.h" - -#ifdef __cplusplus -extern "C" { -#endif -using namespace rocketmq; -const char* GetMessageTopic(CMessageExt* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessageExt*)msg)->getTopic().c_str(); -} -const char* GetMessageTags(CMessageExt* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessageExt*)msg)->getTags().c_str(); -} -const char* GetMessageKeys(CMessageExt* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessageExt*)msg)->getKeys().c_str(); -} -const char* GetMessageBody(CMessageExt* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessageExt*)msg)->getBody().c_str(); -} - -int GetMessageBodyLength(CMessageExt* msgExt) { - if (NULL == msgExt) { - return 0; - } - return reinterpret_cast(msgExt)->getBody().length(); -} - -const char* GetMessageProperty(CMessageExt* msg, const char* key) { - if (msg == NULL) { - return NULL; - } - return ((MQMessageExt*)msg)->getProperty(key).c_str(); -} -const char* GetMessageId(CMessageExt* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessageExt*)msg)->getMsgId().c_str(); -} - -int GetMessageDelayTimeLevel(CMessageExt* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - return ((MQMessageExt*)msg)->getDelayTimeLevel(); -} - -int GetMessageQueueId(CMessageExt* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - return ((MQMessageExt*)msg)->getQueueId(); -} - -int GetMessageReconsumeTimes(CMessageExt* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - return ((MQMessageExt*)msg)->getReconsumeTimes(); -} - -int GetMessageStoreSize(CMessageExt* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - return ((MQMessageExt*)msg)->getStoreSize(); -} - -long long GetMessageBornTimestamp(CMessageExt* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - return ((MQMessageExt*)msg)->getBornTimestamp(); -} - -long long GetMessageStoreTimestamp(CMessageExt* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - return ((MQMessageExt*)msg)->getStoreTimestamp(); -} - -long long GetMessageQueueOffset(CMessageExt* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - return ((MQMessageExt*)msg)->getQueueOffset(); -} - -long long GetMessageCommitLogOffset(CMessageExt* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - return ((MQMessageExt*)msg)->getCommitLogOffset(); -} - -long long GetMessagePreparedTransactionOffset(CMessageExt* msg) { - if (msg == NULL) { - return NULL_POINTER; - } - return ((MQMessageExt*)msg)->getPreparedTransactionOffset(); -} - -const char* GetMessageStoreHost(CMessageExt* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessageExt*) msg)->getStoreHostString().c_str(); -} - -const char* GetMessageBornHost(CMessageExt* msg) { - if (msg == NULL) { - return NULL; - } - return ((MQMessageExt*) msg)->getBornHostString().c_str(); -} -#ifdef __cplusplus -}; -#endif diff --git a/src/extern/CProducer.cpp b/src/extern/CProducer.cpp deleted file mode 100644 index 7b22cd1cd..000000000 --- a/src/extern/CProducer.cpp +++ /dev/null @@ -1,859 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CProducer.h" -#include -#include -#include -#include -#include "AsyncCallback.h" -#include "CBatchMessage.h" -#include "CCommon.h" -#include "CMQException.h" -#include "CMessage.h" -#include "CSendResult.h" -#include "DefaultMQProducer.h" -#include "MQClient.h" -#include "MQClientErrorContainer.h" -#include "TransactionListener.h" -#include "TransactionMQProducer.h" -#include "TransactionSendResult.h" -#include "UtilAll.h" - -#ifdef __cplusplus -extern "C" { -#endif -using namespace rocketmq; -using namespace std; -class MyLocalTransactionExecuterInner { - public: - MyLocalTransactionExecuterInner(CLocalTransactionExecutorCallback executor, CMessage* msg, void* userData) - : m_ExcutorCallback(executor), message(msg), data(userData) {} - ~MyLocalTransactionExecuterInner() {} - CLocalTransactionExecutorCallback m_ExcutorCallback; - CMessage* message; - void* data; -}; -class LocalTransactionListenerInner : public TransactionListener { - public: - LocalTransactionListenerInner() {} - - LocalTransactionListenerInner(CProducer* producer, CLocalTransactionCheckerCallback pCallback, void* data) { - m_CheckerCallback = pCallback; - m_producer = producer; - m_data = data; - } - - ~LocalTransactionListenerInner() {} - - LocalTransactionState executeLocalTransaction(const MQMessage& message, void* arg) { - if (m_CheckerCallback == NULL) { - return LocalTransactionState::UNKNOWN; - } - (void)(message); - MyLocalTransactionExecuterInner* executerInner = (MyLocalTransactionExecuterInner*)arg; - CTransactionStatus status = - executerInner->m_ExcutorCallback(m_producer, executerInner->message, executerInner->data); - switch (status) { - case E_COMMIT_TRANSACTION: - return LocalTransactionState::COMMIT_MESSAGE; - - case E_ROLLBACK_TRANSACTION: - return LocalTransactionState::ROLLBACK_MESSAGE; - - default: - return LocalTransactionState::UNKNOWN; - } - } - - LocalTransactionState checkLocalTransaction(const MQMessageExt& msg) { - if (m_CheckerCallback == NULL) { - return LocalTransactionState::UNKNOWN; - } - CMessageExt* msgExt = (CMessageExt*)(&msg); - // CMessage *msg = (CMessage *) (&message); - CTransactionStatus status = m_CheckerCallback(m_producer, msgExt, m_data); - switch (status) { - case E_COMMIT_TRANSACTION: - return LocalTransactionState::COMMIT_MESSAGE; - - case E_ROLLBACK_TRANSACTION: - return LocalTransactionState::ROLLBACK_MESSAGE; - - default: - return LocalTransactionState::UNKNOWN; - } - } - - private: - CLocalTransactionCheckerCallback m_CheckerCallback; - CProducer* m_producer; - void* m_data; -}; - -class SelectMessageQueueInner : public MessageQueueSelector { - public: - MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) { - int index = 0; - std::string shardingKey = rocketmq::UtilAll::to_string((char*)arg); - - index = std::hash{}(shardingKey) % mqs.size(); - return mqs[index % mqs.size()]; - } -}; - -class SelectMessageQueue : public MessageQueueSelector { - public: - SelectMessageQueue(QueueSelectorCallback callback) { m_pCallback = callback; } - - MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) { - CMessage* message = (CMessage*)&msg; - // Get the index of sending MQMessageQueue through callback function. - int index = m_pCallback(mqs.size(), message, arg); - return mqs[index]; - } - - private: - QueueSelectorCallback m_pCallback; -}; -class COnSendCallback : public AutoDeleteSendCallBack { - public: - COnSendCallback(COnSendSuccessCallback cSendSuccessCallback, - COnSendExceptionCallback cSendExceptionCallback, - void* message, - void* userData) { - m_cSendSuccessCallback = cSendSuccessCallback; - m_cSendExceptionCallback = cSendExceptionCallback; - m_message = message; - m_userData = userData; - } - - virtual ~COnSendCallback() {} - - virtual void onSuccess(SendResult& sendResult) { - CSendResult result; - result.sendStatus = CSendStatus((int)sendResult.getSendStatus()); - result.offset = sendResult.getQueueOffset(); - strncpy(result.msgId, sendResult.getMsgId().c_str(), MAX_MESSAGE_ID_LENGTH - 1); - result.msgId[MAX_MESSAGE_ID_LENGTH - 1] = 0; - m_cSendSuccessCallback(result, (CMessage*)m_message, m_userData); - } - - virtual void onException(MQException& e) { - CMQException exception; - exception.error = e.GetError(); - exception.line = e.GetLine(); - strncpy(exception.msg, e.what(), MAX_EXEPTION_MSG_LENGTH - 1); - strncpy(exception.file, e.GetFile(), MAX_EXEPTION_FILE_LENGTH - 1); - m_cSendExceptionCallback(exception, (CMessage*)m_message, m_userData); - } - - private: - COnSendSuccessCallback m_cSendSuccessCallback; - COnSendExceptionCallback m_cSendExceptionCallback; - void* m_message; - void* m_userData; -}; - -class CSendCallback : public AutoDeleteSendCallBack { - public: - CSendCallback(CSendSuccessCallback cSendSuccessCallback, CSendExceptionCallback cSendExceptionCallback) { - m_cSendSuccessCallback = cSendSuccessCallback; - m_cSendExceptionCallback = cSendExceptionCallback; - } - - virtual ~CSendCallback() {} - - virtual void onSuccess(SendResult& sendResult) { - CSendResult result; - result.sendStatus = CSendStatus((int)sendResult.getSendStatus()); - result.offset = sendResult.getQueueOffset(); - strncpy(result.msgId, sendResult.getMsgId().c_str(), MAX_MESSAGE_ID_LENGTH - 1); - result.msgId[MAX_MESSAGE_ID_LENGTH - 1] = 0; - m_cSendSuccessCallback(result); - } - - virtual void onException(MQException& e) { - CMQException exception; - exception.error = e.GetError(); - exception.line = e.GetLine(); - strncpy(exception.msg, e.what(), MAX_EXEPTION_MSG_LENGTH - 1); - strncpy(exception.file, e.GetFile(), MAX_EXEPTION_FILE_LENGTH - 1); - m_cSendExceptionCallback(exception); - } - - private: - CSendSuccessCallback m_cSendSuccessCallback; - CSendExceptionCallback m_cSendExceptionCallback; -}; -#ifndef CAPI_C_PRODUCER_TYPE_COMMON -#define CAPI_C_PRODUCER_TYPE_COMMON 0 -#endif - -#ifndef CAPI_C_PRODUCER_TYPE_ORDERLY -#define CAPI_C_PRODUCER_TYPE_ORDERLY 1 -#endif - -#ifndef CAPI_C_PRODUCER_TYPE_TRANSACTION -#define CAPI_C_PRODUCER_TYPE_TRANSACTION 2 -#endif -typedef struct __DefaultProducer__ { - DefaultMQProducer* innerProducer; - TransactionMQProducer* innerTransactionProducer; - LocalTransactionListenerInner* listenerInner; - int producerType; - char* version; -} DefaultProducer; -CProducer* CreateProducer(const char* groupId) { - if (groupId == NULL) { - return NULL; - } - DefaultProducer* defaultMQProducer = new DefaultProducer(); - defaultMQProducer->producerType = CAPI_C_PRODUCER_TYPE_COMMON; - defaultMQProducer->innerProducer = new DefaultMQProducer(groupId); - defaultMQProducer->version = new char[MAX_SDK_VERSION_LENGTH]; - strncpy(defaultMQProducer->version, defaultMQProducer->innerProducer->version().c_str(), MAX_SDK_VERSION_LENGTH - 1); - defaultMQProducer->version[MAX_SDK_VERSION_LENGTH - 1] = 0; - defaultMQProducer->innerTransactionProducer = NULL; - defaultMQProducer->listenerInner = NULL; - return (CProducer*)defaultMQProducer; -} - -CProducer* CreateOrderlyProducer(const char* groupId) { - if (groupId == NULL) { - return NULL; - } - DefaultProducer* defaultMQProducer = new DefaultProducer(); - defaultMQProducer->producerType = CAPI_C_PRODUCER_TYPE_ORDERLY; - defaultMQProducer->innerProducer = new DefaultMQProducer(groupId); - - defaultMQProducer->version = new char[MAX_SDK_VERSION_LENGTH]; - strncpy(defaultMQProducer->version, defaultMQProducer->innerProducer->version().c_str(), MAX_SDK_VERSION_LENGTH - 1); - defaultMQProducer->version[MAX_SDK_VERSION_LENGTH - 1] = 0; - defaultMQProducer->innerTransactionProducer = NULL; - defaultMQProducer->listenerInner = NULL; - return (CProducer*)defaultMQProducer; -} - -CProducer* CreateTransactionProducer(const char* groupId, CLocalTransactionCheckerCallback callback, void* userData) { - if (groupId == NULL) { - return NULL; - } - DefaultProducer* defaultMQProducer = new DefaultProducer(); - defaultMQProducer->producerType = CAPI_C_PRODUCER_TYPE_TRANSACTION; - defaultMQProducer->innerProducer = NULL; - defaultMQProducer->innerTransactionProducer = new TransactionMQProducer(groupId); - defaultMQProducer->listenerInner = - new LocalTransactionListenerInner((CProducer*)defaultMQProducer, callback, userData); - defaultMQProducer->innerTransactionProducer->setTransactionListener(defaultMQProducer->listenerInner); - - defaultMQProducer->version = new char[MAX_SDK_VERSION_LENGTH]; - strncpy(defaultMQProducer->version, defaultMQProducer->innerTransactionProducer->version().c_str(), - MAX_SDK_VERSION_LENGTH - 1); - defaultMQProducer->version[MAX_SDK_VERSION_LENGTH - 1] = 0; - return (CProducer*)defaultMQProducer; -} -int DestroyProducer(CProducer* pProducer) { - if (pProducer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)pProducer; - if (defaultMQProducer->version != NULL) { - delete[] defaultMQProducer->version; - defaultMQProducer->version = NULL; - } - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - if (defaultMQProducer->innerTransactionProducer != NULL) { - delete defaultMQProducer->innerTransactionProducer; - defaultMQProducer->innerTransactionProducer = NULL; - } - } else { - if (defaultMQProducer->innerProducer != NULL) { - delete defaultMQProducer->innerProducer; - defaultMQProducer->innerProducer = NULL; - } - } - delete reinterpret_cast(pProducer); - return OK; -} -int StartProducer(CProducer* producer) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->start(); - } else { - defaultMQProducer->innerProducer->start(); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} -int ShutdownProducer(CProducer* producer) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->shutdown(); - } else { - defaultMQProducer->innerProducer->shutdown(); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} -const char* ShowProducerVersion(CProducer* producer) { - if (producer == NULL) { - return DEFAULT_SDK_VERSION; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - - return defaultMQProducer->version; -} -int SetProducerNameServerAddress(CProducer* producer, const char* namesrv) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setNamesrvAddr(namesrv); - } else { - defaultMQProducer->innerProducer->setNamesrvAddr(namesrv); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} -int SetProducerNameServerDomain(CProducer* producer, const char* domain) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setNamesrvDomain(domain); - } else { - defaultMQProducer->innerProducer->setNamesrvDomain(domain); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} -int SendMessageSync(CProducer* producer, CMessage* msg, CSendResult* result) { - // CSendResult sendResult; - if (producer == NULL || msg == NULL || result == NULL) { - return NULL_POINTER; - } - try { - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - MQMessage* message = (MQMessage*)msg; - SendResult sendResult = defaultMQProducer->innerProducer->send(*message); - switch (sendResult.getSendStatus()) { - case SEND_OK: - result->sendStatus = E_SEND_OK; - break; - case SEND_FLUSH_DISK_TIMEOUT: - result->sendStatus = E_SEND_FLUSH_DISK_TIMEOUT; - break; - case SEND_FLUSH_SLAVE_TIMEOUT: - result->sendStatus = E_SEND_FLUSH_SLAVE_TIMEOUT; - break; - case SEND_SLAVE_NOT_AVAILABLE: - result->sendStatus = E_SEND_SLAVE_NOT_AVAILABLE; - break; - default: - result->sendStatus = E_SEND_OK; - break; - } - result->offset = sendResult.getQueueOffset(); - strncpy(result->msgId, sendResult.getMsgId().c_str(), MAX_MESSAGE_ID_LENGTH - 1); - result->msgId[MAX_MESSAGE_ID_LENGTH - 1] = 0; - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_SEND_SYNC_FAILED; - } - return OK; -} - -int SendBatchMessage(CProducer* producer, CBatchMessage* batcMsg, CSendResult* result) { - // CSendResult sendResult; - if (producer == NULL || batcMsg == NULL || result == NULL) { - return NULL_POINTER; - } - try { - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - vector* message = (vector*)batcMsg; - SendResult sendResult = defaultMQProducer->innerProducer->send(*message); - switch (sendResult.getSendStatus()) { - case SEND_OK: - result->sendStatus = E_SEND_OK; - break; - case SEND_FLUSH_DISK_TIMEOUT: - result->sendStatus = E_SEND_FLUSH_DISK_TIMEOUT; - break; - case SEND_FLUSH_SLAVE_TIMEOUT: - result->sendStatus = E_SEND_FLUSH_SLAVE_TIMEOUT; - break; - case SEND_SLAVE_NOT_AVAILABLE: - result->sendStatus = E_SEND_SLAVE_NOT_AVAILABLE; - break; - default: - result->sendStatus = E_SEND_OK; - break; - } - result->offset = sendResult.getQueueOffset(); - strncpy(result->msgId, sendResult.getMsgId().c_str(), MAX_MESSAGE_ID_LENGTH - 1); - result->msgId[MAX_MESSAGE_ID_LENGTH - 1] = 0; - } catch (exception& e) { - return PRODUCER_SEND_SYNC_FAILED; - } - return OK; -} - -int SendMessageAsync(CProducer* producer, - CMessage* msg, - CSendSuccessCallback cSendSuccessCallback, - CSendExceptionCallback cSendExceptionCallback) { - if (producer == NULL || msg == NULL || cSendSuccessCallback == NULL || cSendExceptionCallback == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - MQMessage* message = (MQMessage*)msg; - CSendCallback* cSendCallback = new CSendCallback(cSendSuccessCallback, cSendExceptionCallback); - - try { - defaultMQProducer->innerProducer->send(*message, cSendCallback); - } catch (exception& e) { - if (cSendCallback != NULL) { - if (std::type_index(typeid(e)) == std::type_index(typeid(MQException))) { - MQException& mqe = (MQException&)e; - cSendCallback->onException(mqe); - } - delete cSendCallback; - cSendCallback = NULL; - } - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_SEND_ASYNC_FAILED; - } - return OK; -} - -int SendAsync(CProducer* producer, - CMessage* msg, - COnSendSuccessCallback onSuccess, - COnSendExceptionCallback onException, - void* usrData) { - if (producer == NULL || msg == NULL || onSuccess == NULL || onException == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - MQMessage* message = (MQMessage*)msg; - COnSendCallback* cSendCallback = new COnSendCallback(onSuccess, onException, (void*)msg, usrData); - - try { - defaultMQProducer->innerProducer->send(*message, cSendCallback); - } catch (exception& e) { - if (cSendCallback != NULL) { - if (std::type_index(typeid(e)) == std::type_index(typeid(MQException))) { - MQException& mqe = (MQException&)e; - cSendCallback->onException(mqe); - } - delete cSendCallback; - cSendCallback = NULL; - } - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_SEND_ASYNC_FAILED; - } - return OK; -} - -int SendMessageOneway(CProducer* producer, CMessage* msg) { - if (producer == NULL || msg == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - MQMessage* message = (MQMessage*)msg; - try { - defaultMQProducer->innerProducer->sendOneway(*message); - } catch (exception& e) { - return PRODUCER_SEND_ONEWAY_FAILED; - } - return OK; -} - -int SendMessageOnewayOrderly(CProducer* producer, CMessage* msg, QueueSelectorCallback selector, void* arg) { - if (producer == NULL || msg == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - MQMessage* message = (MQMessage*)msg; - try { - SelectMessageQueue selectMessageQueue(selector); - defaultMQProducer->innerProducer->sendOneway(*message, &selectMessageQueue, arg); - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_SEND_ONEWAY_FAILED; - } - return OK; -} - -int SendMessageOrderlyAsync(CProducer* producer, - CMessage* msg, - QueueSelectorCallback callback, - void* arg, - CSendSuccessCallback cSendSuccessCallback, - CSendExceptionCallback cSendExceptionCallback) { - if (producer == NULL || msg == NULL || callback == NULL || cSendSuccessCallback == NULL || - cSendExceptionCallback == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - MQMessage* message = (MQMessage*)msg; - CSendCallback* cSendCallback = new CSendCallback(cSendSuccessCallback, cSendExceptionCallback); - - try { - // Constructing SelectMessageQueue objects through function pointer callback - SelectMessageQueue selectMessageQueue(callback); - defaultMQProducer->innerProducer->send(*message, &selectMessageQueue, arg, cSendCallback); - } catch (exception& e) { - printf("%s\n", e.what()); - // std::count<innerProducer->send(*message, &selectMessageQueue, arg, autoRetryTimes); - // Convert SendStatus to CSendStatus - result->sendStatus = CSendStatus((int)sendResult.getSendStatus()); - result->offset = sendResult.getQueueOffset(); - strncpy(result->msgId, sendResult.getMsgId().c_str(), MAX_MESSAGE_ID_LENGTH - 1); - result->msgId[MAX_MESSAGE_ID_LENGTH - 1] = 0; - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_SEND_ORDERLY_FAILED; - } - return OK; -} -int SendMessageOrderlyByShardingKey(CProducer* producer, CMessage* msg, const char* shardingKey, CSendResult* result) { - if (producer == NULL || msg == NULL || shardingKey == NULL || result == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - MQMessage* message = (MQMessage*)msg; - - string sKey(shardingKey); - message->setProperty("__SHARDINGKEY", sKey); - try { - // Constructing SelectMessageQueue objects through function pointer callback - int retryTimes = 3; - SelectMessageQueueInner selectMessageQueue; - SendResult sendResult = - defaultMQProducer->innerProducer->send(*message, &selectMessageQueue, (void*)shardingKey, retryTimes); - // Convert SendStatus to CSendStatus - result->sendStatus = CSendStatus((int)sendResult.getSendStatus()); - result->offset = sendResult.getQueueOffset(); - strncpy(result->msgId, sendResult.getMsgId().c_str(), MAX_MESSAGE_ID_LENGTH - 1); - result->msgId[MAX_MESSAGE_ID_LENGTH - 1] = 0; - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_SEND_ORDERLY_FAILED; - } - return OK; -} - -int SendMessageTransaction(CProducer* producer, - CMessage* msg, - CLocalTransactionExecutorCallback callback, - void* userData, - CSendResult* result) { - if (producer == NULL || msg == NULL || callback == NULL || result == NULL) { - return NULL_POINTER; - } - try { - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - MQMessage* message = (MQMessage*)msg; - MyLocalTransactionExecuterInner executerInner(callback, msg, userData); - // defaultMQProducer->listenerInner->setM_m_ExcutorCallback(callback); - SendResult sendResult = - defaultMQProducer->innerTransactionProducer->sendMessageInTransaction(*message, &executerInner); - result->sendStatus = CSendStatus((int)sendResult.getSendStatus()); - result->offset = sendResult.getQueueOffset(); - strncpy(result->msgId, sendResult.getMsgId().c_str(), MAX_MESSAGE_ID_LENGTH - 1); - result->msgId[MAX_MESSAGE_ID_LENGTH - 1] = 0; - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_SEND_TRANSACTION_FAILED; - } - return OK; -} -int SetProducerGroupName(CProducer* producer, const char* groupName) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setGroupName(groupName); - } else { - defaultMQProducer->innerProducer->setGroupName(groupName); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} -int SetProducerInstanceName(CProducer* producer, const char* instanceName) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setInstanceName(instanceName); - } else { - defaultMQProducer->innerProducer->setInstanceName(instanceName); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} -int SetProducerSessionCredentials(CProducer* producer, - const char* accessKey, - const char* secretKey, - const char* onsChannel) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setSessionCredentials(accessKey, secretKey, onsChannel); - } else { - defaultMQProducer->innerProducer->setSessionCredentials(accessKey, secretKey, onsChannel); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} -int SetProducerLogPath(CProducer* producer, const char* logPath) { - if (producer == NULL) { - return NULL_POINTER; - } - setenv(ROCKETMQ_CLIENT_LOG_DIR.c_str(), logPath, 1); - return OK; -} - -int SetProducerLogFileNumAndSize(CProducer* producer, int fileNum, long fileSize) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setLogFileSizeAndNum(fileNum, fileSize); - } else { - defaultMQProducer->innerProducer->setLogFileSizeAndNum(fileNum, fileSize); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} - -int SetProducerLogLevel(CProducer* producer, CLogLevel level) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setLogLevel((elogLevel)level); - } else { - defaultMQProducer->innerProducer->setLogLevel((elogLevel)level); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} - -int SetProducerSendMsgTimeout(CProducer* producer, int timeout) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setSendMsgTimeout(timeout); - } else { - defaultMQProducer->innerProducer->setSendMsgTimeout(timeout); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} - -int SetProducerCompressMsgBodyOverHowmuch(CProducer* producer, int howmuch) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setCompressMsgBodyOverHowmuch(howmuch); - } else { - defaultMQProducer->innerProducer->setCompressMsgBodyOverHowmuch(howmuch); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} - -int SetProducerCompressLevel(CProducer* producer, int level) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setCompressLevel(level); - } else { - defaultMQProducer->innerProducer->setCompressLevel(level); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} - -int SetProducerMaxMessageSize(CProducer* producer, int size) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setMaxMessageSize(size); - } else { - defaultMQProducer->innerProducer->setMaxMessageSize(size); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} -int SetProducerMessageTrace(CProducer* producer, CTraceModel openTrace) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - bool messageTrace = openTrace == OPEN ? true : false; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setMessageTrace(messageTrace); - } else { - defaultMQProducer->innerProducer->setMessageTrace(messageTrace); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} - -int SetProducerSsl(CProducer* producer, int enableSsl) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - bool ssl = enableSsl != 0; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setEnableSsl(ssl); - } else { - defaultMQProducer->innerProducer->setEnableSsl(ssl); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} - -int SetProducerSslPropertyFile(CProducer* producer, const char* sslPropertyFile) { - if (producer == NULL) { - return NULL_POINTER; - } - DefaultProducer* defaultMQProducer = (DefaultProducer*)producer; - try { - if (CAPI_C_PRODUCER_TYPE_TRANSACTION == defaultMQProducer->producerType) { - defaultMQProducer->innerTransactionProducer->setSslPropertyFile(sslPropertyFile); - } else { - defaultMQProducer->innerProducer->setSslPropertyFile(sslPropertyFile); - } - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PRODUCER_START_FAILED; - } - return OK; -} - -#ifdef __cplusplus -}; -#endif diff --git a/src/extern/CPullConsumer.cpp b/src/extern/CPullConsumer.cpp deleted file mode 100644 index 9ea26710e..000000000 --- a/src/extern/CPullConsumer.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CPullConsumer.h" -#include "CCommon.h" -#include "CMessageExt.h" -#include "DefaultMQPullConsumer.h" -#include "MQClientErrorContainer.h" -#include "Logging.h" - -using namespace rocketmq; -using namespace std; - -#ifdef __cplusplus -extern "C" { -#endif -char VERSION_FOR_PULL_CONSUMER[MAX_SDK_VERSION_LENGTH]; -CPullConsumer* CreatePullConsumer(const char* groupId) { - if (groupId == NULL) { - return NULL; - } - DefaultMQPullConsumer* defaultMQPullConsumer = new DefaultMQPullConsumer(groupId); - strncpy(VERSION_FOR_PULL_CONSUMER, defaultMQPullConsumer->version().c_str(), MAX_SDK_VERSION_LENGTH - 1); - VERSION_FOR_PULL_CONSUMER[MAX_SDK_VERSION_LENGTH - 1] = 0; - return (CPullConsumer*)defaultMQPullConsumer; -} -int DestroyPullConsumer(CPullConsumer* consumer) { - if (consumer == NULL) { - return NULL_POINTER; - } - delete reinterpret_cast(consumer); - return OK; -} -int StartPullConsumer(CPullConsumer* consumer) { - if (consumer == NULL) { - return NULL_POINTER; - } - try { - ((DefaultMQPullConsumer*)consumer)->start(); - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PULLCONSUMER_START_FAILED; - } - return OK; -} -int ShutdownPullConsumer(CPullConsumer* consumer) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPullConsumer*)consumer)->shutdown(); - return OK; -} -const char* ShowPullConsumerVersion(CPullConsumer* consumer) { - if (consumer == NULL) { - return NULL; - } - return VERSION_FOR_PULL_CONSUMER; -} - -int SetPullConsumerGroupID(CPullConsumer* consumer, const char* groupId) { - if (consumer == NULL || groupId == NULL) { - return NULL_POINTER; - } - ((DefaultMQPullConsumer*)consumer)->setGroupName(groupId); - return OK; -} -const char* GetPullConsumerGroupID(CPullConsumer* consumer) { - if (consumer == NULL) { - return NULL; - } - return ((DefaultMQPullConsumer*)consumer)->getGroupName().c_str(); -} -int SetPullConsumerNameServerAddress(CPullConsumer* consumer, const char* namesrv) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPullConsumer*)consumer)->setNamesrvAddr(namesrv); - return OK; -} -int SetPullConsumerNameServerDomain(CPullConsumer* consumer, const char* domain) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPullConsumer*)consumer)->setNamesrvDomain(domain); - return OK; -} -int SetPullConsumerSessionCredentials(CPullConsumer* consumer, - const char* accessKey, - const char* secretKey, - const char* channel) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPullConsumer*)consumer)->setSessionCredentials(accessKey, secretKey, channel); - return OK; -} - -int SetPullConsumerLogPath(CPullConsumer* consumer, const char* logPath) { - if (consumer == NULL) { - return NULL_POINTER; - } - // Todo, This api should be implemented by core api. - //((DefaultMQPullConsumer *) consumer)->setInstanceName(instanceName); - setenv(ROCKETMQ_CLIENT_LOG_DIR.c_str(), logPath, 1); - return OK; -} - -int SetPullConsumerLogFileNumAndSize(CPullConsumer* consumer, int fileNum, long fileSize) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPullConsumer*)consumer)->setLogFileSizeAndNum(fileNum, fileSize); - return OK; -} - -int SetPullConsumerLogLevel(CPullConsumer* consumer, CLogLevel level) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPullConsumer*)consumer)->setLogLevel((elogLevel)level); - return OK; -} - -int SetPullConsumerSsl(CPullConsumer* consumer, int enableSsl) { - if (consumer == NULL) { - return NULL_POINTER; - } - bool ssl = enableSsl != 0; - ((DefaultMQPullConsumer*)consumer)->setEnableSsl(ssl); - return OK; -} - -int SetPullConsumerSslPropertyFile(CPullConsumer* consumer, const char* sslPropertyFile) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPullConsumer*)consumer)->setSslPropertyFile(sslPropertyFile); - return OK; -} - -int FetchSubscriptionMessageQueues(CPullConsumer* consumer, const char* topic, CMessageQueue** mqs, int* size) { - if (consumer == NULL) { - return NULL_POINTER; - } - unsigned int index = 0; - CMessageQueue* temMQ = NULL; - std::vector fullMQ; - try { - ((DefaultMQPullConsumer*)consumer)->fetchSubscribeMessageQueues(topic, fullMQ); - *size = fullMQ.size(); - // Alloc memory to save the pointer to CPP MessageQueue, and the MessageQueues may be changed. - // Thus, this memory should be released by users using @ReleaseSubscribeMessageQueue every time. - temMQ = (CMessageQueue*)malloc(*size * sizeof(CMessageQueue)); - if (temMQ == NULL) { - *size = 0; - *mqs = NULL; - return MALLOC_FAILED; - } - auto iter = fullMQ.begin(); - for (index = 0; iter != fullMQ.end() && index <= fullMQ.size(); ++iter, index++) { - strncpy(temMQ[index].topic, iter->getTopic().c_str(), MAX_TOPIC_LENGTH - 1); - strncpy(temMQ[index].brokerName, iter->getBrokerName().c_str(), MAX_BROKER_NAME_ID_LENGTH - 1); - temMQ[index].queueId = iter->getQueueId(); - } - *mqs = temMQ; - } catch (MQException& e) { - *size = 0; - *mqs = NULL; - MQClientErrorContainer::setErr(string(e.what())); - return PULLCONSUMER_FETCH_MQ_FAILED; - } - return OK; -} -int ReleaseSubscriptionMessageQueue(CMessageQueue* mqs) { - if (mqs == NULL) { - return NULL_POINTER; - } - free((void*)mqs); - mqs = NULL; - return OK; -} -CPullResult Pull(CPullConsumer* consumer, - const CMessageQueue* mq, - const char* subExpression, - long long offset, - int maxNums) { - CPullResult pullResult; - memset(&pullResult, 0, sizeof(CPullResult)); - if (consumer == NULL || subExpression == NULL) { - pullResult.pullStatus = E_BROKER_TIMEOUT; - return pullResult; - } - MQMessageQueue messageQueue(mq->topic, mq->brokerName, mq->queueId); - PullResult cppPullResult; - try { - cppPullResult = ((DefaultMQPullConsumer*)consumer)->pull(messageQueue, subExpression, offset, maxNums); - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - cppPullResult.pullStatus = BROKER_TIMEOUT; - } - - if (cppPullResult.pullStatus != BROKER_TIMEOUT) { - pullResult.maxOffset = cppPullResult.maxOffset; - pullResult.minOffset = cppPullResult.minOffset; - pullResult.nextBeginOffset = cppPullResult.nextBeginOffset; - } - - switch (cppPullResult.pullStatus) { - case FOUND: { - pullResult.pullStatus = E_FOUND; - pullResult.size = cppPullResult.msgFoundList.size(); - PullResult* tmpPullResult = new PullResult(cppPullResult); - pullResult.pData = tmpPullResult; - // Alloc memory to save the pointer to CPP MQMessageExt, which will be release by the CPP SDK core. - // Thus, this memory should be released by users using @ReleasePullResult - pullResult.msgFoundList = (CMessageExt**)malloc(pullResult.size * sizeof(CMessageExt*)); - for (size_t i = 0; i < cppPullResult.msgFoundList.size(); i++) { - MQMessageExt* msg = const_cast(&tmpPullResult->msgFoundList[i]); - pullResult.msgFoundList[i] = (CMessageExt*)(msg); - } - break; - } - case NO_NEW_MSG: { - pullResult.pullStatus = E_NO_NEW_MSG; - break; - } - case NO_MATCHED_MSG: { - pullResult.pullStatus = E_NO_MATCHED_MSG; - break; - } - case OFFSET_ILLEGAL: { - pullResult.pullStatus = E_OFFSET_ILLEGAL; - break; - } - case BROKER_TIMEOUT: { - pullResult.pullStatus = E_BROKER_TIMEOUT; - break; - } - default: - pullResult.pullStatus = E_NO_NEW_MSG; - break; - } - return pullResult; -} -int ReleasePullResult(CPullResult pullResult) { - if (pullResult.size == 0 || pullResult.msgFoundList == NULL || pullResult.pData == NULL) { - return NULL_POINTER; - } - if (pullResult.pData != NULL) { - try { - delete ((PullResult*)pullResult.pData); - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return NULL_POINTER; - } - } - free((void*)pullResult.msgFoundList); - pullResult.msgFoundList = NULL; - return OK; -} - -#ifdef __cplusplus -}; -#endif diff --git a/src/extern/CPushConsumer.cpp b/src/extern/CPushConsumer.cpp deleted file mode 100644 index 431a5671b..000000000 --- a/src/extern/CPushConsumer.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CPushConsumer.h" -#include -#include "CCommon.h" -#include "CMessageExt.h" -#include "DefaultMQPushConsumer.h" -#include "MQClientErrorContainer.h" -#include "Logging.h" - -using namespace rocketmq; -using namespace std; - -class MessageListenerInner : public MessageListenerConcurrently { - public: - MessageListenerInner() {} - - MessageListenerInner(CPushConsumer* consumer, MessageCallBack pCallback) { - m_pconsumer = consumer; - m_pMsgReceiveCallback = pCallback; - } - - ~MessageListenerInner() {} - - ConsumeStatus consumeMessage(const std::vector& msgs) { - // to do user call back - if (m_pMsgReceiveCallback == NULL) { - return RECONSUME_LATER; - } - for (size_t i = 0; i < msgs.size(); ++i) { - MQMessageExt* msg = const_cast(&msgs[i]); - CMessageExt* message = (CMessageExt*)(msg); - if (m_pMsgReceiveCallback(m_pconsumer, message) != E_CONSUME_SUCCESS) - return RECONSUME_LATER; - } - return CONSUME_SUCCESS; - } - - private: - MessageCallBack m_pMsgReceiveCallback; - CPushConsumer* m_pconsumer; -}; - -class MessageListenerOrderlyInner : public MessageListenerOrderly { - public: - MessageListenerOrderlyInner(CPushConsumer* consumer, MessageCallBack pCallback) { - m_pconsumer = consumer; - m_pMsgReceiveCallback = pCallback; - } - - ConsumeStatus consumeMessage(const std::vector& msgs) { - if (m_pMsgReceiveCallback == NULL) { - return RECONSUME_LATER; - } - for (size_t i = 0; i < msgs.size(); ++i) { - MQMessageExt* msg = const_cast(&msgs[i]); - CMessageExt* message = (CMessageExt*)(msg); - if (m_pMsgReceiveCallback(m_pconsumer, message) != E_CONSUME_SUCCESS) - return RECONSUME_LATER; - } - return CONSUME_SUCCESS; - } - - private: - MessageCallBack m_pMsgReceiveCallback; - CPushConsumer* m_pconsumer; -}; - -map g_ListenerMap; -map g_OrderListenerMap; -#ifdef __cplusplus -extern "C" { -#endif -char VERSION_FOR_PUSH_CONSUMER[MAX_SDK_VERSION_LENGTH]; -CPushConsumer* CreatePushConsumer(const char* groupId) { - if (groupId == NULL) { - return NULL; - } - DefaultMQPushConsumer* defaultMQPushConsumer = new DefaultMQPushConsumer(groupId); - defaultMQPushConsumer->setConsumeFromWhere(CONSUME_FROM_LAST_OFFSET); - - strncpy(VERSION_FOR_PUSH_CONSUMER, defaultMQPushConsumer->version().c_str(), MAX_SDK_VERSION_LENGTH - 1); - VERSION_FOR_PUSH_CONSUMER[MAX_SDK_VERSION_LENGTH - 1] = 0; - return (CPushConsumer*)defaultMQPushConsumer; -} -int DestroyPushConsumer(CPushConsumer* consumer) { - if (consumer == NULL) { - return NULL_POINTER; - } - delete reinterpret_cast(consumer); - return OK; -} -int StartPushConsumer(CPushConsumer* consumer) { - if (consumer == NULL) { - return NULL_POINTER; - } - try { - ((DefaultMQPushConsumer*)consumer)->start(); - } catch (exception& e) { - MQClientErrorContainer::setErr(string(e.what())); - return PUSHCONSUMER_START_FAILED; - } - return OK; -} -int ShutdownPushConsumer(CPushConsumer* consumer) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->shutdown(); - return OK; -} - -const char* ShowPushConsumerVersion(CPushConsumer* consumer) { - if (consumer == NULL) { - return NULL; - } - return VERSION_FOR_PUSH_CONSUMER; -} -int SetPushConsumerGroupID(CPushConsumer* consumer, const char* groupId) { - if (consumer == NULL || groupId == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setGroupName(groupId); - return OK; -} -const char* GetPushConsumerGroupID(CPushConsumer* consumer) { - if (consumer == NULL) { - return NULL; - } - return ((DefaultMQPushConsumer*)consumer)->getGroupName().c_str(); -} -int SetPushConsumerNameServerAddress(CPushConsumer* consumer, const char* namesrv) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setNamesrvAddr(namesrv); - return OK; -} -int SetPushConsumerNameServerDomain(CPushConsumer* consumer, const char* domain) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setNamesrvDomain(domain); - return OK; -} -int Subscribe(CPushConsumer* consumer, const char* topic, const char* expression) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->subscribe(topic, expression); - return OK; -} - -int RegisterMessageCallback(CPushConsumer* consumer, MessageCallBack pCallback) { - if (consumer == NULL || pCallback == NULL) { - return NULL_POINTER; - } - MessageListenerInner* listenerInner = new MessageListenerInner(consumer, pCallback); - ((DefaultMQPushConsumer*)consumer)->registerMessageListener(listenerInner); - g_ListenerMap[consumer] = listenerInner; - return OK; -} - -int RegisterMessageCallbackOrderly(CPushConsumer* consumer, MessageCallBack pCallback) { - if (consumer == NULL || pCallback == NULL) { - return NULL_POINTER; - } - MessageListenerOrderlyInner* messageListenerOrderlyInner = new MessageListenerOrderlyInner(consumer, pCallback); - ((DefaultMQPushConsumer*)consumer)->registerMessageListener(messageListenerOrderlyInner); - g_OrderListenerMap[consumer] = messageListenerOrderlyInner; - return OK; -} - -int UnregisterMessageCallbackOrderly(CPushConsumer* consumer) { - if (consumer == NULL) { - return NULL_POINTER; - } - map::iterator iter; - iter = g_OrderListenerMap.find(consumer); - if (iter != g_OrderListenerMap.end()) { - MessageListenerOrderlyInner* listenerInner = iter->second; - if (listenerInner != NULL) { - delete listenerInner; - } - g_OrderListenerMap.erase(iter); - } - return OK; -} - -int UnregisterMessageCallback(CPushConsumer* consumer) { - if (consumer == NULL) { - return NULL_POINTER; - } - map::iterator iter; - iter = g_ListenerMap.find(consumer); - - if (iter != g_ListenerMap.end()) { - MessageListenerInner* listenerInner = iter->second; - if (listenerInner != NULL) { - delete listenerInner; - } - g_ListenerMap.erase(iter); - } - return OK; -} - -int SetPushConsumerMessageModel(CPushConsumer* consumer, CMessageModel messageModel) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setMessageModel(MessageModel((int)messageModel)); - return OK; -} -int SetPushConsumerThreadCount(CPushConsumer* consumer, int threadCount) { - if (consumer == NULL || threadCount == 0) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setConsumeThreadCount(threadCount); - return OK; -} -int SetPushConsumerMessageBatchMaxSize(CPushConsumer* consumer, int batchSize) { - if (consumer == NULL || batchSize == 0) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setConsumeMessageBatchMaxSize(batchSize); - return OK; -} -int SetPushConsumerMaxCacheMessageSize(CPushConsumer* consumer, int maxCacheSize) { - if (consumer == NULL || maxCacheSize <= 0) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setMaxCacheMsgSizePerQueue(maxCacheSize); - return OK; -} - -int SetPushConsumerMaxCacheMessageSizeInMb(CPushConsumer* consumer, int maxCacheSizeInMb) { - if (consumer == NULL || maxCacheSizeInMb <= 0) { - return NULL_POINTER; - } - return Not_Support; -} -int SetPushConsumerInstanceName(CPushConsumer* consumer, const char* instanceName) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setInstanceName(instanceName); - return OK; -} - -int SetPushConsumerSessionCredentials(CPushConsumer* consumer, - const char* accessKey, - const char* secretKey, - const char* channel) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setSessionCredentials(accessKey, secretKey, channel); - return OK; -} - -int SetPushConsumerLogPath(CPushConsumer* consumer, const char* logPath) { - if (consumer == NULL) { - return NULL_POINTER; - } - // Todo, This api should be implemented by core api. - //((DefaultMQPushConsumer *) consumer)->setInstanceName(instanceName); - setenv(ROCKETMQ_CLIENT_LOG_DIR.c_str(), logPath, 1); - return OK; -} - -int SetPushConsumerLogFileNumAndSize(CPushConsumer* consumer, int fileNum, long fileSize) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setLogFileSizeAndNum(fileNum, fileSize); - return OK; -} - -int SetPushConsumerLogLevel(CPushConsumer* consumer, CLogLevel level) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setLogLevel((elogLevel)level); - return OK; -} -int SetPushConsumerMessageTrace(CPushConsumer* consumer, CTraceModel openTrace) { - if (consumer == NULL) { - return NULL_POINTER; - } - bool messageTrace = openTrace == OPEN ? true : false; - ((DefaultMQPushConsumer*)consumer)->setMessageTrace(messageTrace); - return OK; -} - -int SetPushConsumerSsl(CPushConsumer* consumer, int enableSsl) { - if (consumer == NULL) { - return NULL_POINTER; - } - bool ssl = enableSsl != 0; - ((DefaultMQPushConsumer*)consumer)->setEnableSsl(ssl); - return OK; -} - -int SetPushConsumerSslPropertyFile(CPushConsumer* consumer, const char* sslPropertyFile) { - if (consumer == NULL) { - return NULL_POINTER; - } - ((DefaultMQPushConsumer*)consumer)->setSslPropertyFile(sslPropertyFile); - return OK; -} - -#ifdef __cplusplus -}; -#endif diff --git a/src/extern/CSendResult.cpp b/src/extern/CSendResult.cpp deleted file mode 100644 index c43464a95..000000000 --- a/src/extern/CSendResult.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CSendResult.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif diff --git a/src/include/BatchMessage.h b/src/include/BatchMessage.h deleted file mode 100644 index bca467a00..000000000 --- a/src/include/BatchMessage.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __BATCHMESSAGE_H__ -#define __BATCHMESSAGE_H__ -#include -#include "MQMessage.h" -namespace rocketmq { -class BatchMessage : public MQMessage { - public: - static std::string encode(std::vector& msgs); - static std::string encode(MQMessage& message); -}; -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/src/include/DefaultMQClient.h b/src/include/DefaultMQClient.h deleted file mode 100644 index 10399c517..000000000 --- a/src/include/DefaultMQClient.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEFAULTMQADMIN_H__ -#define __DEFAULTMQADMIN_H__ -#include -#include -#include -#include -#include -#include -#include "MQClient.h" -#include "MQMessageExt.h" -#include "MQMessageQueue.h" -#include "QueryResult.h" -#include "RocketMQClient.h" -#include "SessionCredentials.h" -#include "UtilAll.h" - -namespace rocketmq { -class MQClientFactory; -// getTopicMessageQueueInfo(const std::string& topic); - - // log configuration interface, default LOG_LEVEL is LOG_LEVEL_INFO, default - // log file num is 3, each log size is 100M - void setLogLevel(elogLevel inputLevel); - elogLevel getLogLevel(); - void setLogFileSizeAndNum(int fileNum, long perFileSize); // perFileSize is MB unit - - /** set TcpTransport pull thread num, which dermine the num of threads to - distribute network data, - 1. its default value is CPU num, it must be setted before producer/consumer - start, minimum value is CPU num; - 2. this pullThread num must be tested on your environment to find the best - value for RT of sendMsg or delay time of consume msg before you change it; - 3. producer and consumer need different pullThread num, if set this num, - producer and consumer must set different instanceName. - 4. configuration suggestion: - 1>. minimum RT of sendMsg: - pullThreadNum = brokerNum*2 - **/ - void setTcpTransportPullThreadNum(int num); - int getTcpTransportPullThreadNum() const; - - /** timeout of tcp connect, it is same meaning for both producer and consumer; - 1. default value is 3000ms - 2. input parameter could only be milliSecond, suggestion value is - 1000-3000ms; - **/ - void setTcpTransportConnectTimeout(uint64_t timeout); // ms - uint64_t getTcpTransportConnectTimeout() const; - - /** timeout of tryLock tcpTransport before sendMsg/pullMsg, if timeout, - returns NULL - 1. paremeter unit is ms, default value is 3000ms, the minimun value is - 1000ms - suggestion value is 3000ms; - 2. if configured with value smaller than 1000ms, the tryLockTimeout value - will be setted to 1000ms - **/ - void setTcpTransportTryLockTimeout(uint64_t timeout); // ms - uint64_t getTcpTransportTryLockTimeout() const; - - void setUnitName(const std::string& unitName); - const std::string& getUnitName() const; - - void setSessionCredentials(const std::string& input_accessKey, - const std::string& input_secretKey, - const std::string& input_onsChannel); - const SessionCredentials& getSessionCredentials() const; - - virtual void setFactory(MQClientFactory*); - - void setEnableSsl(bool enableSsl); - bool getEnableSsl() const; - - void setSslPropertyFile(const std::string& sslPropertyFile); - const std::string& getSslPropertyFile() const; - bool getMessageTrace() const; - void setMessageTrace(bool mMessageTrace); - - protected: - virtual void start(); - virtual void shutdown(); - MQClientFactory* getFactory() const; - virtual bool isServiceStateOk(); - void showClientConfigs(); - - protected: - std::string m_namesrvAddr; - std::string m_namesrvDomain; - std::string m_instanceName; - std::string m_nameSpace; - std::string m_GroupName; - std::string m_sslPropertyFile{DEFAULT_SSL_PROPERTY_FILE}; - bool m_enableSsl{false}; - MQClientFactory* m_clientFactory; - int m_serviceState; - int m_pullThreadNum; - uint64_t m_tcpConnectTimeout; // ms - uint64_t m_tcpTransportTryLockTimeout; // s - - std::string m_unitName; - SessionCredentials m_SessionCredentials; - bool m_messageTrace; -}; -// -#include "AsyncCallback.h" -#include "ConsumeType.h" -#include "DefaultMQClient.h" -#include "RocketMQClient.h" - -namespace rocketmq { -class SubscriptionData; -class PullRequest; -class Rebalance; -class ConsumerRunningInfo; -//& mqs) = 0; - virtual void doRebalance() = 0; - virtual void persistConsumerOffset() = 0; - virtual void persistConsumerOffsetByResetOffset() = 0; - virtual void updateTopicSubscribeInfo(const std::string& topic, std::vector& info) = 0; - virtual void updateConsumeOffset(const MQMessageQueue& mq, int64 offset) = 0; - virtual void removeConsumeOffset(const MQMessageQueue& mq) = 0; - virtual ConsumeType getConsumeType() = 0; - virtual ConsumeFromWhere getConsumeFromWhere() = 0; - virtual void getSubscriptions(std::vector&) = 0; - virtual bool producePullMsgTask(boost::weak_ptr) = 0; - virtual Rebalance* getRebalance() const = 0; - virtual PullResult pull(const MQMessageQueue& mq, const std::string& subExpression, int64 offset, int maxNums) = 0; - virtual void pull(const MQMessageQueue& mq, - const std::string& subExpression, - int64 offset, - int maxNums, - PullCallback* pPullCallback) = 0; - virtual ConsumerRunningInfo* getConsumerRunningInfo() = 0; - - public: - MessageModel getMessageModel() const { return m_messageModel; } - void setMessageModel(MessageModel messageModel) { m_messageModel = messageModel; } - bool isUseNameSpaceMode() const { return m_useNameSpaceMode; } - - protected: - MessageModel m_messageModel; - bool m_useNameSpaceMode = false; -}; - -//& msgs) = 0; - virtual SendResult send(std::vector& msgs, const MQMessageQueue& mq) = 0; - virtual void sendOneway(MQMessage& msg, bool bSelectActiveBroker = false) = 0; - virtual void sendOneway(MQMessage& msg, const MQMessageQueue& mq) = 0; - virtual void sendOneway(MQMessage& msg, MessageQueueSelector* selector, void* arg) = 0; -}; -//& messageList) { - m_indexLastUpdateTimestamp = indexLastUpdateTimestamp; - m_messageList = messageList; - } - - uint64 getIndexLastUpdateTimestamp() { return m_indexLastUpdateTimestamp; } - - std::vector& getMessageList() { return m_messageList; } - - private: - uint64 m_indexLastUpdateTimestamp; - std::vector m_messageList; -}; -// -#include "UtilAll.h" -#define BOOST_DATE_TIME_SOURCE - -namespace rocketmq { -logAdapter* logAdapter::alogInstance; -boost::mutex logAdapter::m_imtx; - -logAdapter::~logAdapter() { - logging::core::get()->remove_all_sinks(); -} - -logAdapter* logAdapter::getLogInstance() { - if (alogInstance == NULL) { - boost::mutex::scoped_lock guard(m_imtx); - if (alogInstance == NULL) { - alogInstance = new logAdapter(); - } - } - return alogInstance; -} - -logAdapter::logAdapter() : m_logLevel(eLOG_LEVEL_INFO) { - setLogDir(); - string homeDir(UtilAll::getHomeDirectory()); - homeDir.append(m_log_dir); - m_logFile += homeDir; - std::string fileName = "rocketmq_client.log"; - m_logFile += fileName; - - // boost::log::expressions::attr< - // boost::log::attributes::current_thread_id::value_type>("ThreadID"); - boost::log::register_simple_formatter_factory("Severity"); - m_logSink = logging::add_file_log(keywords::file_name = m_logFile, - keywords::target_file_name = "rocketmq_client_%Y%m%d-%N.log", - keywords::rotation_size = 100 * 1024 * 1024, - keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0), - keywords::format = "[%TimeStamp%](%Severity%):%Message%", - keywords::min_free_space = 300 * 1024 * 1024, keywords::target = homeDir, - keywords::max_size = 200 * 1024 * 1024, // max keep 3 log file defaultly - keywords::auto_flush = true); - // logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info); - setLogLevelInner(m_logLevel); - - logging::add_common_attributes(); -} - -void logAdapter::setLogLevelInner(elogLevel logLevel) { - switch (logLevel) { - case eLOG_LEVEL_FATAL: - logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::fatal); - break; - case eLOG_LEVEL_ERROR: - logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::error); - - break; - case eLOG_LEVEL_WARN: - logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::warning); - - break; - case eLOG_LEVEL_INFO: - logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info); - - break; - case eLOG_LEVEL_DEBUG: - logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::debug); - - break; - case eLOG_LEVEL_TRACE: - logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::trace); - - break; - default: - logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info); - - break; - } -} -void logAdapter::setLogLevel(elogLevel logLevel) { - m_logLevel = logLevel; - setLogLevelInner(logLevel); -} - -elogLevel logAdapter::getLogLevel() { - return m_logLevel; -} - -void logAdapter::setLogDir() { - char* p = nullptr; - if ((p = getenv(ROCKETMQ_CLIENT_LOG_DIR.c_str()))) { - m_log_dir = p; - } - if (!m_log_dir.empty()) { - if (m_log_dir[m_log_dir.length() - 1] != '/') { - m_log_dir += '/'; - } - } else { - m_log_dir = "/logs/rocketmq-client/"; - } -} - -void logAdapter::setLogFileNumAndSize(int logNum, int sizeOfPerFile) { - string homeDir(UtilAll::getHomeDirectory()); - homeDir.append(m_log_dir); - m_logSink->locked_backend()->set_rotation_size(sizeOfPerFile * 1024 * 1024); - m_logSink->locked_backend()->set_file_collector(sinks::file::make_collector( - keywords::target = homeDir, keywords::max_size = logNum * sizeOfPerFile * 1024 * 1024)); -} -} // namespace rocketmq diff --git a/src/log/Logging.h b/src/log/Logging.h deleted file mode 100644 index 3917328d1..000000000 --- a/src/log/Logging.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _ALOG_ADAPTER_H_ -#define _ALOG_ADAPTER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "include/DefaultMQClient.h" - -namespace logging = boost::log; -namespace src = boost::log::sources; -namespace sinks = boost::log::sinks; -namespace expr = boost::log::expressions; -namespace keywords = boost::log::keywords; -using namespace boost::log::trivial; -namespace rocketmq { - -class logAdapter { - public: - ~logAdapter(); - static logAdapter* getLogInstance(); - void setLogDir(); - void setLogLevel(elogLevel logLevel); - elogLevel getLogLevel(); - void setLogFileNumAndSize(int logNum, int sizeOfPerFile); - src::severity_logger& getSeverityLogger() { return m_severityLogger; } - - private: - logAdapter(); - void setLogLevelInner(elogLevel logLevel); - elogLevel m_logLevel; - std::string m_logFile; - src::severity_logger m_severityLogger; - typedef sinks::synchronous_sink logSink_t; - boost::shared_ptr m_logSink; - static logAdapter* alogInstance; - static boost::mutex m_imtx; - std::string m_log_dir; -}; - -#define ALOG_ADAPTER logAdapter::getLogInstance() - -#define AGENT_LOGGER ALOG_ADAPTER->getSeverityLogger() - -class LogUtil { - public: - static void LogMessage(boost::log::trivial::severity_level level, int line, const char* format, ...) { - va_list arg_ptr; - va_start(arg_ptr, format); - boost::scoped_array formattedString(new char[1024]); - vsnprintf(formattedString.get(), 1024, format, arg_ptr); - BOOST_LOG_SEV(AGENT_LOGGER, level) << formattedString.get(); - va_end(arg_ptr); - } - static void LogMessageFull(boost::log::trivial::severity_level level, - const char* file, - const char* func, - int line, - const char* format, - ...) { - va_list arg_ptr; - va_start(arg_ptr, format); - boost::scoped_array formattedString(new char[1024]); - vsnprintf(formattedString.get(), 1024, format, arg_ptr); - // BOOST_LOG_SEV(AGENT_LOGGER, level) << formattedString.get() << "[" << file << ":" << func << ":"<< line << "]"; - BOOST_LOG_SEV(AGENT_LOGGER, level) << formattedString.get() << "[" << func << ":" << line << "]"; - va_end(arg_ptr); - } -}; - -#define LOG_FATAL(...) \ - LogUtil::LogMessageFull(boost::log::trivial::fatal, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) -#define LOG_ERROR(...) \ - LogUtil::LogMessageFull(boost::log::trivial::error, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) -#define LOG_WARN(...) \ - LogUtil::LogMessageFull(boost::log::trivial::warning, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) -//#define LOG_INFO(...) LogUtil::LogMessage(boost::log::trivial::info, __LINE__, __VA_ARGS__) -#define LOG_INFO(...) LogUtil::LogMessageFull(boost::log::trivial::info, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) -#define LOG_DEBUG(...) \ - LogUtil::LogMessageFull(boost::log::trivial::debug, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) -} // namespace rocketmq -#endif diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt new file mode 100644 index 000000000..b79f938a1 --- /dev/null +++ b/src/main/cpp/CMakeLists.txt @@ -0,0 +1,63 @@ +add_subdirectory(log) +add_subdirectory(base) +add_subdirectory(admin) +add_subdirectory(scheduler) +add_subdirectory(concurrent) +add_subdirectory(client) +add_subdirectory(tracing) +add_subdirectory(rocketmq) +add_subdirectory(ons) + +add_library(rocketmq + STATIC + $ + $ + $ + $ + $ + $ + $ + $ + $ + $) + +target_link_libraries(rocketmq + PUBLIC + absl::base + gRPC::grpc++ + fmt + proto + opencensus::trace + opencensus::stats + opencensus_proto + spdlog) +set_target_properties(rocketmq PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + + +add_library(rocketmq_shared + SHARED + $ + $ + $ + $ + $ + $ + $ + $ + $ + $) + +target_link_libraries(rocketmq_shared + PUBLIC + absl::base + gRPC::grpc++ + fmt + proto + opencensus::trace + opencensus::stats + opencensus_proto + spdlog) +set_target_properties(rocketmq_shared + PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + LIBRARY_OUTPUT_NAME rocketmq) \ No newline at end of file diff --git a/src/main/cpp/admin/AdminClient.cpp b/src/main/cpp/admin/AdminClient.cpp new file mode 100644 index 000000000..8a20d649a --- /dev/null +++ b/src/main/cpp/admin/AdminClient.cpp @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "AdminClient.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +namespace admin { + +Status AdminClient::changeLogLevel(const rmq::ChangeLogLevelRequest& request, rmq::ChangeLogLevelResponse& response) { + grpc::ClientContext context; + auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(3); + context.set_deadline(deadline); + Status status = stub_->ChangeLogLevel(&context, request, &response); + return status; +} + +} // namespace admin + +ROCKETMQ_NAMESPACE_END + +void wrapChangeLogLevelRequest(rmq::ChangeLogLevelRequest& request, const std::string& level) { + if (level.empty()) { + return; + } + + if ("trace" == level) { + request.set_level(rmq::ChangeLogLevelRequest_Level_TRACE); + return; + } + + if ("debug" == level) { + request.set_level(rmq::ChangeLogLevelRequest_Level_DEBUG); + return; + } + + if ("info" == level) { + request.set_level(rmq::ChangeLogLevelRequest_Level_INFO); + return; + } + + if ("warn" == level) { + request.set_level(rmq::ChangeLogLevelRequest_Level_WARN); + return; + } + + if ("error" == level) { + request.set_level(rmq::ChangeLogLevelRequest_Level_ERROR); + return; + } +} + +int main(int argc, char* argv[]) { + if (argc <= 1) { + std::cerr << "Usage: 'admin_client level [port]' where level is among trace/debug/info/warn/error.\n"; + return EXIT_SUCCESS; + } + + int port = 9877; + + char* env_port = getenv("ROCKETMQ_ADMIN_PORT"); + if (nullptr != env_port) { + try { + port = std::stoi(env_port); + } catch (...) { + std::cerr << "Failed to parse port from environment variable ROCKETMQ_ADMIN_PORT" << env_port << "\n"; + } + } + + if (argc > 2) { + try { + port = std::stoi(argv[2]); + } catch (...) { + std::cerr << "Failed to parse port from command line:" << argv[2] << "\n"; + } + } + + std::string target("127.0.0.1:"); + target.append(std::to_string(port)); + + std::cout << "Target address: " << target << "\n"; + auto channel = grpc::CreateChannel(target, grpc::InsecureChannelCredentials()); + rocketmq::admin::AdminClient client(channel); + + rmq::ChangeLogLevelRequest request; + wrapChangeLogLevelRequest(request, argv[1]); + rmq::ChangeLogLevelResponse response; + auto status = client.changeLogLevel(request, response); + if (status.ok()) { + std::cout << "Log level changed OK" << std::endl; + } else { + std::cerr << "Failed to change log level. GRPC error code: " << status.error_code() + << ", GRPC error message: " << status.error_message() << "\n"; + std::cerr << "Server remark: " << response.remark() << std::endl; + } + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/main/cpp/admin/AdminFacade.cpp b/src/main/cpp/admin/AdminFacade.cpp new file mode 100644 index 000000000..a224abe75 --- /dev/null +++ b/src/main/cpp/admin/AdminFacade.cpp @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "AdminServerImpl.h" + +using namespace ROCKETMQ_NAMESPACE::admin; + +AdminServer& AdminFacade::getServer() { + static AdminServerImpl admin_server; + return admin_server; +} \ No newline at end of file diff --git a/src/main/cpp/admin/AdminServerImpl.cpp b/src/main/cpp/admin/AdminServerImpl.cpp new file mode 100644 index 000000000..ce16072cc --- /dev/null +++ b/src/main/cpp/admin/AdminServerImpl.cpp @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "AdminServerImpl.h" + +#include "AdminServiceImpl.h" +#include "ServerCall.h" +#include "spdlog/spdlog.h" +#include + +using grpc::ServerBuilder; + +ROCKETMQ_NAMESPACE_BEGIN + +namespace admin { +bool AdminServerImpl::start() { + if (State::CREATED != state_) { + return false; + } + State current = State::CREATED; + if (state_.compare_exchange_weak(current, State::STARTING, std::memory_order_relaxed)) { + std::string server_address("127.0.0.1:"); + server_address.append(std::to_string(port_)); + + grpc::EnableDefaultHealthCheckService(true); + + ServerBuilder server_builder; + server_builder.AddListeningPort(server_address, grpc::InsecureServerCredentials(), &port_); + server_builder.RegisterService(async_stub_.get()); + // A gRPC server may have multiple per-thread CompletionQueue + completion_queue_ = server_builder.AddCompletionQueue(); + server_ = server_builder.BuildAndStart(); + { + loop(); + std::unique_lock lk(loop_mtx_); + loop_cv_.wait(lk, [this]() { return State::STARTED == state_.load(); }); + } + SPDLOG_INFO("Admin server started and listening 127.0.0.1:{}", port_); + return true; + } + return false; +} + +void AdminServerImpl::loop() { + auto loop_lambda = [this] { + state_.store(State::STARTED); + { + std::unique_lock lk(loop_mtx_); + loop_cv_.notify_all(); + } + + SPDLOG_DEBUG("Prepare initial ServerCall"); + new ServerCall(async_stub_.get(), service_.get(), completion_queue_.get()); + void* tag; + bool ok; + + while (completion_queue_->Next(&tag, &ok)) { + if (!ok) { + delete static_cast(tag); + break; + } + static_cast(tag)->proceed(); + } + }; + loop_thread_ = std::thread(loop_lambda); +} + +bool AdminServerImpl::stop() { + if (State::STARTED != state_.load()) { + return false; + } + + if (server_) { + State expected = State::STARTED; + if (state_.compare_exchange_strong(expected, State::STOPPING, std::memory_order_relaxed)) { + SPDLOG_INFO("Stopping admin server"); + server_->Shutdown(); + + if (completion_queue_) { + completion_queue_->Shutdown(); + // Drain completion_queue_ + { + void* tag; + bool ignore_ok; + while (completion_queue_->Next(&tag, &ignore_ok)) { + } + } + } + + if (loop_thread_.joinable()) { + loop_thread_.join(); + } + + state_.store(State::STOPPED); + SPDLOG_INFO("Admin server stopped"); + return true; + } + return false; + } else { + SPDLOG_ERROR("Admin server is unexpected nullptr"); + } + return false; +} +} // namespace admin + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/admin/AdminServiceImpl.cpp b/src/main/cpp/admin/AdminServiceImpl.cpp new file mode 100644 index 000000000..3a4d0fa35 --- /dev/null +++ b/src/main/cpp/admin/AdminServiceImpl.cpp @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "AdminServiceImpl.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +namespace admin { + +Status applyChange(const rmq::ChangeLogLevelRequest* request) { + auto logger = spdlog::get("rocketmq_logger"); + if (!logger) { + return Status(grpc::StatusCode::INTERNAL, "rocketmq_logger is not registered"); + } + + auto level = request->level(); + switch (level) { + case rmq::ChangeLogLevelRequest_Level_TRACE: + logger->set_level(spdlog::level::trace); + break; + case rmq::ChangeLogLevelRequest_Level_DEBUG: + logger->set_level(spdlog::level::debug); + break; + case rmq::ChangeLogLevelRequest_Level_INFO: + logger->set_level(spdlog::level::info); + break; + case rmq::ChangeLogLevelRequest_Level_WARN: + logger->set_level(spdlog::level::warn); + break; + case rmq::ChangeLogLevelRequest_Level_ERROR: + logger->set_level(spdlog::level::err); + break; + default: + logger->set_level(spdlog::level::info); + break; + } + return grpc::Status::OK; +} + +Status AdminServiceImpl::ChangeLogLevel(ServerContext* context, const rmq::ChangeLogLevelRequest* request, + rmq::ChangeLogLevelResponse* reply) { + Status status = applyChange(request); + if (status.ok()) { + reply->set_remark("OK"); + } + return status; +} + +} // namespace admin + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/admin/BUILD.bazel b/src/main/cpp/admin/BUILD.bazel new file mode 100644 index 000000000..1e8efe9bb --- /dev/null +++ b/src/main/cpp/admin/BUILD.bazel @@ -0,0 +1,59 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_binary") +cc_library( + name = "admin_server_library", + hdrs = [ + "include/AdminServiceImpl.h", + "include/AdminServerImpl.h", + "include/ServerCall.h", + ], + srcs = [ + "AdminFacade.cpp", + "AdminServerImpl.cpp", + "AdminServiceImpl.cpp", + ], + strip_include_prefix = "//src/main/cpp/admin/include", + deps = [ + "//api:rocketmq_interface", + "//proto:rocketmq_grpc_library", + "@com_github_gabime_spdlog//:spdlog", + ], + visibility = ["//visibility:public"], +) + +cc_library( + name = "admin_client_interface", + hdrs = [ + "include/AdminClient.h", + ], + strip_include_prefix = "//src/main/cpp/admin/include", + deps = [ + "//api:rocketmq_interface", + ], +) + +cc_binary( + name = "admin_client", + srcs = [ + "AdminClient.cpp", + ], + deps = [ + ":admin_client_interface", + "//proto:rocketmq_grpc_library", + ], +) \ No newline at end of file diff --git a/src/main/cpp/admin/CMakeLists.txt b/src/main/cpp/admin/CMakeLists.txt new file mode 100644 index 000000000..6a0ad7b0b --- /dev/null +++ b/src/main/cpp/admin/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(admin + OBJECT + AdminFacade.cpp + AdminServerImpl.cpp + AdminServiceImpl.cpp) +target_include_directories(admin + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(admin + PRIVATE + api + proto + spdlog) \ No newline at end of file diff --git a/src/main/cpp/admin/include/AdminClient.h b/src/main/cpp/admin/include/AdminClient.h new file mode 100644 index 000000000..968d837d7 --- /dev/null +++ b/src/main/cpp/admin/include/AdminClient.h @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "apache/rocketmq/v1/admin.grpc.pb.h" +#include "rocketmq/RocketMQ.h" +#include +#include + +namespace rmq = apache::rocketmq::v1; + +ROCKETMQ_NAMESPACE_BEGIN + +using grpc::Channel; +using grpc::Status; + +namespace admin { + +class AdminClient { +public: + explicit AdminClient(std::shared_ptr& channel) : stub_(rmq::Admin::NewStub(channel)) { + } + + Status changeLogLevel(const rmq::ChangeLogLevelRequest& request, rmq::ChangeLogLevelResponse& response); + +private: + std::unique_ptr stub_; +}; + +} // namespace admin + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/admin/include/AdminServerImpl.h b/src/main/cpp/admin/include/AdminServerImpl.h new file mode 100644 index 000000000..b6056352a --- /dev/null +++ b/src/main/cpp/admin/include/AdminServerImpl.h @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "AdminServiceImpl.h" +#include "rocketmq/AdminServer.h" +#include +#include +#include +#include +#include +#include + +using grpc::Server; + +ROCKETMQ_NAMESPACE_BEGIN + +namespace admin { +class AdminServerImpl : public AdminServer { +public: + AdminServerImpl() + : port_(0), state_(State::CREATED), async_stub_(new rmq::Admin::AsyncService), service_(new AdminServiceImpl) { + } + + bool start() override; + + bool stop() override; + + int port() const override { + return port_; + } + +private: + int port_; + std::unique_ptr server_; + std::atomic state_; + std::unique_ptr async_stub_; + std::unique_ptr service_; + std::unique_ptr completion_queue_; + std::thread loop_thread_; + + std::mutex loop_mtx_; + std::condition_variable loop_cv_; + void loop(); +}; +} // namespace admin + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/admin/include/AdminServiceImpl.h b/src/main/cpp/admin/include/AdminServiceImpl.h new file mode 100644 index 000000000..58933abd9 --- /dev/null +++ b/src/main/cpp/admin/include/AdminServiceImpl.h @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "apache/rocketmq/v1/admin.grpc.pb.h" +#include "rocketmq/RocketMQ.h" +#include + +using grpc::ServerContext; +using grpc::Status; +namespace rmq = apache::rocketmq::v1; + +ROCKETMQ_NAMESPACE_BEGIN + +namespace admin { +class AdminServiceImpl final : public rmq::Admin::Service { +public: + Status ChangeLogLevel(ServerContext* context, const rmq::ChangeLogLevelRequest* request, + rmq::ChangeLogLevelResponse* reply) override; +}; +} // namespace admin + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/admin/include/ServerCall.h b/src/main/cpp/admin/include/ServerCall.h new file mode 100644 index 000000000..138d43341 --- /dev/null +++ b/src/main/cpp/admin/include/ServerCall.h @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "apache/rocketmq/v1/admin.grpc.pb.h" + +#include "rocketmq/RocketMQ.h" + +namespace rmq = apache::rocketmq::v1; + +ROCKETMQ_NAMESPACE_BEGIN + +enum ServerCallStatus : int8_t +{ + CREATE = 0, + PROCESS = 1, + FINISH = 2 +}; + +class ServerCall { +public: + ServerCall(rmq::Admin::AsyncService* async_service, rmq::Admin::Service* service, + grpc::ServerCompletionQueue* completion_queue) + : async_stub_(async_service), service_(service), completion_queue_(completion_queue), + response_observer_(&context_), status_(ServerCallStatus::CREATE) { + proceed(); + } + + void proceed() { + switch (status_) { + case CREATE: { + status_ = PROCESS; + async_stub_->RequestChangeLogLevel(&context_, &request_, &response_observer_, completion_queue_, + completion_queue_, this); + break; + } + case PROCESS: { + // Create a new ServerCall to serve the next incoming request. + new ServerCall(async_stub_, service_, completion_queue_); + + // Now that request_ is already filled with actual data from clients, invoke the actual process function + const grpc::Status rpc_status = service_->ChangeLogLevel(&context_, &request_, &response_); + + status_ = FINISH; + response_observer_.Finish(response_, rpc_status, this); + break; + } + default: { + assert(FINISH == status_); + delete this; + } + } + } + +private: + rmq::Admin::AsyncService* async_stub_; + rmq::Admin::Service* service_; + grpc::ServerCompletionQueue* completion_queue_; + grpc::ServerContext context_; + rmq::ChangeLogLevelRequest request_; + rmq::ChangeLogLevelResponse response_; + grpc::ServerAsyncResponseWriter response_observer_; + ServerCallStatus status_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel new file mode 100644 index 000000000..5c7ab81c0 --- /dev/null +++ b/src/main/cpp/base/BUILD.bazel @@ -0,0 +1,45 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "base_library", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/base/include", + deps = [ + "//src/main/cpp/log:log_library", + "@com_github_fmtlib_fmt//:fmtlib", + "@com_google_absl//absl/base", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/random", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_github_gabime_spdlog//:spdlog", + "@com_github_grpc_grpc//:grpc", + "@com_googlesource_code_re2//:re2", + "@boringssl//:crypto", + "@boringssl//:ssl", + "//external:madler_zlib", + "@com_github_yhirose_cpp_httplib//:cpp_httplib", + "@asio//:asio", + ], +) \ No newline at end of file diff --git a/src/main/cpp/base/CMakeLists.txt b/src/main/cpp/base/CMakeLists.txt new file mode 100644 index 000000000..252fef742 --- /dev/null +++ b/src/main/cpp/base/CMakeLists.txt @@ -0,0 +1,17 @@ +file(GLOB SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +add_library(base OBJECT ${SRC_FILES}) +target_include_directories(base + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(base + PRIVATE + absl::flat_hash_map + api + asio + filesystem + fmt + httplib + log + OpenSSL::SSL + proto + spdlog) \ No newline at end of file diff --git a/src/main/cpp/base/ErrorCategory.cpp b/src/main/cpp/base/ErrorCategory.cpp new file mode 100644 index 000000000..166f632e1 --- /dev/null +++ b/src/main/cpp/base/ErrorCategory.cpp @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/ErrorCategory.h" + +ROCKETMQ_NAMESPACE_BEGIN + +std::string ErrorCategory::message(int code) const { + ErrorCode ec = static_cast(code); + switch (ec) { + case ErrorCode::Success: + return "Success"; + + case ErrorCode::IllegalState: + return "Client state illegal. Forgot to call start()?"; + + case ErrorCode::BadConfiguration: + return "Bad configuration."; + + case ErrorCode::BadRequest: + return "Message is ill-formed. Check validity of your topic, tag, " + "etc"; + + case ErrorCode::Unauthorized: + return "Authentication failed. Possibly caused by invalid credentials."; + + case ErrorCode::Forbidden: + return "Authenticated user does not have privilege to perform the " + "requested action"; + + case ErrorCode::NotFound: + return "Topic not found, which should be created through console or " + "administration API before hand."; + + case ErrorCode::RequestTimeout: + return "Timeout when connecting, reading from or writing to brokers."; + + case ErrorCode::PayloadTooLarge: + return "Message body is too large."; + + case ErrorCode::PreconditionRequired: + return "State of dependent procedure is not right"; + + case ErrorCode::TooManyRequest: + return "Quota exchausted. The user has sent too many requests in a given " + "amount of time."; + + case ErrorCode::UnavailableForLegalReasons: + return "A server operator has received a legal demand to deny access to " + "a resource or to a set of resources that " + "includes the requested resource."; + + case ErrorCode::HeaderFieldsTooLarge: + return "The server is unwilling to process the request because either an " + "individual header field, or all the header fields collectively, " + "are too large"; + + case ErrorCode::InternalServerError: + return "Server side interval error"; + + case ErrorCode::NotImplemented: + return "The server either does not recognize the request method, or it " + "lacks the ability to fulfil the request."; + + case ErrorCode::BadGateway: + return "The server was acting as a gateway or proxy and received an " + "invalid response from the upstream server."; + + case ErrorCode::ServiceUnavailable: + return "The server cannot handle the request (because it is overloaded " + "or down for maintenance). Generally, this " + "is a temporary state."; + + case ErrorCode::GatewayTimeout: + return "The server was acting as a gateway or proxy and did not receive " + "a timely response from the upstream " + "server."; + + case ErrorCode::ProtocolVersionNotSupported: + return "The server does not support the protocol version used in the " + "request."; + + case ErrorCode::InsufficientStorage: + return "The server is unable to store the representation needed to " + "complete the request."; + + default: + return "Not-Implemented"; + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/ErrorCode.cpp b/src/main/cpp/base/ErrorCode.cpp new file mode 100644 index 000000000..59c1aec41 --- /dev/null +++ b/src/main/cpp/base/ErrorCode.cpp @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/ErrorCategory.h" + +ROCKETMQ_NAMESPACE_BEGIN + +std::error_code make_error_code(ErrorCode code) { + const ErrorCategory& instance = ErrorCategory::instance(); + return {static_cast(code), instance}; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/HostInfo.cpp b/src/main/cpp/base/HostInfo.cpp new file mode 100644 index 000000000..8b57247b3 --- /dev/null +++ b/src/main/cpp/base/HostInfo.cpp @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "HostInfo.h" +#include "absl/strings/match.h" +#include "rocketmq/RocketMQ.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +const char* HostInfo::ENV_LABEL_SITE = "SIGMA_APP_SITE"; +const char* HostInfo::ENV_LABEL_UNIT = "SIGMA_APP_UNIT"; +const char* HostInfo::ENV_LABEL_APP = "SIGMA_APP_NAME"; +const char* HostInfo::ENV_LABEL_STAGE = "SIGMA_APP_STAGE"; + +HostInfo::HostInfo() { + getEnv(ENV_LABEL_SITE, site_); + getEnv(ENV_LABEL_UNIT, unit_); + getEnv(ENV_LABEL_APP, app_); + getEnv(ENV_LABEL_STAGE, stage_); +} + +void HostInfo::getEnv(const char* env, std::string& holder) { + if (!strlen(env)) { + return; + } + + char* value = getenv(env); + if (nullptr != value) { + holder.clear(); + holder.append(value); + } +} + +bool HostInfo::hasHostInfo() const { + return !unit_.empty() && !stage_.empty(); +} + +std::string HostInfo::queryString() const { + if (!hasHostInfo()) { + return std::string(); + } + + std::string query_string("labels="); + appendLabel(query_string, "site", site_); + appendLabel(query_string, "unit", unit_); + appendLabel(query_string, "app", app_); + appendLabel(query_string, "stage", stage_); + return query_string; +} + +void HostInfo::appendLabel(std::string& query_string, const char* key, const std::string& value) { + if (value.empty()) { + return; + } + + if (absl::EndsWith(query_string, "=")) { + query_string.append(key).append(":").append(value); + } else { + query_string.append(",").append(key).append(":").append(value); + } +} +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/HttpClientImpl.cpp b/src/main/cpp/base/HttpClientImpl.cpp new file mode 100644 index 000000000..cb25ae96f --- /dev/null +++ b/src/main/cpp/base/HttpClientImpl.cpp @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "HttpClientImpl.h" + +#include +#include + +#include "fmt/format.h" +#include "spdlog/spdlog.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void HttpClientImpl::start() { +} + +void HttpClientImpl::shutdown() { +} + +/** + * @brief We current implement this function in sync mode since async http request in CURL is sort of unnecessarily + * complex. + * + * @param protocol + * @param host + * @param port + * @param path + * @param cb + */ +void HttpClientImpl::get( + HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) { + + std::string key; + switch (protocol) { + case HttpProtocol::HTTP: + key = fmt::format("http://{}:{}", host, port); + break; + case HttpProtocol::HTTPS: + key = fmt::format("https://{}:{}", host, port); + break; + } + + std::shared_ptr client; + { + absl::MutexLock lk(&clients_mtx_); + if (clients_.contains(key)) { + client = clients_[key]; + } + + if (!client || !client->is_valid()) { + client = std::make_shared(key); + clients_.insert_or_assign(key, client); + } + } + + if (!client || !client->is_valid()) { + int code = 400; + std::multimap headers; + std::string response; + cb(code, headers, response); + return; + } + + auto res = client->Get(path.c_str()); + + std::multimap headers; + for (auto& header : headers) { + headers.insert({header.first, header.second}); + } + + cb(res->status, headers, res->body); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp new file mode 100644 index 000000000..2015a618f --- /dev/null +++ b/src/main/cpp/base/MQMessage.cpp @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/MQMessage.h" +#include "MessageAccessor.h" +#include "MessageImpl.h" +#include "MixAll.h" +#include "Protocol.h" +#include "UniqueIdGenerator.h" +#include "UtilAll.h" +#include "rocketmq/MQMessageExt.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +MQMessage::MQMessage() : MQMessage("", "", "", "") { +} + +MQMessage::MQMessage(const std::string& topic, const std::string& body) : MQMessage(topic, "", "", body) { +} + +MQMessage::MQMessage(const std::string& topic, const std::string& tags, const std::string& body) + : MQMessage(topic, tags, "", body) { +} + +MQMessage::MQMessage(const std::string& topic, const std::string& tags, const std::string& keys, + const std::string& body) + : impl_(new MessageImpl) { + impl_->topic_.name = topic; + impl_->system_attribute_.tag = tags; + if (!keys.empty()) { + impl_->system_attribute_.keys.emplace_back(keys); + } + + impl_->system_attribute_.born_host = UtilAll::hostname(); + impl_->system_attribute_.born_timestamp = absl::Now(); + impl_->system_attribute_.message_id = UniqueIdGenerator::instance().next(); + + impl_->body_.clear(); + impl_->body_.reserve(body.length()); + impl_->body_.append(body.data(), body.length()); +} + +MQMessage::~MQMessage() { + delete impl_; +} + +MQMessage::MQMessage(const MQMessage& other) { + impl_ = new MessageImpl(*other.impl_); +} + +MQMessage& MQMessage::operator=(const MQMessage& other) { + if (this == &other) { + return *this; + } + *impl_ = *(other.impl_); + return *this; +} + +const std::string& MQMessage::getMsgId() const { + return impl_->system_attribute_.message_id; +} + +std::string MQMessage::getBornHost() const { + return impl_->system_attribute_.born_host; +} + +std::chrono::system_clock::time_point MQMessage::deliveryTimestamp() const { + return absl::ToChronoTime(impl_->system_attribute_.delivery_timestamp); +} + +void MQMessage::setProperty(const std::string& name, const std::string& value) { + impl_->user_attribute_map_[name] = value; +} + +std::string MQMessage::getProperty(const std::string& name) const { + auto it = impl_->user_attribute_map_.find(name); + if (impl_->user_attribute_map_.end() == it) { + return ""; + } + return it->second; +} + +const std::string& MQMessage::getTopic() const { + return impl_->topic_.name; +} + +void MQMessage::setTopic(const std::string& topic) { + impl_->topic_.name = topic; +} + +void MQMessage::setTopic(const char* data, int len) { + impl_->topic_.name = std::string(data, len); +} + +std::string MQMessage::getTags() const { + return impl_->system_attribute_.tag; +} + +void MQMessage::setTags(const std::string& tags) { + impl_->system_attribute_.tag = tags; +} + +const std::vector& MQMessage::getKeys() const { + return impl_->system_attribute_.keys; +} + +void MQMessage::setKey(const std::string& key) { + impl_->system_attribute_.keys.push_back(key); +} + +void MQMessage::setKeys(const std::vector& keys) { + impl_->system_attribute_.keys = keys; +} + +int MQMessage::getDelayTimeLevel() const { + return impl_->system_attribute_.delay_level; +} + +void MQMessage::setDelayTimeLevel(int level) { + impl_->system_attribute_.delay_level = level; +} + +const std::string& MQMessage::traceContext() const { + return impl_->system_attribute_.trace_context; +} + +void MQMessage::traceContext(const std::string& trace_context) { + impl_->system_attribute_.trace_context = trace_context; +} + +const std::string& MQMessage::getBody() const { + return impl_->body_; +} + +void MQMessage::setBody(const char* body, int len) { + impl_->body_.clear(); + impl_->body_.reserve(len); + impl_->body_.append(body, len); +} + +void MQMessage::setBody(const std::string& body) { + impl_->body_ = body; +} + +uint32_t MQMessage::bodyLength() const { + return impl_->body_.length(); +} + +const std::map& MQMessage::getProperties() const { + return impl_->user_attribute_map_; +} + +void MQMessage::setProperties(const std::map& properties) { + for (const auto& it : properties) { + impl_->user_attribute_map_.insert({it.first, it.second}); + } +} + +void MQMessage::messageType(MessageType message_type) { + impl_->system_attribute_.message_type = message_type; +} + +MessageType MQMessage::messageType() const { + return impl_->system_attribute_.message_type; +} + +void MQMessage::bindMessageGroup(absl::string_view message_group) { + impl_->system_attribute_.message_group.append(message_group.data(), message_group.length()); + messageType(MessageType::FIFO); +} + +const std::string& MQMessage::messageGroup() const { + return impl_->system_attribute_.message_group; +} + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/MQMessageExt.cpp b/src/main/cpp/base/MQMessageExt.cpp new file mode 100644 index 000000000..21a947614 --- /dev/null +++ b/src/main/cpp/base/MQMessageExt.cpp @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/MQMessageExt.h" +#include "MessageImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +MQMessageExt::MQMessageExt() : MQMessage() { +} + +MQMessageExt::MQMessageExt(const MQMessageExt& other) : MQMessage(other) { +} + +MQMessageExt& MQMessageExt::operator=(const MQMessageExt& other) { + if (this == &other) { + return *this; + } + + *impl_ = *(other.impl_); + return *this; +} + +int32_t MQMessageExt::getQueueId() const { + return impl_->system_attribute_.partition_id; +} + +std::chrono::system_clock::time_point MQMessageExt::bornTimestamp() const { + return absl::ToChronoTime(impl_->system_attribute_.born_timestamp); +} + +int64_t MQMessageExt::getBornTimestamp() const { + return absl::ToUnixMillis(impl_->system_attribute_.born_timestamp); +} + +std::chrono::system_clock::time_point MQMessageExt::storeTimestamp() const { + return absl::ToChronoTime(impl_->system_attribute_.store_timestamp); +} + +int64_t MQMessageExt::getStoreTimestamp() const { + return absl::ToUnixMillis(impl_->system_attribute_.store_timestamp); +} + +std::string MQMessageExt::getStoreHost() const { + return impl_->system_attribute_.store_host; +} + +int64_t MQMessageExt::getQueueOffset() const { + return impl_->system_attribute_.partition_offset; +} + +int32_t MQMessageExt::getDeliveryAttempt() const { + return impl_->system_attribute_.attempt_times; +} + +const std::string& MQMessageExt::receiptHandle() const { + return impl_->system_attribute_.receipt_handle; +} + +bool MQMessageExt::operator==(const MQMessageExt& other) { + return impl_->system_attribute_.message_id == other.impl_->system_attribute_.message_id; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MessageAccessor.cpp b/src/main/cpp/base/MessageAccessor.cpp new file mode 100644 index 000000000..957f07652 --- /dev/null +++ b/src/main/cpp/base/MessageAccessor.cpp @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "MessageAccessor.h" +#include "MessageImpl.h" +#include "rocketmq/MQMessage.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void MessageAccessor::setMessageId(MQMessageExt& message, std::string message_id) { + message.impl_->system_attribute_.message_id = std::move(message_id); +} + +void MessageAccessor::setBornTimestamp(MQMessageExt& message, absl::Time born_timestamp) { + message.impl_->system_attribute_.born_timestamp = born_timestamp; +} + +void MessageAccessor::setStoreTimestamp(MQMessageExt& message, absl::Time store_timestamp) { + message.impl_->system_attribute_.store_timestamp = store_timestamp; +} + +void MessageAccessor::setQueueId(MQMessageExt& message, int32_t queue_id) { + message.impl_->system_attribute_.partition_id = queue_id; +} + +void MessageAccessor::setQueueOffset(MQMessageExt& message, int64_t queue_offset) { + message.impl_->system_attribute_.partition_offset = queue_offset; +} + +void MessageAccessor::setBornHost(MQMessageExt& message, std::string born_host) { + message.impl_->system_attribute_.born_host = std::move(born_host); +} + +void MessageAccessor::setStoreHost(MQMessageExt& message, std::string store_host) { + message.impl_->system_attribute_.store_host = std::move(store_host); +} + +void MessageAccessor::setDeliveryTimestamp(MQMessageExt& message, absl::Time delivery_timestamp) { + message.impl_->system_attribute_.delivery_timestamp = delivery_timestamp; +} + +void MessageAccessor::setDeliveryAttempt(MQMessageExt& message, int32_t attempt_times) { + message.impl_->system_attribute_.attempt_times = attempt_times; +} + +void MessageAccessor::setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp) { + message.impl_->system_attribute_.decode_timestamp = decode_timestamp; +} + +absl::Time MessageAccessor::decodedTimestamp(const MQMessageExt& message) { + return message.impl_->system_attribute_.decode_timestamp; +} + +void MessageAccessor::setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period) { + message.impl_->system_attribute_.invisible_period = invisible_period; +} + +void MessageAccessor::setReceiptHandle(MQMessageExt& message, std::string receipt_handle) { + message.impl_->system_attribute_.receipt_handle = std::move(receipt_handle); +} + +void MessageAccessor::setTraceContext(MQMessageExt& message, std::string trace_context) { + message.impl_->system_attribute_.trace_context = std::move(trace_context); +} + +void MessageAccessor::setMessageType(MQMessage& message, MessageType message_type) { + message.impl_->system_attribute_.message_type = message_type; +} + +void MessageAccessor::setTargetEndpoint(MQMessage& message, const std::string& target_endpoint) { + message.impl_->system_attribute_.target_endpoint = target_endpoint; +} + +const std::string& MessageAccessor::targetEndpoint(const MQMessage& message) { + return message.impl_->system_attribute_.target_endpoint; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MetadataConstants.cpp b/src/main/cpp/base/MetadataConstants.cpp new file mode 100644 index 000000000..0aa8d933d --- /dev/null +++ b/src/main/cpp/base/MetadataConstants.cpp @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "MetadataConstants.h" + +ROCKETMQ_NAMESPACE_BEGIN + +const char* MetadataConstants::TENANT_ID_KEY = "x-mq-tenant-id"; +const char* MetadataConstants::NAMESPACE_KEY = "x-mq-namespace"; +const char* MetadataConstants::AUTHORIZATION = "authorization"; +const char* MetadataConstants::STS_SESSION_TOKEN = "x-mq-session-token"; +const char* MetadataConstants::DATE_TIME_KEY = "x-mq-date-time"; +const char* MetadataConstants::ALGORITHM_KEY = "MQv2-HMAC-SHA1"; +const char* MetadataConstants::CREDENTIAL_KEY = "Credential"; +const char* MetadataConstants::SIGNED_HEADERS_KEY = "SignedHeaders"; +const char* MetadataConstants::SIGNATURE_KEY = "Signature"; +const char* MetadataConstants::DATE_TIME_FORMAT = "%Y%m%dT%H%M%SZ"; +const char* MetadataConstants::LANGUAGE_KEY = "x-mq-language"; +const char* MetadataConstants::CLIENT_VERSION_KEY = "x-mq-client-version"; +const char* MetadataConstants::PROTOCOL_VERSION_KEY = "x-mq-protocol-version"; +const char* MetadataConstants::REQUEST_ID_KEY = "x-mq-request-id"; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp new file mode 100644 index 000000000..0bf273a6f --- /dev/null +++ b/src/main/cpp/base/MixAll.cpp @@ -0,0 +1,298 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "MixAll.h" + +#include +#include + +#include "absl/random/random.h" +#include "absl/strings/str_split.h" +#include "fmt/format.h" +#include "openssl/md5.h" +#include "openssl/sha.h" +#include "zlib.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +ROCKETMQ_NAMESPACE_BEGIN + +const int32_t MixAll::MASTER_BROKER_ID = 0; + +const int32_t MixAll::DEFAULT_RECEIVE_MESSAGE_BATCH_SIZE = 32; + +const uint32_t MixAll::MAX_MESSAGE_BODY_SIZE = 1024 * 1024 * 4; +const uint32_t MixAll::MAX_CACHED_MESSAGE_COUNT = 65535; +const uint32_t MixAll::DEFAULT_CACHED_MESSAGE_COUNT = 1024; +const uint64_t MixAll::DEFAULT_CACHED_MESSAGE_MEMORY = 128L * 1024 * 1024; +const uint32_t MixAll::DEFAULT_CONSUME_THREAD_POOL_SIZE = 20; +const uint32_t MixAll::DEFAULT_CONSUME_MESSAGE_BATCH_SIZE = 1; +const int32_t MixAll::DEFAULT_MAX_DELIVERY_ATTEMPTS = 16; + +const RE2 MixAll::TOPIC_REGEX("[a-zA-Z0-9\\-_]{3,64}"); +const RE2 MixAll::IP_REGEX("\\d+\\.\\d+\\.\\d+\\.\\d+"); + +const std::chrono::duration MixAll::DEFAULT_INVISIBLE_TIME_ = std::chrono::seconds(30); + +const std::chrono::duration MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_ = std::chrono::seconds(120); + +const int32_t MixAll::MAX_SEND_MESSAGE_ATTEMPT_TIMES_ = 3; + +const std::string MixAll::PROPERTY_TRANSACTION_PREPARED_ = "TRAN_MSG"; + +const std::string MixAll::DEFAULT_LOAD_BALANCER_STRATEGY_NAME_ = "AVG"; + +const uint32_t MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_ = 1024 * 1024 * 4; + +const char* MixAll::HOME_PROFILE_ENV_ = "HOME"; +const char* MixAll::MESSAGE_KEY_SEPARATOR = " "; + +const char* MixAll::OTLP_NAME_VALUE = "org.apache.rocketmq.message"; + +const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language"; +const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_TELEMETRY_SDK_LANGUAGE = "cpp"; + +const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_HOST_NAME = "host.name"; +const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME = "service.name"; +const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME = "rocketmq-client"; + +// Span attributes follows to the opentelemetry specification, refers to: +// https://github.com/open-telemetry/opentelemetry-specification + +// RocketMQ span attribute name list +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION = "messaging.rocketmq.operation"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE = "messaging.rocketmq.namespace"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG = "messaging.rocketmq.message_tag"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS = "messaging.rocketmq.message_keys"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_ID = "messaging.rocketmq.client_id"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE = "messaging.rocketmq.message_type"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP = "messaging.rocketmq.client_group"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT = "messaging.rocketmq.attempt"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE = "messaging.rocketmq.batch_size"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP = "messaging.rocketmq.delivery_timestamp"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP = "messaging.rocketmq.available_timestamp"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY = "messaging.rocketmq.access_key"; + +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_MESSAGING_SYSTEM = "rocketmq"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_DESTINATION_KIND = "topic"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL = "RMQ-gRPC"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL_VERSION = "v1"; + +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NORMAL_MESSAGE = "normal"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_FIFO_MESSAGE = "fifo"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DELAY_MESSAGE = "delay"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_TRANSACTION_MESSAGE = "transaction"; + +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION = "send"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_RECEIVE_OPERATION = "receive"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PULL_OPERATION = "pull"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION = "await"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION = "process"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ACK_OPERATION = "ack"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NACK_OPERATION = "nack"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_COMMIT_OPERATION = "commit"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ROLLBACK_OPERATION = "rollback"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DLQ_OPERATION = "dlq"; + +// Messaging span attribute name list +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_SYSTEM = "messaging.system"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION = "messaging.destination"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION_KIND = "messaging.destination_kind"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL = "messaging.protocol"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL_VERSION = "messaging.protocol_version"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_URL = "messaging.url"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID = "messaging.message_id"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES = "messaging.message_payload_size_bytes"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION = "messaging.operation"; + +const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION = "send"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_RECEIVE_OPERATION = "receive"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION = "process"; + +const char* MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION = "commitAction"; + +// Span annotation +const char* MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION = "__await_consumption"; +const char* MixAll::SPAN_ANNOTATION_MESSAGE_KEYS = "__message_keys"; +const char* MixAll::SPAN_ANNOTATION_ATTR_START_TIME = "__start_time"; + +bool MixAll::validate(const MQMessage& message) { + if (message.getTopic().empty()) { + return false; + } + const std::string& topic = message.getTopic(); + // Topic should not start with "CID" or "GID" which are reserved prefix + if (absl::StartsWith(topic, "CID") || absl::StartsWith(topic, "GID")) { + return false; + } + + // Legal topic characters are a-z, A-Z, 0-9, hyphen('-') and underline('_') + if (!RE2::FullMatch(topic, TOPIC_REGEX)) { + return false; + } + + uint32_t body_length = message.bodyLength(); + if (!body_length || body_length > MAX_MESSAGE_BODY_SIZE) { + return false; + } + return true; +} + +uint32_t MixAll::random(uint32_t left, uint32_t right) { + static absl::BitGen gen; + return absl::Uniform(gen, left, right); +} + +bool MixAll::crc32(const std::string& data, std::string& digest) { + uLong crc = ::crc32(0L, reinterpret_cast(data.c_str()), data.length()); + uint32_t network_byte_order = htonl(crc); + digest = hex(&network_byte_order, sizeof(network_byte_order)); + return true; +} + +bool MixAll::md5(const std::string& data, std::string& digest) { + MD5_CTX ctx; + MD5_Init(&ctx); + MD5_Update(&ctx, data.data(), data.length()); + unsigned char md[MD5_DIGEST_LENGTH + 1]; + int success = MD5_Final(md, &ctx); + if (!success) { + return false; + } + digest.clear(); + digest.append(hex(md, MD5_DIGEST_LENGTH)); + return true; +} + +bool MixAll::sha1(const std::string& data, std::string& digest) { + unsigned char out[SHA_DIGEST_LENGTH]; + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, data.data(), data.length()); + SHA1_Final(out, &ctx); + digest.clear(); + digest.append(hex(reinterpret_cast(out), SHA_DIGEST_LENGTH)); + return true; +} + +std::string MixAll::format(std::chrono::system_clock::time_point time_point) { + std::time_t creation_time_t = std::chrono::system_clock::to_time_t(time_point); + auto fraction = std::chrono::duration_cast(time_point.time_since_epoch()).count() % 1000; + char fmt_date_time[128]; + + /** + * TODO: std::localtime is not thread-safe, output, as a result, may be less reliable in highly contending + * scenario + */ + std::strftime(fmt_date_time, sizeof(fmt_date_time), "%Y-%m-%d %H:%M:%S", std::localtime(&creation_time_t)); + return fmt::format("{}.{}", fmt_date_time, fraction); +} + +std::string MixAll::hex(const void* data, std::size_t len) { + const char dict[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + std::string s; + const uint8_t* ptr = reinterpret_cast(data); + for (std::size_t i = 0; i < len; i++) { + unsigned char c = *(ptr + i); + s.append(&dict[(0xF0 & c) >> 4], 1); + s.append(&dict[(0x0F & c)], 1); + } + return s; +} + +bool MixAll::hexToBinary(const std::string& hex, std::vector& bin) { + // Length of valid Hex string should always be even. + if (hex.length() % 2) { + return false; + } + for (std::string::size_type i = 0; i < hex.length(); i += 2) { + char c1 = hex.at(i); + char c2 = hex.at(i + 1); + + uint8_t value = 0; + uint8_t tmp; + if (hexCharValue(c1, tmp)) { + value = tmp << 4; + } else { + return false; + } + + if (hexCharValue(c2, tmp)) { + value |= tmp; + bin.push_back(value); + } else { + return false; + } + } + return true; +} + +bool MixAll::hexCharValue(char c, uint8_t& value) { + if ('0' <= c && c <= '9') { + value = c - '0'; + return true; + } + + if ('a' <= c && c <= 'f') { + value = c - 'a' + 10; + return true; + } + + if ('A' <= c && c <= 'F') { + value = c - 'A' + 10; + return true; + } + + return false; +} + +bool MixAll::homeDirectory(std::string& home_dir) { +#ifndef _WIN32 + char* home = getenv(HOME_PROFILE_ENV_); + if (home) { + home_dir.append(home, strlen(home)); + return true; + } else { + struct passwd* pwd = getpwuid(getuid()); + if (pwd) { + home_dir.clear(); + home_dir.append(pwd->pw_dir, strlen(pwd->pw_dir)); + return true; + } + } + return false; +#else + char* home = getenv("USERPROFILE"); + if (home) { + home_dir.clear(); + home_dir.append(home, strlen(home)); + return true; + } + return false; +#endif +} + +bool MixAll::isIPv4(absl::string_view host) { + return RE2::FullMatch(re2::StringPiece(host.data(), host.length()), IP_REGEX); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/Protocol.cpp b/src/main/cpp/base/Protocol.cpp new file mode 100644 index 000000000..be031e324 --- /dev/null +++ b/src/main/cpp/base/Protocol.cpp @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Protocol.h" + +ROCKETMQ_NAMESPACE_BEGIN + +#ifndef CLIENT_PROTOCOL_VERSION +#define CLIENT_PROTOCOL_VERSION "v1" +#endif + +const char* Protocol::PROTOCOL_VERSION = CLIENT_PROTOCOL_VERSION; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/RateLimiter.cpp b/src/main/cpp/base/RateLimiter.cpp new file mode 100644 index 000000000..1a8247ec3 --- /dev/null +++ b/src/main/cpp/base/RateLimiter.cpp @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "RateLimiter.h" + +ROCKETMQ_NAMESPACE_BEGIN + +RateLimiterObserver::RateLimiterObserver() : stopped_(false) { + tick_thread_ = std::thread([this] { + while (!stopped_.load(std::memory_order_relaxed)) { + { + std::lock_guard lk(members_mtx_); + for (auto it = members_.begin(); it != members_.end();) { + std::shared_ptr tick = it->lock(); + if (!tick) { + it = members_.erase(it); + continue; + } else { + ++it; + } + tick->tick(); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); +} + +void RateLimiterObserver::subscribe(const std::shared_ptr& tick) { + std::lock_guard lk(members_mtx_); + members_.emplace_back(tick); +} + +void RateLimiterObserver::stop() { + stopped_.store(true, std::memory_order_relaxed); + if (tick_thread_.joinable()) { + tick_thread_.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/ThreadPoolImpl.cpp b/src/main/cpp/base/ThreadPoolImpl.cpp new file mode 100644 index 000000000..3befa9c35 --- /dev/null +++ b/src/main/cpp/base/ThreadPoolImpl.cpp @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ThreadPoolImpl.h" +#include "absl/memory/memory.h" +#include "asio/executor_work_guard.hpp" +#include "asio/io_context.hpp" +#include "asio/post.hpp" +#include "rocketmq/RocketMQ.h" +#include "rocketmq/State.h" +#include "spdlog/spdlog.h" +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +ThreadPoolImpl::ThreadPoolImpl(std::uint16_t workers) + : work_guard_( + absl::make_unique>(context_.get_executor())), + workers_(workers) { +} + +void ThreadPoolImpl::start() { + for (std::uint16_t i = 0; i < workers_; i++) { + std::thread worker([this]() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTED, std::memory_order_relaxed)) { + absl::MutexLock lk(&start_mtx_); + start_cv_.SignalAll(); + } + + while (true) { +#ifdef __EXCEPTIONS + try { +#endif + std::error_code ec; + context_.run(ec); + if (ec) { + SPDLOG_WARN("Error raised from ThreadPool: {}", ec.message()); + } +#ifdef __EXCEPTIONS + } catch (std::exception& e) { + SPDLOG_WARN("Exception raised from ThreadPool: {}", e.what()); + } +#endif + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_INFO("A thread-pool worker quit"); + break; + } + } + }); + threads_.emplace_back(std::move(worker)); + } + + { + absl::MutexLock lk(&start_mtx_); + if (State::CREATED == state_.load(std::memory_order_relaxed)) { + start_cv_.Wait(&start_mtx_); + } + } +} + +void ThreadPoolImpl::shutdown() { + State expected = State::STARTED; + if (state_.compare_exchange_strong(expected, State::STOPPING, std::memory_order_relaxed)) { + work_guard_->reset(); + context_.stop(); + for (auto& thread : threads_) { + if (thread.joinable()) { + thread.join(); + } + } + state_.store(State::STOPPED, std::memory_order_relaxed); + } +} + +void ThreadPoolImpl::submit(std::function task) { + if (State::STARTED == state_.load(std::memory_order_relaxed)) { + asio::post(context_, task); + } else { + SPDLOG_WARN("State of ThreadPool is not STARTED"); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/TopAddressing.cpp b/src/main/cpp/base/TopAddressing.cpp new file mode 100644 index 000000000..dd45e67fa --- /dev/null +++ b/src/main/cpp/base/TopAddressing.cpp @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TopAddressing.h" + +#include "HttpClientImpl.h" +#include "absl/memory/memory.h" +#include "absl/strings/match.h" +#include "absl/strings/str_split.h" +#include "spdlog/spdlog.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +TopAddressing::TopAddressing() : TopAddressing("jmenv.tbsite.net", 8080, "/rocketmq/nsaddr") { +} + +TopAddressing::TopAddressing(std::string host, int port, std::string path) + : host_(std::move(host)), port_(port), path_(std::move(path)), http_client_(absl::make_unique()) { + http_client_->start(); +} + +TopAddressing::~TopAddressing() { + http_client_->shutdown(); +} + +void TopAddressing::fetchNameServerAddresses(const std::function&)>& cb) { + SPDLOG_DEBUG("Prepare to send HTTP request, timeout=3s"); + std::string base(fmt::format("http://{}:{}", host_, port_)); + // Append host info if necessary. + std::string query_string(path_); + + if (absl::StrContains(query_string, "?")) { + query_string.append("&"); + } else { + query_string.append("?"); + } + + if (host_info_.hasHostInfo()) { + query_string.append(host_info_.queryString()); + } else { + query_string.append("nofix=1"); + } + + auto callback = [cb](int code, const std::multimap& metadata, const std::string& body) { + SPDLOG_DEBUG("Receive HTTP response. Code: {}, body: {}", code, body); + if (static_cast(HttpStatus::OK) == code) { + cb(true, absl::StrSplit(body, ';')); + } else { + std::vector name_server_list; + cb(false, name_server_list); + } + }; + + http_client_->get(HttpProtocol::HTTP, host_, port_, query_string, callback); +} + +void TopAddressing::injectHttpClient(std::unique_ptr http_client) { + http_client_->shutdown(); + http_client_.swap(http_client); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/UniqueIdGenerator.cpp b/src/main/cpp/base/UniqueIdGenerator.cpp new file mode 100644 index 000000000..aab5b146d --- /dev/null +++ b/src/main/cpp/base/UniqueIdGenerator.cpp @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "UniqueIdGenerator.h" +#include "LoggerImpl.h" +#include "MixAll.h" +#include "UtilAll.h" +#include "absl/base/internal/endian.h" +#include + +#ifdef _WIN32 +#include +#endif + +ROCKETMQ_NAMESPACE_BEGIN + +const uint8_t UniqueIdGenerator::VERSION = 1; + +UniqueIdGenerator::UniqueIdGenerator() + : prefix_(), since_custom_epoch_(std::chrono::system_clock::now() - customEpoch()), + start_time_point_(std::chrono::steady_clock::now()), seconds_(deltaSeconds()), sequence_(0) { + std::vector mac_address; + if (UtilAll::macAddress(mac_address)) { + memcpy(prefix_.data(), mac_address.data(), mac_address.size()); + } else { + SPDLOG_WARN("Failed to get network interface MAC address"); + } + +#ifdef _WIN32 + int pid = _getpid(); +#else + pid_t pid = getpid(); +#endif + + uint32_t big_endian_pid = absl::big_endian::FromHost32(pid); + // Copy the lower 2 bytes + memcpy(prefix_.data() + 6, reinterpret_cast(&big_endian_pid) + 2, sizeof(uint16_t)); +} + +UniqueIdGenerator& UniqueIdGenerator::instance() { + static UniqueIdGenerator generator; + return generator; +} + +std::string UniqueIdGenerator::next() { + Slot slot = {}; + { + absl::MutexLock lk(&mtx_); + uint32_t delta = deltaSeconds(); + if (seconds_ != delta) { + seconds_ = delta; + sequence_ = 0; + SPDLOG_DEBUG("Second: {} and sequence: {}", seconds_, sequence_); + } else { + sequence_++; + } + slot.seconds = seconds_; + slot.sequence = sequence_; + } + std::array raw{}; + raw[0] = VERSION; + memcpy(raw.data() + sizeof(VERSION), prefix_.data(), prefix_.size()); + memcpy(raw.data() + sizeof(VERSION) + prefix_.size(), &slot, sizeof(slot)); + return MixAll::hex(raw.data(), raw.size()); +} + +std::chrono::system_clock::time_point UniqueIdGenerator::customEpoch() { + return absl::ToChronoTime(absl::FromDateTime(2021, 1, 1, 0, 0, 0, absl::UTCTimeZone())); +} + +uint32_t UniqueIdGenerator::deltaSeconds() { + return std::chrono::duration_cast((std::chrono::steady_clock::now() - start_time_point_) + + since_custom_epoch_) + .count(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/UtilAll.cpp b/src/main/cpp/base/UtilAll.cpp new file mode 100644 index 000000000..892dc87c2 --- /dev/null +++ b/src/main/cpp/base/UtilAll.cpp @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "UtilAll.h" + +#include "LoggerImpl.h" +#include "zlib.h" +#include + +#if defined(__APPLE__) +#include +#define AF_FAMILY AF_LINK +#elif defined(__linux__) +#include +#include +#include +#define AF_FAMILY AF_PACKET +#elif defined(_WIN32) +#include +#include +#pragma comment(lib, "iphlpapi.lib") +#endif + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#endif + +#include "asio.hpp" + +ROCKETMQ_NAMESPACE_BEGIN + +std::string UtilAll::hostname() { + return asio::ip::host_name(); +} + +bool UtilAll::macAddress(std::vector& mac) { + static std::vector cache; + static bool mac_cached = false; + + if (mac_cached) { + mac = cache; + return true; + } + +#ifndef _WIN32 + struct ifaddrs *head = nullptr, *node; + if (getifaddrs(&head)) { + return false; + } + + for (node = head; node; node = node->ifa_next) { + if (node->ifa_addr->sa_family == AF_FAMILY) { +#if defined(__APPLE__) + auto* ptr = reinterpret_cast(LLADDR(reinterpret_cast(node->ifa_addr))); +#elif defined(__linux__) + auto* ptr = reinterpret_cast(reinterpret_cast(node->ifa_addr)->sll_addr); +#endif + bool all_zero = true; + for (int i = 0; i < 6; i++) { + if (*(ptr + i) != 0) { + all_zero = false; + break; + } + } + if (all_zero) { + SPDLOG_TRACE("Skip MAC address of network interface {}", node->ifa_name); + continue; + } + SPDLOG_DEBUG("Use MAC address of network interface {}", node->ifa_name); + // MAC address has 48 bits + cache.resize(6); + memcpy(cache.data(), ptr, 6); + mac_cached = true; + mac = cache; + break; + } + } + freeifaddrs(head); + return true; +#else + PIP_ADAPTER_INFO adaptor_info; + DWORD buf_len = sizeof(IP_ADAPTER_INFO); + char mac_address[18]; + adaptor_info = (IP_ADAPTER_INFO*)malloc(buf_len); + if (!adaptor_info) { + // TODO: running out of memroy + } + + if (GetAdaptersInfo(adaptor_info, &buf_len) == NO_ERROR) { + PIP_ADAPTER_INFO item = adaptor_info; + do { + bool all_zero = true; + for (auto& b : item->Address) { + if (b != 0) { + all_zero = false; + break; + } + } + if (!all_zero) { + cache.resize(6); + memcpy(cache.data(), item->Address, 6); + mac_cached = true; + mac = cache; + break; + } + item = item->Next; + } while (item); + } else { + free(adaptor_info); + } +#endif +} + +bool UtilAll::compress(const std::string& src, std::string& dst) { + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.next_in = reinterpret_cast(const_cast(src.c_str())); + stream.avail_in = src.length(); + + deflateInit(&stream, Z_DEFAULT_COMPRESSION); + uint32_t bound = deflateBound(&stream, src.length()); + std::vector buffer(bound); + stream.next_out = buffer.data(); + stream.avail_out = bound; + + int status = deflate(&stream, Z_FINISH); + + // zlib is unexpected wrong. + if (Z_STREAM_END != status) { + deflateEnd(&stream); + return false; + } + + dst.reserve(stream.total_out); + assert(stream.total_out == bound - stream.avail_out); + std::copy(buffer.data(), buffer.data() + stream.total_out, std::back_inserter(dst)); + deflateEnd(&stream); + return true; +} + +bool UtilAll::uncompress(const std::string& src, std::string& dst) { + int status; + uint32_t buffer_length = 4096; + std::vector buffer(buffer_length); + z_stream stream; + + // Use default malloc / free allocator. + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.next_in = reinterpret_cast(const_cast(src.c_str())); + stream.avail_in = src.length(); + + inflateInit(&stream); + stream.next_out = buffer.data(); + stream.avail_out = buffer_length; + + while (true) { + status = inflate(&stream, Z_SYNC_FLUSH); + + if (Z_STREAM_ERROR == status) { + return false; + } + std::copy(buffer.data(), buffer.data() + buffer_length - stream.avail_out, std::back_inserter(dst)); + stream.avail_out = buffer_length; + stream.next_out = buffer.data(); + + // inflation completed OK + if (Z_STREAM_END == status) { + inflateEnd(&stream); + return true; + } + + // inflation made some progress + if (Z_OK == status) { + continue; + } + + // Something is wrong + inflateEnd(&stream); + return false; + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/DigestType.h b/src/main/cpp/base/include/DigestType.h new file mode 100644 index 000000000..e255d828a --- /dev/null +++ b/src/main/cpp/base/include/DigestType.h @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class DigestType : int8_t +{ + CRC32 = 0, + MD5 = 1, + SHA1 = 2, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Encoding.h b/src/main/cpp/base/include/Encoding.h new file mode 100644 index 000000000..cfb71cae0 --- /dev/null +++ b/src/main/cpp/base/include/Encoding.h @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class Encoding : int8_t +{ + IDENTITY = 0, + GZIP = 1, + SNAPPY = 2, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Histogram.h b/src/main/cpp/base/include/Histogram.h new file mode 100644 index 000000000..89b7f3fcc --- /dev/null +++ b/src/main/cpp/base/include/Histogram.h @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Histogram { +public: + Histogram(std::string title, int32_t capacity) : title_(std::move(title)), capacity_(capacity) { + data_.reserve(capacity); + for (int i = 0; i < capacity_; ++i) { + data_.push_back(std::unique_ptr>(new std::atomic(0))); + } + } + + void countIn(int grade) { + if (grade < 0) { + return; + } + + if (grade >= capacity_) { + data_[capacity_ - 1]->fetch_add(1, std::memory_order_relaxed); + return; + } + + data_[grade]->fetch_add(1, std::memory_order_relaxed); + } + + /** + * Change labels of histogram duration the initialization phase only. + * @return + */ + std::vector& labels() { + return labels_; + } + + void reportAndReset(std::string& result) { + assert(labels_.size() == static_cast::size_type>(capacity_)); + std::vector values; + values.reserve(capacity_); + for (auto& item : data_) { + int value = item->load(std::memory_order_relaxed); + values.push_back(value); + item->fetch_sub(value, std::memory_order_relaxed); + } + result.clear(); + result.append(title_).append(":"); + for (std::vector::size_type i = 0; i < labels_.size(); ++i) { + if (i) { + result.append(", "); + } + result.append(labels_[i]).append(std::to_string(values[i])); + } + } + +private: + std::string title_; + std::vector>> data_; + int32_t capacity_; + std::vector labels_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/HostInfo.h b/src/main/cpp/base/include/HostInfo.h new file mode 100644 index 000000000..1b2a543c9 --- /dev/null +++ b/src/main/cpp/base/include/HostInfo.h @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN +struct HostInfo { + std::string site_; + std::string unit_; + std::string app_; + std::string stage_; + + explicit HostInfo(); + + bool hasHostInfo() const; + + std::string queryString() const; + + static const char* ENV_LABEL_SITE; + static const char* ENV_LABEL_UNIT; + static const char* ENV_LABEL_APP; + static const char* ENV_LABEL_STAGE; + +private: + static void getEnv(const char* env, std::string& holder); + + static void appendLabel(std::string& query_string, const char* key, const std::string& value); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/HttpClient.h b/src/main/cpp/base/include/HttpClient.h new file mode 100644 index 000000000..77f695516 --- /dev/null +++ b/src/main/cpp/base/include/HttpClient.h @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class HttpProtocol : int8_t +{ + HTTP = 1, + HTTPS = 2, +}; + +enum class HttpStatus : int +{ + OK = 200, + INTERNAL = 500, +}; + +class HttpClient { +public: + virtual ~HttpClient() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual void + get(HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/HttpClientImpl.h b/src/main/cpp/base/include/HttpClientImpl.h new file mode 100644 index 000000000..e54bb4372 --- /dev/null +++ b/src/main/cpp/base/include/HttpClientImpl.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "httplib.h" + +#include "HttpClient.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class HttpClientImpl : public HttpClient { +public: + ~HttpClientImpl() override = default; + + void start() override; + + void shutdown() override; + + void + get(HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) override; + +private: + absl::flat_hash_map> clients_ GUARDED_BY(clients_mtx_); + absl::Mutex clients_mtx_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/InvocationContext.h b/src/main/cpp/base/include/InvocationContext.h new file mode 100644 index 000000000..e81dbfa74 --- /dev/null +++ b/src/main/cpp/base/include/InvocationContext.h @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "absl/synchronization/mutex.h" +#include "absl/time/time.h" +#include "grpcpp/client_context.h" +#include "grpcpp/grpcpp.h" +#include "grpcpp/impl/codegen/async_stream.h" +#include "grpcpp/impl/codegen/async_unary_call.h" + +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" +#include "MetadataConstants.h" +#include "UniqueIdGenerator.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * Note: + * Before modifying anything in this file, ensure you have read all comments in completion_queue_impl.h and + * async_stream.h + */ +struct BaseInvocationContext { + BaseInvocationContext() : request_id_(UniqueIdGenerator::instance().next()) { + context.AddMetadata(MetadataConstants::REQUEST_ID_KEY, request_id_); + } + + virtual ~BaseInvocationContext() = default; + virtual void onCompletion(bool ok) = 0; + std::string request_id_; + std::string remote_address; + grpc::ClientContext context; + grpc::Status status; + std::string task_name; + absl::Time created_time{absl::Now()}; + std::chrono::steady_clock::time_point start_time{std::chrono::steady_clock::now()}; +}; + +template +struct InvocationContext : public BaseInvocationContext { + + void onCompletion(bool ok) override { + auto elapsed = + std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time).count(); + SPDLOG_DEBUG("RPC[{}] costs {}ms", task_name, elapsed); + /// Client-side Read, Server-side Read, Client-side + /// RecvInitialMetadata (which is typically included in Read if not + /// done explicitly): ok indicates whether there is a valid message + /// that got read. If not, you know that there are certainly no more + /// messages that can ever be read from this stream. For the client-side + /// operations, this only happens because the call is dead. For the + /// server-side operation, though, this could happen because the client + /// has done a WritesDone already. + if (!ok) { + SPDLOG_WARN("One async call is already dead"); + if (callback) { + callback(this); + } + delete this; + return; + } + + if (!status.ok() && grpc::StatusCode::DEADLINE_EXCEEDED == status.error_code()) { + auto diff = + std::chrono::duration_cast(std::chrono::system_clock::now() - context.deadline()) + .count(); + SPDLOG_WARN("Asynchronous RPC[{}.{}] timed out, elapsing {}ms, deadline-over-due: {}ms", + absl::FormatTime(created_time, absl::UTCTimeZone()), elapsed, diff); + } + try { + if (callback) { + callback(this); + } + } catch (const std::exception& e) { + SPDLOG_WARN("Unexpected error while invoking user-defined callback. Reason: {}", e.what()); + } catch (...) { + SPDLOG_WARN("Unexpected error while invoking user-defined callback"); + } + delete this; + } + + T response; + std::function*)> callback; + std::unique_ptr> response_reader; +}; +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MessageAccessor.h b/src/main/cpp/base/include/MessageAccessor.h new file mode 100644 index 000000000..371bd23a4 --- /dev/null +++ b/src/main/cpp/base/include/MessageAccessor.h @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "rocketmq/MQMessageExt.h" + +#include "Protocol.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageAccessor { + +public: + static void setMessageId(MQMessageExt& message, std::string message_id); + + static void setBornTimestamp(MQMessageExt& message, absl::Time born_timestamp); + + static void setStoreTimestamp(MQMessageExt& message, absl::Time store_timestamp); + + static void setQueueId(MQMessageExt& message, int32_t queue_id); + + static void setQueueOffset(MQMessageExt& message, int64_t queue_offset); + + static void setBornHost(MQMessageExt& message, std::string born_host); + + static void setStoreHost(MQMessageExt& message, std::string store_host); + + static void setDeliveryTimestamp(MQMessageExt& message, absl::Time delivery_timestamp); + + static void setDeliveryAttempt(MQMessageExt& message, int32_t attempt_times); + + static void setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp); + static absl::Time decodedTimestamp(const MQMessageExt& message); + + static void setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period); + + static void setReceiptHandle(MQMessageExt& message, std::string receipt_handle); + static void setTraceContext(MQMessageExt& message, std::string trace_context); + + static void setMessageType(MQMessage& message, MessageType message_type); + + static void setTargetEndpoint(MQMessage& message, const std::string& target_endpoint); + + static const std::string& targetEndpoint(const MQMessage& message); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MessageImpl.h b/src/main/cpp/base/include/MessageImpl.h new file mode 100644 index 000000000..079ba33fe --- /dev/null +++ b/src/main/cpp/base/include/MessageImpl.h @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "Protocol.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageImpl { +protected: + Resource topic_; + std::map user_attribute_map_; + SystemAttribute system_attribute_; + std::string body_; + friend class MQMessage; + friend class MQMessageExt; + friend class MessageAccessor; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MetadataConstants.h b/src/main/cpp/base/include/MetadataConstants.h new file mode 100644 index 000000000..ac3af5ba8 --- /dev/null +++ b/src/main/cpp/base/include/MetadataConstants.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MetadataConstants { +public: + static const char* REQUEST_ID_KEY; + static const char* TENANT_ID_KEY; + static const char* NAMESPACE_KEY; + static const char* AUTHORIZATION; + static const char* STS_SESSION_TOKEN; + static const char* DATE_TIME_KEY; + static const char* ALGORITHM_KEY; + static const char* CREDENTIAL_KEY; + static const char* SIGNED_HEADERS_KEY; + static const char* SIGNATURE_KEY; + static const char* DATE_TIME_FORMAT; + static const char* LANGUAGE_KEY; + static const char* CLIENT_VERSION_KEY; + static const char* PROTOCOL_VERSION_KEY; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h new file mode 100644 index 000000000..ae272dc2e --- /dev/null +++ b/src/main/cpp/base/include/MixAll.h @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "re2/re2.h" + +#include "rocketmq/MQMessage.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MixAll { +public: + static const int32_t MASTER_BROKER_ID; + static const int32_t DEFAULT_RECEIVE_MESSAGE_BATCH_SIZE; + static const uint32_t MAX_MESSAGE_BODY_SIZE; + static const uint32_t MAX_CACHED_MESSAGE_COUNT; + static const uint32_t DEFAULT_CACHED_MESSAGE_COUNT; + static const uint64_t DEFAULT_CACHED_MESSAGE_MEMORY; + static const uint32_t DEFAULT_CONSUME_THREAD_POOL_SIZE; + static const uint32_t DEFAULT_CONSUME_MESSAGE_BATCH_SIZE; + static const int32_t DEFAULT_MAX_DELIVERY_ATTEMPTS; + + static const RE2 TOPIC_REGEX; + static const RE2 IP_REGEX; + + /** + * The amount of time required before a popped message is eligible to be consumed again. By default, 30s. + */ + static const std::chrono::duration DEFAULT_INVISIBLE_TIME_; + + static const std::chrono::duration PROCESS_QUEUE_EXPIRATION_THRESHOLD_; + + static const int32_t MAX_SEND_MESSAGE_ATTEMPT_TIMES_; + + static const std::string PROPERTY_TRANSACTION_PREPARED_; + + static const std::string DEFAULT_LOAD_BALANCER_STRATEGY_NAME_; + + static const uint32_t DEFAULT_COMPRESS_BODY_THRESHOLD_; + + static const char* HOME_PROFILE_ENV_; + + static const char* MESSAGE_KEY_SEPARATOR; + + static const char* OTLP_NAME_VALUE; + + static const char* TRACE_RESOURCE_ATTRIBUTE_KEY_TELEMETRY_SDK_LANGUAGE; + static const char* TRACE_RESOURCE_ATTRIBUTE_VALUE_TELEMETRY_SDK_LANGUAGE; + + static const char* TRACE_RESOURCE_ATTRIBUTE_KEY_HOST_NAME; + static const char* TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME; + static const char* TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME; + + // RocketMQ span attribute name list + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_ID; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY; + + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_MESSAGING_SYSTEM; + static const char* SPAN_ATTRIBUTE_VALUE_DESTINATION_KIND; + static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL; + static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL_VERSION; + + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NORMAL_MESSAGE; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_FIFO_MESSAGE; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DELAY_MESSAGE; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_TRANSACTION_MESSAGE; + + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_RECEIVE_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PULL_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ACK_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NACK_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_COMMIT_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ROLLBACK_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DLQ_OPERATION; + + // Messaging attribute name list + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_SYSTEM; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION_KIND; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL_VERSION; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_URL; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_ID; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION; + + static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_RECEIVE_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION; + + static const char* SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION; + + // Tracing annotation + static const char* SPAN_ANNOTATION_AWAIT_CONSUMPTION; + static const char* SPAN_ANNOTATION_MESSAGE_KEYS; + static const char* SPAN_ANNOTATION_ATTR_START_TIME; + + template + static int64_t millisecondsOf(std::chrono::duration duration) { + return std::chrono::duration_cast(duration).count(); + } + + template + static int64_t microsecondsOf(std::chrono::duration duration) { + return std::chrono::duration_cast(duration).count(); + } + + /** + * Validate message is legal. Aka, topic + * @param message + * @return + */ + static bool validate(const MQMessage& message); + + static uint32_t random(uint32_t left, uint32_t right); + + static bool crc32(const std::string& data, std::string& digest); + + static bool md5(const std::string& data, std::string& digest); + + static bool sha1(const std::string& data, std::string& digest); + + static std::string format(std::chrono::system_clock::time_point time_point); + + static std::string hex(const void* data, std::size_t len); + + static bool hexToBinary(const std::string& hex, std::vector& bin); + + static bool homeDirectory(std::string& home); + + static bool isIPv4(absl::string_view host); + +private: + static bool hexCharValue(char c, uint8_t& value); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h new file mode 100644 index 000000000..7d52fd6c7 --- /dev/null +++ b/src/main/cpp/base/include/Protocol.h @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "absl/time/clock.h" +#include "absl/time/time.h" + +#include "DigestType.h" +#include "Encoding.h" +#include "rocketmq/MessageType.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Protocol { +public: + static const char* PROTOCOL_VERSION; +}; + +struct Digest { + DigestType digest_type{DigestType::MD5}; + std::string checksum; + Digest() = default; +}; + +struct Resource { + /** + * Abstract resource namespace + */ + std::string resource_namespace; + + /** + * Resource name, which remains unique within given abstract resource namespace. + */ + std::string name; +}; + +struct SystemAttribute { + std::string tag; + std::vector keys; + std::string message_id; + Digest digest; + Encoding body_encoding; + MessageType message_type; + absl::Time born_timestamp{absl::Now()}; + std::string born_host; + absl::Time store_timestamp{absl::UnixEpoch()}; + std::string store_host; + absl::Time delivery_timestamp{absl::UnixEpoch()}; + absl::Time decode_timestamp{absl::Now()}; + int32_t delay_level{0}; + std::string receipt_handle; + int32_t partition_id{0}; + int64_t partition_offset{0}; + absl::Duration invisible_period; + int32_t attempt_times{0}; + Resource publisher_group; + std::string trace_context; + std::string target_endpoint; + std::string message_group; +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/include/RateLimiter.h b/src/main/cpp/base/include/RateLimiter.h new file mode 100644 index 000000000..76982a34d --- /dev/null +++ b/src/main/cpp/base/include/RateLimiter.h @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Tick { +public: + virtual ~Tick() = default; + + virtual void tick() = 0; +}; + +class RateLimiterObserver { +public: + RateLimiterObserver(); + + void subscribe(const std::shared_ptr& tick); + + void stop(); + +private: + std::vector> members_; + std::mutex members_mtx_; + + std::atomic_bool stopped_; + + std::thread tick_thread_; +}; + +template +class RateLimiter : public Tick { +public: + explicit RateLimiter(uint32_t permit) : permits_{0}, interval_(1000 / PARTITION) { + uint32_t avg = permit / PARTITION; + for (auto& i : partition_) { + i = avg; + } + + uint32_t r = permit % PARTITION; + + if (r) { + uint32_t step = PARTITION / r; + for (uint32_t i = 0; i < r; ++i) { + partition_[i * step]++; + } + } + } + + ~RateLimiter() override = default; + + std::array& partition() { + return permits_; + } + + int slot() { + auto current = std::chrono::steady_clock::now(); + long ms = std::chrono::duration_cast(current.time_since_epoch()).count(); + return ms / interval_ % PARTITION; + } + + uint32_t available() { + uint32_t available_permit = 0; + int idx = slot(); + // Reuse quota of the past half of the cycle + for (int j = 0; j <= PARTITION / 2; ++j) { + int index = idx - j; + if (index < 0) { + index += PARTITION; + } + if (permits_[index] > 0) { + available_permit += permits_[index]; + } + } + + return available_permit; + } + + uint32_t acquire(uint32_t permit) { + int idx = slot(); + { + std::unique_lock lk(mtx_); + if (permits_[idx] >= permit) { + permits_[idx] -= permit; + return permit; + } + + uint32_t acquired = 0; + if (permits_[idx] > 0) { + acquired += permits_[idx]; + permits_[idx] = 0; + } + + // Reuse quota of the past half of the cycle + for (int j = 1; j <= PARTITION / 2; ++j) { + int index = idx - j; + if (index < 0) { + index += PARTITION; + } + while (permits_[index] > 0) { + --permits_[index]; + if (++acquired >= permit) { + break; + } + } + } + return acquired; + } + } + + void acquire() { + int idx = slot(); + { + std::unique_lock lk(mtx_); + if (permits_[idx] > 0) { + --permits_[idx]; + return; + } + + // Reuse quota of the past half of the cycle + for (int j = 1; j <= PARTITION / 2; ++j) { + int index = idx - j; + if (index < 0) { + index += PARTITION; + } + if (permits_[index] > 0) { + --permits_[index]; + return; + } + } + + cv_.wait(lk, [this]() { + int idx = slot(); + return permits_[idx] > 0; + }); + idx = slot(); + --permits_[idx]; + } + } + + void tick() override { + std::this_thread::sleep_for(std::chrono::milliseconds(1000 / PARTITION)); + int idx = slot(); + { + std::unique_lock lk(mtx_); + permits_[idx] = partition_[idx]; + cv_.notify_all(); + } + } + +private: + std::array partition_; + std::array permits_; + int interval_; + std::mutex mtx_; + std::condition_variable cv_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/ThreadPool.h b/src/main/cpp/base/include/ThreadPool.h new file mode 100644 index 000000000..d4582252a --- /dev/null +++ b/src/main/cpp/base/include/ThreadPool.h @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ThreadPool { +public: + virtual ~ThreadPool() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual void submit(std::function task) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/ThreadPoolImpl.h b/src/main/cpp/base/include/ThreadPoolImpl.h new file mode 100644 index 000000000..b5d754cf8 --- /dev/null +++ b/src/main/cpp/base/include/ThreadPoolImpl.h @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "absl/synchronization/mutex.h" +#include "asio.hpp" +#include "asio/io_context.hpp" + +#include "ThreadPool.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ThreadPoolImpl : public ThreadPool { +public: + explicit ThreadPoolImpl(std::uint16_t workers); + + ~ThreadPoolImpl() override = default; + + void start() override; + + void shutdown() override; + + void submit(std::function task) override; + +private: + asio::io_context context_; + std::unique_ptr> work_guard_; + std::uint16_t workers_; + std::vector threads_; + std::atomic state_{State::CREATED}; + absl::Mutex start_mtx_; + absl::CondVar start_cv_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/TopAddressing.h b/src/main/cpp/base/include/TopAddressing.h new file mode 100644 index 000000000..02602598f --- /dev/null +++ b/src/main/cpp/base/include/TopAddressing.h @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "HostInfo.h" +#include "HttpClient.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TopAddressing { +public: + TopAddressing(); + + TopAddressing(std::string host, int port, std::string path); + + virtual ~TopAddressing(); + + void fetchNameServerAddresses(const std::function&)>& cb); + + void injectHttpClient(std::unique_ptr http_client); + +private: + std::string host_; + int port_{8080}; + std::string path_; + HostInfo host_info_; + + std::unique_ptr http_client_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/UniqueIdGenerator.h b/src/main/cpp/base/include/UniqueIdGenerator.h new file mode 100644 index 000000000..e44c5e64c --- /dev/null +++ b/src/main/cpp/base/include/UniqueIdGenerator.h @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class UniqueIdGenerator { +public: + static UniqueIdGenerator& instance(); + + std::string next() LOCKS_EXCLUDED(mtx_); + + UniqueIdGenerator(const UniqueIdGenerator&) = delete; + + UniqueIdGenerator(UniqueIdGenerator&&) = delete; + + UniqueIdGenerator& operator=(const UniqueIdGenerator&) = delete; + + UniqueIdGenerator& operator=(UniqueIdGenerator&&) = delete; + +private: + UniqueIdGenerator(); + + struct Slot { + uint32_t seconds; + uint32_t sequence; + }; + + static std::chrono::system_clock::time_point customEpoch(); + + /** + * Seconds since 2021-01-01 00:00:00.0 +0000 + * @return + */ + uint32_t deltaSeconds(); + + std::array prefix_; + absl::Mutex mtx_; + + /** + * Duration since 2021-01-01 00:00:00.0(UTC) + */ + std::chrono::system_clock::duration since_custom_epoch_; + + /** + * @brief Generator created time point using steady clock. Such that when system time are adjusted backward using NTP + * service, generated message identifiers remain unique. + * + * Note: when program restarts, duplicated message-id may still get generated. However, if program run in + * containerized environment, say Kubernetes, this will NOT be a problem since MAC address would change on restart. + */ + std::chrono::steady_clock::time_point start_time_point_; + + uint32_t seconds_; + uint32_t sequence_; + static const uint8_t VERSION; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/UtilAll.h b/src/main/cpp/base/include/UtilAll.h new file mode 100644 index 000000000..d4cb2eac4 --- /dev/null +++ b/src/main/cpp/base/include/UtilAll.h @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class UtilAll { +public: + static std::string hostname(); + + static bool macAddress(std::vector& mac); + + /** + * Compress + * @param src + * @param dst + * @return + */ + static bool compress(const std::string& src, std::string& dst); + + static bool uncompress(const std::string& src, std::string& dst); + +private: +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/mocks/BUILD.bazel b/src/main/cpp/base/mocks/BUILD.bazel new file mode 100644 index 000000000..be924e72f --- /dev/null +++ b/src/main/cpp/base/mocks/BUILD.bazel @@ -0,0 +1,28 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "base_mocks", + hdrs = glob(["include/*h"]), + strip_include_prefix = "//src/main/cpp/base/mocks/include", + deps = [ + "//api:rocketmq_interface", + "//external:gtest", + ], +) \ No newline at end of file diff --git a/src/main/cpp/base/mocks/include/HttpClientMock.h b/src/main/cpp/base/mocks/include/HttpClientMock.h new file mode 100644 index 000000000..c2e946470 --- /dev/null +++ b/src/main/cpp/base/mocks/include/HttpClientMock.h @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "HttpClient.h" +#include "rocketmq/RocketMQ.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class HttpClientMock : public HttpClient { +public: + ~HttpClientMock() override = default; + + MOCK_METHOD(void, start, (), (override)); + MOCK_METHOD(void, shutdown, (), (override)); + MOCK_METHOD(void, get, + (HttpProtocol, const std::string&, std::uint16_t, const std::string&, + const std::function&, const std::string&)>&), + (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/mocks/include/MessageListenerMock.h b/src/main/cpp/base/mocks/include/MessageListenerMock.h new file mode 100644 index 000000000..431d1bb79 --- /dev/null +++ b/src/main/cpp/base/mocks/include/MessageListenerMock.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "rocketmq/MessageListener.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class StandardMessageListenerMock : public StandardMessageListener { +public: + MOCK_METHOD(ConsumeMessageResult, consumeMessage, (const std::vector&), (override)); +}; + +class FifoMessageListenerMock : public FifoMessageListener { +public: + MOCK_METHOD(ConsumeMessageResult, consumeMessage, (const MQMessageExt&), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/BUILD.bazel b/src/main/cpp/client/BUILD.bazel new file mode 100644 index 000000000..c56f27931 --- /dev/null +++ b/src/main/cpp/client/BUILD.bazel @@ -0,0 +1,41 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "client_library", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/client/include", + deps = [ + "//api:rocketmq_interface", + "//proto:rocketmq_grpc_library", + "//src/main/cpp/admin:admin_server_library", + "//src/main/cpp/base:base_library", + "//src/main/cpp/scheduler:scheduler_library", + "//src/main/cpp/concurrent:countdown_latch_library", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_github_grpc_grpc//:grpc_secure", + "@com_github_grpc_grpc//:grpc++", + "@boringssl//:ssl", + "//external:gtest", + ], + defines = [ + ], +) \ No newline at end of file diff --git a/src/main/cpp/client/CMakeLists.txt b/src/main/cpp/client/CMakeLists.txt new file mode 100644 index 000000000..3a8531ab9 --- /dev/null +++ b/src/main/cpp/client/CMakeLists.txt @@ -0,0 +1,19 @@ +file(GLOB SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +add_library(client OBJECT ${SRC_FILES}) +target_include_directories(client + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(client + PRIVATE + api + asio + base + proto + fmt + gRPC::grpc + log + OpenSSL::SSL + opencensus::stats + opencensus::trace + scheduler + spdlog) \ No newline at end of file diff --git a/src/main/cpp/client/ClientConfigImpl.cpp b/src/main/cpp/client/ClientConfigImpl.cpp new file mode 100644 index 000000000..29304e0a2 --- /dev/null +++ b/src/main/cpp/client/ClientConfigImpl.cpp @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ClientConfigImpl.h" + +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include "UtilAll.h" + +ROCKETMQ_NAMESPACE_BEGIN + +#ifndef CLIENT_VERSION_MAJOR +#define CLIENT_VERSION_MAJOR "5" +#endif + +#ifndef CLIENT_VERSION_MINOR +#define CLIENT_VERSION_MINOR "0" +#endif + +#ifndef CLIENT_VERSION_PATCH +#define CLIENT_VERSION_PATCH "0" +#endif + +const char* ClientConfigImpl::CLIENT_VERSION = CLIENT_VERSION_MAJOR "." CLIENT_VERSION_MINOR "." CLIENT_VERSION_PATCH; + +std::string ClientConfigImpl::steadyName() { + auto duration = std::chrono::steady_clock::now().time_since_epoch(); + return std::to_string(std::chrono::duration_cast(duration).count()); +} + +ClientConfigImpl::ClientConfigImpl(absl::string_view group_name) + : group_name_(group_name.data(), group_name.length()), io_timeout_(absl::Seconds(3)), + long_polling_timeout_(absl::Seconds(30)) { +} + +std::string ClientConfigImpl::clientId() const { + std::stringstream ss; + ss << UtilAll::hostname(); + ss << "@"; + std::string processID = std::to_string(getpid()); + ss << processID << "#"; + ss << instance_name_; + return ss.str(); +} + +const std::string& ClientConfigImpl::getInstanceName() const { + return instance_name_; +} + +void ClientConfigImpl::setInstanceName(std::string instance_name) { + instance_name_ = std::move(instance_name); +} + +const std::string& ClientConfigImpl::getGroupName() const { + return group_name_; +} + +void ClientConfigImpl::setGroupName(std::string group_name) { + group_name_ = std::move(group_name); +} + +void ClientConfigImpl::setIoTimeout(absl::Duration timeout) { + io_timeout_ = timeout; +} + +void ClientConfigImpl::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { + credentials_provider_ = std::move(credentials_provider); +} + +CredentialsProviderPtr ClientConfigImpl::credentialsProvider() { + return credentials_provider_; +} + +absl::Duration ClientConfigImpl::getIoTimeout() const { + return io_timeout_; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientManagerFactory.cpp b/src/main/cpp/client/ClientManagerFactory.cpp new file mode 100644 index 000000000..c72aedbfa --- /dev/null +++ b/src/main/cpp/client/ClientManagerFactory.cpp @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ClientManagerFactory.h" +#include "ClientManagerImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ClientManagerFactory& ClientManagerFactory::getInstance() { + static ClientManagerFactory instance; + return instance; +} + +ClientManagerPtr ClientManagerFactory::getClientManager(const ClientConfig& client_config) { + { + absl::MutexLock lock(&client_manager_table_mtx_); + auto search = client_manager_table_.find(client_config.resourceNamespace()); + if (search != client_manager_table_.end()) { + ClientManagerPtr client_manager = search->second.lock(); + if (client_manager) { + SPDLOG_DEBUG("Re-use existing client_manager[resource_namespace={}]", client_config.resourceNamespace()); + return client_manager; + } else { + client_manager_table_.erase(client_config.resourceNamespace()); + } + } + ClientManagerPtr client_manager = std::make_shared(client_config.resourceNamespace()); + std::weak_ptr client_instance_weak_ptr(client_manager); + client_manager_table_.insert_or_assign(client_config.resourceNamespace(), client_instance_weak_ptr); + SPDLOG_INFO("Created a new client manager[resource_namespace={}]", client_config.resourceNamespace()); + return client_manager; + } +} + +void ClientManagerFactory::addClientManager(const std::string& resource_namespace, + const ClientManagerPtr& client_manager) { + absl::MutexLock lk(&client_manager_table_mtx_); + client_manager_table_.insert_or_assign(resource_namespace, std::weak_ptr(client_manager)); +} + +ClientManagerFactory::ClientManagerFactory() : admin_server_(admin::AdminFacade::getServer()) { + admin_server_.start(); +} + +ClientManagerFactory::~ClientManagerFactory() { + admin_server_.stop(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp new file mode 100644 index 000000000..476fce783 --- /dev/null +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -0,0 +1,1603 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ClientManagerImpl.h" + +#include +#include +#include +#include +#include +#include + +#include "ReceiveMessageResult.h" +#include "Scheduler.h" +#include "google/rpc/code.pb.h" + +#include "InvocationContext.h" +#include "LogInterceptor.h" +#include "LogInterceptorFactory.h" +#include "LoggerImpl.h" +#include "MessageAccessor.h" +#include "MetadataConstants.h" +#include "MixAll.h" +#include "Partition.h" +#include "Protocol.h" +#include "RpcClient.h" +#include "RpcClientImpl.h" +#include "TlsHelper.h" +#include "UtilAll.h" +#include "grpcpp/create_channel.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/MQMessageExt.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) + : scheduler_(std::make_shared()), resource_namespace_(std::move(resource_namespace)), + state_(State::CREATED), completion_queue_(std::make_shared()), + callback_thread_pool_(absl::make_unique(std::thread::hardware_concurrency())), + latency_histogram_("Message-Latency", 11) { + spdlog::set_level(spdlog::level::trace); + assignLabels(latency_histogram_); + + grpc::SslCredentialsOptions options = {}; + channel_credential_ = grpc::SslCredentials(options); + + // Use unlimited receive message size. + channel_arguments_.SetMaxReceiveMessageSize(-1); + + int max_send_message_size = 1024 * 1024 * 16; + channel_arguments_.SetMaxSendMessageSize(max_send_message_size); + + /* + * Keep-alive settings: + * https://github.com/grpc/grpc/blob/master/doc/keepalive.md + * Keep-alive ping timeout duration: 3s + * Keep-alive ping interval, 30s + */ + channel_arguments_.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 60000); + channel_arguments_.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 3000); + channel_arguments_.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1); + channel_arguments_.SetInt(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA, 0); + + /* + * If set to zero, disables retry behavior. Otherwise, transparent retries + * are enabled for all RPCs, and configurable retries are enabled when they + * are configured via the service config. For details, see: + * https://github.com/grpc/proposal/blob/master/A6-client-retries.md + */ + channel_arguments_.SetInt(GRPC_ARG_ENABLE_RETRIES, 0); + + SPDLOG_INFO("ClientManager[ResourceNamespace={}] created", resource_namespace_); +} + +ClientManagerImpl::~ClientManagerImpl() { + shutdown(); + SPDLOG_INFO("ClientManager[ResourceNamespace={}] destructed", resource_namespace_); +} + +void ClientManagerImpl::start() { + if (State::CREATED != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); + return; + } + state_.store(State::STARTING, std::memory_order_relaxed); + + callback_thread_pool_->start(); + + scheduler_->start(); + + std::weak_ptr client_instance_weak_ptr = shared_from_this(); + + auto health_check_functor = [client_instance_weak_ptr]() { + auto client_instance = client_instance_weak_ptr.lock(); + if (client_instance) { + client_instance->doHealthCheck(); + } + }; + health_check_task_id_ = scheduler_->schedule(health_check_functor, HEALTH_CHECK_TASK_NAME, std::chrono::seconds(5), + std::chrono::seconds(5)); + auto heartbeat_functor = [client_instance_weak_ptr]() { + auto client_instance = client_instance_weak_ptr.lock(); + if (client_instance) { + client_instance->doHeartbeat(); + } + }; + heartbeat_task_id_ = + scheduler_->schedule(heartbeat_functor, HEARTBEAT_TASK_NAME, std::chrono::seconds(1), std::chrono::seconds(10)); + + completion_queue_thread_ = std::thread(std::bind(&ClientManagerImpl::pollCompletionQueue, this)); + + auto stats_functor_ = [client_instance_weak_ptr]() { + auto client_instance = client_instance_weak_ptr.lock(); + if (client_instance) { + client_instance->logStats(); + } + }; + stats_task_id_ = + scheduler_->schedule(stats_functor_, STATS_TASK_NAME, std::chrono::seconds(0), std::chrono::seconds(10)); + state_.store(State::STARTED, std::memory_order_relaxed); +} + +void ClientManagerImpl::shutdown() { + SPDLOG_INFO("Client manager shutdown"); + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); + return; + } + state_.store(STOPPING, std::memory_order_relaxed); + + callback_thread_pool_->shutdown(); + + if (health_check_task_id_) { + scheduler_->cancel(health_check_task_id_); + } + + if (heartbeat_task_id_) { + scheduler_->cancel(heartbeat_task_id_); + } + + if (stats_task_id_) { + scheduler_->cancel(stats_task_id_); + } + + scheduler_->shutdown(); + + { + absl::MutexLock lk(&rpc_clients_mtx_); + rpc_clients_.clear(); + SPDLOG_DEBUG("CompletionQueue of active clients stopped"); + } + + completion_queue_->Shutdown(); + if (completion_queue_thread_.joinable()) { + completion_queue_thread_.join(); + } + SPDLOG_DEBUG("Completion queue thread completes OK"); + + state_.store(State::STOPPED, std::memory_order_relaxed); + SPDLOG_DEBUG("Client instance stopped"); +} + +void ClientManagerImpl::assignLabels(Histogram& histogram) { + histogram.labels().emplace_back("[000ms~020ms): "); + histogram.labels().emplace_back("[020ms~040ms): "); + histogram.labels().emplace_back("[040ms~060ms): "); + histogram.labels().emplace_back("[060ms~080ms): "); + histogram.labels().emplace_back("[080ms~100ms): "); + histogram.labels().emplace_back("[100ms~120ms): "); + histogram.labels().emplace_back("[120ms~140ms): "); + histogram.labels().emplace_back("[140ms~160ms): "); + histogram.labels().emplace_back("[160ms~180ms): "); + histogram.labels().emplace_back("[180ms~200ms): "); + histogram.labels().emplace_back("[200ms~inf): "); +} + +void ClientManagerImpl::healthCheck( + const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) { + std::error_code ec; + auto client = getRpcClient(target_host); + if (!client) { + ec = ErrorCode::RequestTimeout; + cb(ec, nullptr); + return; + } + + SPDLOG_DEBUG("Prepare to send health-check to {}. Request: {}", target_host, request.DebugString()); + + auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("HealthCheck to {}", target_host); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + + for (const auto& entry : metadata) { + invocation_context->context.AddMetadata(entry.first, entry.second); + } + + auto callback = [cb](const InvocationContext* ctx) { + std::error_code ec; + if (!ctx->status.ok()) { + ec = ErrorCode::RequestTimeout; + cb(ec, ctx); + return; + } + + const auto& common = ctx->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + cb(ec, ctx); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + ec = ErrorCode::Unauthorized; + cb(ec, ctx); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + ec = ErrorCode::Forbidden; + cb(ec, ctx); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}", common.status().message()); + ec = ErrorCode::InternalServerError; + cb(ec, ctx); + } break; + default: { + SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release"); + ec = ErrorCode::NotImplemented; + cb(ec, ctx); + } break; + } + }; + + invocation_context->callback = callback; + client->asyncHealthCheck(request, invocation_context); +} + +void ClientManagerImpl::doHealthCheck() { + SPDLOG_DEBUG("Start to perform health check for inactive clients"); + if (State::STARTED != state_.load(std::memory_order_relaxed) && + State::STARTING != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); + return; + } + + auto&& rpc_clients_removed = cleanOfflineRpcClients(); + + std::vector> clients; + { + absl::MutexLock lk(&clients_mtx_); + for (auto& item : clients_) { + auto client = item.lock(); + if (client && client->active()) { + clients.emplace_back(std::move(client)); + } + } + } + + if (!rpc_clients_removed.empty()) { + for (auto& client : clients) { + client->onRemoteEndpointRemoval(rpc_clients_removed); + } + } + + for (auto& client : clients) { + client->healthCheck(); + } + SPDLOG_DEBUG("Health check completed"); +} + +std::vector ClientManagerImpl::cleanOfflineRpcClients() { + absl::flat_hash_set hosts; + { + absl::MutexLock lk(&clients_mtx_); + for (const auto& item : clients_) { + std::shared_ptr client = item.lock(); + if (!client) { + continue; + } + client->endpointsInUse(hosts); + } + } + + std::vector removed; + { + absl::MutexLock lk(&rpc_clients_mtx_); + for (auto it = rpc_clients_.begin(); it != rpc_clients_.end();) { + std::string host = it->first; + if (it->second->needHeartbeat() && !hosts.contains(host)) { + SPDLOG_INFO("Removed RPC client whose peer is offline. RemoteHost={}", host); + removed.push_back(host); + rpc_clients_.erase(it++); + } else { + it++; + } + } + } + + return removed; +} + +void ClientManagerImpl::heartbeat(const std::string& target_host, const Metadata& metadata, + const HeartbeatRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + SPDLOG_DEBUG("Prepare to send heartbeat to {}. Request: {}", target_host, request.DebugString()); + auto client = getRpcClient(target_host, true); + auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("Heartbeat to {}", target_host); + invocation_context->remote_address = target_host; + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + + auto callback = [cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to send heartbeat to target_host={}. gRPC code: {}, message: {}", + invocation_context->remote_address, invocation_context->status.error_code(), + invocation_context->status.error_message()); + std::error_code ec = ErrorCode::RequestTimeout; + cb(ec, invocation_context->response); + return; + } + + const auto& common = invocation_context->response.common(); + std::error_code ec; + switch (common.status().code()) { + case google::rpc::Code::OK: { + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + ec = ErrorCode::Unauthorized; + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + ec = ErrorCode::Forbidden; + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}", common.status().message()); + ec = ErrorCode::BadRequest; + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}", common.status().message()); + ec = ErrorCode::InternalServerError; + cb(ec, invocation_context->response); + } break; + default: { + SPDLOG_WARN("NotImplemented: Please upgrade SDK to latest release"); + } break; + } + }; + + invocation_context->callback = callback; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + client->asyncHeartbeat(request, invocation_context); +} + +void ClientManagerImpl::doHeartbeat() { + if (State::STARTED != state_.load(std::memory_order_relaxed) && + State::STARTING != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); + return; + } + + std::vector> clients; + { + absl::MutexLock lk(&clients_mtx_); + for (const auto& item : clients_) { + auto client = item.lock(); + if (client && client->active()) { + clients.emplace_back(std::move(client)); + } + } + } + + for (auto& client : clients) { + client->heartbeat(); + } +} + +void ClientManagerImpl::pollCompletionQueue() { + while (State::STARTED == state_.load(std::memory_order_relaxed) || + State::STARTING == state_.load(std::memory_order_relaxed)) { + bool ok = false; + void* opaque_invocation_context; + while (completion_queue_->Next(&opaque_invocation_context, &ok)) { + auto invocation_context = static_cast(opaque_invocation_context); + if (!ok) { + // the call is dead + SPDLOG_WARN("CompletionQueue#Next assigned ok false, indicating the call is dead"); + } + auto callback = [invocation_context, ok]() { invocation_context->onCompletion(ok); }; + callback_thread_pool_->submit(callback); + } + SPDLOG_INFO("CompletionQueue is fully drained and shut down"); + } + SPDLOG_INFO("pollCompletionQueue completed and quit"); +} + +bool ClientManagerImpl::send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) { + assert(cb); + SPDLOG_DEBUG("Prepare to send message to {} asynchronously", target_host); + RpcClientSharedPtr client = getRpcClient(target_host); + // Invocation context will be deleted in its onComplete() method. + auto invocation_context = new InvocationContext(); + invocation_context->task_name = + fmt::format("Send message[] to {}", request.message().system_attribute().message_id(), target_host); + invocation_context->remote_address = target_host; + for (const auto& entry : metadata) { + invocation_context->context.AddMetadata(entry.first, entry.second); + } + + const std::string& topic = request.message().topic().name(); + std::weak_ptr client_manager(shared_from_this()); + auto completion_callback = [topic, cb, + client_manager](const InvocationContext* invocation_context) { + ClientManagerPtr client_manager_ptr = client_manager.lock(); + if (!client_manager_ptr) { + return; + } + + if (State::STARTED != client_manager_ptr->state()) { + // TODO: Would this leak some memroy? + return; + } + + const auto& common = invocation_context->response.common(); + + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to send message to {} due to gRPC error. gRPC code: {}, gRPC error message: {}", + invocation_context->remote_address, invocation_context->status.error_code(), + invocation_context->status.error_message()); + std::error_code ec = ErrorCode::RequestTimeout; + cb->onFailure(ec); + return; + } + + if (invocation_context->status.ok()) { + switch (invocation_context->response.common().status().code()) { + case google::rpc::Code::OK: { + SendResult send_result; + send_result.setSendStatus(SendStatus::SEND_OK); + send_result.setMsgId(invocation_context->response.message_id()); + send_result.setTransactionId(invocation_context->response.transaction_id()); + cb->onSuccess(send_result); + } break; + + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}", common.status().message()); + std::error_code ec = ErrorCode::BadRequest; + cb->onFailure(ec); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + std::error_code ec = ErrorCode::Unauthorized; + cb->onFailure(ec); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + std::error_code ec = ErrorCode::Forbidden; + cb->onFailure(ec); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}", common.status().message()); + std::error_code ec = ErrorCode::InternalServerError; + cb->onFailure(ec); + } break; + default: { + SPDLOG_WARN("Unsupported status code. Check and upgrade SDK to the latest"); + std::error_code ec = ErrorCode::NotImplemented; + cb->onFailure(ec); + } break; + } + } + }; + + invocation_context->callback = completion_callback; + client->asyncSend(request, invocation_context); + return true; +} + +/** + * @brief Create a gRPC channel to target host. + * + * @param target_host + * @return std::shared_ptr + */ +std::shared_ptr ClientManagerImpl::createChannel(const std::string& target_host) { + std::vector> interceptor_factories; + interceptor_factories.emplace_back(absl::make_unique()); + return grpc::CreateCustomChannel(target_host, channel_credential_, channel_arguments_); +} + +RpcClientSharedPtr ClientManagerImpl::getRpcClient(const std::string& target_host, bool need_heartbeat) { + std::shared_ptr client; + { + absl::MutexLock lock(&rpc_clients_mtx_); + auto search = rpc_clients_.find(target_host); + if (search == rpc_clients_.end() || !search->second->ok()) { + if (search == rpc_clients_.end()) { + SPDLOG_INFO("Create a RPC client to {}", target_host.data()); + } else if (!search->second->ok()) { + SPDLOG_INFO("Prior RPC client to {} is not OK. Re-create one", target_host); + } + std::vector> interceptor_factories; + interceptor_factories.emplace_back(absl::make_unique()); + auto channel = createChannel(target_host); + client = std::make_shared(completion_queue_, channel, need_heartbeat); + rpc_clients_.insert_or_assign(target_host, client); + } else { + client = search->second; + } + } + + if (need_heartbeat && !client->needHeartbeat()) { + client->needHeartbeat(need_heartbeat); + } + + return client; +} + +void ClientManagerImpl::addRpcClient(const std::string& target_host, const RpcClientSharedPtr& client) { + { + absl::MutexLock lock(&rpc_clients_mtx_); + rpc_clients_.insert_or_assign(target_host, client); + } +} + +void ClientManagerImpl::cleanRpcClients() { + absl::MutexLock lk(&rpc_clients_mtx_); + rpc_clients_.clear(); +} + +SendResult ClientManagerImpl::processSendResponse(const MQMessageQueue& message_queue, + const SendMessageResponse& response) { + if (google::rpc::Code::OK != response.common().status().code()) { + THROW_MQ_EXCEPTION(MQClientException, response.common().DebugString(), response.common().status().code()); + } + SendResult send_result; + send_result.setSendStatus(SendStatus::SEND_OK); + send_result.setMsgId(response.message_id()); + send_result.setQueueOffset(-1); + send_result.setMessageQueue(message_queue); + send_result.setTransactionId(response.transaction_id()); + return send_result; +} + +void ClientManagerImpl::addClientObserver(std::weak_ptr client) { + absl::MutexLock lk(&clients_mtx_); + clients_.emplace_back(std::move(client)); +} + +void ClientManagerImpl::resolveRoute(const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + SPDLOG_DEBUG("Name server connection URL: {}", target_host); + SPDLOG_DEBUG("Query route request: {}", request.DebugString()); + RpcClientSharedPtr client = getRpcClient(target_host, false); + if (!client) { + SPDLOG_WARN("Failed to create RPC client for name server[host={}]", target_host); + std::error_code ec = ErrorCode::RequestTimeout; + cb(ec, nullptr); + return; + } + + auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("Query route of topic={} from {}", request.topic().name(), target_host); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + + auto callback = [cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to send query route request to server[host={}]. Reason: {}", + invocation_context->remote_address, invocation_context->status.error_message()); + std::error_code ec = ErrorCode::RequestTimeout; + cb(ec, nullptr); + return; + } + + std::error_code ec; + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + auto& partitions = invocation_context->response.partitions(); + std::vector topic_partitions; + for (const auto& partition : partitions) { + Topic t(partition.topic().resource_namespace(), partition.topic().name()); + + auto& broker = partition.broker(); + AddressScheme scheme = AddressScheme::IPv4; + switch (broker.endpoints().scheme()) { + case rmq::AddressScheme::IPv4: + scheme = AddressScheme::IPv4; + break; + case rmq::AddressScheme::IPv6: + scheme = AddressScheme::IPv6; + break; + case rmq::AddressScheme::DOMAIN_NAME: + scheme = AddressScheme::DOMAIN_NAME; + break; + default: + break; + } + + std::vector
addresses; + for (const auto& address : broker.endpoints().addresses()) { + addresses.emplace_back(Address{address.host(), address.port()}); + } + ServiceAddress service_address(scheme, addresses); + Broker b(partition.broker().name(), partition.broker().id(), service_address); + + Permission permission = Permission::READ_WRITE; + switch (partition.permission()) { + case rmq::Permission::READ: + permission = Permission::READ; + break; + + case rmq::Permission::WRITE: + permission = Permission::WRITE; + break; + case rmq::Permission::READ_WRITE: + permission = Permission::READ_WRITE; + break; + default: + break; + } + Partition topic_partition(t, partition.id(), permission, std::move(b)); + topic_partitions.emplace_back(std::move(topic_partition)); + } + auto ptr = + std::make_shared(std::move(topic_partitions), invocation_context->response.DebugString()); + cb(ec, ptr); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + cb(ec, nullptr); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + cb(ec, nullptr); + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::BadRequest; + cb(ec, nullptr); + } break; + case google::rpc::Code::NOT_FOUND: { + SPDLOG_WARN("NotFound: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::NotFound; + cb(ec, nullptr); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + cb(ec, nullptr); + } break; + default: { + SPDLOG_WARN("NotImplement: Please upgrade to latest SDK release. Host={}", invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + cb(ec, nullptr); + } break; + } + }; + invocation_context->callback = callback; + client->asyncQueryRoute(request, invocation_context); +} + +void ClientManagerImpl::queryAssignment( + const std::string& target, const Metadata& metadata, const QueryAssignmentRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + SPDLOG_DEBUG("Prepare to send query assignment request to broker[address={}]", target); + std::shared_ptr client = getRpcClient(target); + + auto callback = [&, cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to query assignment. Reason: {}", invocation_context->status.error_message()); + std::error_code ec = ErrorCode::RequestTimeout; + cb(ec, invocation_context->response); + return; + } + + const auto& common = invocation_context->response.common(); + std::error_code ec; + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("Query assignment OK"); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::BadRequest; + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + } break; + default: { + SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release. Host={}", + invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + } break; + } + cb(ec, invocation_context->response); + }; + + auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("QueryAssignment from {}", target); + invocation_context->remote_address = target; + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + invocation_context->callback = callback; + client->asyncQueryAssignment(request, invocation_context); +} + +void ClientManagerImpl::receiveMessage(const std::string& target_host, const Metadata& metadata, + const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, + const std::shared_ptr& cb) { + SPDLOG_DEBUG("Prepare to receive message from {} asynchronously. Request: {}", target_host, request.DebugString()); + RpcClientSharedPtr client = getRpcClient(target_host); + + auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("ReceiveMessage from queue[{}-{}-{}-{}], host={}", request.group().name(), + request.partition().topic().name(), request.partition().broker().name(), + request.partition().id(), target_host); + invocation_context->remote_address = target_host; + if (!metadata.empty()) { + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + } + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + + auto callback = [this, cb](const InvocationContext* invocation_context) { + std::error_code ec; + ReceiveMessageResult result; + + // Handle network error. + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to pop messages through gRPC from {}, gRPC code: {}, gRPC error message: {}", + invocation_context->remote_address, invocation_context->status.error_code(), + invocation_context->status.error_message()); + ec = ErrorCode::RequestTimeout; + cb->onCompletion(ec, result); + return; + } + + // Handle application layer logic + result.source_host = invocation_context->remote_address; + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_TRACE("ReceivedMessage Resonse: {}, host={}", invocation_context->response.DebugString(), + invocation_context->remote_address); + for (auto& item : invocation_context->response.messages()) { + MQMessageExt message_ext; + MessageAccessor::setTargetEndpoint(message_ext, invocation_context->remote_address); + if (wrapMessage(item, message_ext)) { + result.messages.emplace_back(message_ext); + } else { + SPDLOG_WARN("A message fails to pass body checksum validation. Skip processing it."); + } + } + break; + } + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + } break; + + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + } break; + + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::BadRequest; + } break; + + case google::rpc::Code::DEADLINE_EXCEEDED: { + SPDLOG_WARN("DeadlineExceeded: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::GatewayTimeout; + } break; + + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("IntervalServerError: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + } break; + default: { + SPDLOG_WARN("Unsupported code. Please upgrade to use the latest release. Host={}", + invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + } break; + } + cb->onCompletion(ec, result); + }; + invocation_context->callback = callback; + client->asyncReceive(request, invocation_context); +} + +State ClientManagerImpl::state() const { + return state_.load(std::memory_order_relaxed); +} + +bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) { + assert(item.topic().resource_namespace() == resource_namespace_); + + // base + message_ext.setTopic(item.topic().name()); + + const auto& system_attributes = item.system_attribute(); + + // Receipt-handle + MessageAccessor::setReceiptHandle(message_ext, system_attributes.receipt_handle()); + + // Tag + message_ext.setTags(system_attributes.tag()); + + // Keys + std::vector keys; + for (const auto& key : system_attributes.keys()) { + keys.push_back(key); + } + message_ext.setKeys(keys); + + // Message-Id + MessageAccessor::setMessageId(message_ext, system_attributes.message_id()); + + // Validate body digest + const rmq::Digest& digest = system_attributes.body_digest(); + bool body_digest_match = false; + if (item.body().empty()) { + SPDLOG_WARN("Body of message[topic={}, msgId={}] is empty", item.topic().name(), + item.system_attribute().message_id()); + body_digest_match = true; + } else { + switch (digest.type()) { + case rmq::DigestType::CRC32: { + std::string checksum; + bool success = MixAll::crc32(item.body(), checksum); + if (success) { + body_digest_match = (digest.checksum() == checksum); + if (body_digest_match) { + SPDLOG_DEBUG("Message body CRC32 checksum validation passed."); + } else { + SPDLOG_WARN("Body CRC32 checksum validation failed. Actual: {}, expect: {}", checksum, digest.checksum()); + } + } else { + SPDLOG_WARN("Failed to calculate CRC32 checksum. Skip."); + } + break; + } + case rmq::DigestType::MD5: { + std::string checksum; + bool success = MixAll::md5(item.body(), checksum); + if (success) { + body_digest_match = (digest.checksum() == checksum); + if (body_digest_match) { + SPDLOG_DEBUG("Body of message[{}] MD5 checksum validation passed.", message_ext.getMsgId()); + } else { + SPDLOG_WARN("Body of message[{}] MD5 checksum validation failed. Expect: {}, Actual: {}", + message_ext.getMsgId(), digest.checksum(), checksum); + } + } else { + SPDLOG_WARN("Failed to calculate MD5 digest. Skip."); + body_digest_match = true; + } + break; + } + case rmq::DigestType::SHA1: { + std::string checksum; + bool success = MixAll::sha1(item.body(), checksum); + if (success) { + body_digest_match = (checksum == digest.checksum()); + if (body_digest_match) { + SPDLOG_DEBUG("Body of message[{}] SHA1 checksum validation passed", message_ext.getMsgId()); + } else { + SPDLOG_WARN("Body of message[{}] SHA1 checksum validation failed. Expect: {}, Actual: {}", + message_ext.getMsgId(), digest.checksum(), checksum); + } + } else { + SPDLOG_WARN("Failed to calculate SHA1 digest for message[{}]. Skip.", message_ext.getMsgId()); + } + break; + } + default: { + SPDLOG_WARN("Unsupported message body digest algorithm"); + body_digest_match = true; + break; + } + } + } + + if (!body_digest_match) { + SPDLOG_WARN("Message body checksum failed. MsgId={}", system_attributes.message_id()); + // TODO: NACK it immediately + return false; + } + + // Body encoding + switch (system_attributes.body_encoding()) { + case rmq::Encoding::GZIP: { + std::string uncompressed; + UtilAll::uncompress(item.body(), uncompressed); + message_ext.setBody(uncompressed); + break; + } + case rmq::Encoding::IDENTITY: { + message_ext.setBody(item.body()); + break; + } + default: { + SPDLOG_WARN("Unsupported encoding algorithm"); + break; + } + } + + timeval tv{}; + + // Message-type + MessageType message_type; + switch (system_attributes.message_type()) { + case rmq::MessageType::NORMAL: + message_type = MessageType::NORMAL; + break; + case rmq::MessageType::FIFO: + message_type = MessageType::FIFO; + break; + case rmq::MessageType::DELAY: + message_type = MessageType::DELAY; + break; + case rmq::MessageType::TRANSACTION: + message_type = MessageType::TRANSACTION; + break; + default: + SPDLOG_WARN("Unknown message type. Treat it as normal message"); + message_type = MessageType::NORMAL; + break; + } + MessageAccessor::setMessageType(message_ext, message_type); + + // Born-timestamp + if (system_attributes.has_born_timestamp()) { + tv.tv_sec = system_attributes.born_timestamp().seconds(); + tv.tv_usec = system_attributes.born_timestamp().nanos() / 1000; + auto born_timestamp = absl::TimeFromTimeval(tv); + MessageAccessor::setBornTimestamp(message_ext, born_timestamp); + } + + // Born-host + MessageAccessor::setBornHost(message_ext, system_attributes.born_host()); + + // Store-timestamp + if (system_attributes.has_store_timestamp()) { + tv.tv_sec = system_attributes.store_timestamp().seconds(); + tv.tv_usec = system_attributes.store_timestamp().nanos() / 1000; + MessageAccessor::setStoreTimestamp(message_ext, absl::TimeFromTimeval(tv)); + } + + // Store-host + MessageAccessor::setStoreHost(message_ext, system_attributes.store_host()); + + // Process one-of: delivery-timestamp and delay-level. + switch (system_attributes.timed_delivery_case()) { + case rmq::SystemAttribute::TimedDeliveryCase::kDelayLevel: { + message_ext.setDelayTimeLevel(system_attributes.delay_level()); + break; + } + + case rmq::SystemAttribute::TimedDeliveryCase::kDeliveryTimestamp: { + tv.tv_sec = system_attributes.delivery_timestamp().seconds(); + tv.tv_usec = system_attributes.delivery_timestamp().nanos(); + MessageAccessor::setDeliveryTimestamp(message_ext, absl::TimeFromTimeval(tv)); + break; + } + + default: + break; + } + + // Partition-id + MessageAccessor::setQueueId(message_ext, system_attributes.partition_id()); + + // Partition-offset + MessageAccessor::setQueueOffset(message_ext, system_attributes.partition_offset()); + + // Invisible-period + if (system_attributes.has_invisible_period()) { + absl::Duration invisible_period = absl::Seconds(system_attributes.invisible_period().seconds()) + + absl::Nanoseconds(system_attributes.invisible_period().nanos()); + MessageAccessor::setInvisiblePeriod(message_ext, invisible_period); + } + + // Delivery attempt + MessageAccessor::setDeliveryAttempt(message_ext, system_attributes.delivery_attempt()); + + // Trace-context + MessageAccessor::setTraceContext(message_ext, system_attributes.trace_context()); + + // Decoded Time-Point + MessageAccessor::setDecodedTimestamp(message_ext, absl::Now()); + + // User-properties + std::map properties; + for (const auto& it : item.user_attribute()) { + properties.insert(std::make_pair(it.first, it.second)); + } + message_ext.setProperties(properties); + + // Extension + { + auto elapsed = static_cast(absl::ToUnixMillis(absl::Now()) - message_ext.getStoreTimestamp()); + if (elapsed >= 0) { + latency_histogram_.countIn(elapsed / 20); + } + } + return true; +} + +SchedulerSharedPtr ClientManagerImpl::getScheduler() { + return scheduler_; +} + +void ClientManagerImpl::ack(const std::string& target, const Metadata& metadata, const AckMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& cb) { + std::string target_host(target.data(), target.length()); + SPDLOG_DEBUG("Prepare to ack message against {} asynchronously. AckMessageRequest: {}", target_host, + request.DebugString()); + RpcClientSharedPtr client = getRpcClient(target_host); + + auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("Ack message[{}] against {}", request.message_id(), target); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + + // TODO: Use capture by move and pass-by-value paradigm when C++ 14 is available. + auto callback = [request, cb](const InvocationContext* invocation_context) { + std::error_code ec; + if (!invocation_context->status.ok()) { + ec = ErrorCode::RequestTimeout; + cb(ec); + return; + } + + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("Ack OK. host={}", invocation_context->remote_address); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::BadRequest; + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + } break; + default: { + SPDLOG_WARN("NotImplement: please upgrade SDK to latest release. host={}", invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + } break; + } + cb(ec); + }; + invocation_context->callback = callback; + client->asyncAck(request, invocation_context); +} + +void ClientManagerImpl::nack(const std::string& target_host, const Metadata& metadata, + const NackMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& completion_callback) { + RpcClientSharedPtr client = getRpcClient(target_host); + assert(client); + auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("Nack Message[{}] against {}", request.message_id(), target_host); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + + auto callback = [completion_callback](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to write Nack request to wire. gRPC-code: {}, gRPC-message: {}", + invocation_context->status.error_code(), invocation_context->status.error_message()); + std::error_code ec = ErrorCode::RequestTimeout; + completion_callback(ec); + return; + } + + std::error_code ec; + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("Nack to {} OK", invocation_context->remote_address); + break; + }; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + break; + } + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + break; + } + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + break; + } + default: { + SPDLOG_WARN("NotImplemented: Please upgrade to latest SDK, host={}", invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + break; + } + } + completion_callback(ec); + }; + invocation_context->callback = callback; + client->asyncNack(request, invocation_context); +} + +void ClientManagerImpl::endTransaction( + const std::string& target_host, const Metadata& metadata, const EndTransactionRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + RpcClientSharedPtr client = getRpcClient(target_host); + if (!client) { + SPDLOG_WARN("No RPC client for {}", target_host); + EndTransactionResponse response; + std::error_code ec = ErrorCode::BadRequest; + cb(ec, response); + return; + } + + SPDLOG_DEBUG("Prepare to endTransaction. TargetHost={}, Request: {}", target_host.data(), request.DebugString()); + + auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("End transaction[{}] of message[] against {}", request.transaction_id(), + request.message_id(), target_host); + invocation_context->remote_address = target_host; + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + + // Set RPC deadline. + auto deadline = std::chrono::system_clock::now() + timeout; + invocation_context->context.set_deadline(deadline); + + auto callback = [target_host, cb](const InvocationContext* invocation_context) { + std::error_code ec; + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to write EndTransaction to wire. gRPC-code: {}, gRPC-message: {}, host={}", + invocation_context->status.error_code(), invocation_context->status.error_message(), + invocation_context->remote_address); + ec = ErrorCode::BadRequest; + cb(ec, invocation_context->response); + return; + } + + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("endTransaction completed OK. Response: {}, host={}", invocation_context->response.DebugString(), + invocation_context->remote_address); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + } break; + case google::rpc::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + } break; + default: { + SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release. {}, host={}", common.status().message(), + invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + } + } + cb(ec, invocation_context->response); + }; + + invocation_context->callback = callback; + client->asyncEndTransaction(request, invocation_context); +} + +void ClientManagerImpl::pollCommand(const std::string& target, const Metadata& metadata, + const PollCommandRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) { + auto client = getRpcClient(target); + + auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target; + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + auto deadline = std::chrono::system_clock::now() + timeout; + invocation_context->context.set_deadline(deadline); + + auto callback = [cb](const InvocationContext* invocation_context) { cb(invocation_context); }; + + invocation_context->callback = callback; + client->asyncPollCommand(request, invocation_context); +} + +void ClientManagerImpl::queryOffset(const std::string& target_host, const Metadata& metadata, + const QueryOffsetRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + auto client = getRpcClient(target_host); + std::error_code ec; + if (!client) { + SPDLOG_WARN("Failed to get/create RPC client for {}", target_host); + ec = ErrorCode::RequestTimeout; + QueryOffsetResponse response; + cb(ec, response); + return; + } + + auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + + for (const auto& entry : metadata) { + invocation_context->context.AddMetadata(entry.first, entry.second); + } + + auto callback = [cb](const InvocationContext* invocation_context) { + std::error_code ec; + + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to write QueryOffset request to wire. gRPC-code: {}, gRPC-message: {}, host={}", + invocation_context->status.error_code(), invocation_context->status.error_message(), + invocation_context->remote_address); + ec = ErrorCode::RequestTimeout; + cb(ec, invocation_context->response); + return; + } + + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("Query offset from server[host={}] OK", invocation_context->remote_address); + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + cb(ec, invocation_context->response); + } break; + default: { + SPDLOG_WARN("NotImplemented: please upgrade SDK to the latest release. host={}", + invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + cb(ec, invocation_context->response); + } + } + }; + invocation_context->callback = callback; + client->asyncQueryOffset(request, invocation_context); +} + +void ClientManagerImpl::pullMessage( + const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + SPDLOG_DEBUG("PullMessage Request: {}, target_host={}", request.DebugString(), target_host); + auto client = getRpcClient(target_host); + auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("PullMessage for queue[{}-{}-{}-{}] from {}", request.group().name(), + request.partition().topic().name(), request.partition().broker().name(), + request.partition().id(), target_host); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + + auto callback = [cb, this](const InvocationContext* invocation_context) { + std::error_code ec; + ReceiveMessageResult result; + result.source_host = invocation_context->remote_address; + // Handle network issue. + if (!invocation_context->status.ok()) { + ec = ErrorCode::RequestTimeout; + cb(ec, result); + return; + } + + // Handle application layer logic: map status::code to corresponding error_code. + const auto& common = invocation_context->response.common(); + result.min_offset = invocation_context->response.min_offset(); + result.next_offset = invocation_context->response.next_offset(); + result.max_offset = invocation_context->response.max_offset(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_TRACE("Received PullMessage Response: {}, host={}", invocation_context->response.DebugString(), + invocation_context->remote_address); + for (const auto& item : invocation_context->response.messages()) { + MQMessageExt message; + if (!wrapMessage(item, message)) { + return; + } + result.messages.emplace_back(message); + } + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + } break; + case google::rpc::Code::NOT_FOUND: { + SPDLOG_WARN("NotFound: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::NotFound; + break; + } + case google::rpc::Code::DEADLINE_EXCEEDED: { + SPDLOG_WARN("DeadlineExceeded: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::GatewayTimeout; + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::BadRequest; + } break; + case google::rpc::Code::FAILED_PRECONDITION: { + SPDLOG_WARN("FailedPrecondition: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::PreconditionRequired; + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + } break; + default: { + SPDLOG_WARN("Unimplemented: Please upgrade to use latest SDK release, host={}", + invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + } break; + } + cb(ec, result); + }; + + invocation_context->callback = callback; + client->asyncPull(request, invocation_context); +} + +void ClientManagerImpl::forwardMessageToDeadLetterQueue( + const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) { + SPDLOG_DEBUG("ForwardMessageToDeadLetterQueue Request: {}", request.DebugString()); + auto client = getRpcClient(target_host); + auto invocation_context = new InvocationContext(); + invocation_context->task_name = + fmt::format("Forward message[{}] to DLQ against {}", request.message_id(), target_host); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + + auto callback = [cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to transmit SendMessageToDeadLetterQueueRequest to host={}", + invocation_context->remote_address); + cb(invocation_context); + return; + } + + SPDLOG_DEBUG("Received forwardToDeadLetterQueue response from server[host={}]", invocation_context->remote_address); + cb(invocation_context); + }; + invocation_context->callback = callback; + client->asyncForwardMessageToDeadLetterQueue(request, invocation_context); +} + +std::error_code ClientManagerImpl::reportThreadStackTrace(const std::string& target_host, const Metadata& metadata, + const ReportThreadStackTraceRequest& request, + std::chrono::milliseconds timeout) { + std::error_code ec; + auto client = getRpcClient(target_host); + grpc::ClientContext context; + auto deadline = std::chrono::system_clock::now() + timeout; + context.set_deadline(deadline); + + for (const auto& item : metadata) { + context.AddMetadata(item.first, item.second); + } + + ReportThreadStackTraceResponse response; + auto status = client->reportThreadStackTrace(&context, request, &response); + if (!status.ok()) { + ec = ErrorCode::RequestTimeout; + SPDLOG_WARN("Failed to report thread-stack-trace to {}. Cause: {}", target_host, status.error_message()); + return ec; + } + + switch (response.common().status().code()) { + case google::rpc::Code::OK: { + return ec; + } + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthorized. Host={}, Cause: {}", target_host, response.common().status().message()); + ec = ErrorCode::Unauthorized; + break; + } + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("Forbidden. Host={}, Cause: {}", target_host, response.common().status().message()); + ec = ErrorCode::Forbidden; + break; + } + default: { + ec = ErrorCode::NotImplemented; + SPDLOG_WARN("Unsupported response code, please update client to latest release. Host={}", target_host); + } + } + return ec; +} + +std::error_code ClientManagerImpl::reportMessageConsumptionResult(const std::string& target_host, + const Metadata& metadata, + const ReportMessageConsumptionResultRequest& request, + std::chrono::milliseconds timeout) { + std::error_code ec; + auto client = getRpcClient(target_host); + grpc::ClientContext context; + auto deadline = std::chrono::system_clock::now() + timeout; + context.set_deadline(deadline); + + for (const auto& item : metadata) { + context.AddMetadata(item.first, item.second); + } + + ReportMessageConsumptionResultResponse response; + auto status = client->reportMessageConsumptionResult(&context, request, &response); + if (!status.ok()) { + ec = ErrorCode::RequestTimeout; + SPDLOG_WARN("Failed to report thread-stack-trace to {}. Cause: {}", target_host, status.error_message()); + return ec; + } + + switch (response.common().status().code()) { + case google::rpc::Code::OK: { + return ec; + } + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthorized. Host={}, Cause: {}", target_host, response.common().status().message()); + ec = ErrorCode::Unauthorized; + break; + } + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("Forbidden. Host={}, Cause: {}", target_host, response.common().status().message()); + ec = ErrorCode::Forbidden; + break; + } + default: { + ec = ErrorCode::NotImplemented; + SPDLOG_WARN("Unsupported response code, please update client to latest release. Host={}", target_host); + } + } + return ec; +} + +std::error_code ClientManagerImpl::notifyClientTermination(const std::string& target_host, const Metadata& metadata, + const NotifyClientTerminationRequest& request, + std::chrono::milliseconds timeout) { + std::error_code ec; + auto client = getRpcClient(target_host); + if (!client) { + SPDLOG_WARN("Failed to create RpcClient for host={}", target_host); + ec = ErrorCode::RequestTimeout; + return ec; + } + + grpc::ClientContext context; + context.set_deadline(std::chrono::system_clock::now() + timeout); + for (const auto& item : metadata) { + context.AddMetadata(item.first, item.second); + } + + SPDLOG_DEBUG("NotifyClientTermination request: {}", request.DebugString()); + + NotifyClientTerminationResponse response; + grpc::Status status = client->notifyClientTermination(&context, request, &response); + if (!status.ok()) { + SPDLOG_WARN("NotifyClientTermination failed. gRPC-code={}, gRPC-message={}, host={}", status.error_code(), + status.error_message(), target_host); + ec = ErrorCode::RequestTimeout; + return ec; + } + + const auto& common = response.common(); + + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("NotifyClientTermination OK. host={}", target_host); + break; + } + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: Cause={}, host={}", common.status().message(), target_host); + ec = ErrorCode::InternalServerError; + break; + } + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: Cause={}, host={}", common.status().message(), target_host); + ec = ErrorCode::Unauthorized; + break; + } + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: Cause={}, host={}", common.status().message(), target_host); + ec = ErrorCode::Forbidden; + break; + } + default: { + SPDLOG_WARN("NotImplemented. Please upgrade to latest SDK release. host={}", target_host); + ec = ErrorCode::NotImplemented; + break; + } + } + return ec; +} + +void ClientManagerImpl::logStats() { + std::string stats; + latency_histogram_.reportAndReset(stats); + SPDLOG_INFO("{}", stats); +} + +void ClientManagerImpl::submit(std::function task) { + callback_thread_pool_->submit(task); +} + +const char* ClientManagerImpl::HEARTBEAT_TASK_NAME = "heartbeat-task"; +const char* ClientManagerImpl::STATS_TASK_NAME = "stats-task"; +const char* ClientManagerImpl::HEALTH_CHECK_TASK_NAME = "health-check-task"; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/LogInterceptor.cpp b/src/main/cpp/client/LogInterceptor.cpp new file mode 100644 index 000000000..09dbc9fff --- /dev/null +++ b/src/main/cpp/client/LogInterceptor.cpp @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "LogInterceptor.h" +#include "InterceptorContinuation.h" +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_join.h" +#include "google/protobuf/message.h" +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +void LogInterceptor::Intercept(grpc::experimental::InterceptorBatchMethods* methods) { + InterceptorContinuation continuation(methods); + + auto level = spdlog::default_logger()->level(); + switch (level) { + case spdlog::level::trace: + // fall-through on purpose. + case spdlog::level::debug: { + break; + } + case spdlog::level::info: + // fall-through on purpose. + case spdlog::level::warn: + // fall-through on purpose. + case spdlog::level::err: { + return; + } + default: { + return; + } + } + + if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) { + std::multimap* metadata = methods->GetSendInitialMetadata(); + if (metadata) { + SPDLOG_DEBUG("[Outbound]Headers of {}: \n{}", client_rpc_info_->method(), + absl::StrJoin(*metadata, "\n", absl::PairFormatter(" --> "))); + } + } + + if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) { + grpc::ByteBuffer* buffer = methods->GetSerializedSendMessage(); + if (buffer) { + SPDLOG_DEBUG("[Outbound] {}: Buffer: {}bytes", client_rpc_info_->method(), buffer->Length()); + } + } + + if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA)) { + std::multimap* metadata = methods->GetRecvInitialMetadata(); + if (metadata) { + absl::flat_hash_map response_headers; + for (const auto& it : *metadata) { + response_headers.insert({absl::string_view(it.first.data(), it.first.length()), + absl::string_view(it.second.data(), it.second.length())}); + } + if (!response_headers.empty()) { + SPDLOG_DEBUG("[Inbound]Response Headers of {}:\n{}", client_rpc_info_->method(), + absl::StrJoin(response_headers, "\n", absl::PairFormatter(" --> "))); + } else { + SPDLOG_DEBUG("[Inbound]Response metadata of {} is empty", client_rpc_info_->method()); + } + } + } + + if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::POST_RECV_MESSAGE)) { + void* message = methods->GetRecvMessage(); + if (message) { + auto* response = reinterpret_cast(message); + std::string&& response_text = response->DebugString(); + std::size_t limit = 1024; + if (response_text.size() <= limit) { + SPDLOG_DEBUG("[Inbound] {}\n{}", client_rpc_info_->method(), response_text); + } else { + SPDLOG_DEBUG("[Inbound] {}\n{}...", client_rpc_info_->method(), response_text.substr(0, limit)); + } + } + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/LogInterceptorFactory.cpp b/src/main/cpp/client/LogInterceptorFactory.cpp new file mode 100644 index 000000000..16247e0ef --- /dev/null +++ b/src/main/cpp/client/LogInterceptorFactory.cpp @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "LogInterceptorFactory.h" +#include "LogInterceptor.h" + +ROCKETMQ_NAMESPACE_BEGIN + +grpc::experimental::Interceptor* +LogInterceptorFactory::CreateClientInterceptor(grpc::experimental::ClientRpcInfo* info) { + return new LogInterceptor(info); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/RpcClientImpl.cpp b/src/main/cpp/client/RpcClientImpl.cpp new file mode 100644 index 000000000..5d546b00e --- /dev/null +++ b/src/main/cpp/client/RpcClientImpl.cpp @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "RpcClientImpl.h" + +#include + +#include "ClientConfigImpl.h" +#include "TlsHelper.h" +#include "absl/time/time.h" + +using ClientContext = grpc::ClientContext; + +ROCKETMQ_NAMESPACE_BEGIN + +void RpcClientImpl::asyncQueryRoute(const QueryRouteRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader = + stub_->PrepareAsyncQueryRoute(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +void RpcClientImpl::asyncSend(const SendMessageRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader = + stub_->PrepareAsyncSendMessage(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +void RpcClientImpl::asyncQueryAssignment(const QueryAssignmentRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader = + stub_->PrepareAsyncQueryAssignment(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +std::shared_ptr& rocketmq::RpcClientImpl::completionQueue() { + return completion_queue_; +} + +void RpcClientImpl::asyncReceive(const ReceiveMessageRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader = + stub_->PrepareAsyncReceiveMessage(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +void RpcClientImpl::asyncAck(const AckMessageRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncAckMessage(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +void RpcClientImpl::asyncNack(const NackMessageRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncNackMessage(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +void RpcClientImpl::asyncHeartbeat(const HeartbeatRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncHeartbeat(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +void RpcClientImpl::asyncHealthCheck(const HealthCheckRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncHealthCheck(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +void RpcClientImpl::asyncEndTransaction(const EndTransactionRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncEndTransaction(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +bool RpcClientImpl::ok() const { + return channel_ && grpc_connectivity_state::GRPC_CHANNEL_SHUTDOWN != channel_->GetState(false); +} + +void RpcClientImpl::addMetadata(grpc::ClientContext& context, + const absl::flat_hash_map& metadata) { + for (const auto& entry : metadata) { + context.AddMetadata(entry.first, entry.second); + } +} + +bool RpcClientImpl::needHeartbeat() { + return need_heartbeat_; +} + +void RpcClientImpl::needHeartbeat(bool need_heartbeat) { + need_heartbeat_ = need_heartbeat; +} + +void RpcClientImpl::asyncPollCommand(const PollCommandRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader = + stub_->PrepareAsyncPollCommand(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +grpc::Status RpcClientImpl::reportThreadStackTrace(grpc::ClientContext* context, + const ReportThreadStackTraceRequest& request, + ReportThreadStackTraceResponse* response) { + return stub_->ReportThreadStackTrace(context, request, response); +} + +grpc::Status RpcClientImpl::reportMessageConsumptionResult(grpc::ClientContext* context, + const ReportMessageConsumptionResultRequest& request, + ReportMessageConsumptionResultResponse* response) { + return stub_->ReportMessageConsumptionResult(context, request, response); +} + +grpc::Status RpcClientImpl::notifyClientTermination(grpc::ClientContext* context, + const NotifyClientTerminationRequest& request, + NotifyClientTerminationResponse* response) { + return stub_->NotifyClientTermination(context, request, response); +} + +void RpcClientImpl::asyncQueryOffset(const QueryOffsetRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncQueryOffset(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +void RpcClientImpl::asyncPull(const PullMessageRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader = + stub_->PrepareAsyncPullMessage(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +void RpcClientImpl::asyncForwardMessageToDeadLetterQueue( + const ForwardMessageToDeadLetterQueueRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader = stub_->PrepareAsyncForwardMessageToDeadLetterQueue( + &invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/Signature.cpp b/src/main/cpp/client/Signature.cpp new file mode 100644 index 000000000..33641582d --- /dev/null +++ b/src/main/cpp/client/Signature.cpp @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Signature.h" +#include "ClientConfigImpl.h" +#include "MetadataConstants.h" +#include "Protocol.h" +#include "TlsHelper.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void Signature::sign(ClientConfig* client, absl::flat_hash_map& metadata) { + assert(client); + + metadata.insert({MetadataConstants::LANGUAGE_KEY, "CPP"}); + // Add common headers + metadata.insert({MetadataConstants::CLIENT_VERSION_KEY, ClientConfigImpl::CLIENT_VERSION}); + metadata.insert({MetadataConstants::PROTOCOL_VERSION_KEY, Protocol::PROTOCOL_VERSION}); + + if (!client->tenantId().empty()) { + metadata.insert({MetadataConstants::TENANT_ID_KEY, client->tenantId()}); + } + + if (!client->resourceNamespace().empty()) { + metadata.insert({MetadataConstants::NAMESPACE_KEY, client->resourceNamespace()}); + } + + absl::Time now = absl::Now(); + absl::TimeZone utc_time_zone = absl::UTCTimeZone(); + const std::string request_date_time = absl::FormatTime(MetadataConstants::DATE_TIME_FORMAT, now, utc_time_zone); + metadata.insert({MetadataConstants::DATE_TIME_KEY, request_date_time}); + + if (client->credentialsProvider()) { + Credentials&& credentials = client->credentialsProvider()->getCredentials(); + if (credentials.accessKey().empty() || credentials.accessSecret().empty()) { + SPDLOG_WARN("Access credential is incomplete. Check your access key/secret."); + return; + } + + std::string authorization; + authorization.append(MetadataConstants::ALGORITHM_KEY) + .append(" ") + .append(MetadataConstants::CREDENTIAL_KEY) + .append("=") + .append(credentials.accessKey()) + .append("/") + .append(client->region()) + .append("/") + .append(client->serviceName()) + .append(", ") + .append(MetadataConstants::SIGNED_HEADERS_KEY) + .append("=") + .append(MetadataConstants::DATE_TIME_KEY) + .append(", ") + .append(MetadataConstants::SIGNATURE_KEY) + .append("=") + .append(TlsHelper::sign(credentials.accessSecret(), request_date_time)); + SPDLOG_DEBUG("Add authorization header: {}", authorization); + metadata.insert({MetadataConstants::AUTHORIZATION, authorization}); + + if (!credentials.sessionToken().empty()) { + metadata.insert({MetadataConstants::STS_SESSION_TOKEN, credentials.sessionToken()}); + } + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/TlsHelper.cpp b/src/main/cpp/client/TlsHelper.cpp new file mode 100644 index 000000000..f572a08ef --- /dev/null +++ b/src/main/cpp/client/TlsHelper.cpp @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TlsHelper.h" + +#include "MixAll.h" +#include "OpenSSLCompatible.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +std::string TlsHelper::sign(const std::string& access_secret, const std::string& content) { + HMAC_CTX* ctx = HMAC_CTX_new(); + HMAC_Init_ex(ctx, access_secret.c_str(), access_secret.length(), EVP_sha1(), nullptr); + HMAC_Update(ctx, reinterpret_cast(content.c_str()), content.length()); + auto result = new unsigned char[EVP_MD_size(EVP_sha1())]; + unsigned int len; + HMAC_Final(ctx, result, &len); + HMAC_CTX_free(ctx); + + std::string hex_str = MixAll::hex(result, len); + delete[] result; + return hex_str; +} + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/client/TopicAssignmentInfo.cpp b/src/main/cpp/client/TopicAssignmentInfo.cpp new file mode 100644 index 000000000..88e997601 --- /dev/null +++ b/src/main/cpp/client/TopicAssignmentInfo.cpp @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TopicAssignmentInfo.h" +#include "google/rpc/code.pb.h" +#include "rocketmq/RocketMQ.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +thread_local uint32_t TopicAssignment::query_which_broker_ = 0; + +TopicAssignment::TopicAssignment(const QueryAssignmentResponse& response) : debug_string_(response.DebugString()) { + if (response.common().status().code() != google::rpc::Code::OK) { + SPDLOG_WARN("QueryAssignmentResponse#code is not SUCCESS. Keep assignment info intact. QueryAssignmentResponse: {}", + response.DebugString()); + return; + } + + for (const auto& item : response.assignments()) { + const rmq::Partition& partition = item.partition(); + if (rmq::Permission::READ != partition.permission() && rmq::Permission::READ_WRITE != partition.permission()) { + continue; + } + + assert(partition.has_broker()); + const auto& broker = partition.broker(); + + if (broker.endpoints().addresses().empty()) { + SPDLOG_WARN("Broker[{}] is not addressable", broker.DebugString()); + continue; + } + + MQMessageQueue message_queue(partition.topic().name(), partition.broker().name(), partition.id()); + std::string service_address; + for (const auto& address : broker.endpoints().addresses()) { + if (service_address.empty()) { + switch (broker.endpoints().scheme()) { + case rmq::AddressScheme::IPv4: + service_address.append("ipv4:"); + break; + case rmq::AddressScheme::IPv6: + service_address.append("ipv6:"); + break; + case rmq::AddressScheme::DOMAIN_NAME: + service_address.append("dns:"); + break; + default: + SPDLOG_WARN("Unsupported gRPC naming scheme"); + break; + } + } else { + service_address.append(","); + } + service_address.append(address.host()).append(":").append(std::to_string(address.port())); + + if (rmq::AddressScheme::DOMAIN_NAME == broker.endpoints().scheme()) { + break; + } + } + message_queue.serviceAddress(service_address); + assignment_list_.emplace_back(Assignment(message_queue)); + } + std::sort(assignment_list_.begin(), assignment_list_.end()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/TopicPublishInfo.cpp b/src/main/cpp/client/TopicPublishInfo.cpp new file mode 100644 index 000000000..9d43afb12 --- /dev/null +++ b/src/main/cpp/client/TopicPublishInfo.cpp @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TopicPublishInfo.h" +#include "TopicRouteData.h" + +#include "LoggerImpl.h" +#include "MixAll.h" +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_join.h" + +ROCKETMQ_NAMESPACE_BEGIN + +thread_local uint32_t TopicPublishInfo::send_which_queue_ = MixAll::random(0, 100); + +TopicPublishInfo::TopicPublishInfo(absl::string_view topic, TopicRouteDataPtr topic_route_data) + : topic_(topic.data(), topic.length()), topic_route_data_(std::move(topic_route_data)) { + updatePublishInfo(); +} + +bool TopicPublishInfo::selectOneMessageQueue(MQMessageQueue& message_queue) { + unsigned int index = ++send_which_queue_; + { + absl::MutexLock lock(&partition_list_mtx_); + if (partition_list_.empty()) { + return false; + } + auto& partition = partition_list_[index % (partition_list_.size())]; + message_queue = partition.asMessageQueue(); + } + return true; +} + +bool TopicPublishInfo::selectOneActiveMessageQueue(absl::flat_hash_set& isolated, + MQMessageQueue& message_queue) { + unsigned int index = ++send_which_queue_; + { + absl::MutexLock lock(&partition_list_mtx_); + if (partition_list_.empty()) { + SPDLOG_DEBUG("message queue list is empty"); + return false; + } + + for (std::vector::size_type i = 0; i < partition_list_.size(); ++i) { + message_queue = partition_list_[index++ % (partition_list_.size())].asMessageQueue(); + if (!isolated.contains(message_queue.serviceAddress())) { + SPDLOG_DEBUG("Selected host={}", message_queue.serviceAddress()); + return true; + } + } + } + return false; +} + +bool TopicPublishInfo::takeMessageQueues(absl::flat_hash_set& isolated, + std::vector& candidates, uint32_t count) { + + unsigned int index = ++send_which_queue_; + { + absl::MutexLock lock(&partition_list_mtx_); + if (partition_list_.empty()) { + SPDLOG_DEBUG("message queue list empty"); + return false; + } + + for (std::vector::size_type i = 0; i < partition_list_.size(); ++i) { + const MQMessageQueue& message_queue = partition_list_[index++ % (partition_list_.size())].asMessageQueue(); + if (!isolated.contains(message_queue.serviceAddress())) { + auto search = std::find_if(candidates.begin(), candidates.end(), [&](const MQMessageQueue& item) { + return item.getBrokerName() == message_queue.getBrokerName(); + }); + if (std::end(candidates) == search) { + candidates.emplace_back(message_queue); + } + if (candidates.size() >= count) { + return true; + } + } + } + } + return !candidates.empty(); +} + +void TopicPublishInfo::updatePublishInfo() { + + std::vector writable_partition_list; + { + for (const auto& partition : topic_route_data_->partitions()) { + assert(partition.topic().name() == topic_); + if (Permission::READ == partition.permission() || Permission::NONE == partition.permission()) { + continue; + } + if (MixAll::MASTER_BROKER_ID != partition.broker().id()) { + continue; + } + writable_partition_list.push_back(partition); + } + } + + if (writable_partition_list.empty()) { + SPDLOG_WARN("No writable partition is current available. Skip updating publish table for topic={}", topic_); + return; + } + + { + absl::MutexLock lk(&partition_list_mtx_); + partition_list_.swap(writable_partition_list); + } +} + +void TopicPublishInfo::topicRouteData(TopicRouteDataPtr topic_route_data) { + + SPDLOG_DEBUG("Update publish info according to renewed route data of topic={}", topic_); + { topic_route_data_ = std::move(topic_route_data); } + updatePublishInfo(); +} + +std::vector TopicPublishInfo::getMessageQueueList() { + std::vector message_queue_list; + { + absl::MutexLock lock(&partition_list_mtx_); + for (const auto& partition : partition_list_) { + if (Permission::READ == partition.permission() || Permission::NONE == partition.permission()) { + continue; + } + + MQMessageQueue message_queue(partition.asMessageQueue()); + if (message_queue.serviceAddress().empty()) { + SPDLOG_WARN("Failed to resolve service address for {}", message_queue.simpleName()); + continue; + } + message_queue_list.emplace_back(message_queue); + } + + std::sort(message_queue_list.begin(), message_queue_list.end()); + + return message_queue_list; + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Assignment.h b/src/main/cpp/client/include/Assignment.h new file mode 100644 index 000000000..eea6bd0d6 --- /dev/null +++ b/src/main/cpp/client/include/Assignment.h @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "ConsumeMessageType.h" +#include "rocketmq/MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Assignment { +public: + Assignment(MQMessageQueue message_queue) : message_queue_(std::move(message_queue)) { + } + + bool operator==(const Assignment& rhs) const { + if (this == &rhs) { + return true; + } + + return message_queue_ == rhs.message_queue_; + } + + bool operator<(const Assignment& rhs) const { + return message_queue_ < rhs.message_queue_; + } + + const MQMessageQueue& messageQueue() const { + return message_queue_; + } + +private: + MQMessageQueue message_queue_; +}; +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Broker.h b/src/main/cpp/client/include/Broker.h new file mode 100644 index 000000000..35d24b881 --- /dev/null +++ b/src/main/cpp/client/include/Broker.h @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "ServiceAddress.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Broker { +public: + Broker(std::string name, int id, ServiceAddress service_address) + : name_(std::move(name)), id_(id), service_address_(std::move(service_address)) { + } + + const std::string& name() const { + return name_; + } + + int32_t id() const { + return id_; + } + + explicit operator bool() const { + return service_address_.operator bool(); + } + + bool operator==(const Broker& other) const { + return name_ == other.name_; + } + + bool operator<(const Broker& other) const { + return name_ < other.name_; + } + + std::string serviceAddress() const { + return service_address_.address(); + } + +private: + std::string name_; + int32_t id_; + ServiceAddress service_address_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Client.h b/src/main/cpp/client/include/Client.h new file mode 100644 index 000000000..0ad6e3dc3 --- /dev/null +++ b/src/main/cpp/client/include/Client.h @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "absl/container/flat_hash_set.h" + +#include "ClientConfig.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Client : virtual public ClientConfig { +public: + ~Client() override = default; + + virtual void endpointsInUse(absl::flat_hash_set& endpoints) = 0; + + virtual void heartbeat() = 0; + + virtual bool active() = 0; + + virtual void onRemoteEndpointRemoval(const std::vector&) = 0; + + /** + * For endpoints that are marked as inactive due to one or multiple business + * operation failure, this function is to initiate health-check RPCs; Once the + * health-check passes, they are conceptually add back to serve further + * business workload. + */ + virtual void healthCheck() = 0; + + virtual void schedule(const std::string& task_name, const std::function& task, + std::chrono::milliseconds delay) = 0; + + virtual void notifyClientTermination() = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h new file mode 100644 index 000000000..97a1ec239 --- /dev/null +++ b/src/main/cpp/client/include/ClientConfig.h @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "absl/time/time.h" + +#include "rocketmq/CredentialsProvider.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientConfig { +public: + virtual ~ClientConfig() = default; + + virtual const std::string& region() const = 0; + + virtual const std::string& serviceName() const = 0; + + virtual const std::string& resourceNamespace() const = 0; + + virtual CredentialsProviderPtr credentialsProvider() = 0; + + virtual const std::string& tenantId() const = 0; + + virtual absl::Duration getIoTimeout() const = 0; + + virtual absl::Duration getLongPollingTimeout() const = 0; + + virtual const std::string& getGroupName() const = 0; + + virtual std::string clientId() const = 0; + + virtual bool isTracingEnabled() const = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientConfigImpl.h b/src/main/cpp/client/include/ClientConfigImpl.h new file mode 100644 index 000000000..116eb7e42 --- /dev/null +++ b/src/main/cpp/client/include/ClientConfigImpl.h @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/time/time.h" + +#include "ClientConfig.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientConfigImpl : virtual public ClientConfig { +public: + explicit ClientConfigImpl(absl::string_view group_name); + + ~ClientConfigImpl() override = default; + + const std::string& resourceNamespace() const override { + return resource_namespace_; + } + + void resourceNamespace(absl::string_view resource_namespace) { + resource_namespace_ = std::string(resource_namespace.data(), resource_namespace.length()); + } + + std::string clientId() const override; + + const std::string& getInstanceName() const; + + void setInstanceName(std::string instance_name); + + const std::string& getGroupName() const override; + void setGroupName(std::string group_name); + + const std::string& getUnitName() const { + return unit_name_; + } + void setUnitName(std::string unit_name) { + unit_name_ = std::move(unit_name); + } + + absl::Duration getIoTimeout() const override; + void setIoTimeout(absl::Duration timeout); + + absl::Duration getLongPollingTimeout() const override { + return long_polling_timeout_; + } + + void setLongPollingTimeout(absl::Duration timeout) { + long_polling_timeout_ = timeout; + } + + bool isTracingEnabled() const override { + return enable_tracing_.load(); + } + + void enableTracing(bool enabled) { + enable_tracing_.store(enabled); + } + + CredentialsProviderPtr credentialsProvider() override; + void setCredentialsProvider(CredentialsProviderPtr credentials_provider); + + void serviceName(std::string service_name) { + service_name_ = std::move(service_name); + } + const std::string& serviceName() const override { + return service_name_; + } + + void region(std::string region) { + region_ = std::move(region); + } + const std::string& region() const override { + return region_; + } + + void tenantId(std::string tenant_id) { + tenant_id_ = std::move(tenant_id); + } + const std::string& tenantId() const override { + return tenant_id_; + } + + static const char* CLIENT_VERSION; + +protected: + /** + * Name of the service. + */ + std::string service_name_{"ONS"}; + + /** + * Region of the service to connect to. + */ + std::string region_; + + /** + * RocketMQ instance namespace, in which topic, consumer group and any other + * abstract resources remain unique. + */ + std::string resource_namespace_; + + /** + * Tenant identifier. + */ + std::string tenant_id_; + + std::string instance_name_{steadyName()}; + + std::string group_name_; + + std::string unit_name_; + + CredentialsProviderPtr credentials_provider_; + + absl::Duration io_timeout_; + + absl::Duration long_polling_timeout_; + + std::atomic enable_tracing_{true}; + + static std::string steadyName(); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h new file mode 100644 index 000000000..dceace394 --- /dev/null +++ b/src/main/cpp/client/include/ClientManager.h @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "Client.h" +#include "ReceiveMessageCallback.h" +#include "RpcClient.h" +#include "Scheduler.h" +#include "TopAddressing.h" +#include "TopicRouteData.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +using Metadata = absl::flat_hash_map; + +class ClientManager { +public: + virtual ~ClientManager() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual SchedulerSharedPtr getScheduler() = 0; + + virtual std::shared_ptr createChannel(const std::string& target_host) = 0; + + virtual void resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) = 0; + + virtual void heartbeat(const std::string& target_host, const Metadata& metadata, const HeartbeatRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) = 0; + + virtual void pollCommand(const std::string& target, const Metadata& metadata, const PollCommandRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) = 0; + + virtual bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) = 0; + + virtual void ack(const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& cb) = 0; + + virtual void nack(const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& callback) = 0; + + virtual void forwardMessageToDeadLetterQueue( + const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) = 0; + + virtual void endTransaction(const std::string& target_host, const Metadata& metadata, + const EndTransactionRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) = 0; + + virtual void queryOffset(const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) = 0; + + virtual void + healthCheck(const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) = 0; + + virtual void addClientObserver(std::weak_ptr client) = 0; + + virtual void + queryAssignment(const std::string& target, const Metadata& metadata, const QueryAssignmentRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) = 0; + + virtual void receiveMessage(const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, + std::chrono::milliseconds timeout, const std::shared_ptr& cb) = 0; + + virtual bool send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) = 0; + + virtual void pullMessage(const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) = 0; + + virtual std::error_code notifyClientTermination(const std::string& target_host, const Metadata& metadata, + const NotifyClientTerminationRequest& request, + std::chrono::milliseconds timeout) = 0; + + virtual std::error_code reportThreadStackTrace(const std::string& target_host, const Metadata& metadata, + const ReportThreadStackTraceRequest& request, + std::chrono::milliseconds timeout) = 0; + + virtual std::error_code reportMessageConsumptionResult(const std::string& target_host, const Metadata& metadata, + const ReportMessageConsumptionResultRequest& request, + std::chrono::milliseconds timeout) = 0; + + virtual State state() const = 0; + + virtual void submit(std::function task) = 0; +}; + +using ClientManagerPtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientManagerFactory.h b/src/main/cpp/client/include/ClientManagerFactory.h new file mode 100644 index 000000000..ec6488de7 --- /dev/null +++ b/src/main/cpp/client/include/ClientManagerFactory.h @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" + +#include "ClientConfig.h" +#include "ClientManager.h" +#include "rocketmq/AdminServer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientManagerFactory { +public: + static ClientManagerFactory& getInstance(); + + ClientManagerPtr getClientManager(const ClientConfig& client_config) LOCKS_EXCLUDED(client_manager_table_mtx_); + + // For test purpose only + void addClientManager(const std::string& resource_namespace, const ClientManagerPtr& client_manager) + LOCKS_EXCLUDED(client_manager_table_mtx_); + +private: + ClientManagerFactory(); + + virtual ~ClientManagerFactory(); + + /** + * Client Id --> Client Instance + */ + absl::flat_hash_map> + client_manager_table_ GUARDED_BY(client_manager_table_mtx_); + absl::Mutex client_manager_table_mtx_; // protects client_manager_table_ + + rocketmq::admin::AdminServer& admin_server_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h new file mode 100644 index 000000000..5f6335997 --- /dev/null +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -0,0 +1,274 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Scheduler.h" +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +#include "Client.h" +#include "ClientManager.h" +#include "HeartbeatDataCallback.h" +#include "Histogram.h" +#include "InvocationContext.h" +#include "OrphanTransactionCallback.h" +#include "ReceiveMessageCallback.h" +#include "RpcClient.h" +#include "RpcClientImpl.h" +#include "SchedulerImpl.h" +#include "SendMessageContext.h" +#include "ThreadPoolImpl.h" +#include "TopAddressing.h" +#include "TopicRouteChangeCallback.h" +#include "TopicRouteData.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientManagerImpl : virtual public ClientManager, public std::enable_shared_from_this { +public: + /** + * @brief Construct a new Client Manager Impl object + * TODO: Make it protected such that instantiating it through ClientManagerFactory only, achieving Singleton + * effectively. + * @param resource_namespace Abstract resource namespace, in which this client manager lives. + */ + explicit ClientManagerImpl(std::string resource_namespace); + + ~ClientManagerImpl() override; + + void start() override; + + void shutdown() override LOCKS_EXCLUDED(rpc_clients_mtx_); + + static void assignLabels(Histogram& histogram); + + std::shared_ptr createChannel(const std::string& target_host) override; + + /** + * Resolve route data from name server for the given topic. + * + * @param target_host Name server host address; + * @param metadata Request headers; + * @param request Query route entries request. + * @param timeout RPC timeout. + * @param cb Callback to execute once the request/response completes. + */ + void resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override + LOCKS_EXCLUDED(rpc_clients_mtx_); + + void doHealthCheck() LOCKS_EXCLUDED(clients_mtx_); + + /** + * If inactive RPC clients refer to remote hosts that are absent from topic_route_table_, we need to purge them + * immediately. + */ + std::vector cleanOfflineRpcClients() LOCKS_EXCLUDED(clients_mtx_, rpc_clients_mtx_); + + /** + * Execute health-check on behalf of the client. + */ + void healthCheck(const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) + override LOCKS_EXCLUDED(rpc_clients_mtx_); + + bool send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); + + /** + * Get a RpcClient according to the given target hosts, which follows scheme specified + * https://github.com/grpc/grpc/blob/master/doc/naming.md + * + * Note that a channel in gRPC is composted of one or more sub-channels. Every sub-channel represents a solid TCP + * connection. gRPC supports a number of configurable load-balancing policy with "pick-first" as the default option. + * Requests are distributed + * @param target_host + * @param need_heartbeat + * @return + */ + RpcClientSharedPtr getRpcClient(const std::string& target_host, bool need_heartbeat = true) + LOCKS_EXCLUDED(rpc_clients_mtx_); + + static SendResult processSendResponse(const MQMessageQueue& message_queue, const SendMessageResponse& response); + + // only for test + void addRpcClient(const std::string& target_host, const RpcClientSharedPtr& client) LOCKS_EXCLUDED(rpc_clients_mtx_); + + // Test purpose only + void cleanRpcClients() LOCKS_EXCLUDED(rpc_clients_mtx_); + + void addClientObserver(std::weak_ptr client) override; + + void queryAssignment(const std::string& target, const Metadata& metadata, const QueryAssignmentRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override; + + void receiveMessage(const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, + std::chrono::milliseconds timeout, const std::shared_ptr& cb) override + LOCKS_EXCLUDED(rpc_clients_mtx_); + + /** + * Translate protobuf message struct to domain model. + * + * @param item + * @param message_ext + * @return true if the translation succeeded; false if something wrong happens, including checksum verification, etc. + */ + bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) override; + + SchedulerSharedPtr getScheduler() override; + + /** + * Ack message asynchronously. + * @param target_host Target broker host address. + * @param request Ack message request. + */ + void ack(const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& cb) override; + + void nack(const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& callback) override; + + void forwardMessageToDeadLetterQueue( + const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) override; + + /** + * End a transaction asynchronously. + * + * Callback conforms the following method signature: + * void callable(bool rpc_ok, const EndTransactionResponse& response) + * + * if rpc_ok is false, the end transaction request may never reach the server and consequently no need to inspect the + * response. If rpc_ok is true, response should be further inspected to determine business tier code and logic. + * @param target_host + * @param metadata + * @param request + * @param timeout + * @param cb + */ + void endTransaction(const std::string& target_host, const Metadata& metadata, const EndTransactionRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override; + + void pollCommand(const std::string& target, const Metadata& metadata, const PollCommandRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) override; + + void queryOffset(const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override; + + void pullMessage(const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override; + + std::error_code notifyClientTermination(const std::string& target_host, const Metadata& metadata, + const NotifyClientTerminationRequest& request, + std::chrono::milliseconds timeout) override; + + std::error_code reportThreadStackTrace(const std::string& target_host, const Metadata& metadata, + const ReportThreadStackTraceRequest& request, + std::chrono::milliseconds timeout) override; + + std::error_code reportMessageConsumptionResult(const std::string& target_host, const Metadata& metadata, + const ReportMessageConsumptionResultRequest& request, + std::chrono::milliseconds timeout) override; + + void trace(bool trace) { + trace_ = trace; + } + + void heartbeat(const std::string& target_host, const Metadata& metadata, const HeartbeatRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override; + + State state() const override; + + void submit(std::function task) override; + +private: + void doHeartbeat(); + + void pollCompletionQueue(); + + void logStats(); + + SchedulerSharedPtr scheduler_; + + static const char* HEARTBEAT_TASK_NAME; + static const char* STATS_TASK_NAME; + static const char* HEALTH_CHECK_TASK_NAME; + + std::string resource_namespace_; + + std::atomic state_; + + std::vector> clients_ GUARDED_BY(clients_mtx_); + absl::Mutex clients_mtx_; + + absl::flat_hash_map> rpc_clients_ GUARDED_BY(rpc_clients_mtx_); + absl::Mutex rpc_clients_mtx_; // protects rpc_clients_ + + std::uint32_t heartbeat_task_id_{0}; + std::uint32_t health_check_task_id_{0}; + std::uint32_t stats_task_id_{0}; + + std::shared_ptr completion_queue_; + std::unique_ptr callback_thread_pool_; + + std::thread completion_queue_thread_; + + Histogram latency_histogram_; + + absl::flat_hash_set exporter_endpoint_set_ GUARDED_BY(exporter_endpoint_set_mtx_); + absl::Mutex exporter_endpoint_set_mtx_; + + /** + * Tenant-id. Each user shall have one unique identifier. + */ + std::string tenant_id_; + + std::string service_name_{"MQ"}; + + /** + * TLS configuration + */ + std::shared_ptr channel_credential_; + grpc::ChannelArguments channel_arguments_; + + bool trace_{false}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ConsumeMessageType.h b/src/main/cpp/client/include/ConsumeMessageType.h new file mode 100644 index 000000000..9af69ba8f --- /dev/null +++ b/src/main/cpp/client/include/ConsumeMessageType.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class ConsumeMessageType : int8_t +{ + ACTIVE = 0, + PASSIVE = 1, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/HeartbeatDataCallback.h b/src/main/cpp/client/include/HeartbeatDataCallback.h new file mode 100644 index 000000000..f4466e2b0 --- /dev/null +++ b/src/main/cpp/client/include/HeartbeatDataCallback.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "RpcClient.h" + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * This callback will be invoked to collect and prepare heartbeat data, which will be sent to brokers. + */ +class HeartbeatDataCallback { +public: + virtual void onHeartbeatDataCallback(HeartbeatRequest& request) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/InterceptorContinuation.h b/src/main/cpp/client/include/InterceptorContinuation.h new file mode 100644 index 000000000..07c64335f --- /dev/null +++ b/src/main/cpp/client/include/InterceptorContinuation.h @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "grpcpp/impl/codegen/interceptor.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class InterceptorContinuation { +public: + explicit InterceptorContinuation(grpc::experimental::InterceptorBatchMethods* methods) : methods_(methods) { + } + + ~InterceptorContinuation() { + if (!hijacked_) { + methods_->Proceed(); + } + } + + void hijack() { + hijacked_ = true; + } + +private: + grpc::experimental::InterceptorBatchMethods* methods_; + bool hijacked_{false}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/LogInterceptor.h b/src/main/cpp/client/include/LogInterceptor.h new file mode 100644 index 000000000..0bec16162 --- /dev/null +++ b/src/main/cpp/client/include/LogInterceptor.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "grpcpp/impl/codegen/client_interceptor.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class LogInterceptor : public grpc::experimental::Interceptor { +public: + explicit LogInterceptor(grpc::experimental::ClientRpcInfo* client_rpc_info) : client_rpc_info_(client_rpc_info) { + } + + void Intercept(grpc::experimental::InterceptorBatchMethods* methods) override; + +private: + grpc::experimental::ClientRpcInfo* client_rpc_info_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/LogInterceptorFactory.h b/src/main/cpp/client/include/LogInterceptorFactory.h new file mode 100644 index 000000000..b18ab10e5 --- /dev/null +++ b/src/main/cpp/client/include/LogInterceptorFactory.h @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "grpcpp/impl/codegen/client_interceptor.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class LogInterceptorFactory : public grpc::experimental::ClientInterceptorFactoryInterface { +public: + grpc::experimental::Interceptor* CreateClientInterceptor(grpc::experimental::ClientRpcInfo* info) override; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/OpenSSLCompatible.h b/src/main/cpp/client/include/OpenSSLCompatible.h new file mode 100644 index 000000000..cca778726 --- /dev/null +++ b/src/main/cpp/client/include/OpenSSLCompatible.h @@ -0,0 +1,390 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +/** + * This is to make our code work with openssl both 1.0.x and 1.1.y + * https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes#Compatibility_Layer + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#include +#include + +static void* OPENSSL_zalloc(size_t num) { + void* ret = OPENSSL_malloc(num); + + if (ret != NULL) + memset(ret, 0, num); + return ret; +} + +int RSA_set0_key(RSA* r, BIGNUM* n, BIGNUM* e, BIGNUM* d) { + /* If the fields n and e in r are NULL, the corresponding input + * parameters MUST be non-NULL for n and e. d may be + * left NULL (in case only the public key is used). + */ + if ((r->n == NULL && n == NULL) || (r->e == NULL && e == NULL)) + return 0; + + if (n != NULL) { + BN_free(r->n); + r->n = n; + } + if (e != NULL) { + BN_free(r->e); + r->e = e; + } + if (d != NULL) { + BN_free(r->d); + r->d = d; + } + + return 1; +} + +int RSA_set0_factors(RSA* r, BIGNUM* p, BIGNUM* q) { + /* If the fields p and q in r are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((r->p == NULL && p == NULL) || (r->q == NULL && q == NULL)) + return 0; + + if (p != NULL) { + BN_free(r->p); + r->p = p; + } + if (q != NULL) { + BN_free(r->q); + r->q = q; + } + + return 1; +} + +int RSA_set0_crt_params(RSA* r, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp) { + /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((r->dmp1 == NULL && dmp1 == NULL) || (r->dmq1 == NULL && dmq1 == NULL) || (r->iqmp == NULL && iqmp == NULL)) + return 0; + + if (dmp1 != NULL) { + BN_free(r->dmp1); + r->dmp1 = dmp1; + } + if (dmq1 != NULL) { + BN_free(r->dmq1); + r->dmq1 = dmq1; + } + if (iqmp != NULL) { + BN_free(r->iqmp); + r->iqmp = iqmp; + } + + return 1; +} + +void RSA_get0_key(const RSA* r, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d) { + if (n != NULL) + *n = r->n; + if (e != NULL) + *e = r->e; + if (d != NULL) + *d = r->d; +} + +void RSA_get0_factors(const RSA* r, const BIGNUM** p, const BIGNUM** q) { + if (p != NULL) + *p = r->p; + if (q != NULL) + *q = r->q; +} + +void RSA_get0_crt_params(const RSA* r, const BIGNUM** dmp1, const BIGNUM** dmq1, const BIGNUM** iqmp) { + if (dmp1 != NULL) + *dmp1 = r->dmp1; + if (dmq1 != NULL) + *dmq1 = r->dmq1; + if (iqmp != NULL) + *iqmp = r->iqmp; +} + +void DSA_get0_pqg(const DSA* d, const BIGNUM** p, const BIGNUM** q, const BIGNUM** g) { + if (p != NULL) + *p = d->p; + if (q != NULL) + *q = d->q; + if (g != NULL) + *g = d->g; +} + +int DSA_set0_pqg(DSA* d, BIGNUM* p, BIGNUM* q, BIGNUM* g) { + /* If the fields p, q and g in d are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((d->p == NULL && p == NULL) || (d->q == NULL && q == NULL) || (d->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free(d->p); + d->p = p; + } + if (q != NULL) { + BN_free(d->q); + d->q = q; + } + if (g != NULL) { + BN_free(d->g); + d->g = g; + } + + return 1; +} + +void DSA_get0_key(const DSA* d, const BIGNUM** pub_key, const BIGNUM** priv_key) { + if (pub_key != NULL) + *pub_key = d->pub_key; + if (priv_key != NULL) + *priv_key = d->priv_key; +} + +int DSA_set0_key(DSA* d, BIGNUM* pub_key, BIGNUM* priv_key) { + /* If the field pub_key in d is NULL, the corresponding input + * parameters MUST be non-NULL. The priv_key field may + * be left NULL. + */ + if (d->pub_key == NULL && pub_key == NULL) + return 0; + + if (pub_key != NULL) { + BN_free(d->pub_key); + d->pub_key = pub_key; + } + if (priv_key != NULL) { + BN_free(d->priv_key); + d->priv_key = priv_key; + } + + return 1; +} + +void DSA_SIG_get0(const DSA_SIG* sig, const BIGNUM** pr, const BIGNUM** ps) { + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} + +int DSA_SIG_set0(DSA_SIG* sig, BIGNUM* r, BIGNUM* s) { + if (r == NULL || s == NULL) + return 0; + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + return 1; +} + +void ECDSA_SIG_get0(const ECDSA_SIG* sig, const BIGNUM** pr, const BIGNUM** ps) { + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} + +int ECDSA_SIG_set0(ECDSA_SIG* sig, BIGNUM* r, BIGNUM* s) { + if (r == NULL || s == NULL) + return 0; + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + return 1; +} + +void DH_get0_pqg(const DH* dh, const BIGNUM** p, const BIGNUM** q, const BIGNUM** g) { + if (p != NULL) + *p = dh->p; + if (q != NULL) + *q = dh->q; + if (g != NULL) + *g = dh->g; +} + +int DH_set0_pqg(DH* dh, BIGNUM* p, BIGNUM* q, BIGNUM* g) { + /* If the fields p and g in d are NULL, the corresponding input + * parameters MUST be non-NULL. q may remain NULL. + */ + if ((dh->p == NULL && p == NULL) || (dh->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free(dh->p); + dh->p = p; + } + if (q != NULL) { + BN_free(dh->q); + dh->q = q; + } + if (g != NULL) { + BN_free(dh->g); + dh->g = g; + } + + if (q != NULL) { + dh->length = BN_num_bits(q); + } + + return 1; +} + +void DH_get0_key(const DH* dh, const BIGNUM** pub_key, const BIGNUM** priv_key) { + if (pub_key != NULL) + *pub_key = dh->pub_key; + if (priv_key != NULL) + *priv_key = dh->priv_key; +} + +int DH_set0_key(DH* dh, BIGNUM* pub_key, BIGNUM* priv_key) { + /* If the field pub_key in dh is NULL, the corresponding input + * parameters MUST be non-NULL. The priv_key field may + * be left NULL. + */ + if (dh->pub_key == NULL && pub_key == NULL) + return 0; + + if (pub_key != NULL) { + BN_free(dh->pub_key); + dh->pub_key = pub_key; + } + if (priv_key != NULL) { + BN_free(dh->priv_key); + dh->priv_key = priv_key; + } + + return 1; +} + +int DH_set_length(DH* dh, long length) { + dh->length = length; + return 1; +} + +const unsigned char* EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX* ctx) { + return ctx->iv; +} + +unsigned char* EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX* ctx) { + return ctx->iv; +} + +EVP_MD_CTX* EVP_MD_CTX_new(void) { + return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); +} + +void EVP_MD_CTX_free(EVP_MD_CTX* ctx) { + EVP_MD_CTX_cleanup(ctx); + OPENSSL_free(ctx); +} + +RSA_METHOD* RSA_meth_dup(const RSA_METHOD* meth) { + RSA_METHOD* ret; + + ret = OPENSSL_malloc(sizeof(RSA_METHOD)); + + if (ret != NULL) { + memcpy(ret, meth, sizeof(*meth)); + ret->name = OPENSSL_strdup(meth->name); + if (ret->name == NULL) { + OPENSSL_free(ret); + return NULL; + } + } + + return ret; +} + +int RSA_meth_set1_name(RSA_METHOD* meth, const char* name) { + char* tmpname; + + tmpname = OPENSSL_strdup(name); + if (tmpname == NULL) { + return 0; + } + + OPENSSL_free((char*)meth->name); + meth->name = tmpname; + + return 1; +} + +int RSA_meth_set_priv_enc(RSA_METHOD* meth, int (*priv_enc)(int flen, const unsigned char* from, unsigned char* to, + RSA* rsa, int padding)) { + meth->rsa_priv_enc = priv_enc; + return 1; +} + +int RSA_meth_set_priv_dec(RSA_METHOD* meth, int (*priv_dec)(int flen, const unsigned char* from, unsigned char* to, + RSA* rsa, int padding)) { + meth->rsa_priv_dec = priv_dec; + return 1; +} + +int RSA_meth_set_finish(RSA_METHOD* meth, int (*finish)(RSA* rsa)) { + meth->finish = finish; + return 1; +} + +void RSA_meth_free(RSA_METHOD* meth) { + if (meth != NULL) { + OPENSSL_free((char*)meth->name); + OPENSSL_free(meth); + } +} + +int RSA_bits(const RSA* r) { + return (BN_num_bits(r->n)); +} + +RSA* EVP_PKEY_get0_RSA(EVP_PKEY* pkey) { + if (pkey->type != EVP_PKEY_RSA) { + return NULL; + } + return pkey->pkey.rsa; +} + +HMAC_CTX* HMAC_CTX_new(void) { + HMAC_CTX* ctx = OPENSSL_malloc(sizeof(*ctx)); + if (ctx != NULL) { + if (!HMAC_CTX_reset(ctx)) { + HMAC_CTX_free(ctx); + return NULL; + } + } + return ctx; +} + +void HMAC_CTX_free(HMAC_CTX* ctx) { + if (ctx != NULL) { + hmac_ctx_cleanup(ctx); + EVP_MD_CTX_free(ctx->i_ctx); + EVP_MD_CTX_free(ctx->o_ctx); + EVP_MD_CTX_free(ctx->md_ctx); + OPENSSL_free(ctx); + } +} +#endif \ No newline at end of file diff --git a/src/main/cpp/client/include/OrphanTransactionCallback.h b/src/main/cpp/client/include/OrphanTransactionCallback.h new file mode 100644 index 000000000..0e78db11c --- /dev/null +++ b/src/main/cpp/client/include/OrphanTransactionCallback.h @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "rocketmq/MQMessage.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class OrphanTransactionCallback { +public: + virtual ~OrphanTransactionCallback() = default; + + virtual void onOrphanTransaction(const MQMessage& message) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Partition.h b/src/main/cpp/client/include/Partition.h new file mode 100644 index 000000000..1d5d2658c --- /dev/null +++ b/src/main/cpp/client/include/Partition.h @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "Broker.h" +#include "Topic.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum Permission : int8_t +{ + NONE = 0, + READ = 1, + WRITE = 2, + READ_WRITE = 3 +}; + +class Partition { +public: + Partition(Topic topic, int32_t id, Permission permission, Broker broker) + : topic_(std::move(topic)), broker_(std::move(broker)), id_(id), permission_(permission) { + } + + const Topic& topic() const { + return topic_; + } + + int32_t id() const { + return id_; + } + + const Broker& broker() const { + return broker_; + } + + Permission permission() const { + return permission_; + } + + MQMessageQueue asMessageQueue() const { + MQMessageQueue message_queue(topic_.name(), broker_.name(), id_); + message_queue.serviceAddress(broker_.serviceAddress()); + return message_queue; + } + + bool operator==(const Partition& other) const { + return topic_ == other.topic_ && id_ == other.id_ && broker_ == other.broker_ && permission_ == other.permission_; + } + + bool operator<(const Partition& other) const { + if (topic_ < other.topic_) { + return true; + } else if (other.topic_ < topic_) { + return false; + } + + if (broker_ < other.broker_) { + return true; + } else if (other.broker_ < broker_) { + return false; + } + + return id_ < other.id_; + } + +private: + Topic topic_; + Broker broker_; + int32_t id_; + Permission permission_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ReceiveMessageCallback.h b/src/main/cpp/client/include/ReceiveMessageCallback.h new file mode 100644 index 000000000..10ddaf7ac --- /dev/null +++ b/src/main/cpp/client/include/ReceiveMessageCallback.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "ReceiveMessageResult.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/ErrorCode.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ReceiveMessageCallback : public AsyncCallback { +public: + ~ReceiveMessageCallback() override = default; + + virtual void onCompletion(const std::error_code& ec, const ReceiveMessageResult& result) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ReceiveMessageResult.h b/src/main/cpp/client/include/ReceiveMessageResult.h new file mode 100644 index 000000000..32efad98a --- /dev/null +++ b/src/main/cpp/client/include/ReceiveMessageResult.h @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "MixAll.h" +#include "absl/time/time.h" +#include "rocketmq/MQMessageExt.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct ReceiveMessageResult { + absl::Time pop_time; + absl::Duration invisible_time; + + std::vector messages; + + std::string source_host; + + std::int64_t min_offset{0}; + std::int64_t next_offset{0}; + std::int64_t max_offset{0}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h new file mode 100644 index 000000000..add9931c9 --- /dev/null +++ b/src/main/cpp/client/include/RpcClient.h @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/string_view.h" +#include "apache/rocketmq/v1/definition.grpc.pb.h" +#include "apache/rocketmq/v1/service.grpc.pb.h" +#include "apache/rocketmq/v1/service.pb.h" +#include "grpcpp/grpcpp.h" + +#include "InvocationContext.h" +#include "OrphanTransactionCallback.h" + +ROCKETMQ_NAMESPACE_BEGIN + +namespace rmq = apache::rocketmq::v1; + +using Channel = grpc::Channel; +using Status = grpc::Status; +using CompletionQueue = grpc::CompletionQueue; +using QueryRouteRequest = rmq::QueryRouteRequest; +using QueryRouteResponse = rmq::QueryRouteResponse; +using SendMessageRequest = rmq::SendMessageRequest; +using SendMessageResponse = rmq::SendMessageResponse; +using QueryAssignmentRequest = rmq::QueryAssignmentRequest; +using QueryAssignmentResponse = rmq::QueryAssignmentResponse; +using ReceiveMessageRequest = rmq::ReceiveMessageRequest; +using ReceiveMessageResponse = rmq::ReceiveMessageResponse; +using AckMessageRequest = rmq::AckMessageRequest; +using AckMessageResponse = rmq::AckMessageResponse; +using NackMessageRequest = rmq::NackMessageRequest; +using NackMessageResponse = rmq::NackMessageResponse; +using HeartbeatRequest = rmq::HeartbeatRequest; +using HeartbeatResponse = rmq::HeartbeatResponse; +using HealthCheckRequest = rmq::HealthCheckRequest; +using HealthCheckResponse = rmq::HealthCheckResponse; +using EndTransactionRequest = rmq::EndTransactionRequest; +using EndTransactionResponse = rmq::EndTransactionResponse; +using QueryOffsetRequest = rmq::QueryOffsetRequest; +using QueryOffsetResponse = rmq::QueryOffsetResponse; +using PullMessageRequest = rmq::PullMessageRequest; +using PullMessageResponse = rmq::PullMessageResponse; +using PollCommandRequest = rmq::PollCommandRequest; +using PollCommandResponse = rmq::PollCommandResponse; +using ReportThreadStackTraceRequest = rmq::ReportThreadStackTraceRequest; +using ReportThreadStackTraceResponse = rmq::ReportThreadStackTraceResponse; +using ReportMessageConsumptionResultRequest = rmq::ReportMessageConsumptionResultRequest; +using ReportMessageConsumptionResultResponse = rmq::ReportMessageConsumptionResultResponse; +using ForwardMessageToDeadLetterQueueRequest = rmq::ForwardMessageToDeadLetterQueueRequest; +using ForwardMessageToDeadLetterQueueResponse = rmq::ForwardMessageToDeadLetterQueueResponse; +using NotifyClientTerminationRequest = rmq::NotifyClientTerminationRequest; +using NotifyClientTerminationResponse = rmq::NotifyClientTerminationResponse; + +/** + * @brief A RpcClient represents a session between client and a remote broker. + * + */ +class RpcClient { +public: + RpcClient() = default; + + virtual ~RpcClient() = default; + + virtual void asyncQueryRoute(const QueryRouteRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncSend(const SendMessageRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncQueryAssignment(const QueryAssignmentRequest& request, + InvocationContext* invocation_context) = 0; + + virtual std::shared_ptr& completionQueue() = 0; + + virtual void asyncReceive(const ReceiveMessageRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncAck(const AckMessageRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncNack(const NackMessageRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncHeartbeat(const HeartbeatRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncHealthCheck(const HealthCheckRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncEndTransaction(const EndTransactionRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncPollCommand(const PollCommandRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncQueryOffset(const QueryOffsetRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncPull(const PullMessageRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncForwardMessageToDeadLetterQueue( + const ForwardMessageToDeadLetterQueueRequest& request, + InvocationContext* invocation_context) = 0; + + virtual grpc::Status reportThreadStackTrace(grpc::ClientContext* context, + const ReportThreadStackTraceRequest& request, + ReportThreadStackTraceResponse* response) = 0; + + virtual grpc::Status reportMessageConsumptionResult(grpc::ClientContext* context, + const ReportMessageConsumptionResultRequest& request, + ReportMessageConsumptionResultResponse* response) = 0; + + virtual grpc::Status notifyClientTermination(grpc::ClientContext* context, + const NotifyClientTerminationRequest& request, + NotifyClientTerminationResponse* response) = 0; + + /** + * Indicate if heartbeat is required. + * @return true if periodic heartbeat is required; false otherwise. + */ + virtual bool needHeartbeat() = 0; + + virtual void needHeartbeat(bool need_heartbeat) = 0; + + /** + * Indicate if current client connection state is OK or recoverable. + * + * @return true if underlying connection is OK or recoverable; false otherwise. + */ + virtual bool ok() const = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h new file mode 100644 index 000000000..80a397241 --- /dev/null +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "absl/container/flat_hash_map.h" + +#include "RpcClient.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class RpcClientImpl : public RpcClient, public std::enable_shared_from_this { +public: + RpcClientImpl(std::shared_ptr completion_queue, std::shared_ptr channel, + bool need_heartbeat = true) + : completion_queue_(std::move(completion_queue)), channel_(std::move(channel)), + stub_(rmq::MessagingService::NewStub(channel_)), need_heartbeat_(need_heartbeat) { + } + + RpcClientImpl(const RpcClientImpl&) = delete; + + RpcClientImpl& operator=(const RpcClientImpl&) = delete; + + ~RpcClientImpl() override = default; + + void asyncQueryRoute(const QueryRouteRequest& request, + InvocationContext* invocation_context) override; + + void asyncSend(const SendMessageRequest& request, + InvocationContext* invocation_context) override; + + void asyncQueryAssignment(const QueryAssignmentRequest& request, + InvocationContext* invocation_context) override; + + std::shared_ptr& completionQueue() override; + + void asyncReceive(const ReceiveMessageRequest& request, + InvocationContext* invocation_context) override; + + void asyncAck(const AckMessageRequest& request, InvocationContext* invocation_context) override; + + void asyncNack(const NackMessageRequest& request, + InvocationContext* invocation_context) override; + + void asyncHeartbeat(const HeartbeatRequest& request, + InvocationContext* invocation_context) override; + + void asyncHealthCheck(const HealthCheckRequest& request, + InvocationContext* invocation_context) override; + + void asyncEndTransaction(const EndTransactionRequest& request, + InvocationContext* invocation_context) override; + + void asyncPollCommand(const PollCommandRequest& request, + InvocationContext* invocation_context) override; + + void asyncQueryOffset(const QueryOffsetRequest& request, + InvocationContext* invocation_context) override; + + void asyncPull(const PullMessageRequest& request, + InvocationContext* invocation_context) override; + + void asyncForwardMessageToDeadLetterQueue( + const ForwardMessageToDeadLetterQueueRequest& request, + InvocationContext* invocation_context) override; + + grpc::Status reportThreadStackTrace(grpc::ClientContext* context, const ReportThreadStackTraceRequest& request, + ReportThreadStackTraceResponse* response) override; + + grpc::Status reportMessageConsumptionResult(grpc::ClientContext* context, + const ReportMessageConsumptionResultRequest& request, + ReportMessageConsumptionResultResponse* response) override; + + grpc::Status notifyClientTermination(grpc::ClientContext* context, const NotifyClientTerminationRequest& request, + NotifyClientTerminationResponse* response) override; + + bool needHeartbeat() override; + + void needHeartbeat(bool need_heartbeat) override; + + bool ok() const override; + +private: + static void addMetadata(grpc::ClientContext& context, const absl::flat_hash_map& metadata); + + std::shared_ptr completion_queue_; + std::shared_ptr channel_; + std::unique_ptr stub_; + std::chrono::milliseconds connect_timeout_{3000}; + bool need_heartbeat_{true}; +}; + +using RpcClientSharedPtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/SendMessageContext.h b/src/main/cpp/client/include/SendMessageContext.h new file mode 100644 index 000000000..fa1660921 --- /dev/null +++ b/src/main/cpp/client/include/SendMessageContext.h @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN +class SendMessageContext { +public: + const std::string& getProducerGroup() const { + return producer_group_; + }; + void setProducerGroup(const std::string& producer_group) { + this->producer_group_ = producer_group; + }; + const MQMessage& getMessage() const { + return message_; + } + void setMessage(const MQMessage& message) { + this->message_ = message; + } + const MQMessageQueue& getMessageQueue() const { + return message_queue_; + } + void setMessageQueue(const MQMessageQueue& message_queue) { + this->message_queue_ = message_queue; + } + const std::string& getBornHost() const { + return born_host_; + } + void setBornHost(const std::string& born_host) { + this->born_host_ = born_host; + } + const std::string& getMessageId() const { + return message_id_; + } + void setMessageId(const std::string& message_id) { + this->message_id_ = message_id; + } + long long int getQueueOffset() const { + return queue_offset_; + } + void setQueueOffset(long long queue_offset) { + this->queue_offset_ = queue_offset; + } + +private: + std::string producer_group_; + MQMessage message_; + MQMessageQueue message_queue_; + std::string born_host_; + std::string message_id_; + long long queue_offset_{-1}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ServiceAddress.h b/src/main/cpp/client/include/ServiceAddress.h new file mode 100644 index 000000000..b5b8c7fe7 --- /dev/null +++ b/src/main/cpp/client/include/ServiceAddress.h @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "absl/strings/string_view.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum AddressScheme : int8_t +{ + IPv4 = 0, + IPv6 = 1, + DOMAIN_NAME = 2, +}; + +class Address { +public: + Address(absl::string_view host, int32_t port) : host_(host.data(), host.length()), port_(port) { + } + + bool operator==(const Address& other) const { + return host_ == other.host_ && port_ == other.port_; + } + + bool operator<(const Address& other) const { + if (host_ < other.host_) { + return true; + } else if (host_ > other.host_) { + return false; + } + + if (port_ < other.port_) { + return true; + } else if (port_ > other.port_) { + return false; + } + + return false; + } + + const std::string& host() const { + return host_; + } + + int32_t port() const { + return port_; + } + +private: + std::string host_; + int32_t port_; +}; + +/** + * Thread Safety Analysis: + * Once initialized, this class remains immutable. Thus it is threads-safe. + */ +class ServiceAddress { +public: + ServiceAddress(AddressScheme scheme, std::vector
addresses) + : scheme_(scheme), addresses_(std::move(addresses)) { + std::sort(addresses_.begin(), addresses_.end()); + } + + AddressScheme scheme() const { + return scheme_; + } + + explicit operator bool() const { + return !addresses_.empty(); + } + + std::string address() const { + std::string result; + switch (scheme_) { + case AddressScheme::IPv4: + result.append("ipv4:"); + break; + case AddressScheme::IPv6: + result.append("ipv6:"); + break; + case AddressScheme::DOMAIN_NAME: + result.append("dns:"); + break; + } + + bool first = true; + for (const auto& item : addresses_) { + if (!first) { + result.append(","); + } else { + first = false; + } + result.append(item.host()).append(":").append(std::to_string(item.port())); + } + return result; + } + +private: + AddressScheme scheme_; + std::vector
addresses_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Signature.h b/src/main/cpp/client/include/Signature.h new file mode 100644 index 000000000..b10581c0c --- /dev/null +++ b/src/main/cpp/client/include/Signature.h @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "absl/container/flat_hash_map.h" + +#include "ClientConfig.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Signature { +public: + static void sign(ClientConfig* client, absl::flat_hash_map& metadata); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TlsHelper.h b/src/main/cpp/client/include/TlsHelper.h new file mode 100644 index 000000000..b5a2f510c --- /dev/null +++ b/src/main/cpp/client/include/TlsHelper.h @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "rocketmq/RocketMQ.h" +#include +#include + +#include "grpcpp/security/tls_credentials_options.h" +#include +#include +#include + +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TlsHelper { + +public: + static std::string sign(const std::string& access_secret, const std::string& content); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Topic.h b/src/main/cpp/client/include/Topic.h new file mode 100644 index 000000000..258abab55 --- /dev/null +++ b/src/main/cpp/client/include/Topic.h @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Topic { +public: + Topic(std::string resource_namespace, std::string name) + : resource_namespace_(std::move(resource_namespace)), name_(std::move(name)) { + } + + const std::string& resourceNamespace() const { + return resource_namespace_; + } + + const std::string& name() const { + return name_; + } + + bool operator==(const Topic& other) const { + return resource_namespace_ == other.resource_namespace_ && name_ == other.name_; + } + + bool operator<(const Topic& other) const { + if (resource_namespace_ < other.resource_namespace_) { + return true; + } else if (resource_namespace_ > other.resource_namespace_) { + return false; + } + + if (name_ < other.name_) { + return true; + } else if (name_ > other.name_) { + return false; + } + return false; + } + +private: + std::string resource_namespace_; + std::string name_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TopicAssignmentInfo.h b/src/main/cpp/client/include/TopicAssignmentInfo.h new file mode 100644 index 000000000..7d522fd96 --- /dev/null +++ b/src/main/cpp/client/include/TopicAssignmentInfo.h @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "Assignment.h" +#include "RpcClient.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TopicAssignment { +public: + explicit TopicAssignment(std::vector&& assignments) : assignment_list_(std::move(assignments)) { + std::sort(assignment_list_.begin(), assignment_list_.end()); + } + + explicit TopicAssignment(const QueryAssignmentResponse& response); + + ~TopicAssignment() = default; + + const std::vector& assignmentList() const { + return assignment_list_; + } + + bool operator==(const TopicAssignment& rhs) const { + return assignment_list_ == rhs.assignment_list_; + } + + bool operator!=(const TopicAssignment& rhs) const { + return assignment_list_ != rhs.assignment_list_; + } + + const std::string& debugString() const { + return debug_string_; + } + + static unsigned int getAndIncreaseQueryWhichBroker() { + return ++query_which_broker_; + } + +private: + /** + * Once it is set, it will be immutable. + */ + std::vector assignment_list_; + + std::string debug_string_; + + thread_local static uint32_t query_which_broker_; +}; + +using TopicAssignmentPtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TopicPublishInfo.h b/src/main/cpp/client/include/TopicPublishInfo.h new file mode 100644 index 000000000..b1d22ddca --- /dev/null +++ b/src/main/cpp/client/include/TopicPublishInfo.h @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +#include "TopicRouteData.h" +#include "rocketmq/MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TopicPublishInfo { +public: + TopicPublishInfo(absl::string_view topic, TopicRouteDataPtr topic_route_data); + + /** + * @param message_queue Reference to target message queue. + * @return true if manage to select one; false otherwise. + */ + bool selectOneMessageQueue(MQMessageQueue& message_queue) LOCKS_EXCLUDED(partition_list_mtx_); + + bool selectOneActiveMessageQueue(absl::flat_hash_set& isolated, MQMessageQueue& message_queue) + LOCKS_EXCLUDED(partition_list_mtx_); + + bool takeMessageQueues(absl::flat_hash_set& isolated, std::vector& candidates, + uint32_t count) LOCKS_EXCLUDED(partition_list_mtx_); + + void topicRouteData(TopicRouteDataPtr topic_route_data); + + /** + * Expose partition list in perspective of message queue list. + * + * @return + */ + std::vector getMessageQueueList() LOCKS_EXCLUDED(partition_list_mtx_); + +private: + std::vector partition_list_ GUARDED_BY(partition_list_mtx_); + absl::Mutex partition_list_mtx_; // protects message_queue_list_ + + std::string topic_; + TopicRouteDataPtr topic_route_data_; + + void updatePublishInfo(); + + thread_local static uint32_t send_which_queue_; +}; + +using TopicPublishInfoPtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TopicRouteChangeCallback.h b/src/main/cpp/client/include/TopicRouteChangeCallback.h new file mode 100644 index 000000000..0490808f7 --- /dev/null +++ b/src/main/cpp/client/include/TopicRouteChangeCallback.h @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "absl/strings/string_view.h" + +#include "TopicRouteData.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TopicRouteChangeCallback { +public: + virtual void onTopicRouteChange(absl::string_view topic, const TopicRouteDataPtr& topic_route_data) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TopicRouteData.h b/src/main/cpp/client/include/TopicRouteData.h new file mode 100644 index 000000000..f151d0058 --- /dev/null +++ b/src/main/cpp/client/include/TopicRouteData.h @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "Partition.h" +#include "RpcClient.h" + +ROCKETMQ_NAMESPACE_BEGIN + +namespace rmq = apache::rocketmq::v1; + +/** + * Thread Safety: This class is immutable and thus effectively thread safe. + */ +class TopicRouteData { +public: + TopicRouteData(std::vector partitions, std::string debug_string) + : partitions_(std::move(partitions)), debug_string_(std::move(debug_string)) { + std::sort(partitions_.begin(), partitions_.end()); + } + + const std::vector& partitions() const { + return partitions_; + } + + const std::string& debugString() const { + return debug_string_; + } + + bool operator==(const TopicRouteData& other) const { + return partitions_ == other.partitions_; + } + + bool operator!=(const TopicRouteData& other) const { + return !this->operator==(other); + } + +private: + std::vector partitions_; + std::string debug_string_; +}; + +using TopicRouteDataPtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/BUILD.bazel b/src/main/cpp/client/mocks/BUILD.bazel new file mode 100644 index 000000000..6da613f6f --- /dev/null +++ b/src/main/cpp/client/mocks/BUILD.bazel @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "client_mocks", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/client/mocks/include", + deps = [ + "//src/main/cpp/client:client_library", + "//external:gtest", + ], +) \ No newline at end of file diff --git a/src/main/cpp/client/mocks/RpcClientMock.cpp b/src/main/cpp/client/mocks/RpcClientMock.cpp new file mode 100644 index 000000000..f2fc94ac9 --- /dev/null +++ b/src/main/cpp/client/mocks/RpcClientMock.cpp @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "RpcClientMock.h" + +using namespace testing; + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * TODO: Add commonly used mock operation for RpcClient + */ +RpcClientMock::RpcClientMock() : completion_queue_(std::make_shared()) { + ON_CALL(*this, completionQueue()).WillByDefault(ReturnRef(completion_queue_)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientConfigMock.h b/src/main/cpp/client/mocks/include/ClientConfigMock.h new file mode 100644 index 000000000..115e0bf0c --- /dev/null +++ b/src/main/cpp/client/mocks/include/ClientConfigMock.h @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "ClientConfig.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientConfigMock : virtual public ClientConfig { +public: + ~ClientConfigMock() override = default; + MOCK_METHOD(const std::string&, region, (), (const override)); + MOCK_METHOD(const std::string&, serviceName, (), (const override)); + MOCK_METHOD(const std::string&, resourceNamespace, (), (const overide)); + MOCK_METHOD(CredentialsProviderPtr, credentialsProvider, (), (override)); + MOCK_METHOD(const std::string&, tenantId, (), (const override)); + MOCK_METHOD(absl::Duration, getIoTimeout, (), (const override)); + MOCK_METHOD(absl::Duration, getLongPollingTimeout, (), (const override)); + MOCK_METHOD(const std::string&, getGroupName, (), (const override)); + MOCK_METHOD(std::string, clientId, (), (const override)); + MOCK_METHOD(bool, isTracingEnabled, (), (const override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h new file mode 100644 index 000000000..01d6d41b3 --- /dev/null +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "ClientManager.h" +#include "gmock/gmock.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientManagerMock : public ClientManager { +public: + MOCK_METHOD(void, start, (), (override)); + + MOCK_METHOD(void, shutdown, (), (override)); + + MOCK_METHOD(SchedulerSharedPtr, getScheduler, (), (override)); + + MOCK_METHOD((std::shared_ptr), createChannel, (const std::string&), (override)); + + MOCK_METHOD(void, resolveRoute, + (const std::string&, const Metadata&, const QueryRouteRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, heartbeat, + (const std::string&, const Metadata&, const HeartbeatRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, pollCommand, + (const std::string&, const Metadata&, const PollCommandRequest&, std::chrono::milliseconds, + const std::function*)>&), + (override)); + + MOCK_METHOD(bool, wrapMessage, (const rmq::Message&, MQMessageExt&), (override)); + + MOCK_METHOD(void, ack, + (const std::string&, const Metadata&, const AckMessageRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, nack, + (const std::string&, const Metadata&, const NackMessageRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, forwardMessageToDeadLetterQueue, + (const std::string&, const Metadata&, const ForwardMessageToDeadLetterQueueRequest&, + std::chrono::milliseconds, + (const std::function*)>&)), + (override)); + + MOCK_METHOD(void, endTransaction, + (const std::string&, const Metadata&, const EndTransactionRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, queryOffset, + (const std::string&, const Metadata&, const QueryOffsetRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, healthCheck, + (const std::string&, const Metadata&, const HealthCheckRequest&, std::chrono::milliseconds, + (const std::function*)>&)), + (override)); + + MOCK_METHOD(void, addClientObserver, (std::weak_ptr), (override)); + + MOCK_METHOD(void, queryAssignment, + (const std::string& target, const Metadata&, const QueryAssignmentRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, receiveMessage, + (const std::string&, const Metadata&, const ReceiveMessageRequest&, std::chrono::milliseconds, + (const std::shared_ptr&)), + (override)); + + MOCK_METHOD(bool, send, (const std::string&, const Metadata&, SendMessageRequest&, SendCallback*), (override)); + + MOCK_METHOD(void, pullMessage, + (const std::string&, const Metadata&, const PullMessageRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(std::error_code, notifyClientTermination, + (const std::string&, const Metadata&, const NotifyClientTerminationRequest&, std::chrono::milliseconds), + (override)); + + MOCK_METHOD(std::error_code, reportThreadStackTrace, + (const std::string&, const Metadata&, const ReportThreadStackTraceRequest&, std::chrono::milliseconds), + (override)); + + MOCK_METHOD(std::error_code, reportMessageConsumptionResult, + (const std::string&, const Metadata&, const ReportMessageConsumptionResultRequest&, + std::chrono::milliseconds), + (override)); + + MOCK_METHOD(State, state, (), (const override)); + + MOCK_METHOD(void, submit, (std::function), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientMock.h b/src/main/cpp/client/mocks/include/ClientMock.h new file mode 100644 index 000000000..d7fbf782d --- /dev/null +++ b/src/main/cpp/client/mocks/include/ClientMock.h @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "Client.h" +#include "ClientConfigMock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientMock : virtual public Client, virtual public ClientConfigMock { +public: + MOCK_METHOD(void, endpointsInUse, (absl::flat_hash_set&), (override)); + + MOCK_METHOD(void, heartbeat, (), (override)); + + MOCK_METHOD(bool, active, (), (override)); + + MOCK_METHOD(void, onRemoteEndpointRemoval, (const std::vector&), (override)); + + MOCK_METHOD(void, healthCheck, (), (override)); + + MOCK_METHOD(void, schedule, (const std::string&, const std::function&, std::chrono::milliseconds), + (override)); + + MOCK_METHOD(void, notifyClientTermination, (), (override)); +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h new file mode 100644 index 000000000..02c958838 --- /dev/null +++ b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "gmock/gmock.h" + +#include "ReceiveMessageCallback.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ReceiveMessageCallbackMock : public ReceiveMessageCallback { +public: + MOCK_METHOD(void, onCompletion, (const std::error_code&, const ReceiveMessageResult&), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/RpcClientMock.h b/src/main/cpp/client/mocks/include/RpcClientMock.h new file mode 100644 index 000000000..2cde10fd9 --- /dev/null +++ b/src/main/cpp/client/mocks/include/RpcClientMock.h @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "InvocationContext.h" +#include "RpcClient.h" +#include "gmock/gmock.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class RpcClientMock : public RpcClient { +public: + RpcClientMock(); + + ~RpcClientMock() override { + std::cout << "~RpcClientMock()" << std::endl; + } + + MOCK_METHOD(void, asyncQueryRoute, (const QueryRouteRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncSend, (const SendMessageRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncQueryAssignment, (const QueryAssignmentRequest&, InvocationContext*), + (override)); + + MOCK_METHOD(std::shared_ptr&, completionQueue, (), (override)); + + MOCK_METHOD(void, asyncReceive, (const ReceiveMessageRequest&, InvocationContext*), + (override)); + + MOCK_METHOD(void, asyncAck, (const AckMessageRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncNack, (const NackMessageRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncHeartbeat, (const HeartbeatRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncHealthCheck, (const HealthCheckRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncEndTransaction, (const EndTransactionRequest&, InvocationContext*), + (override)); + + MOCK_METHOD(void, asyncQueryOffset, (const QueryOffsetRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncPull, (const PullMessageRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncForwardMessageToDeadLetterQueue, + (const ForwardMessageToDeadLetterQueueRequest&, + InvocationContext*), + (override)); + + MOCK_METHOD(void, asyncPollCommand, (const PollCommandRequest&, InvocationContext*), (override)); + + MOCK_METHOD(grpc::Status, reportThreadStackTrace, + (grpc::ClientContext*, const ReportThreadStackTraceRequest&, ReportThreadStackTraceResponse*), + (override)); + + MOCK_METHOD(grpc::Status, reportMessageConsumptionResult, + (grpc::ClientContext*, const ReportMessageConsumptionResultRequest&, + ReportMessageConsumptionResultResponse*), + (override)); + + MOCK_METHOD(grpc::Status, notifyClientTermination, + (grpc::ClientContext*, const NotifyClientTerminationRequest&, NotifyClientTerminationResponse* response), + (override)); + + MOCK_METHOD(bool, needHeartbeat, (), (override)); + + MOCK_METHOD(void, needHeartbeat, (bool), (override)); + + MOCK_METHOD(bool, ok, (), (const override)); + +protected: + std::shared_ptr completion_queue_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/concurrent/BUILD.bazel b/src/main/cpp/concurrent/BUILD.bazel new file mode 100644 index 000000000..0501d2005 --- /dev/null +++ b/src/main/cpp/concurrent/BUILD.bazel @@ -0,0 +1,37 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "countdown_latch_library", + hdrs = [ + "include/CountdownLatch.h", + ], + srcs = [ + "CountdownLatch.cpp" + ], + strip_include_prefix = "//src/main/cpp/concurrent/include", + deps = [ + "//src/main/cpp/base:base_library", + "//src/main/cpp/log:log_library", + "@com_google_absl//absl/base", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + ], + visibility = [ + "//visibility:public", + ], +) \ No newline at end of file diff --git a/src/main/cpp/concurrent/CMakeLists.txt b/src/main/cpp/concurrent/CMakeLists.txt new file mode 100644 index 000000000..c79bc2a3b --- /dev/null +++ b/src/main/cpp/concurrent/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(concurrent OBJECT CountdownLatch.cpp) +target_include_directories(concurrent + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(concurrent + PRIVATE + absl::base + api + log + spdlog) \ No newline at end of file diff --git a/src/main/cpp/concurrent/CountdownLatch.cpp b/src/main/cpp/concurrent/CountdownLatch.cpp new file mode 100644 index 000000000..363494e06 --- /dev/null +++ b/src/main/cpp/concurrent/CountdownLatch.cpp @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "CountdownLatch.h" +#include "LoggerImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void CountdownLatch::await() { + absl::MutexLock lock(&mtx_); + if (count_ <= 0) { + return; + } + while (count_ > 0) { + cv_.Wait(&mtx_); + } +} + +void CountdownLatch::countdown() { + absl::MutexLock lock(&mtx_); + if (--count_ <= 0) { + cv_.SignalAll(); + } + if (!name_.empty()) { + if (count_ >= 0) { + SPDLOG_TRACE("After countdown(), latch[{}]={}", name_, count_); + } + } +} + +void CountdownLatch::increaseCount() { + absl::MutexLock lock(&mtx_); + ++count_; + if (!name_.empty()) { + SPDLOG_TRACE("After increaseCount(), latch[{}]={}", name_, count_); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/concurrent/include/CountdownLatch.h b/src/main/cpp/concurrent/include/CountdownLatch.h new file mode 100644 index 000000000..966b5e291 --- /dev/null +++ b/src/main/cpp/concurrent/include/CountdownLatch.h @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "absl/base/thread_annotations.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class CountdownLatch { +public: + explicit CountdownLatch(int32_t count) : CountdownLatch(count, "anonymous") { + } + CountdownLatch(int32_t count, absl::string_view name) : count_(count), name_(name.data(), name.length()) { + } + + void await() LOCKS_EXCLUDED(mtx_); + + void countdown() LOCKS_EXCLUDED(mtx_); + + void increaseCount() LOCKS_EXCLUDED(mtx_); + +private: + int32_t count_ GUARDED_BY(mtx_); + + absl::Mutex mtx_; // protects count_ + absl::CondVar cv_; + + std::string name_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/log/BUILD.bazel b/src/main/cpp/log/BUILD.bazel new file mode 100644 index 000000000..4b7670607 --- /dev/null +++ b/src/main/cpp/log/BUILD.bazel @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "log_library", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/log/include", + deps = [ + "//api:rocketmq_interface", + "@com_github_gabime_spdlog//:spdlog", + "@com_github_gulrak_filesystem//:filesystem", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/src/main/cpp/log/CMakeLists.txt b/src/main/cpp/log/CMakeLists.txt new file mode 100644 index 000000000..5f90a7180 --- /dev/null +++ b/src/main/cpp/log/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(log + OBJECT + LoggerImpl.cpp) +target_include_directories(log + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(log + PRIVATE + api + filesystem + spdlog) \ No newline at end of file diff --git a/src/main/cpp/log/LoggerImpl.cpp b/src/main/cpp/log/LoggerImpl.cpp new file mode 100644 index 000000000..6ff39f105 --- /dev/null +++ b/src/main/cpp/log/LoggerImpl.cpp @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "LoggerImpl.h" +#include +#include +#include +#include + +// Check C++17 is used +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) +#if __has_include() +#define GHC_USE_STD_FS +#include +namespace fs = std::filesystem; +#endif +#endif + +#ifndef GHC_USE_STD_FS +#include "ghc/filesystem.hpp" +namespace fs = ghc::filesystem; +#endif + +ROCKETMQ_NAMESPACE_BEGIN + +const char* LoggerImpl::LOG_FILE = "/logs/rocketmq/client.log"; +const char* LoggerImpl::LOGGER_NAME = "rocketmq_logger"; + +void LoggerImpl::setLevel(Level level) { + level_ = level; + auto logger = spdlog::get(LOGGER_NAME); + if (logger) { + setLevel(logger, level_); + } +} + +void LoggerImpl::setConsoleLevel(Level level) { + console_level_ = level; + setLevel(console_sink_, console_level_); +} + +void LoggerImpl::init() { + std::call_once(init_once_, [this] { init0(); }); +} + +void LoggerImpl::init0() { + if (log_home_.empty()) { + char* home = getenv(USER_HOME_ENV); + if (home) { + log_home_.append(home); + } else { + log_home_.append(fs::temp_directory_path().string()); + } + log_home_.append(LOG_FILE); + } + + // Create directories if necessary + fs::path log_file(log_home_); + fs::path log_dir = log_file.parent_path(); + if (!fs::exists(log_dir)) { + if (!fs::create_directories(log_dir)) { + std::cerr << "Failed to mkdir " << log_dir << std::endl; + abort(); + } + } + std::cout << "RocketMQ log files path: " << log_dir.c_str() << std::endl; + + if (pattern_.empty()) { + pattern_ = DEFAULT_PATTERN; + } + + console_sink_ = std::make_shared(); + setLevel(console_sink_, console_level_); + console_sink_->set_pattern(pattern_); + + file_sink_ = std::make_shared(log_home_, file_size_, file_count_, + /*rotate_on_open=*/false); + file_sink_->set_pattern(pattern_); + setLevel(file_sink_, level_); + + // std::make_shared does not with initializer_list. + auto default_logger = + std::make_shared(LOGGER_NAME, spdlog::sinks_init_list{console_sink_, file_sink_}); + default_logger->flush_on(spdlog::level::warn); + Level logger_level = + static_cast(std::min(static_cast(level_), static_cast(console_level_))); + switch (logger_level) { + case Level::Trace: { + default_logger->set_level(spdlog::level::trace); + break; + } + case Level::Debug: { + default_logger->set_level(spdlog::level::debug); + break; + } + case Level::Info: { + default_logger->set_level(spdlog::level::info); + break; + } + case Level::Warn: { + default_logger->set_level(spdlog::level::warn); + break; + } + default: { + default_logger->set_level(spdlog::level::info); + break; + } + } + spdlog::flush_every(std::chrono::seconds(1)); + spdlog::set_default_logger(default_logger); +} + +Logger& getLogger() { + static LoggerImpl logger; + return logger; +} + +const std::size_t LoggerImpl::DEFAULT_MAX_LOG_FILE_QUANTITY = 16; +const std::size_t LoggerImpl::DEFAULT_FILE_SIZE = 1048576 * 256; +const char* LoggerImpl::USER_HOME_ENV = "HOME"; +const char* LoggerImpl::DEFAULT_PATTERN = "[%Y/%m/%d-%H:%M:%S.%e %z] [%n] [%^---%L---%$] [thread %t] %v %@"; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/log/include/LoggerImpl.h b/src/main/cpp/log/include/LoggerImpl.h new file mode 100644 index 000000000..338b7e2e7 --- /dev/null +++ b/src/main/cpp/log/include/LoggerImpl.h @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "rocketmq/Logger.h" +#include "spdlog/sinks/rotating_file_sink.h" +#include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class LoggerImpl : public Logger { + +public: + ~LoggerImpl() override = default; + + void setLevel(Level level) override; + + void setConsoleLevel(Level level) override; + + void setFileSize(std::size_t file_size) override { + file_size_ = file_size; + } + + void setFileCount(std::size_t file_count) override { + file_count_ = file_count; + } + + void setPattern(std::string pattern) override { + pattern_ = std::move(pattern); + auto logger = spdlog::get(LOGGER_NAME); + if (logger) { + logger->set_pattern(pattern_); + } + } + + void init() override; + + static const char* LOGGER_NAME; + +private: + void init0(); + + template + void setLevel(std::shared_ptr& target, Level level) { + if (!target) { + return; + } + + switch (level) { + case Level::Trace: + target->set_level(spdlog::level::trace); + break; + case Level::Debug: + target->set_level(spdlog::level::debug); + break; + case Level::Info: + target->set_level(spdlog::level::info); + break; + case Level::Warn: + target->set_level(spdlog::level::warn); + break; + case Level::Error: + target->set_level(spdlog::level::err); + break; + } + } + + static const char* LOG_FILE; + + Level level_{Level::Info}; + Level console_level_{Level::Warn}; + std::string log_home_; + std::size_t file_size_{DEFAULT_FILE_SIZE}; + std::size_t file_count_{DEFAULT_MAX_LOG_FILE_QUANTITY}; + std::string pattern_; + + std::once_flag init_once_; + + std::shared_ptr file_sink_; + std::shared_ptr console_sink_; + + static const std::size_t DEFAULT_MAX_LOG_FILE_QUANTITY; + static const std::size_t DEFAULT_FILE_SIZE; + static const char* DEFAULT_PATTERN; + static const char* USER_HOME_ENV; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/AccessPoint.cpp b/src/main/cpp/ons/AccessPoint.cpp new file mode 100644 index 000000000..2f6ab4d80 --- /dev/null +++ b/src/main/cpp/ons/AccessPoint.cpp @@ -0,0 +1,38 @@ +#include "AccessPoint.h" + +#include + +#include "absl/strings/str_replace.h" +#include "absl/strings/str_split.h" + +ONS_NAMESPACE_BEGIN + +const char* AccessPoint::PREFIX = "http://MQ_INST_"; + +const char* AccessPoint::SCHEMA = "http://"; + +const char* AccessPoint::RESOURCE_NAMESPACE_PREFIX = "MQ_INST_"; + +AccessPoint::operator bool() const { + if (!absl::StartsWith(access_point_, PREFIX)) { + return false; + } + + return absl::StrContains(access_point_, '.'); +} + +std::string AccessPoint::resourceNamespace() const { + std::vector segments = absl::StrSplit(absl::StripPrefix(access_point_, SCHEMA), '.'); + return std::string(segments[0].data(), segments[0].length()); +} + +std::string AccessPoint::nameServerAddress() const { + // If extensive domain name is supported, use the following comment-out line. + return absl::AsciiStrToLower(absl::StrReplaceAll(absl::StripPrefix(access_point_, SCHEMA), {{"_", "-"}})); + // If wildcard sub-domain name is preferred + // absl::string_view name_server = + // absl::StripPrefix(absl::StripPrefix(absl::StripPrefix(access_point_, SCHEMA), resourceNamespace()), "."); + // return std::string(name_server.data(), name_server.length()); +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/CMakeLists.txt b/src/main/cpp/ons/CMakeLists.txt new file mode 100644 index 000000000..bbccb3435 --- /dev/null +++ b/src/main/cpp/ons/CMakeLists.txt @@ -0,0 +1,27 @@ +file(GLOB SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + +add_library(ons OBJECT ${SRC_FILES}) +target_include_directories(ons + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(ons + PRIVATE + api + absl::strings + asio + base + fmt + proto + client + filesystem + httplib + log + opencensus_api + opencensus_proto + rocketmq_stats + rocketmq_trace + otlp_exporter + scheduler + spdlog + impl + ) \ No newline at end of file diff --git a/src/main/cpp/ons/ConsumerImpl.cpp b/src/main/cpp/ons/ConsumerImpl.cpp new file mode 100644 index 000000000..cb5338185 --- /dev/null +++ b/src/main/cpp/ons/ConsumerImpl.cpp @@ -0,0 +1,47 @@ +#include "ConsumerImpl.h" + +#include "absl/memory/memory.h" + +#include "MessageListenerWrapper.h" +#include "OffsetStoreAdaptor.h" + +ONS_NAMESPACE_BEGIN + +ConsumerImpl::ConsumerImpl(const ONSFactoryProperty& ons_factory_property) : ONSConsumerAbstract(ons_factory_property) { +} + +void ConsumerImpl::start() { + ONSConsumerAbstract::start(); +} + +void ConsumerImpl::shutdown() { + ONSConsumerAbstract::shutdown(); +} + +void ConsumerImpl::subscribe(const std::string& topic, const std::string& sub_expression) { + if (topic.empty()) { + THROW_ONS_EXCEPTION(ONSClientException, "Subscribed topic is null", OTHER_ERROR); + } + + if (sub_expression.empty()) { + THROW_ONS_EXCEPTION(ONSClientException, "SubExpression topic is null", OTHER_ERROR); + } + + ONSConsumerAbstract::subscribe(topic, sub_expression); +} + +void ConsumerImpl::registerMessageListener(MessageListener* listener) { + if (nullptr == listener) { + THROW_ONS_EXCEPTION(ONSClientException, "MessageListener may not be nullptr.", CONSUME_MESSAGE_LISTENER_IS_NULL); + } + + auto message_listener = absl::make_unique(listener); + ONSConsumerAbstract::registerMessageListener(std::move(message_listener)); +} + +void ConsumerImpl::withOffsetStore(std::unique_ptr offset_store) { + auto store = absl::make_unique(std::move(offset_store)); + consumer_.setOffsetStore(std::move(store)); +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/FAQ.cpp b/src/main/cpp/ons/FAQ.cpp new file mode 100644 index 000000000..1c2ac8835 --- /dev/null +++ b/src/main/cpp/ons/FAQ.cpp @@ -0,0 +1,13 @@ +#include "FAQ.h" + +ONS_NAMESPACE_BEGIN + +const std::string FAQ::FIND_NS_FAILED = "https://github.com/alibaba/ons/issues/1"; +const std::string FAQ::CONNECT_BROKER_FAILED = "https://github.com/alibaba/ons/issues/2"; +const std::string FAQ::SEND_MSG_TO_BROKER_TIMEOUT = "https://github.com/alibaba/ons/issues/3"; +const std::string FAQ::SERVICE_STATE_WRONG = "https://github.com/alibaba/ons/issues/4"; +const std::string FAQ::BROKER_RESPONSE_EXCEPTION = "https://github.com/alibaba/ons/issues/5"; +const std::string FAQ::CLIENT_CHECK_MSG_EXCEPTION = "https://github.com/alibaba/ons/issues/6"; +const std::string FAQ::TOPIC_ROUTE_NOT_EXIST = "https://github.com/alibaba/ons/issues/7"; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/InstanceUtil.cpp b/src/main/cpp/ons/InstanceUtil.cpp new file mode 100644 index 000000000..79db50bdf --- /dev/null +++ b/src/main/cpp/ons/InstanceUtil.cpp @@ -0,0 +1,36 @@ +#include "InstanceUtil.h" +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" + +ONS_NAMESPACE_BEGIN + +bool InstanceUtil::validateInstanceEndpoint(const std::string& endpoint) { + size_t position = endpoint.find(MISC_HEAD); + if (position == 0) { + position = endpoint.find('.'); + if (position != std::string::npos && position > MISC_HEAD.length()) { + return true; + } + } + return false; +} + +std::string InstanceUtil::parseInstanceIdFromEndpoint(const std::string& endpoint) { + std::string instanceId; + if (!endpoint.empty()) { + instanceId = endpoint.substr(ENDPOINT_PREFIX.length(), endpoint.find('.') - ENDPOINT_PREFIX.length()); + } + SPDLOG_INFO("nameSpace is:{}, nameserver is:{}", instanceId.c_str(), endpoint.c_str()); + return instanceId; +} + +bool InstanceUtil::validateNameSrvAddr(const std::string& name_server_address) { + size_t position = name_server_address.find(ENDPOINT_PREFIX); + if (position == 0 && name_server_address.length() > ENDPOINT_PREFIX.length()) { + SPDLOG_INFO("validate true {}, nameserver is:{}", position, name_server_address.c_str()); + return true; + } + return false; +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/Message.cpp b/src/main/cpp/ons/Message.cpp new file mode 100755 index 000000000..89e414b61 --- /dev/null +++ b/src/main/cpp/ons/Message.cpp @@ -0,0 +1,170 @@ +#include "ons/Message.h" + +#include +#include +#include + +#include "absl/strings/numbers.h" +#include "absl/strings/str_join.h" +#include "absl/time/time.h" + +ONS_NAMESPACE_BEGIN + +const char* SystemPropKey::TAG = "__TAG"; +const char* SystemPropKey::KEY_SEPARATOR = " "; +const char* SystemPropKey::MSGID = "__MSGID"; +const char* SystemPropKey::RECONSUMETIMES = "__RECONSUMETIMES"; +const char* SystemPropKey::STARTDELIVERTIME = "__STARTDELIVERTIME"; + +Message::Message(const std::string& topic, const std::string& body) { + topic_ = std::string(topic.data(), topic.length()); + body_ = std::string(body.data(), body.length()); +} + +Message::Message(const std::string& topic, const std::string& tag, const std::string& body) : Message(topic, body) { + if (!tag.empty()) { + setTag(tag); + } +} + +Message::Message(const std::string& topic, const std::string& tag, const std::string& key, const std::string& body) + : Message(topic, tag, body) { + if (!key.empty()) { + attachKey(key); + } +} + +void Message::putUserProperty(const std::string& key, const std::string& value) { + std::string k(key.data(), key.length()); + std::string v(value.data(), value.length()); + + auto search = user_properties_.find(k); + if (user_properties_.end() != search) { + if (search->second == v) { + return; + } + user_properties_.erase(search); + } + user_properties_.insert({k, v}); +} + +std::string Message::getUserProperty(const std::string& key) const { + std::string k(key.data(), key.length()); + auto it = user_properties_.find(k); + if (user_properties_.end() != it) { + return it->second; + } + return std::string(); +} + +void Message::setUserProperties(const std::map& user_properties) { + for (const auto& it : user_properties) { + putUserProperty(it.first, it.second); + } +} + +std::map Message::getUserProperties() const { + return user_properties_; +} + +std::string Message::getTopic() const { + return topic_; +} + +void Message::setTopic(const std::string& topic) { + if (topic.empty()) { + return; + } + topic_ = std::string(topic.data(), topic.length()); +} + +std::string Message::getTag() const { + return tag_; +} + +void Message::setTag(const std::string& tag) { + if (tag.empty()) { + return; + } + + tag_ = std::string(tag.data(), tag.length()); +} + +std::string Message::getMsgID() const { + return message_id_; +} + +void Message::setMsgID(const std::string& message_id) { + if (message_id.empty()) { + return; + } + message_id_ = std::string(message_id.data(), message_id.length()); +} + +std::vector Message::getKeys() const { + return keys_; +} + +void Message::attachKey(const std::string& key) { + if (key.empty()) { + return; + } + + keys_.push_back(std::string(key.data(), key.length())); +} + +std::chrono::system_clock::time_point Message::getStartDeliverTime() const { + return delivery_timestamp_; +} + +void Message::setStartDeliverTime(std::chrono::system_clock::time_point delivery_timepoint) { + delivery_timestamp_ = delivery_timepoint; +} + +std::string Message::getBody() const { + return body_; +} + +void Message::setBody(const std::string& body) { + if (body.empty()) { + body_.clear(); + return; + } + body_ = std::string(body.data(), body.length()); +} + +std::int32_t Message::getReconsumeTimes() const { + return reconsume_times_; +} + +void Message::setReconsumeTimes(std::int32_t reconsume_times) { + reconsume_times_ = reconsume_times; +} + +std::chrono::system_clock::time_point Message::getStoreTimestamp() const { + return store_timestamp_; +} + +void Message::setStoreTimestamp(std::chrono::system_clock::time_point store_timepoint) { + store_timestamp_ = store_timepoint; +} + +std::int64_t Message::getQueueOffset() const { + return queue_offset_; +} + +void Message::setQueueOffset(std::int64_t queue_offset) { + queue_offset_ = queue_offset; +} + +std::string Message::toString() const { + std::stringstream ss; + ss << "Message [topic=" << topic_ << ", body=" << body_ << "]"; + return ss.str(); +} + +std::string Message::toUserString() const { + return absl::StrJoin(user_properties_, ",", absl::PairFormatter("=")); +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/MessageListenerWrapper.cpp b/src/main/cpp/ons/MessageListenerWrapper.cpp new file mode 100644 index 000000000..e2cea6bb3 --- /dev/null +++ b/src/main/cpp/ons/MessageListenerWrapper.cpp @@ -0,0 +1,30 @@ +#include "MessageListenerWrapper.h" +#include "ONSUtil.h" +#include "ons/ConsumeContext.h" +#include "rocketmq/RocketMQ.h" + +ONS_NAMESPACE_BEGIN + +MessageListenerWrapper::MessageListenerWrapper(ons::MessageListener* message_listener) + : message_listener_(message_listener) { +} + +ROCKETMQ_NAMESPACE::ConsumeMessageResult +MessageListenerWrapper::consumeMessage(const std::vector& msgs) { + ConsumeContext consume_context; + ONSUtil ons_util = ONSUtil::get(); + ROCKETMQ_NAMESPACE::MQMessageExt mq_message = *msgs.begin(); + Message message = ons_util.msgConvert(mq_message); + Action action = message_listener_->consume(message, consume_context); + + switch (action) { + case Action::CommitMessage: + return ROCKETMQ_NAMESPACE::ConsumeMessageResult::SUCCESS; + + case Action::ReconsumeLater: + default: + return ROCKETMQ_NAMESPACE::ConsumeMessageResult::FAILURE; + } +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/MessageQueueONS.cpp b/src/main/cpp/ons/MessageQueueONS.cpp new file mode 100755 index 000000000..ee0c2cea7 --- /dev/null +++ b/src/main/cpp/ons/MessageQueueONS.cpp @@ -0,0 +1,68 @@ +#include "ons/MessageQueueONS.h" +#include "ons/ONSClientException.h" + +ONS_NAMESPACE_BEGIN + +std::string MessageQueueONS::getTopic() const { + return topic_; +} + +void MessageQueueONS::setTopic(const std::string& topic) { + topic_ = topic; +} + +std::string MessageQueueONS::getBrokerName() const { + return broker_name_; +} + +void MessageQueueONS::setBrokerName(const std::string& broker_name) { + broker_name_ = broker_name; +} + +int MessageQueueONS::getQueueId() const { + return queue_id_; +} + +void MessageQueueONS::setQueueId(int queue_id) { + queue_id_ = queue_id; +} + +bool MessageQueueONS::operator==(const MessageQueueONS& mq) const { + if (this == &mq) { + return true; + } + + if (broker_name_ != mq.broker_name_) { + return false; + } + + if (queue_id_ != mq.queue_id_) { + return false; + } + + if (topic_ != mq.topic_) { + return false; + } + + return true; +} + +int MessageQueueONS::compareTo(const MessageQueueONS& mq) const { + int result = topic_.compare(mq.topic_); + if (result != 0) { + return result; + } + + result = broker_name_.compare(mq.broker_name_); + if (result != 0) { + return result; + } + + return queue_id_ - mq.queue_id_; +} + +bool MessageQueueONS::operator<(const MessageQueueONS& mq) const { + return compareTo(mq) < 0; +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/ONSClientAbstract.cpp b/src/main/cpp/ons/ONSClientAbstract.cpp new file mode 100644 index 000000000..0258a32c1 --- /dev/null +++ b/src/main/cpp/ons/ONSClientAbstract.cpp @@ -0,0 +1,21 @@ +#include "ONSClientAbstract.h" + +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" + +ONS_NAMESPACE_BEGIN + +// TODO: set AccessKey and SecretKey here. +ONSClientAbstract::ONSClientAbstract(const ONSFactoryProperty& factory_property) + : factory_property_(factory_property), access_point_(factory_property.getNameSrvAddr()) { + SPDLOG_INFO("AbstractResourceNamespace={}", access_point_.resourceNamespace()); +} + +void ONSClientAbstract::start() {} + +void ONSClientAbstract::shutdown() {} + +// TODO: not yet implemented. +std::string ONSClientAbstract::buildInstanceName() { return "DefaultInstanceName"; } + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/ONSClientException.cpp b/src/main/cpp/ons/ONSClientException.cpp new file mode 100755 index 000000000..f1999f719 --- /dev/null +++ b/src/main/cpp/ons/ONSClientException.cpp @@ -0,0 +1,19 @@ +#include "ons/ONSClientException.h" +#include + +ONS_NAMESPACE_BEGIN + +ONSClientException::ONSClientException(std::string msg, int error) noexcept : msg_(std::move(msg)), error_(error) {} + +ONSClientException::ONSClientException(const ONSClientException& e) noexcept { + this->msg_ = std::string(e.what()); + this->error_ = e.GetError(); +} + +const char* ONSClientException::what() const noexcept { return msg_.c_str(); } + +const char* ONSClientException::GetMsg() const noexcept { return msg_.c_str(); } + +int ONSClientException::GetError() const noexcept { return error_; } + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/ONSConsumerAbstract.cpp b/src/main/cpp/ons/ONSConsumerAbstract.cpp new file mode 100644 index 000000000..ed204d8a6 --- /dev/null +++ b/src/main/cpp/ons/ONSConsumerAbstract.cpp @@ -0,0 +1,86 @@ +#include "ONSConsumerAbstract.h" +#include "absl/strings/ascii.h" +#include "rocketmq/RocketMQ.h" + +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" + +ONS_NAMESPACE_BEGIN + +ONSConsumerAbstract::ONSConsumerAbstract(const ONSFactoryProperty& factory_property) + : ONSClientAbstract(factory_property), consumer_(factory_property.getConsumerId()) { + SPDLOG_INFO("Consumer[GroupId={}] constructed", factory_property.getConsumerId()); + int consume_thread_nums = factory_property.getConsumeThreadNums(); + if (consume_thread_nums > 0) { + consumer_.setConsumeThreadCount(consume_thread_nums); + } + consumer_.setNamesrvAddr(factory_property.getNameSrvAddr()); + + absl::string_view instanceName = absl::StripAsciiWhitespace(factory_property.getInstanceId()); + if (instanceName.empty()) { + consumer_.setInstanceName(buildInstanceName()); + } else { + consumer_.setInstanceName(std::string(instanceName)); + } + + auto credentials_provider = std::make_shared( + factory_property.getAccessKey(), factory_property.getSecretKey()); + + consumer_.setCredentialsProvider(credentials_provider); + + if (access_point_) { + consumer_.setResourceNamespace(access_point_.resourceNamespace()); + consumer_.setNamesrvAddr(access_point_.nameServerAddress()); + } else if (!factory_property.getNameSrvAddr().empty()) { + consumer_.setNamesrvAddr(factory_property.getNameSrvAddr()); + } else if (!factory_property.getNameSrvDomain().empty()) { + consumer_.setNameServerListDiscoveryEndpoint(factory_property.getNameSrvDomain()); + } + + auto message_model = factory_property.getMessageModel(); + if (ONSFactoryProperty::BROADCASTING == message_model) { + consumer_.setMessageModel(ROCKETMQ_NAMESPACE::MessageModel::BROADCASTING); + } else { + consumer_.setMessageModel(ROCKETMQ_NAMESPACE::MessageModel::CLUSTERING); + } + + int thread_number = factory_property.getConsumeThreadNums(); + if (thread_number > 0 && thread_number <= 1024) { + consumer_.setConsumeThreadCount(thread_number); + } + + bool traceSwitchOn = factory_property.getOnsTraceSwitch(); + consumer_.enableTracing(traceSwitchOn); + + const auto& throttle = factory_property.throttle(); + if (!throttle.empty()) { + for (const auto& entry : throttle) { + consumer_.setThrottle(entry.first, entry.second); + } + } +} + +void ONSConsumerAbstract::start() { + ONSClientAbstract::start(); + consumer_.start(); +} + +void ONSConsumerAbstract::shutdown() { + consumer_.shutdown(); + ONSClientAbstract::shutdown(); +} + +void ONSConsumerAbstract::subscribe(absl::string_view topic, absl::string_view sub_expression) { + SPDLOG_INFO("Subscribe topic={}, filter-expression={}", topic.data(), sub_expression.data()); + consumer_.subscribe(std::string(topic.data(), topic.length()), + std::string(sub_expression.data(), sub_expression.length())); +} + +void ONSConsumerAbstract::registerMessageListener( + std::unique_ptr message_listener) { + message_listener_ = std::move(message_listener); + consumer_.registerMessageListener(message_listener_.get()); + SPDLOG_INFO("Message listener registered"); +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/ONSEnvironmentVariableFactoryProperty.cpp b/src/main/cpp/ons/ONSEnvironmentVariableFactoryProperty.cpp new file mode 100644 index 000000000..dd73d9774 --- /dev/null +++ b/src/main/cpp/ons/ONSEnvironmentVariableFactoryProperty.cpp @@ -0,0 +1,35 @@ +#include "ons/ONSEnvironmentVariableFactoryProperty.h" + +#include +#include + +ONS_NAMESPACE_BEGIN + +ONSEnvironmentVariableFactoryProperty::ONSEnvironmentVariableFactoryProperty() { + setDefaults(); + parseEnvironmentVariables(); +} + +void ONSEnvironmentVariableFactoryProperty::parseEnvironmentVariables() { + char* value = getenv(AccessKey); + if (value && strlen(value)) { + setFactoryProperty(AccessKey, value); + } + + value = getenv(SecretKey); + if (value && strlen(value)) { + setFactoryProperty(SecretKey, value); + } + + value = getenv(GroupId); + if (value && strlen(value)) { + setFactoryProperty(GroupId, value); + } + + value = getenv(NAMESRV_ADDR); + if (value && strlen(value)) { + setFactoryProperty(NAMESRV_ADDR, value); + } +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/ONSFactory.cpp b/src/main/cpp/ons/ONSFactory.cpp new file mode 100644 index 000000000..eb6a5622d --- /dev/null +++ b/src/main/cpp/ons/ONSFactory.cpp @@ -0,0 +1,12 @@ +#include "ons/ONSFactory.h" + +#include "ONSFactoryInstance.h" + +ONS_NAMESPACE_BEGIN + +ONSFactoryAPI* ONSFactory::getInstance() { + static ONSFactoryInstance instance_; + return &instance_; +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/ONSFactoryInstance.cpp b/src/main/cpp/ons/ONSFactoryInstance.cpp new file mode 100644 index 000000000..6e109312b --- /dev/null +++ b/src/main/cpp/ons/ONSFactoryInstance.cpp @@ -0,0 +1,137 @@ +#include + +#include "ONSFactoryInstance.h" + +#include "ConsumerImpl.h" +#include "FAQ.h" +#include "ONSUtil.h" +#include "OrderConsumerImpl.h" +#include "OrderProducerImpl.h" +#include "ProducerImpl.h" +#include "TransactionProducerImpl.h" +#include "ons/ONSClientException.h" +#include "ons/ONSFactory.h" + +ONS_NAMESPACE_BEGIN + +Producer* ONSFactoryInstance::createProducer(ONSFactoryProperty& factory_properties) { + if (ONSChannel::INNER == factory_properties.getOnsChannel()) { + factory_properties.setOnsTraceSwitch(false); + factory_properties.setFactoryProperty(ONSFactoryProperty::AccessKey, "DefaultKey"); + factory_properties.setFactoryProperty(ONSFactoryProperty::SecretKey, "DefaultKey"); + factory_properties.setFactoryProperty(ONSFactoryProperty::NAMESRV_ADDR, "LocalDefault"); + } + + if (!factory_properties) { + ons::ONSClientException e( + FAQ::errorMessage("Required configuration items are missing", FAQ::CLIENT_CHECK_MSG_EXCEPTION)); + throw e; + } + + std::shared_ptr producer = std::make_shared(factory_properties); + { + absl::MutexLock guard(&producer_table_mtx_); + producer_table_.push_back(producer); + } + return producer.get(); +} + +OrderProducer* ONSFactoryInstance::createOrderProducer(ONSFactoryProperty& factory_properties) { + if (ONSChannel::INNER == factory_properties.getOnsChannel()) { + factory_properties.setOnsTraceSwitch(false); + factory_properties.setFactoryProperty(ONSFactoryProperty::AccessKey, "DefaultKey"); + factory_properties.setFactoryProperty(ONSFactoryProperty::SecretKey, "DefaultKey"); + factory_properties.setFactoryProperty(ONSFactoryProperty::NAMESRV_ADDR, "LocalDefault"); + } + + if (!factory_properties) { + ons::ONSClientException e( + FAQ::errorMessage("Required configuration items are missing", FAQ::CLIENT_CHECK_MSG_EXCEPTION)); + throw e; + } + + std::shared_ptr order_producer = std::make_shared(factory_properties); + { + absl::MutexLock guard(&order_producer_table_mtx_); + order_producer_table_.push_back(order_producer); + } + return order_producer.get(); +} + +OrderConsumer* ONSFactoryInstance::createOrderConsumer(ONSFactoryProperty& factory_properties) { + if (ONSChannel::INNER == factory_properties.getOnsChannel()) { + factory_properties.setOnsTraceSwitch(false); + factory_properties.setFactoryProperty(ONSFactoryProperty::AccessKey, "DefaultKey"); + factory_properties.setFactoryProperty(ONSFactoryProperty::SecretKey, "DefaultKey"); + factory_properties.setFactoryProperty(ONSFactoryProperty::NAMESRV_ADDR, "LocalDefault"); + } + + if (!factory_properties) { + ons::ONSClientException e( + FAQ::errorMessage("Required configuration items are missing", FAQ::CLIENT_CHECK_MSG_EXCEPTION)); + throw e; + } + + std::shared_ptr order_consumer = std::make_shared(factory_properties); + { + absl::MutexLock guard(&order_consumer_table_mtx_); + order_consumer_table_.push_back(order_consumer); + } + return order_consumer.get(); +} + +TransactionProducer* ONSFactoryInstance::createTransactionProducer(ONSFactoryProperty& factory_properties, + LocalTransactionChecker* checker) { + if (ONSChannel::INNER == factory_properties.getOnsChannel()) { + factory_properties.setOnsTraceSwitch(false); + factory_properties.setFactoryProperty(ONSFactoryProperty::AccessKey, "DefaultKey"); + factory_properties.setFactoryProperty(ONSFactoryProperty::SecretKey, "DefaultKey"); + factory_properties.setFactoryProperty(ONSFactoryProperty::NAMESRV_ADDR, "LocalDefault"); + } + + if (!factory_properties) { + ons::ONSClientException e( + FAQ::errorMessage("Required configuration items are missing", FAQ::CLIENT_CHECK_MSG_EXCEPTION)); + throw e; + } + + if (checker == nullptr) { + std::string msg = "Transaction Checker cannot be NULL. Please check your ONS property set."; + throw ONSClientException(msg); + } + std::shared_ptr transaction_producer = + std::make_shared(factory_properties, checker); + { + absl::MutexLock guard(&transaction_producer_table_mtx_); + transaction_producer_table_.push_back(transaction_producer); + } + return transaction_producer.get(); +} + +PullConsumer* ONSFactoryInstance::createPullConsumer(ONSFactoryProperty&) { + throw ONSClientException("Pull is not supported for now."); +} + +PushConsumer* ONSFactoryInstance::createPushConsumer(ONSFactoryProperty& factory_properties) { + if (ONSChannel::INNER == factory_properties.getOnsChannel()) { + factory_properties.setOnsTraceSwitch(false); + factory_properties.setFactoryProperty(ONSFactoryProperty::AccessKey, "DefaultKey"); + factory_properties.setFactoryProperty(ONSFactoryProperty::SecretKey, "DefaultKey"); + factory_properties.setFactoryProperty(ONSFactoryProperty::NAMESRV_ADDR, "LocalDefault"); + } + + if (!factory_properties) { + ons::ONSClientException e( + FAQ::errorMessage("Required configuration items are missing", FAQ::CLIENT_CHECK_MSG_EXCEPTION)); + throw e; + } + + std::shared_ptr consumer = std::make_shared(factory_properties); + { + absl::MutexLock guard(&consumer_table_mtx_); + consumer_table_.push_back(consumer); + } + return consumer.get(); +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/ONSFactoryProperty.cpp b/src/main/cpp/ons/ONSFactoryProperty.cpp new file mode 100644 index 000000000..3706db85a --- /dev/null +++ b/src/main/cpp/ons/ONSFactoryProperty.cpp @@ -0,0 +1,412 @@ +#include "ons/ONSFactoryProperty.h" + +#include +#include +#include +#include + +#include "absl/strings/numbers.h" +#include "absl/strings/str_join.h" +#include "absl/types/optional.h" +#include "ghc/filesystem.hpp" +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" +#include "spdlog/spdlog.h" + +#include "FAQ.h" +#include "MixAll.h" +#include "ons/ONSClientException.h" + +ONS_NAMESPACE_BEGIN + +const char* ONSFactoryProperty::LogPath = "LogPath"; +const char* ONSFactoryProperty::ProducerId = "ProducerId"; +const char* ONSFactoryProperty::ConsumerId = "ConsumerId"; +const char* ONSFactoryProperty::GroupId = "GroupId"; +const char* ONSFactoryProperty::AccessKey = "AccessKey"; +const char* ONSFactoryProperty::SecretKey = "SecretKey"; +const char* ONSFactoryProperty::MessageModel = "MessageModel"; +const char* ONSFactoryProperty::BROADCASTING = "BROADCASTING"; +const char* ONSFactoryProperty::CLUSTERING = "CLUSTERING"; +const char* ONSFactoryProperty::SendMsgTimeoutMillis = "SendMsgTimeoutMillis"; +const char* ONSFactoryProperty::SuspendTimeMillis = "SuspendTimeMillis"; +const char* ONSFactoryProperty::SendMsgRetryTimes = "SendMsgRetryTimes"; +const char* ONSFactoryProperty::MaxMsgCacheSize = "MaxMsgCacheSize"; +const char* ONSFactoryProperty::MaxCachedMessageSizeInMiB = "MaxCachedMessageSizeInMiB"; +const char* ONSFactoryProperty::ONSAddr = "ONSAddr"; // name server domain name +const char* ONSFactoryProperty::NAMESRV_ADDR = "NAMESRV_ADDR"; // name server ip addr +const char* ONSFactoryProperty::ConsumeThreadNums = "ConsumeThreadNums"; +const char* ONSFactoryProperty::OnsChannel = "OnsChannel"; +const char* ONSFactoryProperty::OnsTraceSwitch = "OnsTraceSwitch"; +const char* ONSFactoryProperty::ConsumerInstanceName = "ConsumerInstanceName"; +const char* ONSFactoryProperty::InstanceId = "InstanceId"; +const char* ONSFactoryProperty::DEFAULT_CHANNEL = "ALIYUN"; + +const std::string ONSFactoryProperty::EMPTY_STRING; + +ONSFactoryProperty::ONSFactoryProperty(bool set_defaults) { + if (set_defaults) { + setDefaults(); + loadConfigFile(); + } +} + +void ONSFactoryProperty::setDefaults() { + setMessageModel(ons::MessageModel::CLUSTERING); + setSendMsgTimeout(std::chrono::seconds(3)); + setSuspendDuration(std::chrono::seconds(3)); + setFactoryProperty(MaxMsgCacheSize, "1000"); + this->withTraceFeature(Trace::ON); +} + +void ONSFactoryProperty::loadConfigFile() { + std::string home_directory; + if (!ROCKETMQ_NAMESPACE::MixAll::homeDirectory(home_directory)) { + return; + } + + std::vector config_file_path_segments = {home_directory, "ons", "credential"}; + + std::string path_separator; + path_separator.push_back(ghc::filesystem::path::preferred_separator); + std::string config_path_string = absl::StrJoin(config_file_path_segments, path_separator); + ghc::filesystem::path config_file_path(config_path_string); + + std::error_code ec; + if (!ghc::filesystem::exists(config_file_path, ec) || !ghc::filesystem::is_regular_file(config_file_path, ec)) { + SPDLOG_INFO("No default config file found at {}", config_file_path.c_str()); + return; + } + + std::fstream config_file_stream; + config_file_stream.open(config_file_path.c_str(), std::ios_base::in); + if (!config_file_stream.is_open() || !config_file_stream.good()) { + SPDLOG_WARN("Failed to read config file: {}", config_file_path.c_str()); + return; + } + + std::string json; + + std::string line; + while (std::getline(config_file_stream, line)) { + json.append(line).append("\n"); + } + config_file_stream.close(); + + google::protobuf::Struct root; + google::protobuf::util::Status status = google::protobuf::util::JsonStringToMessage(json, &root); + if (!status.ok()) { + SPDLOG_WARN("Failed to parse config JSON. Cause: {}", status.message().as_string()); + return; + } + + auto fields = root.fields(); + if (fields.contains(AccessKey)) { + setFactoryProperty(AccessKey, fields[AccessKey].string_value()); + SPDLOG_INFO("Set {} through default config file", AccessKey); + } + + if (fields.contains(SecretKey)) { + setFactoryProperty(SecretKey, fields[SecretKey].string_value()); + SPDLOG_INFO("Set {} through default config file", SecretKey); + } + + if (fields.contains(NAMESRV_ADDR)) { + setFactoryProperty(NAMESRV_ADDR, fields[NAMESRV_ADDR].string_value()); + SPDLOG_INFO("Set {} through default config file", NAMESRV_ADDR); + } + + if (fields.contains(GroupId)) { + setFactoryProperty(GroupId, fields[GroupId].string_value()); + SPDLOG_INFO("Set {} through default config file", GroupId); + } +} + +bool ONSFactoryProperty::validate(const std::string& key, const std::string& value) { + if (key == MessageModel) { + if (value != BROADCASTING && value != CLUSTERING) { + throw ONSClientException(FAQ::errorMessage("MessageModel could only be set to BROADCASTING " + "or CLUSTERING, please set it.", + FAQ::CLIENT_CHECK_MSG_EXCEPTION)); + } + } + + if (key == AccessKey) { + if (value.empty()) { + throw ONSClientException(FAQ::errorMessage("AccessKey must be set.", FAQ::CLIENT_CHECK_MSG_EXCEPTION)); + } + } + + if (key == SecretKey) { + if (value.empty()) { + throw ONSClientException(FAQ::errorMessage("SecretKey must be set.", FAQ::CLIENT_CHECK_MSG_EXCEPTION)); + } + } + return true; +} + +std::string ONSFactoryProperty::getLogPath() const { + return getProperty(LogPath); +} + +ONSFactoryProperty& ONSFactoryProperty::setSendMsgTimeout(int value) { + char tmp[16]; + sprintf(tmp, "%d", value); + setFactoryProperty(SendMsgTimeoutMillis, tmp); + return *this; +} + +ONSFactoryProperty& ONSFactoryProperty::setSendMsgTimeout(std::chrono::milliseconds timeout) { + setFactoryProperty(SendMsgTimeoutMillis, std::to_string(timeout.count())); + return *this; +} + +ONSFactoryProperty& ONSFactoryProperty::setSendMsgRetryTimes(int value) { + char tmp[16]; + sprintf(tmp, "%d", value); + setFactoryProperty(SendMsgRetryTimes, tmp); + return *this; +} + +ONSFactoryProperty& ONSFactoryProperty::setMaxMsgCacheSize(int value) { + char tmp[256] = {0}; + sprintf(tmp, "%d", value); + setFactoryProperty(MaxMsgCacheSize, tmp); + return *this; +} + +ONSFactoryProperty& ONSFactoryProperty::withTraceFeature(Trace trace_flag) { + switch (trace_flag) { + case Trace::ON: + setFactoryProperty(OnsTraceSwitch, "true"); + break; + + case Trace::OFF: + setFactoryProperty(OnsTraceSwitch, "false"); + break; + } + return *this; +} + +ONSFactoryProperty& ONSFactoryProperty::setOnsTraceSwitch(bool should_trace) { + if (should_trace) { + setFactoryProperty(OnsTraceSwitch, "true"); + } else { + setFactoryProperty(OnsTraceSwitch, "false"); + } + return *this; +} + +void ONSFactoryProperty::setOnsChannel(ONSChannel channel) { + if (channel == ONSChannel::CLOUD) { + setFactoryProperty(OnsChannel, "CLOUD"); + } else if (channel == ONSChannel::ALIYUN) { + setFactoryProperty(OnsChannel, "ALIYUN"); + } else if (channel == ONSChannel::ALL) { + setFactoryProperty(OnsChannel, "ALL"); + } else if (channel == ONSChannel::LOCAL) { + setFactoryProperty(OnsChannel, "LOCAL"); + } else if (channel == ONSChannel::INNER) { + setFactoryProperty(OnsChannel, "INNER"); + } else { + throw ONSClientException(FAQ::errorMessage("ONSChannel could only be set to " + "CLOUD/ALIYUN/ALL, please reset it.", + FAQ::CLIENT_CHECK_MSG_EXCEPTION)); + } +} + +std::string ONSFactoryProperty::getProperty(const std::string& key) const { + std::string k(key.data(), key.length()); + auto it = property_map_.find(k); + if (property_map_.end() == it) { + return std::string(); + } + return it->second; +} + +std::string ONSFactoryProperty::getProperty(const std::string& key, std::string default_value) const { + auto&& value = getProperty(key); + if (value.empty()) { + return default_value; + } + return std::move(value); +} + +std::map ONSFactoryProperty::getFactoryProperties() const { + return property_map_; +} + +std::string ONSFactoryProperty::getProducerId() const { + auto&& group_id = getProperty(GroupId); + if (!group_id.empty()) { + return std::move(group_id); + } + + return getProperty(ProducerId, EMPTY_STRING); +} + +std::string ONSFactoryProperty::getConsumerId() const { + auto&& group_id = getProperty(GroupId); + if (!group_id.empty()) { + return std::move(group_id); + } + return getProperty(ConsumerId, EMPTY_STRING); +} + +std::string ONSFactoryProperty::getGroupId() const { + return getProperty(GroupId, EMPTY_STRING); +} + +std::string ONSFactoryProperty::getMessageModel() const { + return getProperty(MessageModel, EMPTY_STRING); +} + +ONSFactoryProperty& ONSFactoryProperty::setMessageModel(ons::MessageModel message_model) { + switch (message_model) { + case ons::MessageModel::CLUSTERING: + setFactoryProperty(ONSFactoryProperty::MessageModel, ONSFactoryProperty::CLUSTERING); + break; + + case ons::MessageModel::BROADCASTING: + setFactoryProperty(ONSFactoryProperty::MessageModel, ONSFactoryProperty::BROADCASTING); + break; + } + return *this; +} + +std::chrono::milliseconds ONSFactoryProperty::getSendMsgTimeout() const { + auto&& timeout = getProperty(SendMsgTimeoutMillis); + if (!timeout.empty()) { + std::int32_t value; + if (absl::SimpleAtoi(timeout, &value)) { + return std::chrono::milliseconds(value); + } + } + + return std::chrono::milliseconds(3000); +} + +std::chrono::milliseconds ONSFactoryProperty::getSuspendTimeMillis() const { + auto&& interval = getProperty(SuspendTimeMillis); + if (!interval.empty()) { + std::int32_t value; + if (absl::SimpleAtoi(interval, &value)) { + return std::chrono::milliseconds(value); + } + } + return std::chrono::milliseconds(0); +} + +void ONSFactoryProperty::setSuspendDuration(std::chrono::milliseconds duration) { + if (!duration.count()) { + return; + } + + setFactoryProperty(SuspendTimeMillis, std::to_string(duration.count())); +} + +int ONSFactoryProperty::getSendMsgRetryTimes() const { + auto it = property_map_.find(SendMsgRetryTimes); + if (it != property_map_.end()) { + return std::stoi(it->second); + } + return -1; +} + +int ONSFactoryProperty::getConsumeThreadNums() const { + auto it = property_map_.find(ConsumeThreadNums); + if (it != property_map_.end()) { + return std::stoi(it->second); + } + return -1; +} + +int ONSFactoryProperty::getMaxMsgCacheSize() const { + auto it = property_map_.find(MaxMsgCacheSize); + if (it != property_map_.end()) { + return std::stoi(it->second); + } + + return -1; +} + +int ONSFactoryProperty::getMaxMsgCacheSizeInMiB() const { + auto it = property_map_.find(MaxCachedMessageSizeInMiB); + if (it != property_map_.end()) { + return std::stoi(it->second); + } + + return -1; +} + +ONSChannel ONSFactoryProperty::getOnsChannel() const { + + auto&& value = getProperty(OnsChannel, "ALIYUN"); + + if ("CLOUD" == value) { + return ONSChannel::CLOUD; + } + + if ("ALIYUN" == value) { + return ONSChannel::ALIYUN; + } + + if ("ALL" == value) { + return ONSChannel::ALL; + } + + if ("LOCAL" == value) { + return ONSChannel::LOCAL; + } + + if ("INNER" == value) { + return ONSChannel::INNER; + } + + return ONSChannel::ALIYUN; // default value +} + +std::string ONSFactoryProperty::getChannel() const { + return getProperty(OnsChannel, DEFAULT_CHANNEL); +} + +std::string ONSFactoryProperty::getNameSrvAddr() const { + return getProperty(NAMESRV_ADDR, EMPTY_STRING); +} + +std::string ONSFactoryProperty::getNameSrvDomain() const { + return getProperty(ONSAddr, EMPTY_STRING); +} + +std::string ONSFactoryProperty::getAccessKey() const { + return getProperty(AccessKey, EMPTY_STRING); +} + +std::string ONSFactoryProperty::getSecretKey() const { + return getProperty(SecretKey, EMPTY_STRING); +} + +std::string ONSFactoryProperty::getConsumerInstanceName() const { + return getProperty(ConsumerInstanceName, EMPTY_STRING); +} + +bool ONSFactoryProperty::getOnsTraceSwitch() const { + auto&& value = getProperty(OnsTraceSwitch, "true"); + return "true" == value; +} + +std::string ONSFactoryProperty::getInstanceId() const { + return getProperty(InstanceId, EMPTY_STRING); +} + +ONSFactoryProperty::operator bool() { + ONSChannel channel = getOnsChannel(); + switch (channel) { + case ONSChannel::ALIYUN: + return !getAccessKey().empty() && !getSecretKey().empty(); + default: + return true; + } +} + +ONS_NAMESPACE_END diff --git a/src/main/cpp/ons/ONSMessageQueueSelector.cpp b/src/main/cpp/ons/ONSMessageQueueSelector.cpp new file mode 100644 index 000000000..40684369c --- /dev/null +++ b/src/main/cpp/ons/ONSMessageQueueSelector.cpp @@ -0,0 +1,33 @@ +#include "ONSMessageQueueSelector.h" + +#include +#include +#include +#include + +#include "ons/ONSClientException.h" +#include "ons/ONSErrorCode.h" +#include "rocketmq/MQMessageQueue.h" + +ONS_NAMESPACE_BEGIN + +ROCKETMQ_NAMESPACE::MQMessageQueue ONSMessageQueueSelector::select(const std::vector& mqs, + const rocketmq::MQMessage& msg, void* arg) { + if (mqs.empty()) { + ons::ONSClientException e("Message queue to select from is empty", MESSAGE_SELECTOR_QUEUE_EMPTY); + throw e; + } + + std::string* message_group = static_cast(arg); + + std::hash hasher; + std::size_t hash = hasher(*message_group); + hash = std::numeric_limits().max() & hash; + assert(hash >= 0); + + std::size_t remainder = hash % mqs.size(); + assert(remainder >= 0 && remainder < mqs.size()); + return mqs[remainder]; +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/ONSSendCallback.cpp b/src/main/cpp/ons/ONSSendCallback.cpp new file mode 100644 index 000000000..f78ea153d --- /dev/null +++ b/src/main/cpp/ons/ONSSendCallback.cpp @@ -0,0 +1,9 @@ +#include "ONSSendCallback.h" + +ONS_NAMESPACE_BEGIN + +absl::Mutex ONSSendCallback::mutex_; + +ONSSendCallback* ONSSendCallback::instance_ = nullptr; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/ONSUtil.cpp b/src/main/cpp/ons/ONSUtil.cpp new file mode 100644 index 000000000..f7cccc0cb --- /dev/null +++ b/src/main/cpp/ons/ONSUtil.cpp @@ -0,0 +1,119 @@ +#include "ONSUtil.h" + +#include +#include + +#include "Protocol.h" +#include "rocketmq/RocketMQ.h" + +ONS_NAMESPACE_BEGIN + +ONSUtil::ONSUtil() { + reserved_key_set_ext_.insert(SystemPropKey::TAG); + reserved_key_set_ext_.insert(SystemPropKey::MSGID); + reserved_key_set_ext_.insert(SystemPropKey::RECONSUMETIMES); + reserved_key_set_ext_.insert(SystemPropKey::STARTDELIVERTIME); +} + +ONSUtil& ONSUtil::get() { + static ONSUtil ons_util; + return ons_util; +} + +Message ONSUtil::msgConvert(const ROCKETMQ_NAMESPACE::MQMessageExt& rocketmq_message_ext) { + Message message; + + if (!rocketmq_message_ext.getTopic().empty()) { + message.setTopic(rocketmq_message_ext.getTopic()); + } + + if (!rocketmq_message_ext.getKeys().empty()) { + for (const auto& key : rocketmq_message_ext.getKeys()) { + message.attachKey(key); + } + } + + if (!rocketmq_message_ext.getTags().empty()) { + message.setTag(rocketmq_message_ext.getTags()); + } + + if (!rocketmq_message_ext.getBody().empty()) { + message.setBody(rocketmq_message_ext.getBody()); + } + message.setMsgID(rocketmq_message_ext.getMsgId()); + + message.setReconsumeTimes(rocketmq_message_ext.getDeliveryAttempt()); + + auto store_time = + std::chrono::system_clock::time_point() + std::chrono::milliseconds(rocketmq_message_ext.getStoreTimestamp()); + message.setStoreTimestamp(store_time); + + message.setBornTimestamp(rocketmq_message_ext.bornTimestamp()); + + message.setQueueOffset(rocketmq_message_ext.getQueueOffset()); + + std::map properties = rocketmq_message_ext.getProperties(); + for (const auto& entry : properties) { + if (reserved_key_set_ext_.contains(entry.first)) { + if (SystemPropKey::TAG == entry.first) { + message.setTag(entry.second); + continue; + } + + if (SystemPropKey::MSGID == entry.first) { + message.setMsgID(entry.second); + continue; + } + + if (SystemPropKey::RECONSUMETIMES == entry.first) { + std::int32_t reconsume_times; + if (absl::SimpleAtoi(entry.second, &reconsume_times)) { + message.setReconsumeTimes(reconsume_times); + } + } + } else { + message.putUserProperty(entry.first, entry.second); + } + } + return message; +} + +ROCKETMQ_NAMESPACE::MQMessage ONSUtil::msgConvert(const Message& msg) { + ROCKETMQ_NAMESPACE::MQMessage message; + if (!msg.getTopic().empty()) { + message.setTopic(msg.getTopic()); + } + + if (!msg.getKeys().empty()) { + message.setKeys(msg.getKeys()); + } + + if (!msg.getTag().empty()) { + message.setTags(msg.getTag()); + } + + auto delivery_timepoint = msg.getStartDeliverTime(); + if (delivery_timepoint > std::chrono::system_clock::now()) { + auto delivery_ms = + std::chrono::duration_cast(delivery_timepoint.time_since_epoch()).count(); + message.setProperty(SystemPropKey::STARTDELIVERTIME, std::to_string(delivery_ms)); + } + + if (!msg.getBody().empty()) { + message.setBody(msg.getBody()); + } + + std::map properties = msg.getUserProperties(); + if (!properties.empty()) { + auto it = properties.begin(); + for (; it != properties.end(); ++it) { + auto its = reserved_key_set_ext_.find(it->first); + if (its == reserved_key_set_ext_.end()) { + message.setProperty(it->first, it->second); + } + } + } + return message; +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/OrderConsumerImpl.cpp b/src/main/cpp/ons/OrderConsumerImpl.cpp new file mode 100644 index 000000000..423675889 --- /dev/null +++ b/src/main/cpp/ons/OrderConsumerImpl.cpp @@ -0,0 +1,23 @@ +#include "OrderConsumerImpl.h" +#include "OrderListenerWrapper.h" + +ONS_NAMESPACE_BEGIN + +OrderConsumerImpl::OrderConsumerImpl(const ONSFactoryProperty& factory_property) + : ONSConsumerAbstract(factory_property) {} + +void OrderConsumerImpl::start() { ONSConsumerAbstract::start(); } + +void OrderConsumerImpl::shutdown() { ONSConsumerAbstract::shutdown(); } + +void OrderConsumerImpl::subscribe(const std::string& topic, const std::string& expression) { + ONSConsumerAbstract::subscribe(topic, expression); +} + +void OrderConsumerImpl::registerMessageListener(MessageOrderListener* listener) { + std::unique_ptr wrapped_listener = + absl::make_unique(listener); + ONSConsumerAbstract::registerMessageListener(std::move(wrapped_listener)); +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/OrderProducerImpl.cpp b/src/main/cpp/ons/OrderProducerImpl.cpp new file mode 100644 index 000000000..cc79b1f63 --- /dev/null +++ b/src/main/cpp/ons/OrderProducerImpl.cpp @@ -0,0 +1,60 @@ +#include "OrderProducerImpl.h" + +#include "absl/strings/ascii.h" + +#include "ONSUtil.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/RocketMQ.h" + +ONS_NAMESPACE_BEGIN + +OrderProducerImpl::OrderProducerImpl(const ONSFactoryProperty& factory_property) + : ONSClientAbstract(factory_property), producer_(factory_property.getGroupId()) { + auto send_msg_timeout = factory_property.getSendMsgTimeout(); + if (send_msg_timeout.count()) { + producer_.setSendMsgTimeout(send_msg_timeout); + } + + absl::string_view instanceName = absl::StripAsciiWhitespace(factory_property.getInstanceId()); + if (instanceName.empty()) { + producer_.setInstanceName(buildInstanceName()); + } else { + producer_.setInstanceName(std::string(instanceName)); + } + int send_msg_retry_times = factory_property.getSendMsgRetryTimes(); + if (send_msg_retry_times > 0) { + producer_.setMaxAttemptTimes(send_msg_retry_times); + } + + auto credentials_provider = std::make_shared( + factory_property.getAccessKey(), factory_property.getSecretKey()); + producer_.setCredentialsProvider(credentials_provider); + + if (access_point_) { + std::string&& resource_namespace = access_point_.resourceNamespace(); + producer_.setResourceNamespace(resource_namespace); + producer_.setNamesrvAddr(access_point_.nameServerAddress()); + } else if (!factory_property.getNameSrvAddr().empty()) { + producer_.setNamesrvAddr(factory_property.getNameSrvAddr()); + } else if (!factory_property.getNameSrvDomain().empty()) { + producer_.setNameServerListDiscoveryEndpoint(factory_property.getNameSrvDomain()); + } +} + +void OrderProducerImpl::start() { + producer_.start(); +} + +void OrderProducerImpl::shutdown() { + producer_.shutdown(); +} + +SendResultONS OrderProducerImpl::send(Message& msg, std::string message_group) { + ROCKETMQ_NAMESPACE::MQMessage message = ons::ONSUtil::get().msgConvert(msg); + ROCKETMQ_NAMESPACE::SendResult send_result = producer_.send(message, message_group); + SendResultONS ons_send_result = SendResultONS(); + ons_send_result.setMessageId(send_result.getMsgId()); + return ons_send_result; +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/ProducerImpl.cpp b/src/main/cpp/ons/ProducerImpl.cpp new file mode 100644 index 000000000..08b3c0349 --- /dev/null +++ b/src/main/cpp/ons/ProducerImpl.cpp @@ -0,0 +1,114 @@ +#include "ProducerImpl.h" + +#include +#include +#include +#include + +#include "ONSUtil.h" +#include "absl/strings/ascii.h" +#include "ons/ONSClientException.h" +#include "rocketmq/CredentialsProvider.h" +#include "rocketmq/RocketMQ.h" + +ONS_NAMESPACE_BEGIN + +ProducerImpl::ProducerImpl(const ONSFactoryProperty& factory_property) + : ONSClientAbstract(factory_property), producer_(std::string(factory_property.getProducerId())) { + + auto send_msg_timeout = factory_property.getSendMsgTimeout(); + if (send_msg_timeout.count()) { + producer_.setSendMsgTimeout(send_msg_timeout); + } + + absl::string_view instanceName = absl::StripAsciiWhitespace(factory_property.getInstanceId()); + if (instanceName.empty()) { + producer_.setInstanceName(buildInstanceName()); + } else { + producer_.setInstanceName(std::string(instanceName)); + } + int send_msg_retry_times = factory_property.getSendMsgRetryTimes(); + if (send_msg_retry_times > 0) { + producer_.setMaxAttemptTimes(send_msg_retry_times); + } + + auto credentials_provider = std::make_shared( + factory_property.getAccessKey(), factory_property.getSecretKey()); + producer_.setCredentialsProvider(credentials_provider); + + if (access_point_) { + std::string&& resource_namespace = access_point_.resourceNamespace(); + producer_.setResourceNamespace(resource_namespace); + producer_.setNamesrvAddr(access_point_.nameServerAddress()); + } else if (!factory_property.getNameSrvAddr().empty()) { + producer_.setNamesrvAddr(factory_property.getNameSrvAddr()); + } else if (!factory_property.getNameSrvDomain().empty()) { + producer_.setNameServerListDiscoveryEndpoint(factory_property.getNameSrvDomain()); + } + + bool traceSwitchOn = factory_property.getOnsTraceSwitch(); + producer_.enableTracing(traceSwitchOn); +} + +void ProducerImpl::start() { + producer_.start(); +} + +void ProducerImpl::shutdown() { + producer_.shutdown(); +} + +SendResultONS ProducerImpl::send(Message& message) { + ROCKETMQ_NAMESPACE::MQMessage mq_message = ONSUtil::get().msgConvert(message); + + ROCKETMQ_NAMESPACE::SendResult send_result = producer_.send(mq_message); + + SendResultONS send_result_ons; + send_result_ons.setMessageId(send_result.getMsgId()); + return send_result_ons; +} + +SendResultONS ProducerImpl::send(Message& message, std::error_code& ec) noexcept { + ROCKETMQ_NAMESPACE::MQMessage mq_message = ONSUtil::get().msgConvert(message); + ROCKETMQ_NAMESPACE::SendResult send_result = producer_.send(mq_message, ec); + if (ec) { + return {}; + } + SendResultONS send_result_ons; + send_result_ons.setMessageId(send_result.getMsgId()); + return send_result_ons; +} + +void ProducerImpl::sendAsync(Message& message, SendCallbackONS* callback) noexcept { + ROCKETMQ_NAMESPACE::MQMessage mq_message = ONSUtil::get().msgConvert(message); + + if (!callback) { + abort(); + } + + std::shared_ptr send_callback_ons_wrapper_shared_ptr = nullptr; + { + absl::MutexLock lock(&callbacks_mtx_); + if (nullptr == callbacks_[callback]) { + send_callback_ons_wrapper_shared_ptr = std::make_shared(callback); + callbacks_[callback] = send_callback_ons_wrapper_shared_ptr; + } + send_callback_ons_wrapper_shared_ptr = callbacks_[callback]; + } + producer_.send(mq_message, send_callback_ons_wrapper_shared_ptr.get(), true); +} + +void ProducerImpl::sendOneway(Message& message) noexcept { + ROCKETMQ_NAMESPACE::MQMessage mq_message = ONSUtil::get().msgConvert(message); + producer_.sendOneway(mq_message); +} + +ROCKETMQ_NAMESPACE::MQMessageQueue ProducerImpl::messageQueueConvert(const MessageQueueONS& message_queue_ons) { + ROCKETMQ_NAMESPACE::MQMessageQueue mq_message_queue; + mq_message_queue.setBrokerName(message_queue_ons.getBrokerName()); + mq_message_queue.setQueueId(message_queue_ons.getQueueId()); + mq_message_queue.setTopic(message_queue_ons.getTopic()); + return mq_message_queue; +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/SendCallbackONSWrapper.cpp b/src/main/cpp/ons/SendCallbackONSWrapper.cpp new file mode 100644 index 000000000..529ab993b --- /dev/null +++ b/src/main/cpp/ons/SendCallbackONSWrapper.cpp @@ -0,0 +1,29 @@ + +#include "SendCallbackONSWrapper.h" +#include "rocketmq/RocketMQ.h" + +ONS_NAMESPACE_BEGIN + +SendCallbackONSWrapper::SendCallbackONSWrapper(SendCallbackONS* send_callback_ons_ptr) + : send_callback_ons_ptr_(send_callback_ons_ptr) { +} + +void SendCallbackONSWrapper::onSuccess(ROCKETMQ_NAMESPACE::SendResult& send_result) noexcept { + if (nullptr == send_callback_ons_ptr_) { + return; + } + SendResultONS send_result_ons; + send_result_ons.setMessageId(send_result.getMsgId()); + send_callback_ons_ptr_->onSuccess(send_result_ons); +} + +void SendCallbackONSWrapper::onFailure(const std::error_code& ec) noexcept { + if (nullptr == send_callback_ons_ptr_) { + return; + } + + ONSClientException ons_client_exception(ec.message(), ec.value()); + send_callback_ons_ptr_->onException(ons_client_exception); +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/SendResultONS.cpp b/src/main/cpp/ons/SendResultONS.cpp new file mode 100755 index 000000000..89cb79044 --- /dev/null +++ b/src/main/cpp/ons/SendResultONS.cpp @@ -0,0 +1,19 @@ +#include "ons/SendResultONS.h" + +#include + +ONS_NAMESPACE_BEGIN + +SendResultONS::SendResultONS() = default; + +SendResultONS::~SendResultONS() = default; + +void SendResultONS::setMessageId(const std::string& message_id) { + message_id_ = std::string(message_id.data(), message_id.length()); +} + +const std::string& SendResultONS::getMessageId() const { + return message_id_; +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/TransactionProducerImpl.cpp b/src/main/cpp/ons/TransactionProducerImpl.cpp new file mode 100644 index 000000000..a0a307277 --- /dev/null +++ b/src/main/cpp/ons/TransactionProducerImpl.cpp @@ -0,0 +1,77 @@ +#include "TransactionProducerImpl.h" + +#include + +#include "absl/strings/ascii.h" + +#include "ONSUtil.h" +#include "ons/SendResultONS.h" +#include "ons/TransactionStatus.h" + +#include "spdlog/spdlog.h" + +ONS_NAMESPACE_BEGIN + +TransactionProducerImpl::TransactionProducerImpl(const ONSFactoryProperty& factory_property, + LocalTransactionChecker* checker) + : ONSClientAbstract(factory_property), producer_(factory_property.getGroupId()) { + auto send_msg_timeout = factory_property.getSendMsgTimeout(); + if (send_msg_timeout.count()) { + producer_.setSendMsgTimeout(send_msg_timeout); + } + + absl::string_view instanceName = absl::StripAsciiWhitespace(factory_property.getInstanceId()); + if (instanceName.empty()) { + producer_.setInstanceName(buildInstanceName()); + } else { + producer_.setInstanceName(std::string(instanceName)); + } + int send_msg_retry_times = factory_property.getSendMsgRetryTimes(); + if (send_msg_retry_times > 0) { + producer_.setMaxAttemptTimes(send_msg_retry_times); + } + + auto credentials_provider = std::make_shared( + factory_property.getAccessKey(), factory_property.getSecretKey()); + producer_.setCredentialsProvider(credentials_provider); + + if (access_point_) { + absl::string_view resource_namespace = access_point_.resourceNamespace(); + producer_.setResourceNamespace(std::string(resource_namespace.data(), resource_namespace.length())); + producer_.setNamesrvAddr(access_point_.nameServerAddress()); + } else if (!factory_property.getNameSrvAddr().empty()) { + producer_.setNamesrvAddr(factory_property.getNameSrvAddr()); + } else if (!factory_property.getNameSrvDomain().empty()) { + producer_.setNameServerListDiscoveryEndpoint(factory_property.getNameSrvDomain()); + } +} + +void TransactionProducerImpl::start() { + producer_.start(); +} + +void TransactionProducerImpl::shutdown() { + producer_.shutdown(); +} + +SendResultONS TransactionProducerImpl::send(Message& msg, LocalTransactionExecuter* executor) { + assert(executor); + ROCKETMQ_NAMESPACE::MQMessage message = ONSUtil::get().msgConvert(msg); + auto transaction = producer_.prepare(message); + TransactionStatus status = executor->execute(msg); + switch (status) { + case TransactionStatus::CommitTransaction: + transaction->commit(); + break; + case TransactionStatus::RollbackTransaction: + transaction->rollback(); + break; + case TransactionStatus::Unknow: + break; + } + auto send_result = SendResultONS(); + send_result.setMessageId(transaction->messageId()); + return send_result; +} + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/AccessPoint.h b/src/main/cpp/ons/include/AccessPoint.h new file mode 100644 index 000000000..236cdd577 --- /dev/null +++ b/src/main/cpp/ons/include/AccessPoint.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "absl/strings/string_view.h" + +#include "rocketmq/Logger.h" +#include "rocketmq/RocketMQ.h" + +#include "spdlog/spdlog.h" + +#include "ons/ONSClient.h" + +ONS_NAMESPACE_BEGIN + +class AccessPoint { +public: + explicit AccessPoint(absl::string_view access_point) : access_point_(access_point.data(), access_point.length()) { + SPDLOG_INFO("Resource namespace={}, name-server-address={}", resourceNamespace(), nameServerAddress()); + } + + explicit operator bool() const; + + std::string resourceNamespace() const; + + std::string nameServerAddress() const; + +private: + std::string access_point_; + + static const char* SCHEMA; + + static const char* RESOURCE_NAMESPACE_PREFIX; + + static const char* PREFIX; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/ConsumerImpl.h b/src/main/cpp/ons/include/ConsumerImpl.h new file mode 100644 index 000000000..3e1b24ceb --- /dev/null +++ b/src/main/cpp/ons/include/ConsumerImpl.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "MessageListenerWrapper.h" +#include "ONSConsumerAbstract.h" +#include "absl/container/flat_hash_map.h" +#include "ons/ONSFactory.h" +#include "rocketmq/DefaultMQPushConsumer.h" + +ONS_NAMESPACE_BEGIN + +class ConsumerImpl : public PushConsumer, public ONSConsumerAbstract { +public: + explicit ConsumerImpl(const ONSFactoryProperty& factory_property); + + ~ConsumerImpl() override = default; + + void start() override; + + void shutdown() override; + + void subscribe(const std::string& topic, const std::string& sub_expression) override; + + void registerMessageListener(MessageListener* listener) override; + + void withOffsetStore(std::unique_ptr offset_store) override; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/FAQ.h b/src/main/cpp/ons/include/FAQ.h new file mode 100644 index 000000000..d222b4551 --- /dev/null +++ b/src/main/cpp/ons/include/FAQ.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include "ons/ONSClient.h" + +ONS_NAMESPACE_BEGIN + +class FAQ { +public: + FAQ() = default; + virtual ~FAQ() = default; + + static const std::string FIND_NS_FAILED; + static const std::string CONNECT_BROKER_FAILED; + static const std::string SEND_MSG_TO_BROKER_TIMEOUT; + static const std::string SERVICE_STATE_WRONG; + static const std::string BROKER_RESPONSE_EXCEPTION; + static const std::string CLIENT_CHECK_MSG_EXCEPTION; + static const std::string TOPIC_ROUTE_NOT_EXIST; + + static std::string errorMessage(const std::string& error_message, const std::string& url) { + std::stringstream ss; + ss << error_message << "\nSee " << url << " for further details."; + return ss.str(); + } +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/InstanceUtil.h b/src/main/cpp/ons/include/InstanceUtil.h new file mode 100644 index 000000000..8a1b703e7 --- /dev/null +++ b/src/main/cpp/ons/include/InstanceUtil.h @@ -0,0 +1,24 @@ +#pragma once + +#include "ONSUtil.h" +#include + +ONS_NAMESPACE_BEGIN + +const std::string INSTANCE_PREFIX = "MQ_INST_"; +static const std::string ENDPOINT_PREFIX = "http://"; +const std::string INSTANCE_REGEX = INSTANCE_PREFIX + "\\d+_\\w{8}"; +const std::string RETRY_INSTANCE_PREFIX = "%RETRY%" + INSTANCE_PREFIX; +const int INSTANCE_PREFIX_LENGTH = INSTANCE_PREFIX.length(); +const std::string MISC_HEAD = "http://MQ_INST_"; + +class InstanceUtil { +public: + static bool validateInstanceEndpoint(const std::string& endpoint); + + static std::string parseInstanceIdFromEndpoint(const std::string& endpoint); + + static bool validateNameSrvAddr(const std::string& nameServerAddr); +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/MessageListenerWrapper.h b/src/main/cpp/ons/include/MessageListenerWrapper.h new file mode 100644 index 000000000..e0c6e212b --- /dev/null +++ b/src/main/cpp/ons/include/MessageListenerWrapper.h @@ -0,0 +1,21 @@ +#pragma once + +#include "ons/MessageListener.h" +#include "rocketmq/MessageListener.h" +#include "rocketmq/RocketMQ.h" + +ONS_NAMESPACE_BEGIN + +class MessageListenerWrapper : public ROCKETMQ_NAMESPACE::StandardMessageListener { + +public: + explicit MessageListenerWrapper(ons::MessageListener* message_listener); + + ROCKETMQ_NAMESPACE::ConsumeMessageResult + consumeMessage(const std::vector& msgs) override; + +private: + ons::MessageListener* message_listener_{nullptr}; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/ONSClientAbstract.h b/src/main/cpp/ons/include/ONSClientAbstract.h new file mode 100644 index 000000000..e23f8be62 --- /dev/null +++ b/src/main/cpp/ons/include/ONSClientAbstract.h @@ -0,0 +1,26 @@ +#pragma once + +#include "AccessPoint.h" +#include "ons/ONSFactory.h" + +ONS_NAMESPACE_BEGIN + +class ONSClientAbstract { +public: + explicit ONSClientAbstract(const ONSFactoryProperty& factory_property); + + virtual ~ONSClientAbstract() = default; + + virtual void start(); + + virtual void shutdown(); + +protected: + std::string buildInstanceName(); + + ONSFactoryProperty factory_property_; + + AccessPoint access_point_; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/ONSConsumerAbstract.h b/src/main/cpp/ons/include/ONSConsumerAbstract.h new file mode 100644 index 000000000..e5dc3fb00 --- /dev/null +++ b/src/main/cpp/ons/include/ONSConsumerAbstract.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "ONSClientAbstract.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/MessageListener.h" +#include "rocketmq/RocketMQ.h" + +ONS_NAMESPACE_BEGIN + +class ONSConsumerAbstract : public ONSClientAbstract { +public: + explicit ONSConsumerAbstract(const ONSFactoryProperty& factory_property); + + void start() override; + + void shutdown() override; + +protected: + void subscribe(absl::string_view topic, absl::string_view sub_expression); + + void registerMessageListener(std::unique_ptr message_listener); + + ROCKETMQ_NAMESPACE::DefaultMQPushConsumer consumer_; + + std::unique_ptr message_listener_; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/ONSFactoryInstance.h b/src/main/cpp/ons/include/ONSFactoryInstance.h new file mode 100644 index 000000000..88f86fb5d --- /dev/null +++ b/src/main/cpp/ons/include/ONSFactoryInstance.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" + +#include "ons/ONSFactoryAPI.h" + +ONS_NAMESPACE_BEGIN + +class ONSFactoryInstance : public ONSFactoryAPI { +public: + ONSFactoryInstance() = default; + + ~ONSFactoryInstance() override = default; + + Producer* createProducer(ONSFactoryProperty& factory_properties) override LOCKS_EXCLUDED(producer_table_mtx_); + + OrderProducer* createOrderProducer(ONSFactoryProperty& factory_properties) override + LOCKS_EXCLUDED(order_producer_table_mtx_); + + OrderConsumer* createOrderConsumer(ONSFactoryProperty& factory_properties) override + LOCKS_EXCLUDED(order_consumer_table_mtx_); + + TransactionProducer* createTransactionProducer(ONSFactoryProperty& factory_properties, + LocalTransactionChecker* checker) override + LOCKS_EXCLUDED(transaction_producer_table_mtx_); + + PullConsumer* createPullConsumer(ONSFactoryProperty& factory_properties) override; + + PushConsumer* createPushConsumer(ONSFactoryProperty& factory_properties) override LOCKS_EXCLUDED(consumer_table_mtx_); + +private: + friend ONSFactoryAPI* instance(); + + std::vector> producer_table_ GUARDED_BY(producer_table_mtx_); + absl::Mutex producer_table_mtx_; + + std::vector> order_producer_table_ GUARDED_BY(order_producer_table_mtx_); + absl::Mutex order_producer_table_mtx_; + + std::vector> + transaction_producer_table_ GUARDED_BY(transaction_producer_table_mtx_); + absl::Mutex transaction_producer_table_mtx_; + + std::vector> consumer_table_ GUARDED_BY(consumer_table_mtx_); + absl::Mutex consumer_table_mtx_; + + std::vector> order_consumer_table_ GUARDED_BY(order_consumer_table_mtx_); + absl::Mutex order_consumer_table_mtx_; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/ONSMessageQueueSelector.h b/src/main/cpp/ons/include/ONSMessageQueueSelector.h new file mode 100644 index 000000000..27274b191 --- /dev/null +++ b/src/main/cpp/ons/include/ONSMessageQueueSelector.h @@ -0,0 +1,17 @@ +#pragma once + +#include "rocketmq/MQMessageQueue.h" +#include "rocketmq/MQSelector.h" +#include "rocketmq/RocketMQ.h" + +#include "ons/ONSClient.h" + +ONS_NAMESPACE_BEGIN + +class ONSMessageQueueSelector : public ROCKETMQ_NAMESPACE::MessageQueueSelector { +public: + ROCKETMQ_NAMESPACE::MQMessageQueue select(const std::vector& mqs, + const ROCKETMQ_NAMESPACE::MQMessage& msg, void* arg) override; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/ONSSendCallback.h b/src/main/cpp/ons/include/ONSSendCallback.h new file mode 100755 index 000000000..77099a076 --- /dev/null +++ b/src/main/cpp/ons/include/ONSSendCallback.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" + +#include "ons/ONSCallback.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/MQClientException.h" +#include "rocketmq/SendResult.h" + +ONS_NAMESPACE_BEGIN + +class ONSSendCallback : public ROCKETMQ_NAMESPACE::SendCallback { + +public: + ONSSendCallback() = default; + + static ONSSendCallback* instance() { + if (instance_ == nullptr) { + absl::MutexLock lock(&mutex_); + if (instance_ == nullptr) { + instance_ = new ONSSendCallback(); + } + } + + return instance_; + } + + ~ONSSendCallback() override = default; + + void onSuccess(ROCKETMQ_NAMESPACE::SendResult& send_result) noexcept override { + SendResultONS resultONS; + resultONS.setMessageId(send_result.getMsgId()); + assert(callback_ != nullptr); + callback_->onSuccess(resultONS); + } + + void onFailure(const std::error_code& ec) noexcept override { + ONSClientException ons_exception(ec.message(), ec.value()); + assert(callback_ != nullptr); + callback_->onException(ons_exception); + } + + void setOnsCallBack(SendCallbackONS* send_callback) { + callback_ = send_callback; + } + +private: + SendCallbackONS* callback_{nullptr}; + + static absl::Mutex mutex_; + + static ONSSendCallback* instance_; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/ONSUtil.h b/src/main/cpp/ons/include/ONSUtil.h new file mode 100755 index 000000000..ccca48d5c --- /dev/null +++ b/src/main/cpp/ons/include/ONSUtil.h @@ -0,0 +1,41 @@ +#pragma once + +#include "absl/container/flat_hash_set.h" + +#include "ons/Message.h" +#include "rocketmq/Logger.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/RocketMQ.h" +#include "spdlog/spdlog.h" + +ONS_NAMESPACE_BEGIN + +class ONSUtil { +public: + ONSUtil(); + + static ONSUtil& get(); + + /** + * @brief This function translates messages from RocketMQ representation back to ONS. It should be employed during + * message consumption procedure. + * + * @param message_ext + * @return Message + */ + Message msgConvert(const ROCKETMQ_NAMESPACE::MQMessageExt& message_ext); + + /** + * @brief This function translates ONS message to RocketMQ counterpart. It should be used when delivering messages to + * brokers. + * + * @param message + * @return ROCKETMQ_NAMESPACE::MQMessage + */ + ROCKETMQ_NAMESPACE::MQMessage msgConvert(const Message& message); + +private: + absl::flat_hash_set reserved_key_set_ext_; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/OffsetStoreAdaptor.h b/src/main/cpp/ons/include/OffsetStoreAdaptor.h new file mode 100644 index 000000000..2e922929f --- /dev/null +++ b/src/main/cpp/ons/include/OffsetStoreAdaptor.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "ons/ONSClient.h" +#include "ons/OffsetStore.h" +#include "rocketmq/OffsetStore.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class OffsetStoreAdaptor : public ROCKETMQ_NAMESPACE::OffsetStore { +public: + OffsetStoreAdaptor(std::unique_ptr store) : store_(std::move(store)) { + } + + void load() override { + } + + void updateOffset(const MQMessageQueue& message_queue, int64_t offset) override { + store_->writeOffset(message_queue.simpleName(), offset); + } + + bool readOffset(const MQMessageQueue& message_queue, int64_t& offset) override { + return store_->readOffset(message_queue.simpleName(), offset); + } + +private: + std::unique_ptr store_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/OrderConsumerImpl.h b/src/main/cpp/ons/include/OrderConsumerImpl.h new file mode 100644 index 000000000..b7fe24614 --- /dev/null +++ b/src/main/cpp/ons/include/OrderConsumerImpl.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include "ONSConsumerAbstract.h" +#include "OrderListenerWrapper.h" +#include "ons/ONSFactory.h" +#include "ons/OrderConsumer.h" + +#include "rocketmq/DefaultMQPushConsumer.h" + +ONS_NAMESPACE_BEGIN + +class OrderConsumerImpl : public OrderConsumer, public ONSConsumerAbstract { +public: + explicit OrderConsumerImpl(const ONSFactoryProperty& factory_property); + + ~OrderConsumerImpl() override = default; + + void start() override; + + void shutdown() override; + + void subscribe(const std::string& topic, const std::string& subscribe_expression) override; + + void registerMessageListener(MessageOrderListener* listener) override; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/OrderListenerWrapper.h b/src/main/cpp/ons/include/OrderListenerWrapper.h new file mode 100644 index 000000000..588389d16 --- /dev/null +++ b/src/main/cpp/ons/include/OrderListenerWrapper.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "ONSUtil.h" +#include "ons/ConsumeOrderContext.h" +#include "ons/MessageOrderListener.h" +#include "ons/OrderAction.h" +#include "rocketmq/MessageListener.h" + +ONS_NAMESPACE_BEGIN + +class OrderListenerWrapper : public ROCKETMQ_NAMESPACE::FifoMessageListener { +public: + explicit OrderListenerWrapper(MessageOrderListener* listener) : wrapped_listener_(listener) { + assert(wrapped_listener_); + } + + ROCKETMQ_NAMESPACE::ConsumeMessageResult consumeMessage(const ROCKETMQ_NAMESPACE::MQMessageExt& msg) override { + auto&& message = ONSUtil::get().msgConvert(msg); + ConsumeOrderContext context; + OrderAction action = wrapped_listener_->consume(message, context); + switch (action) { + case OrderAction::Success: + return ROCKETMQ_NAMESPACE::ConsumeMessageResult::SUCCESS; + + case OrderAction::Suspend: + return ROCKETMQ_NAMESPACE::ConsumeMessageResult::FAILURE; + default: + return ROCKETMQ_NAMESPACE::ConsumeMessageResult::SUCCESS; + } + } + +private: + MessageOrderListener* wrapped_listener_; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/OrderProducerImpl.h b/src/main/cpp/ons/include/OrderProducerImpl.h new file mode 100644 index 000000000..b357ba089 --- /dev/null +++ b/src/main/cpp/ons/include/OrderProducerImpl.h @@ -0,0 +1,29 @@ +#pragma once + +#include "ONSClientAbstract.h" +#include "ONSMessageQueueSelector.h" +#include "ons/ONSFactory.h" +#include "ons/OrderProducer.h" +#include "rocketmq/DefaultMQProducer.h" + +ONS_NAMESPACE_BEGIN + +class OrderProducerImpl : public OrderProducer, public ONSClientAbstract { + +public: + explicit OrderProducerImpl(const ONSFactoryProperty& factory_property); + + ~OrderProducerImpl() override = default; + + void start() override; + + void shutdown() override; + + SendResultONS send(Message& msg, std::string message_group) override; + +private: + ROCKETMQ_NAMESPACE::DefaultMQProducer producer_; + ONSMessageQueueSelector selector_; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/ProducerImpl.h b/src/main/cpp/ons/include/ProducerImpl.h new file mode 100644 index 000000000..d5aa9aec0 --- /dev/null +++ b/src/main/cpp/ons/include/ProducerImpl.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include "ONSClientAbstract.h" +#include "SendCallbackONSWrapper.h" +#include "absl/container/flat_hash_map.h" +#include "ons/ONSFactory.h" +#include "ons/Producer.h" +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/RocketMQ.h" + +ONS_NAMESPACE_BEGIN + +class ProducerImpl : public Producer, public ONSClientAbstract { +public: + explicit ProducerImpl(const ONSFactoryProperty& factory_property); + + ~ProducerImpl() override = default; + + void start() override; + + void shutdown() override; + + SendResultONS send(Message& message) noexcept(false) override; + + SendResultONS send(Message& message, std::error_code& ec) noexcept override; + + void sendAsync(Message& message, SendCallbackONS* callback) noexcept override; + + void sendOneway(Message& message) noexcept override; + +private: + static ROCKETMQ_NAMESPACE::MQMessageQueue messageQueueConvert(const MessageQueueONS& message_queue_ons); + + ROCKETMQ_NAMESPACE::DefaultMQProducer producer_; + + absl::flat_hash_map> callbacks_ GUARDED_BY(callbacks_mtx_); + + absl::Mutex callbacks_mtx_; // protects clients_ +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/SendCallbackONSWrapper.h b/src/main/cpp/ons/include/SendCallbackONSWrapper.h new file mode 100644 index 000000000..947f8f119 --- /dev/null +++ b/src/main/cpp/ons/include/SendCallbackONSWrapper.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "ons/ONSCallback.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/RocketMQ.h" + +ONS_NAMESPACE_BEGIN + +class SendCallbackONSWrapper : public ROCKETMQ_NAMESPACE::SendCallback { +public: + explicit SendCallbackONSWrapper(SendCallbackONS* callback); + + void onSuccess(ROCKETMQ_NAMESPACE::SendResult& send_result) noexcept override; + + void onFailure(const std::error_code& ec) noexcept override; + +private: + SendCallbackONS* send_callback_ons_ptr_{}; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/ons/include/TransactionProducerImpl.h b/src/main/cpp/ons/include/TransactionProducerImpl.h new file mode 100644 index 000000000..07cb1bceb --- /dev/null +++ b/src/main/cpp/ons/include/TransactionProducerImpl.h @@ -0,0 +1,27 @@ +#pragma once + +#include "ons/TransactionProducer.h" + +#include "ONSClientAbstract.h" +#include "ons/ONSFactory.h" +#include "rocketmq/DefaultMQProducer.h" + +ONS_NAMESPACE_BEGIN + +class TransactionProducerImpl : public TransactionProducer, public ONSClientAbstract { +public: + TransactionProducerImpl(const ONSFactoryProperty& factory_property, LocalTransactionChecker* checker); + + ~TransactionProducerImpl() override = default; + + void start() override; + + void shutdown() override; + + SendResultONS send(Message& msg, LocalTransactionExecuter* executor) override; + +private: + ROCKETMQ_NAMESPACE::DefaultMQProducer producer_; +}; + +ONS_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/BUILD.bazel b/src/main/cpp/remoting/BUILD.bazel new file mode 100644 index 000000000..155672b06 --- /dev/null +++ b/src/main/cpp/remoting/BUILD.bazel @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "remoting", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/remoting/include", + deps = [ + "//api:rocketmq_interface", + "@asio//:asio", + "@com_google_protobuf//:protobuf", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/container:flat_hash_map", + ] +) \ No newline at end of file diff --git a/src/main/cpp/remoting/BrokerData.cpp b/src/main/cpp/remoting/BrokerData.cpp new file mode 100644 index 000000000..51650aeae --- /dev/null +++ b/src/main/cpp/remoting/BrokerData.cpp @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "BrokerData.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +BrokerData BrokerData::decode(const google::protobuf::Struct& root) { + BrokerData broker_data; + auto fields = root.fields(); + if (fields.contains("cluster")) { + broker_data.cluster_ = fields["cluster"].string_value(); + } + + if (fields.contains("brokerName")) { + broker_data.broker_name_ = fields["brokerName"].string_value(); + } + + if (fields.contains("brokerAddrs")) { + auto items = fields["brokerAddrs"].struct_value().fields(); + for (const auto& item : items) { + auto k = std::stoll(item.first); + broker_data.broker_addresses_.insert({k, item.second.string_value()}); + } + } + return broker_data; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/QueryRouteRequestHeader.cpp b/src/main/cpp/remoting/QueryRouteRequestHeader.cpp new file mode 100644 index 000000000..551f3a2c5 --- /dev/null +++ b/src/main/cpp/remoting/QueryRouteRequestHeader.cpp @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "QueryRouteRequestHeader.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void QueryRouteRequestHeader::encode(google::protobuf::Value& root) const { + auto fields = root.mutable_struct_value()->mutable_fields(); + google::protobuf::Value topic; + topic.set_string_value(topic_); + fields->insert({"topic", topic}); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/QueueData.cpp b/src/main/cpp/remoting/QueueData.cpp new file mode 100644 index 000000000..1a0796eaf --- /dev/null +++ b/src/main/cpp/remoting/QueueData.cpp @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "QueueData.h" + +ROCKETMQ_NAMESPACE_BEGIN + +QueueData QueueData::decode(const google::protobuf::Struct& root) { + auto fields = root.fields(); + + QueueData queue_data; + + if (fields.contains("brokerName")) { + queue_data.broker_name_ = fields["brokerName"].string_value(); + } + + if (fields.contains("readQueueNums")) { + queue_data.read_queue_number_ = fields["readQueueNums"].number_value(); + } + + if (fields.contains("writeQueueNums")) { + queue_data.write_queue_number_ = fields["writeQueueNums"].number_value(); + } + + if (fields.contains("perm")) { + queue_data.perm_ = fields["perm"].number_value(); + } + + if (fields.contains("topicSynFlag")) { + queue_data.topic_system_flag_ = fields["topicSynFlag"].number_value(); + } + + return queue_data; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/RemotingCommand.cpp b/src/main/cpp/remoting/RemotingCommand.cpp new file mode 100644 index 000000000..743096aab --- /dev/null +++ b/src/main/cpp/remoting/RemotingCommand.cpp @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "RemotingCommand.h" + +#include +#include +#include + +#include "LanguageCode.h" +#include "absl/memory/memory.h" + +#include "QueryRouteRequestHeader.h" + +ROCKETMQ_NAMESPACE_BEGIN + +std::int32_t RemotingCommand::nextRequestId() { + static std::atomic_int32_t request_id{0}; + return request_id.fetch_add(1, std::memory_order_relaxed); +} + +RemotingCommand RemotingCommand::createRequest(RequestCode code, CommandCustomHeader* ext_fields) { + RemotingCommand command; + command.code_ = static_cast(code); + command.ext_fields_ = ext_fields; + return command; +} + +RemotingCommand RemotingCommand::createResponse(ResponseCode code, CommandCustomHeader* ext_fields) { + RemotingCommand response; + response.code_ = static_cast(code); + response.ext_fields_ = ext_fields; + response.flag_ |= (1 << RPC_TYPE_RESPONSE); + return response; +} + +void RemotingCommand::encodeHeader(google::protobuf::Value& root) { + auto fields = root.mutable_struct_value()->mutable_fields(); + + google::protobuf::Value code; + code.set_number_value(code_); + fields->insert({"code", code}); + + google::protobuf::Value language; + switch (language_) { + case LanguageCode::CPP: { + language.set_string_value("CPP"); + break; + } + case LanguageCode::JAVA: { + language.set_string_value("JAVA"); + break; + } + case LanguageCode::GO: { + language.set_string_value("GO"); + break; + } + case LanguageCode::DOTNET: { + language.set_string_value("DOTNET"); + break; + } + default: { + language.set_string_value("OTHER"); + break; + } + } + fields->insert({"language", language}); + + if (version_) { + google::protobuf::Value version; + version.set_number_value(version_); + fields->insert({"version", version}); + } + + google::protobuf::Value opaque; + opaque.set_number_value(opaque_); + fields->insert({"opaque", opaque}); + + google::protobuf::Value flag; + flag.set_number_value(flag_); + fields->insert({"flag", flag}); + + if (!remark_.empty()) { + google::protobuf::Value remark; + remark.set_string_value(remark_); + fields->insert({"remark", remark}); + } + + if (ext_fields_) { + google::protobuf::Value ext_fields; + ext_fields_->encode(ext_fields); + fields->insert({"extFields", ext_fields}); + } +} + +const std::uint8_t RemotingCommand::RPC_TYPE_RESPONSE = 0; + +const std::uint8_t RemotingCommand::RPC_TYPE_ONE_WAY = 1; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/TopicRouteData.cpp b/src/main/cpp/remoting/TopicRouteData.cpp new file mode 100644 index 000000000..71a9f3c00 --- /dev/null +++ b/src/main/cpp/remoting/TopicRouteData.cpp @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TopicRouteData.h" +#include "BrokerData.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TopicRouteData TopicRouteData::decode(const google::protobuf::Struct& root) { + auto fields = root.fields(); + + TopicRouteData topic_route_data; + + if (fields.contains("queueDatas")) { + auto queue_data_list = fields.at("queueDatas"); + + for (auto& item : queue_data_list.list_value().values()) { + topic_route_data.queue_data_.push_back(QueueData::decode(item.struct_value())); + } + } + + if (fields.contains("brokerDatas")) { + auto broker_data_list = fields.at("brokerDatas"); + for (auto& item : broker_data_list.list_value().values()) { + topic_route_data.broker_data_.push_back(BrokerData::decode(item.struct_value())); + } + } + + return topic_route_data; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/BrokerData.h b/src/main/cpp/remoting/include/BrokerData.h new file mode 100644 index 000000000..83ef5fd81 --- /dev/null +++ b/src/main/cpp/remoting/include/BrokerData.h @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct BrokerData { + std::string cluster_; + std::string broker_name_; + absl::flat_hash_map broker_addresses_; + + static BrokerData decode(const google::protobuf::Struct& root); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/CommandCustomHeader.h b/src/main/cpp/remoting/include/CommandCustomHeader.h new file mode 100644 index 000000000..a6afbc22e --- /dev/null +++ b/src/main/cpp/remoting/include/CommandCustomHeader.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class CommandCustomHeader { +public: + virtual ~CommandCustomHeader() = default; + + virtual void encode(google::protobuf::Value& root) const = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/LanguageCode.h b/src/main/cpp/remoting/include/LanguageCode.h new file mode 100644 index 000000000..7151faf4d --- /dev/null +++ b/src/main/cpp/remoting/include/LanguageCode.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class LanguageCode : std::uint8_t +{ + JAVA = 0, + CPP = 1, + DOTNET = 2, + PYTHON = 3, + DELPHI = 4, + ERLANG = 5, + RUBY = 6, + OTHER = 7, + HTTP = 8, + GO = 9, + PHP = 10, + OMS = 11, +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/remoting/include/QueryRouteRequestHeader.h b/src/main/cpp/remoting/include/QueryRouteRequestHeader.h new file mode 100644 index 000000000..b5669690e --- /dev/null +++ b/src/main/cpp/remoting/include/QueryRouteRequestHeader.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "absl/strings/string_view.h" + +#include "CommandCustomHeader.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class QueryRouteRequestHeader : public CommandCustomHeader { +public: + ~QueryRouteRequestHeader() override = default; + + void topic(absl::string_view topic) { + topic_ = std::string(topic.data(), topic.length()); + } + + const std::string& topic() const { + return topic_; + } + + void encode(google::protobuf::Value& root) const override; + +private: + std::string topic_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/QueueData.h b/src/main/cpp/remoting/include/QueueData.h new file mode 100644 index 000000000..9530cfca8 --- /dev/null +++ b/src/main/cpp/remoting/include/QueueData.h @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct QueueData { + std::string broker_name_; + std::int32_t read_queue_number_{0}; + std::int32_t write_queue_number_{0}; + std::uint32_t perm_{0}; + + /** + * @brief in Java, it's named "topicSynFlag" + * + */ + std::uint32_t topic_system_flag_{0}; + + static QueueData decode(const google::protobuf::Struct& root); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/RemotingCommand.h b/src/main/cpp/remoting/include/RemotingCommand.h new file mode 100644 index 000000000..67e5ff4c5 --- /dev/null +++ b/src/main/cpp/remoting/include/RemotingCommand.h @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "CommandCustomHeader.h" +#include "LanguageCode.h" +#include "RequestCode.h" +#include "ResponseCode.h" +#include "Version.h" + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * RemotingCommand is non-copyable. It is movable. + */ +class RemotingCommand { +public: + RemotingCommand(const RemotingCommand&) = delete; + + RemotingCommand(RemotingCommand&& rhs) noexcept { + code_ = rhs.code_; + language_ = rhs.language_; + version_ = rhs.version_; + opaque_ = rhs.opaque_; + remark_ = std::move(rhs.remark_); + body_ = std::move(rhs.body_); + ext_fields_ = rhs.ext_fields_; + rhs.ext_fields_ = nullptr; + } + + RemotingCommand& operator=(const RemotingCommand&) = delete; + + RemotingCommand& operator=(RemotingCommand&& rhs) noexcept { + if (this == &rhs) { + return *this; + } + + code_ = rhs.code_; + language_ = rhs.language_; + version_ = rhs.version_; + opaque_ = rhs.opaque_; + remark_ = std::move(rhs.remark_); + body_ = std::move(rhs.body_); + ext_fields_ = rhs.ext_fields_; + rhs.ext_fields_ = nullptr; + return *this; + } + + virtual ~RemotingCommand() { + delete ext_fields_; + } + + static std::int32_t nextRequestId(); + + static RemotingCommand createRequest(RequestCode, CommandCustomHeader*); + + static RemotingCommand createResponse(ResponseCode, CommandCustomHeader*); + + virtual void encodeHeader(google::protobuf::Value& root); + +private: + RemotingCommand() = default; + + std::int32_t code_{static_cast(RequestCode::QueryRoute)}; + LanguageCode language_{LanguageCode::CPP}; + std::int32_t version_{static_cast(Version::V4_9_1)}; + std::int32_t opaque_{nextRequestId()}; + std::uint32_t flag_{0}; + std::string remark_; + + CommandCustomHeader* ext_fields_{nullptr}; + + std::vector body_; + + /** + * Bit-field shift amount for flag_ field, indicating the RPC is a response from broker or name-server. + */ + const static std::uint8_t RPC_TYPE_RESPONSE; + + /** + * Bit-field shift amount for flag_ field. Request marked one-way should NOT expect a respose from broker or + * name-server. + */ + const static std::uint8_t RPC_TYPE_ONE_WAY; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/RemotingCommandType.h b/src/main/cpp/remoting/include/RemotingCommandType.h new file mode 100644 index 000000000..9ca78d2c0 --- /dev/null +++ b/src/main/cpp/remoting/include/RemotingCommandType.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class RemotingCommandType : std::uint8_t +{ + REQUEST = 0, + RESPONSE = 1, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/RequestCode.h b/src/main/cpp/remoting/include/RequestCode.h new file mode 100644 index 000000000..cb28eaf2a --- /dev/null +++ b/src/main/cpp/remoting/include/RequestCode.h @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class RequestCode : std::int32_t +{ + SendMessage = 10, + PullMessage = 11, + QueryRoute = 105, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/ResponseCode.h b/src/main/cpp/remoting/include/ResponseCode.h new file mode 100644 index 000000000..4cfa924e4 --- /dev/null +++ b/src/main/cpp/remoting/include/ResponseCode.h @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class ResponseCode : std::uint16_t +{ + Success = 0, + InternalSystemError = 1, + TooManyRequests = 2, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/TopicRouteData.h b/src/main/cpp/remoting/include/TopicRouteData.h new file mode 100644 index 000000000..2c044f8c9 --- /dev/null +++ b/src/main/cpp/remoting/include/TopicRouteData.h @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "BrokerData.h" +#include "QueueData.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct TopicRouteData { + + /** + * @brief In Java, it's named "queueDatas" + * + */ + std::vector queue_data_; + + /** + * @brief In Java, it's named "brokerDatas" + * + */ + std::vector broker_data_; + + static TopicRouteData decode(const google::protobuf::Struct& root); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/Version.h b/src/main/cpp/remoting/include/Version.h new file mode 100644 index 000000000..85fdc4629 --- /dev/null +++ b/src/main/cpp/remoting/include/Version.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class Version : std::int32_t +{ + V4_9_1 = 396, + V4_9_2 = 398, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp new file mode 100644 index 000000000..c3429ab8c --- /dev/null +++ b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "AsyncReceiveMessageCallback.h" + +#include + +#include "ClientManagerImpl.h" +#include "ConsumeMessageType.h" +#include "LoggerImpl.h" +#include "PushConsumer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +AsyncReceiveMessageCallback::AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue) + : process_queue_(std::move(process_queue)) { + receive_message_later_ = std::bind(&AsyncReceiveMessageCallback::checkThrottleThenReceive, this); +} + +void AsyncReceiveMessageCallback::onCompletion(const std::error_code& ec, const ReceiveMessageResult& result) { + ProcessQueueSharedPtr process_queue = process_queue_.lock(); + if (!process_queue) { + SPDLOG_INFO("Process queue has been destructed."); + return; + } + + std::shared_ptr impl = process_queue->getConsumer().lock(); + if (!impl->active()) { + SPDLOG_INFO("Consumer is not active any more. It should be quitting"); + return; + } + + auto consumer = process_queue->getConsumer().lock(); + if (!consumer) { + return; + } + + if (ec) { + SPDLOG_WARN("Receive message from {} failed. Cause: {}. Attempt later.", process_queue->simpleName(), ec.message()); + receiveMessageLater(); + return; + } + + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=FOUND, msgListSize={}, queue={}", + result.source_host, result.messages.size(), process_queue->simpleName()); + process_queue->cacheMessages(result.messages); + impl->getConsumeMessageService()->signalDispatcher(); + checkThrottleThenReceive(); +} + +const char* AsyncReceiveMessageCallback::RECEIVE_LATER_TASK_NAME = "receive-later-task"; + +void AsyncReceiveMessageCallback::checkThrottleThenReceive() { + auto process_queue = process_queue_.lock(); + if (!process_queue) { + SPDLOG_WARN("Process queue should have been destructed"); + return; + } + + if (process_queue->shouldThrottle()) { + SPDLOG_INFO("Number of messages in {} exceeds throttle threshold. Receive messages later.", + process_queue->simpleName()); + process_queue->syncIdleState(); + receiveMessageLater(); + } else { + // Receive message immediately + receiveMessageImmediately(); + } +} + +void AsyncReceiveMessageCallback::receiveMessageLater() { + auto process_queue = process_queue_.lock(); + if (!process_queue) { + return; + } + + auto client_instance = process_queue->getClientManager(); + std::weak_ptr receive_callback_weak_ptr(shared_from_this()); + + auto task = [receive_callback_weak_ptr]() { + auto async_receive_ptr = receive_callback_weak_ptr.lock(); + if (async_receive_ptr) { + async_receive_ptr->checkThrottleThenReceive(); + } + }; + + client_instance->getScheduler()->schedule(task, RECEIVE_LATER_TASK_NAME, std::chrono::seconds(1), + std::chrono::seconds(0)); +} + +void AsyncReceiveMessageCallback::receiveMessageImmediately() { + ProcessQueueSharedPtr process_queue_shared_ptr = process_queue_.lock(); + if (!process_queue_shared_ptr) { + SPDLOG_INFO("ProcessQueue has been released. Ignore further receive message request-response cycles"); + return; + } + + std::shared_ptr impl = process_queue_shared_ptr->getConsumer().lock(); + if (!impl) { + SPDLOG_INFO("Owner of ProcessQueue[{}] has been released. Ignore further receive message request-response cycles", + process_queue_shared_ptr->simpleName()); + return; + } + impl->receiveMessage(process_queue_shared_ptr->getMQMessageQueue(), process_queue_shared_ptr->getFilterExpression()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/AwaitPullCallback.cpp b/src/main/cpp/rocketmq/AwaitPullCallback.cpp new file mode 100644 index 000000000..f4a5d99fd --- /dev/null +++ b/src/main/cpp/rocketmq/AwaitPullCallback.cpp @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "AwaitPullCallback.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void AwaitPullCallback::onSuccess(const PullResult& pull_result) noexcept { + absl::MutexLock lk(&mtx_); + completed_ = true; + // TODO: optimize out messages copy here. + pull_result_ = pull_result; + cv_.SignalAll(); +} + +void AwaitPullCallback::onFailure(const std::error_code& ec) noexcept { + absl::MutexLock lk(&mtx_); + completed_ = true; + ec_ = ec; + cv_.SignalAll(); +} + +bool AwaitPullCallback::await() { + { + absl::MutexLock lk(&mtx_); + while (!completed_) { + cv_.Wait(&mtx_); + } + return !hasFailure(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/BUILD.bazel b/src/main/cpp/rocketmq/BUILD.bazel new file mode 100644 index 000000000..6d6dd7296 --- /dev/null +++ b/src/main/cpp/rocketmq/BUILD.bazel @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "rocketmq_library", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/rocketmq/include", + deps = [ + "//src/main/cpp/client:client_library", + "//src/main/cpp/tracing/exporters:otlp_exporter", + "//src/main/cpp/tracing:tracing_utility", + "//src/main/cpp/log:log_library", + "//src/main/cpp/admin:admin_server_library", + "@com_google_absl//absl/types:optional", + ], +) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/CMakeLists.txt b/src/main/cpp/rocketmq/CMakeLists.txt new file mode 100644 index 000000000..efcf70a69 --- /dev/null +++ b/src/main/cpp/rocketmq/CMakeLists.txt @@ -0,0 +1,25 @@ +file(GLOB SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + +add_library(impl OBJECT ${SRC_FILES}) +target_include_directories(impl + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(impl + PRIVATE + api + absl::strings + asio + base + fmt + proto + client + filesystem + httplib + log + opencensus_api + opencensus_proto + rocketmq_stats + rocketmq_trace + otlp_exporter + scheduler + spdlog) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp new file mode 100644 index 000000000..960296909 --- /dev/null +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -0,0 +1,627 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "RpcClient.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" +#include "apache/rocketmq/v1/definition.pb.h" +#include "google/rpc/code.pb.h" + +#include "ClientImpl.h" +#include "ClientManagerFactory.h" +#include "HttpClientImpl.h" +#include "InvocationContext.h" +#include "LoggerImpl.h" +#include "MessageAccessor.h" +#include "NamingScheme.h" +#include "Signature.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/MessageListener.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ClientImpl::ClientImpl(absl::string_view group_name) : ClientConfigImpl(group_name), state_(State::CREATED) { +} + +void ClientImpl::start() { + State expected = CREATED; + if (!state_.compare_exchange_strong(expected, State::STARTING)) { + SPDLOG_ERROR("Attempt to start ClientImpl failed. Expecting: {} Actual: {}", State::CREATED, + state_.load(std::memory_order_relaxed)); + return; + } + + if (!name_server_resolver_) { + SPDLOG_ERROR("No name server resolver is configured."); + abort(); + } + name_server_resolver_->start(); + + client_manager_ = ClientManagerFactory::getInstance().getClientManager(*this); + client_manager_->start(); + + exporter_ = std::make_shared(client_manager_, this); + exporter_->start(); + + std::weak_ptr ptr(self()); + + auto route_update_functor = [ptr]() { + std::shared_ptr base = ptr.lock(); + if (base) { + base->updateRouteInfo(); + } + }; + + route_update_handle_ = client_manager_->getScheduler()->schedule(route_update_functor, UPDATE_ROUTE_TASK_NAME, + std::chrono::seconds(10), std::chrono::seconds(30)); +} + +void ClientImpl::shutdown() { + State expected = State::STOPPING; + if (state_.compare_exchange_strong(expected, State::STOPPED)) { + name_server_resolver_->shutdown(); + if (route_update_handle_) { + client_manager_->getScheduler()->cancel(route_update_handle_); + } + client_manager_.reset(); + } else { + SPDLOG_ERROR("Try to shutdown ClientImpl, but its state is not as expected. Expecting: {}, Actual: {}", + State::STOPPING, state_.load(std::memory_order_relaxed)); + } +} + +const char* ClientImpl::UPDATE_ROUTE_TASK_NAME = "route_updater"; + +void ClientImpl::endpointsInUse(absl::flat_hash_set& endpoints) { + absl::MutexLock lk(&topic_route_table_mtx_); + for (const auto& item : topic_route_table_) { + for (const auto& partition : item.second->partitions()) { + std::string endpoint = partition.asMessageQueue().serviceAddress(); + if (!endpoints.contains(endpoint)) { + endpoints.emplace(std::move(endpoint)); + } + } + } +} + +void ClientImpl::getRouteFor(const std::string& topic, + const std::function& cb) { + TopicRouteDataPtr route = nullptr; + { + absl::MutexLock lock(&topic_route_table_mtx_); + if (topic_route_table_.contains(topic)) { + route = topic_route_table_.at(topic); + } + } + + if (route) { + std::error_code ec; + cb(ec, route); + return; + } + + bool query_backend = true; + { + absl::MutexLock lk(&inflight_route_requests_mtx_); + { + absl::MutexLock route_table_lock(&topic_route_table_mtx_); + if (topic_route_table_.contains(topic)) { + route = topic_route_table_.at(topic); + query_backend = false; + } + } + + if (query_backend) { + if (inflight_route_requests_.contains(topic)) { + inflight_route_requests_.at(topic).emplace_back(cb); + SPDLOG_DEBUG("Would reuse prior route request for topic={}", topic); + return; + } else { + std::vector> inflight{cb}; + inflight_route_requests_.insert({topic, inflight}); + SPDLOG_INFO("Create inflight route query cache for topic={}", topic); + } + } + } + + if (!query_backend && route) { + std::error_code ec; + cb(ec, route); + } else { + fetchRouteFor(topic, + std::bind(&ClientImpl::onTopicRouteReady, this, topic, std::placeholders::_1, std::placeholders::_2)); + } +} + +void ClientImpl::setAccessPoint(rmq::Endpoints* endpoints) { + std::vector> pairs; + { + std::string naming_address = name_server_resolver_->resolve(); + absl::string_view host_port_csv; + + if (absl::StartsWith(naming_address, NamingScheme::DnsPrefix)) { + endpoints->set_scheme(rmq::AddressScheme::DOMAIN_NAME); + host_port_csv = absl::StripPrefix(naming_address, NamingScheme::DnsPrefix); + } else if (absl::StartsWith(naming_address, NamingScheme::IPv4Prefix)) { + endpoints->set_scheme(rmq::AddressScheme::IPv4); + host_port_csv = absl::StripPrefix(naming_address, NamingScheme::IPv4Prefix); + } else if (absl::StartsWith(naming_address, NamingScheme::IPv6Prefix)) { + endpoints->set_scheme(rmq::AddressScheme::IPv6); + host_port_csv = absl::StripPrefix(naming_address, NamingScheme::IPv6Prefix); + } else { + SPDLOG_WARN("Unsupported naming scheme"); + } + + std::vector name_server_list = absl::StrSplit(host_port_csv, ','); + + for (const auto& name_server_item : name_server_list) { + std::string::size_type pos = name_server_item.rfind(':'); + if (std::string::npos == pos) { + continue; + } + std::string host(name_server_item.substr(0, pos)); + std::string port(name_server_item.substr(pos + 1)); + pairs.emplace_back(std::make_pair(host, std::stoi(port))); + } + } + + if (!pairs.empty()) { + for (const auto& host_port : pairs) { + auto address = new rmq::Address(); + address->set_port(host_port.second); + address->set_host(host_port.first); + endpoints->mutable_addresses()->AddAllocated(address); + } + } +} + +void ClientImpl::fetchRouteFor(const std::string& topic, + const std::function& cb) { + std::string name_server = name_server_resolver_->resolve(); + if (name_server.empty()) { + SPDLOG_WARN("No name server available"); + return; + } + + auto callback = [this, topic, name_server, cb](const std::error_code& ec, const TopicRouteDataPtr& route) { + if (ec) { + SPDLOG_WARN("Failed to resolve route for topic={} from {}", topic, name_server); + std::string name_server_changed = name_server_resolver_->resolve(); + if (!name_server_changed.empty()) { + SPDLOG_INFO("Change current name server from {} to {}", name_server, name_server_changed); + } + cb(ec, nullptr); + return; + } + + SPDLOG_DEBUG("Apply callback of fetchRouteFor({}) since a valid route is fetched", topic); + cb(ec, route); + }; + + QueryRouteRequest request; + request.mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_topic()->set_name(topic); + auto endpoints = request.mutable_endpoints(); + setAccessPoint(endpoints); + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + client_manager_->resolveRoute(name_server, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); +} + +void ClientImpl::updateRouteInfo() { + if (State::STARTED != state_.load(std::memory_order_relaxed) && + State::STARTING != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); + return; + } + + std::vector topics; + { + absl::MutexLock lock(&topic_route_table_mtx_); + for (const auto& entry : topic_route_table_) { + topics.push_back(entry.first); + } + } + + if (!topics.empty()) { + for (const auto& topic : topics) { + fetchRouteFor( + topic, std::bind(&ClientImpl::updateRouteCache, this, topic, std::placeholders::_1, std::placeholders::_2)); + } + } + SPDLOG_DEBUG("Topic route info updated"); +} + +void ClientImpl::heartbeat() { + absl::flat_hash_set hosts; + endpointsInUse(hosts); + if (hosts.empty()) { + SPDLOG_WARN("No hosts to send heartbeat to at present"); + return; + } + + HeartbeatRequest request; + prepareHeartbeatData(request); + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + + for (const auto& target : hosts) { + auto callback = [target](const std::error_code& ec, const HeartbeatResponse& response) { + if (ec) { + SPDLOG_WARN("Failed to heartbeat against {}. Cause: {}", target, ec.message()); + return; + } + SPDLOG_DEBUG("Heartbeat to {} OK", target); + }; + client_manager_->heartbeat(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + } +} + +void ClientImpl::onTopicRouteReady(const std::string& topic, const std::error_code& ec, + const TopicRouteDataPtr& route) { + if (route) { + SPDLOG_DEBUG("Received route data for topic={}", topic); + } + + updateRouteCache(topic, ec, route); + + // Take all pending callbacks + std::vector> pending_requests; + { + absl::MutexLock lk(&inflight_route_requests_mtx_); + assert(inflight_route_requests_.contains(topic)); + auto& inflight_requests = inflight_route_requests_.at(topic); + pending_requests.insert(pending_requests.end(), inflight_requests.begin(), inflight_requests.end()); + inflight_route_requests_.erase(topic); + } + + SPDLOG_DEBUG("Apply cached callbacks with acquired route data for topic={}", topic); + for (const auto& cb : pending_requests) { + cb(ec, route); + } +} + +void ClientImpl::updateTraceHosts() { + absl::flat_hash_set hosts; + absl::MutexLock lk(&topic_route_table_mtx_); + for (const auto& item : topic_route_table_) { + for (const auto& partition : item.second->partitions()) { + if (Permission::NONE == partition.permission()) { + continue; + } + if (MixAll::MASTER_BROKER_ID != partition.broker().id()) { + continue; + } + std::string endpoint = partition.asMessageQueue().serviceAddress(); + if (!hosts.contains(endpoint)) { + hosts.emplace(std::move(endpoint)); + } + } + } + std::vector host_list(hosts.begin(), hosts.end()); + SPDLOG_DEBUG("Trace candidate hosts size={}", host_list.size()); + exporter_->updateHosts(host_list); +} + +void ClientImpl::updateRouteCache(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) { + if (ec || !route || route->partitions().empty()) { + SPDLOG_WARN("Yuck! route for {} is invalid. Cause: {}", topic, ec.message()); + return; + } + + absl::flat_hash_set new_hosts; + { + absl::MutexLock lk(&topic_route_table_mtx_); + absl::flat_hash_set existed_hosts; + for (const auto& item : topic_route_table_) { + for (const auto& partition : item.second->partitions()) { + std::string endpoint = partition.asMessageQueue().serviceAddress(); + if (!existed_hosts.contains(endpoint)) { + existed_hosts.emplace(std::move(endpoint)); + } + } + } + if (!topic_route_table_.contains(topic)) { + topic_route_table_.insert({topic, route}); + SPDLOG_INFO("TopicRouteData for topic={} has changed. NONE --> {}", topic, route->debugString()); + } else { + TopicRouteDataPtr cached = topic_route_table_.at(topic); + if (*cached != *route) { + topic_route_table_.insert_or_assign(topic, route); + std::string previous = cached->debugString(); + SPDLOG_INFO("TopicRouteData for topic={} has changed. {} --> {}", topic, cached->debugString(), + route->debugString()); + } + } + absl::flat_hash_set hosts; + for (const auto& item : topic_route_table_) { + for (const auto& partition : item.second->partitions()) { + std::string endpoint = partition.asMessageQueue().serviceAddress(); + if (!hosts.contains(endpoint)) { + hosts.emplace(std::move(endpoint)); + } + } + } + std::set_difference(hosts.begin(), hosts.end(), existed_hosts.begin(), existed_hosts.end(), + std::inserter(new_hosts, new_hosts.begin())); + } + updateTraceHosts(); + for (const auto& endpoints : new_hosts) { + pollCommand(endpoints); + } +} + +void ClientImpl::pollCommand(const std::string& target) { + SPDLOG_INFO("Start to poll command to remote, target={}", target); + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + + PollCommandRequest request; + auto&& resource_bundle = resourceBundle(); + request.set_client_id(resource_bundle.client_id); + switch (resource_bundle.group_type) { + case GroupType::PUBLISHER: + request.mutable_producer_group()->set_resource_namespace(resource_namespace_); + request.mutable_producer_group()->set_name(group_name_); + break; + + case GroupType::SUBSCRIBER: + request.mutable_consumer_group()->set_resource_namespace(resource_namespace_); + request.mutable_consumer_group()->set_name(group_name_); + break; + } + auto topics = request.mutable_topics(); + for (const auto& item : resource_bundle.topics) { + auto topic = new rmq::Resource(); + topic->set_resource_namespace(resource_namespace_); + topic->set_name(item); + topics->AddAllocated(topic); + } + + client_manager_->pollCommand(target, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), + std::bind(&ClientImpl::onPollCommandResponse, this, std::placeholders::_1)); +} + +void ClientImpl::verifyMessageConsumption(std::string remote_address, std::string command_id, MQMessageExt message) { + SPDLOG_INFO("Received message to verify consumption, messageId={}", message.getMsgId()); + MessageListener* listener = messageListener(); + + Metadata metadata; + Signature::sign(this, metadata); + ReportMessageConsumptionResultRequest request; + request.set_command_id(command_id); + + if (!listener) { + request.mutable_status()->set_code(google::rpc::Code::FAILED_PRECONDITION); + request.mutable_status()->set_message("Target is not a push consumer client"); + client_manager_->reportMessageConsumptionResult(remote_address, metadata, request, + absl::ToChronoMilliseconds(io_timeout_)); + return; + } + + if (MessageListenerType::FIFO == listener->listenerType()) { + request.mutable_status()->set_code(google::rpc::Code::FAILED_PRECONDITION); + request.mutable_status()->set_message("FIFO message does NOT support verification of message consumption"); + client_manager_->reportMessageConsumptionResult(remote_address, metadata, request, + absl::ToChronoMilliseconds(io_timeout_)); + return; + } + + // Execute the actual verification task in dedicated thread-pool. + client_manager_->submit(std::bind(&ClientImpl::doVerify, this, remote_address, command_id, message)); +} + +void ClientImpl::onPollCommandResponse(const InvocationContext* ctx) { + std::string address = ctx->remote_address; + absl::flat_hash_set hosts; + endpointsInUse(hosts); + if (!hosts.contains(address)) { + SPDLOG_INFO("Endpoint={} is now absent from route table. Break poll-command-cycle.", address); + return; + } + if (!ctx->status.ok()) { + static std::string task_name = "Poll-Command-Later"; + client_manager_->getScheduler()->schedule(std::bind(&ClientImpl::pollCommand, this, ctx->remote_address), task_name, + std::chrono::seconds(3), std::chrono::seconds(0)); + return; + } + + switch (ctx->response.type_case()) { + case PollCommandResponse::TypeCase::kPrintThreadStackTraceCommand: { + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + ReportThreadStackTraceRequest request; + auto command_id = ctx->response.print_thread_stack_trace_command().command_id(); + request.set_command_id(command_id); + request.set_thread_stack_trace("--RocketMQ-Client-CPP does NOT support thread stack trace report--"); + client_manager_->reportThreadStackTrace(ctx->remote_address, metadata, request, + absl::ToChronoMilliseconds(io_timeout_)); + break; + } + + case PollCommandResponse::TypeCase::kVerifyMessageConsumptionCommand: { + auto command_id = ctx->response.verify_message_consumption_command().command_id(); + auto data = ctx->response.verify_message_consumption_command().message(); + MQMessageExt message; + ReportMessageConsumptionResultRequest request; + request.set_command_id(command_id); + Metadata metadata; + Signature::sign(this, metadata); + if (!client_manager_->wrapMessage(data, message)) { + SPDLOG_WARN("Message to verify consumption is corrupted"); + request.mutable_status()->set_code(google::rpc::Code::INVALID_ARGUMENT); + request.mutable_status()->set_message("Data corrupted"); + client_manager_->reportMessageConsumptionResult(ctx->remote_address, metadata, request, + absl::ToChronoMilliseconds(io_timeout_)); + } + verifyMessageConsumption(std::move(ctx->remote_address), std::move(command_id), std::move(message)); + break; + } + + case PollCommandResponse::TypeCase::kRecoverOrphanedTransactionCommand: { + auto orphan = ctx->response.recover_orphaned_transaction_command().orphaned_transactional_message(); + MQMessageExt message; + if (client_manager_->wrapMessage(orphan, message)) { + MessageAccessor::setTargetEndpoint(message, ctx->remote_address); + const std::string& transaction_id = ctx->response.recover_orphaned_transaction_command().transaction_id(); + // Dispatch task to thread-pool. + client_manager_->submit( + std::bind(&ClientImpl::resolveOrphanedTransactionalMessage, this, transaction_id, message)); + } else { + SPDLOG_WARN("Failed to resolve orphaned transactional message, potentially caused by message-body checksum " + "verification failure."); + } + break; + } + + case PollCommandResponse::TypeCase::kNoopCommand: { + SPDLOG_DEBUG("A long-polling-command period completed."); + break; + } + + default: { + SPDLOG_WARN("Unsupported multiplex type"); + break; + } + } + + // Initiate next round of long-polling-command immediately. + pollCommand(ctx->remote_address); +} + +void ClientImpl::onRemoteEndpointRemoval(const std::vector& hosts) { + absl::MutexLock lk(&isolated_endpoints_mtx_); + for (auto it = isolated_endpoints_.begin(); it != isolated_endpoints_.end();) { + if (hosts.end() != std::find_if(hosts.begin(), hosts.end(), [&](const std::string& item) { return *it == item; })) { + SPDLOG_INFO("Drop isolated-endoint[{}] as it has been removed from route table", *it); + isolated_endpoints_.erase(it++); + } else { + it++; + } + } +} + +void ClientImpl::healthCheck() { + std::vector endpoints; + { + absl::MutexLock lk(&isolated_endpoints_mtx_); + for (const auto& item : isolated_endpoints_) { + endpoints.push_back(item); + } + } + + std::weak_ptr base(self()); + auto callback = [base](const std::error_code& ec, const InvocationContext* invocation_context) { + std::shared_ptr ptr = base.lock(); + if (!ptr) { + SPDLOG_INFO("BaseImpl has been destructed"); + return; + } + + ptr->onHealthCheckResponse(ec, invocation_context); + }; + + for (const auto& endpoint : endpoints) { + HealthCheckRequest request; + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + client_manager_->healthCheck(endpoint, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + } +} + +void ClientImpl::schedule(const std::string& task_name, const std::function& task, + std::chrono::milliseconds delay) { + client_manager_->getScheduler()->schedule(task, task_name, delay, std::chrono::milliseconds(0)); +} + +void ClientImpl::onHealthCheckResponse(const std::error_code& ec, const InvocationContext* ctx) { + if (ec) { + SPDLOG_WARN("Health check to server[host={}] failed. Cause: {}", ec.message()); + return; + } + + SPDLOG_INFO("Health check to server[host={}] passed. Remove it from isolated endpoint pool", ctx->remote_address); + { + absl::MutexLock lk(&isolated_endpoints_mtx_); + isolated_endpoints_.erase(ctx->remote_address); + } +} + +void ClientImpl::notifyClientTermination() { + SPDLOG_WARN("Should NOT reach here. Subclass should have overridden this function."); + std::abort(); +} + +void ClientImpl::notifyClientTermination(const NotifyClientTerminationRequest& request) { + absl::flat_hash_set endpoints; + endpointsInUse(endpoints); + + Metadata metadata; + Signature::sign(this, metadata); + + for (const auto& endpoint : endpoints) { + client_manager_->notifyClientTermination(endpoint, metadata, request, absl::ToChronoMilliseconds(io_timeout_)); + } +} + +void ClientImpl::doVerify(std::string target, std::string command_id, MQMessageExt message) { + ReportMessageConsumptionResultRequest request; + request.set_command_id(command_id); + StandardMessageListener* callback = reinterpret_cast(messageListener()); + try { + std::vector batch = {message}; + auto result = callback->consumeMessage(batch); + switch (result) { + case ConsumeMessageResult::SUCCESS: { + SPDLOG_DEBUG("Verify message[MsgId={}] OK", message.getMsgId()); + request.mutable_status()->set_message("Consume Success"); + break; + } + case ConsumeMessageResult::FAILURE: { + SPDLOG_WARN("Message Listener failed to consume message[MsgId={}] when verifying", message.getMsgId()); + request.mutable_status()->set_code(google::rpc::Code::INTERNAL); + request.mutable_status()->set_message("Consume Failed"); + break; + } + } + } catch (...) { + SPDLOG_WARN("Exception raised when invoking message listener provided by application developer. MsgId of message " + "to verify: {}", + message.getMsgId()); + request.mutable_status()->set_code(google::rpc::Code::INTERNAL); + request.mutable_status()->set_message( + "Unexpected exception raised while invoking message listener provided by application developer"); + } + + Metadata metadata; + Signature::sign(this, metadata); + client_manager_->reportMessageConsumptionResult(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp new file mode 100644 index 000000000..76db7a593 --- /dev/null +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -0,0 +1,330 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "TracingUtility.h" +#include "absl/strings/str_join.h" + +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" + +#include "ConsumeFifoMessageService.h" +#include "MessageAccessor.h" +#include "ProcessQueue.h" +#include "PushConsumerImpl.h" +#include "rocketmq/MessageListener.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ConsumeFifoMessageService ::ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, + MessageListener* message_listener) + : ConsumeMessageServiceBase(std::move(consumer), thread_count, message_listener) { +} + +void ConsumeFifoMessageService::start() { + ConsumeMessageServiceBase::start(); + State expected = State::STARTING; + if (state_.compare_exchange_strong(expected, State::STARTED)) { + SPDLOG_DEBUG("ConsumeMessageOrderlyService started"); + } +} + +void ConsumeFifoMessageService::shutdown() { + // Wait till consume-message-orderly-service has fully started; otherwise, we may potentially miss closing resources + // in concurrent scenario. + while (State::STARTING == state_.load(std::memory_order_relaxed)) { + absl::SleepFor(absl::Milliseconds(10)); + } + + State expected = State::STARTED; + if (state_.compare_exchange_strong(expected, STOPPING)) { + ConsumeMessageServiceBase::shutdown(); + SPDLOG_INFO("ConsumeMessageOrderlyService shut down"); + } +} + +void ConsumeFifoMessageService::submitConsumeTask0(const std::shared_ptr& consumer, + const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message) { + // In case custom executor is used. + const Executor& custom_executor = consumer->customExecutor(); + if (custom_executor) { + std::function consume_task = + std::bind(&ConsumeFifoMessageService::consumeTask, this, process_queue, message); + custom_executor(consume_task); + SPDLOG_DEBUG("Submit FIFO consume task to custom executor"); + return; + } + + // submit batch message + std::function consume_task = + std::bind(&ConsumeFifoMessageService::consumeTask, this, process_queue, message); + SPDLOG_DEBUG("Submit FIFO consume task to thread pool"); + pool_->submit(consume_task); +} + +void ConsumeFifoMessageService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue) { + ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + SPDLOG_INFO("Process queue has destructed"); + return; + } + + auto consumer = consumer_.lock(); + if (!consumer) { + SPDLOG_INFO("Consumer has destructed"); + return; + } + + assert(1 == consumer->consumeBatchSize()); + + if (process_queue_ptr->bindFifoConsumeTask()) { + std::vector messages; + process_queue_ptr->take(consumer->consumeBatchSize(), messages); + if (!messages.empty()) { + assert(1 == messages.size()); + submitConsumeTask0(consumer, process_queue, *messages.begin()); + } + } +} + +MessageListenerType ConsumeFifoMessageService::messageListenerType() { + return MessageListenerType::FIFO; +} + +void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_queue, MQMessageExt& message) { + ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + return; + } + const std::string& topic = message.getTopic(); + ConsumeMessageResult result; + std::shared_ptr consumer = consumer_.lock(); + // consumer might have been destructed. + if (!consumer) { + return; + } + + std::shared_ptr> rate_limiter = rateLimiter(topic); + if (rate_limiter) { + rate_limiter->acquire(); + SPDLOG_DEBUG("Rate-limit permit acquired"); + } + + // Record await-consumption-span + { + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); + + auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = consumer->resourceNamespace() + "/" + message.getTopic() + " " + + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, &Samplers::always()); + } else { + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); + TracingUtility::addUniversalSpanAttributes(message, *consumer, span); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, message.getStoreTimestamp()); + absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(message); + span.AddAnnotation( + MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, + {{MixAll::SPAN_ANNOTATION_ATTR_START_TIME, + opencensus::trace::AttributeValueRef(absl::ToInt64Milliseconds(decoded_timestamp - absl::UnixEpoch()))}}); + span.End(); + MessageAccessor::setTraceContext(const_cast(message), + opencensus::trace::propagation::ToTraceParentHeader(span.context())); + } + + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); + auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = consumer->resourceNamespace() + "/" + message.getTopic() + " " + + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION; + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context); + } else { + span = opencensus::trace::Span::StartSpan(span_name); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION); + TracingUtility::addUniversalSpanAttributes(message, *consumer, span); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, message.getDeliveryAttempt()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, message.getStoreTimestamp()); + MessageAccessor::setTraceContext(const_cast(message), + opencensus::trace::propagation::ToTraceParentHeader(span.context())); + + auto steady_start = std::chrono::steady_clock::now(); + + try { + assert(message_listener_); + auto message_listener = dynamic_cast(message_listener_); + assert(message_listener); + result = message_listener->consumeMessage(message); + } catch (...) { + result = ConsumeMessageResult::FAILURE; + SPDLOG_ERROR("Business FIFO callback raised an exception when consumeMessage"); + } + + switch (result) { + case ConsumeMessageResult::SUCCESS: + span.SetStatus(opencensus::trace::StatusCode::OK); + break; + case ConsumeMessageResult::FAILURE: + span.SetStatus(opencensus::trace::StatusCode::UNKNOWN); + break; + } + span.End(); + + auto duration = std::chrono::steady_clock::now() - steady_start; + + // Log client consume-time costs + SPDLOG_DEBUG("Business callback spent {}ms processing message[Topic={}, MessageId={}].", + MixAll::millisecondsOf(duration), message.getTopic(), message.getMsgId()); + + if (MessageModel::CLUSTERING == consumer->messageModel()) { + if (result == ConsumeMessageResult::SUCCESS) { + // Release message number and memory quota + process_queue_ptr->release(message.getBody().size(), message.getQueueOffset()); + + // Ensure current message is acked before moving to the next message. + auto callback = std::bind(&ConsumeFifoMessageService::onAck, this, process_queue, message, std::placeholders::_1); + consumer->ack(message, callback); + } else { + MessageAccessor::setDeliveryAttempt(message, message.getDeliveryAttempt() + 1); + if (message.getDeliveryAttempt() < consumer->maxDeliveryAttempts()) { + auto task = std::bind(&ConsumeFifoMessageService::scheduleConsumeTask, this, process_queue, message); + consumer->schedule("Scheduled-Consume-FIFO-Message-Task", task, std::chrono::seconds(1)); + } else { + auto callback = std::bind(&ConsumeFifoMessageService::onForwardToDeadLetterQueue, this, process_queue, message, + std::placeholders::_1); + consumer->forwardToDeadLetterQueue(message, callback); + } + } + } else if (MessageModel::BROADCASTING == consumer->messageModel()) { + process_queue_ptr->release(message.getBody().size(), message.getQueueOffset()); + int64_t committed_offset; + if (process_queue_ptr->committedOffset(committed_offset)) { + consumer->updateOffset(process_queue_ptr->getMQMessageQueue(), committed_offset); + } + } +} + +void ConsumeFifoMessageService::onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, + const std::error_code& ec) { + auto process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + SPDLOG_WARN("ProcessQueue has destructed."); + return; + } + + if (ec) { + SPDLOG_WARN("Failed to acknowledge FIFO message[MessageQueue={}, MsgId={}]. Cause: {}", + process_queue_ptr->simpleName(), message.getMsgId(), ec.message()); + auto consumer = consumer_.lock(); + if (!consumer) { + SPDLOG_WARN("Consumer instance has destructed"); + return; + } + auto task = std::bind(&ConsumeFifoMessageService::scheduleAckTask, this, process_queue, message); + int32_t duration = 100; + consumer->schedule("Ack-FIFO-Message-On-Failure", task, std::chrono::milliseconds(duration)); + SPDLOG_INFO("Scheduled to ack message[Topic={}, MessageId={}] in {}ms", message.getTopic(), message.getMsgId(), + duration); + } else { + SPDLOG_DEBUG("Acknowledge FIFO message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), + message.getMsgId()); + process_queue_ptr->unbindFifoConsumeTask(); + submitConsumeTask(process_queue); + } +} + +void ConsumeFifoMessageService::onForwardToDeadLetterQueue(const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message, bool ok) { + if (ok) { + SPDLOG_DEBUG("Forward message[Topic={}, MessagId={}] to DLQ OK", message.getTopic(), message.getMsgId()); + auto process_queue_ptr = process_queue.lock(); + if (process_queue_ptr) { + process_queue_ptr->unbindFifoConsumeTask(); + } + return; + } + + SPDLOG_INFO("Failed to forward message[topic={}, MessageId={}] to DLQ", message.getTopic(), message.getMsgId()); + auto process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + SPDLOG_INFO("Abort further attempts considering its process queue has destructed"); + return; + } + + auto consumer = consumer_.lock(); + assert(consumer); + + auto task = std::bind(&ConsumeFifoMessageService::scheduleForwardDeadLetterQueueTask, this, process_queue, message); + consumer->schedule("Scheduled-Forward-DLQ-Task", task, std::chrono::milliseconds(100)); +} + +void ConsumeFifoMessageService::scheduleForwardDeadLetterQueueTask(const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message) { + auto process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + return; + } + auto consumer = consumer_.lock(); + assert(consumer); + auto callback = std::bind(&ConsumeFifoMessageService::onForwardToDeadLetterQueue, this, process_queue, message, + std::placeholders::_1); + consumer->forwardToDeadLetterQueue(message, callback); +} + +void ConsumeFifoMessageService::scheduleAckTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message) { + auto process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + return; + } + + auto callback = std::bind(&ConsumeFifoMessageService::onAck, this, process_queue, message, std::placeholders::_1); + auto consumer = consumer_.lock(); + if (consumer) { + consumer->ack(message, callback); + } +} + +void ConsumeFifoMessageService::scheduleConsumeTask(const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message) { + auto consumer_ptr = consumer_.lock(); + if (!consumer_ptr) { + return; + } + + auto process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + return; + } + + submitConsumeTask0(consumer_ptr, process_queue_ptr, message); + SPDLOG_INFO("Business callback failed to process FIFO messages. Re-submit consume task back to thread pool"); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp new file mode 100644 index 000000000..4b34a8df4 --- /dev/null +++ b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ConsumeMessageServiceBase.h" +#include "LoggerImpl.h" +#include "PushConsumer.h" +#include "ThreadPoolImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ConsumeMessageServiceBase::ConsumeMessageServiceBase(std::weak_ptr consumer, int thread_count, + MessageListener* message_listener) + : state_(State::CREATED), thread_count_(thread_count), pool_(absl::make_unique(thread_count_)), + consumer_(std::move(consumer)), message_listener_(message_listener) { +} + +void ConsumeMessageServiceBase::start() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTING, std::memory_order_relaxed)) { + pool_->start(); + dispatch_thread_ = std::thread([this] { + State current_state = state_.load(std::memory_order_relaxed); + while (State::STOPPED != current_state && State::STOPPING != current_state) { + dispatch(); + { + absl::MutexLock lk(&dispatch_mtx_); + dispatch_cv_.WaitWithTimeout(&dispatch_mtx_, absl::Milliseconds(100)); + } + + // Update current state + current_state = state_.load(std::memory_order_relaxed); + } + }); + } +} + +void ConsumeMessageServiceBase::signalDispatcher() { + absl::MutexLock lk(&dispatch_mtx_); + // Wake up dispatch_thread_ + dispatch_cv_.Signal(); +} + +void ConsumeMessageServiceBase::throttle(const std::string& topic, std::uint32_t threshold) { + absl::MutexLock lk(&rate_limiter_table_mtx_); + std::shared_ptr> rate_limiter = std::make_shared>(threshold); + rate_limiter_table_.insert_or_assign(topic, rate_limiter); + rate_limiter_observer_.subscribe(rate_limiter); +} + +void ConsumeMessageServiceBase::shutdown() { + State expected = State::STOPPING; + if (state_.compare_exchange_strong(expected, State::STOPPED, std::memory_order_relaxed)) { + pool_->shutdown(); + { + absl::MutexLock lk(&dispatch_mtx_); + dispatch_cv_.SignalAll(); + } + + if (dispatch_thread_.joinable()) { + dispatch_thread_.join(); + } + + rate_limiter_observer_.stop(); + } +} + +bool ConsumeMessageServiceBase::hasConsumeRateLimiter(const std::string& topic) const { + absl::MutexLock lk(&rate_limiter_table_mtx_); + return rate_limiter_table_.contains(topic); +} + +std::shared_ptr> ConsumeMessageServiceBase::rateLimiter(const std::string& topic) const { + if (!hasConsumeRateLimiter(topic)) { + return nullptr; + } + absl::MutexLock lk(&rate_limiter_table_mtx_); + return rate_limiter_table_[topic]; +} + +void ConsumeMessageServiceBase::dispatch() { + std::shared_ptr consumer = consumer_.lock(); + if (!consumer) { + SPDLOG_WARN("The consumer has already destructed"); + return; + } + + auto callback = [this](const ProcessQueueSharedPtr& process_queue) { submitConsumeTask(process_queue); }; + consumer->iterateProcessQueue(callback); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp new file mode 100644 index 000000000..e31b2edda --- /dev/null +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "TracingUtility.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_join.h" +#include "absl/time/time.h" +#include "absl/types/span.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" + +#include "ConsumeStandardMessageService.h" +#include "LoggerImpl.h" +#include "MessageAccessor.h" +#include "MixAll.h" +#include "OtlpExporter.h" +#include "Protocol.h" +#include "PushConsumer.h" +#include "UtilAll.h" +#include "rocketmq/ConsumeType.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/MessageListener.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ConsumeStandardMessageService::ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, + MessageListener* message_listener_ptr) + : ConsumeMessageServiceBase(std::move(consumer), thread_count, message_listener_ptr) { +} + +void ConsumeStandardMessageService::start() { + ConsumeMessageServiceBase::start(); + State expected = State::STARTING; + if (state_.compare_exchange_strong(expected, State::STARTED)) { + SPDLOG_DEBUG("ConsumeMessageConcurrentlyService started"); + } +} + +void ConsumeStandardMessageService::shutdown() { + while (State::STARTING == state_.load(std::memory_order_relaxed)) { + absl::SleepFor(absl::Milliseconds(10)); + } + + State expected = State::STARTED; + if (state_.compare_exchange_strong(expected, State::STOPPING)) { + ConsumeMessageServiceBase::shutdown(); + SPDLOG_DEBUG("ConsumeMessageConcurrentlyService shut down"); + } +} + +void ConsumeStandardMessageService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue) { + ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + SPDLOG_WARN("ProcessQueue was destructed. It is likely that client should have shutdown."); + return; + } + std::shared_ptr consumer = process_queue_ptr->getConsumer().lock(); + + if (!consumer) { + return; + } + + std::string topic = process_queue_ptr->topic(); + bool has_more = true; + while (has_more) { + std::vector messages; + uint32_t batch_size = consumer->consumeBatchSize(); + has_more = process_queue_ptr->take(batch_size, messages); + if (messages.empty()) { + assert(!has_more); + break; + } + + // In case custom executor is used. + const Executor& custom_executor = consumer->customExecutor(); + if (custom_executor) { + std::function consume_task = + std::bind(&ConsumeStandardMessageService::consumeTask, this, process_queue, messages); + custom_executor(consume_task); + SPDLOG_DEBUG("Submit consumer task to custom executor with message-batch-size={}", messages.size()); + continue; + } + + // submit batch message + std::function consume_task = + std::bind(&ConsumeStandardMessageService::consumeTask, this, process_queue_ptr, messages); + SPDLOG_DEBUG("Submit consumer task to thread pool with message-batch-size={}", messages.size()); + pool_->submit(consume_task); + } +} + +MessageListenerType ConsumeStandardMessageService::messageListenerType() { + return MessageListenerType::STANDARD; +} + +void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& process_queue, + const std::vector& msgs) { + ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr || msgs.empty()) { + return; + } + std::string topic = msgs.begin()->getTopic(); + ConsumeMessageResult status; + std::shared_ptr consumer = consumer_.lock(); + // consumer might have been destructed. + if (!consumer) { + return; + } + + std::shared_ptr> rate_limiter = rateLimiter(topic); + if (rate_limiter) { + // Acquire permits one-by-one to avoid large batch hungry issue. + for (std::size_t i = 0; i < msgs.size(); i++) { + rate_limiter->acquire(); + } + SPDLOG_DEBUG("{} rate-limit permits acquired", msgs.size()); + } + + // Record await-consumption-span + { + for (const auto& msg : msgs) { + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg.traceContext()); + + auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = consumer->resourceNamespace() + "/" + msg.getTopic() + " " + + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, &Samplers::always()); + } else { + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); + TracingUtility::addUniversalSpanAttributes(msg, *consumer, span); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.getStoreTimestamp()); + absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(msg); + span.AddAnnotation( + MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, + {{MixAll::SPAN_ANNOTATION_ATTR_START_TIME, + opencensus::trace::AttributeValueRef(absl::ToInt64Milliseconds(decoded_timestamp - absl::UnixEpoch()))}}); + span.End(); + MessageAccessor::setTraceContext(const_cast(msg), + opencensus::trace::propagation::ToTraceParentHeader(span.context())); + } + } + + // Trace start of consume message + std::vector spans; + { + for (const auto& msg : msgs) { + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg.traceContext()); + auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = consumer->resourceNamespace() + "/" + msg.getTopic() + " " + + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION; + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context); + } else { + span = opencensus::trace::Span::StartSpan(span_name); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION); + TracingUtility::addUniversalSpanAttributes(msg, *consumer, span); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg.getDeliveryAttempt()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.getStoreTimestamp()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE, msgs.size()); + spans.emplace_back(std::move(span)); + MessageAccessor::setTraceContext(const_cast(msg), + opencensus::trace::propagation::ToTraceParentHeader(span.context())); + } + } + + auto steady_start = std::chrono::steady_clock::now(); + + try { + assert(nullptr != message_listener_); + auto message_listener = dynamic_cast(message_listener_); + assert(message_listener); + status = message_listener->consumeMessage(msgs); + } catch (...) { + status = ConsumeMessageResult::FAILURE; + SPDLOG_ERROR("Business callback raised an exception when consumeMessage"); + } + + auto duration = std::chrono::steady_clock::now() - steady_start; + + // Trace end of consumption + { + for (auto& span : spans) { + switch (status) { + case ConsumeMessageResult::SUCCESS: + span.SetStatus(opencensus::trace::StatusCode::OK); + break; + case ConsumeMessageResult::FAILURE: + span.SetStatus(opencensus::trace::StatusCode::UNKNOWN); + break; + } + span.End(); + } + } + + // Log client consume-time costs + SPDLOG_DEBUG("Business callback spent {}ms processing {} messages.", MixAll::millisecondsOf(duration), msgs.size()); + + if (MessageModel::CLUSTERING == consumer->messageModel()) { + for (const auto& msg : msgs) { + const std::string& message_id = msg.getMsgId(); + + // Release message number and memory quota + process_queue_ptr->release(msg.getBody().size(), msg.getQueueOffset()); + + if (status == ConsumeMessageResult::SUCCESS) { + auto callback = [process_queue_ptr, message_id](const std::error_code& ec) { + if (ec) { + SPDLOG_WARN("Failed to acknowledge message[MessageQueue={}, MsgId={}]. Cause: {}", + process_queue_ptr->simpleName(), message_id, ec.message()); + } else { + SPDLOG_DEBUG("Acknowledge message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), + message_id); + } + }; + consumer->ack(msg, callback); + } else { + auto callback = [process_queue_ptr, message_id](const std::error_code& ec) { + if (ec) { + SPDLOG_WARN("Failed to negative acknowledge message[MessageQueue={}, MsgId={}]. Cause: {} Message will be " + "re-consumed after default invisible time", + process_queue_ptr->simpleName(), message_id, ec.message()); + return; + } + + SPDLOG_DEBUG("Nack message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), message_id); + }; + consumer->nack(msg, callback); + } + } + + } else if (MessageModel::BROADCASTING == consumer->messageModel()) { + int64_t committed_offset; + if (process_queue_ptr->committedOffset(committed_offset)) { + consumer->updateOffset(process_queue_ptr->getMQMessageQueue(), committed_offset); + } + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/CredentialsProvider.cpp b/src/main/cpp/rocketmq/CredentialsProvider.cpp new file mode 100644 index 000000000..548e9d810 --- /dev/null +++ b/src/main/cpp/rocketmq/CredentialsProvider.cpp @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/strings/match.h" +#include "fmt/format.h" +#include "ghc/filesystem.hpp" +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" +#include "spdlog/spdlog.h" + +#include "MixAll.h" +#include "StsCredentialsProviderImpl.h" +#include "rocketmq/Logger.h" + +ROCKETMQ_NAMESPACE_BEGIN + +StaticCredentialsProvider::StaticCredentialsProvider(std::string access_key, std::string access_secret) + : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)) { +} + +Credentials StaticCredentialsProvider::getCredentials() { + return Credentials(access_key_, access_secret_); +} + +const char* EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY = "ROCKETMQ_ACCESS_KEY"; +const char* EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET = "ROCKETMQ_ACCESS_SECRET"; + +EnvironmentVariablesCredentialsProvider::EnvironmentVariablesCredentialsProvider() { + char* key = getenv(ENVIRONMENT_ACCESS_KEY); + if (key) { + access_key_ = std::string(key); + } + + char* secret = getenv(ENVIRONMENT_ACCESS_SECRET); + if (secret) { + access_secret_ = std::string(secret); + } +} + +Credentials EnvironmentVariablesCredentialsProvider::getCredentials() { + return Credentials(access_key_, access_secret_); +} + +const char* ConfigFileCredentialsProvider::CREDENTIAL_FILE_ = "rocketmq/credentials"; + +const char* ConfigFileCredentialsProvider::ACCESS_KEY_FIELD_NAME = "AccessKey"; +const char* ConfigFileCredentialsProvider::ACCESS_SECRET_FIELD_NAME = "AccessSecret"; + +ConfigFileCredentialsProvider::ConfigFileCredentialsProvider() { + std::string config_file; + if (MixAll::homeDirectory(config_file)) { + std::string path_separator(1, ghc::filesystem::path::preferred_separator); + if (!absl::EndsWith(config_file, path_separator)) { + config_file.append(1, ghc::filesystem::path::preferred_separator); + } + config_file.append(CREDENTIAL_FILE_); + ghc::filesystem::path config_file_path(config_file); + std::error_code ec; + if (!ghc::filesystem::exists(config_file_path, ec) || ec) { + SPDLOG_WARN("Config file[{}] does not exist.", config_file); + return; + } + std::ifstream config_file_stream; + config_file_stream.open(config_file.c_str(), std::ios::in | std::ios::ate | std::ios::binary); + if (config_file_stream.good()) { + auto size = config_file_stream.tellg(); + std::string content(size, '\0'); + config_file_stream.seekg(0); + config_file_stream.read(&content[0], size); + config_file_stream.close(); + google::protobuf::Struct root; + google::protobuf::util::Status status = google::protobuf::util::JsonStringToMessage(content, &root); + if (status.ok()) { + auto&& fields = root.fields(); + if (fields.contains(ACCESS_KEY_FIELD_NAME)) { + access_key_ = fields.at(ACCESS_KEY_FIELD_NAME).string_value(); + } + + if (fields.contains(ACCESS_SECRET_FIELD_NAME)) { + access_secret_ = fields.at(ACCESS_SECRET_FIELD_NAME).string_value(); + } + SPDLOG_DEBUG("Credentials for access_key={} loaded", access_key_); + } else { + SPDLOG_WARN("Failed to parse credential JSON config file. Message: {}", status.message().data()); + } + } else { + SPDLOG_WARN("Failed to open file: {}", config_file); + return; + } + } +} + +ConfigFileCredentialsProvider::ConfigFileCredentialsProvider(std::string config_file, + std::chrono::milliseconds refresh_interval) { +} + +Credentials ConfigFileCredentialsProvider::getCredentials() { + return Credentials(access_key_, access_secret_); +} + +StsCredentialsProvider::StsCredentialsProvider(std::string ram_role_name) + : impl_(absl::make_unique(std::move(ram_role_name))) { +} + +Credentials StsCredentialsProvider::getCredentials() { + return impl_->getCredentials(); +} + +StsCredentialsProviderImpl::StsCredentialsProviderImpl(std::string ram_role_name) + : ram_role_name_(std::move(ram_role_name)) { +} + +StsCredentialsProviderImpl::~StsCredentialsProviderImpl() { + http_client_->shutdown(); +} + +Credentials StsCredentialsProviderImpl::getCredentials() { + if (std::chrono::system_clock::now() >= expiration_) { + refresh(); + } + + { + absl::MutexLock lk(&mtx_); + return Credentials(access_key_, access_secret_, session_token_, expiration_); + } +} + +void StsCredentialsProviderImpl::refresh() { + std::string path = fmt::format("{}{}", RAM_ROLE_URL_PREFIX, ram_role_name_); + absl::Mutex sync_mtx; + absl::CondVar sync_cv; + bool completed = false; + auto callback = [&, this](int code, const std::multimap& headers, const std::string& body) { + SPDLOG_DEBUG("Received STS response. Code: {}", code); + if (static_cast(HttpStatus::OK) == code) { + google::protobuf::Struct doc; + google::protobuf::util::Status status = google::protobuf::util::JsonStringToMessage(body, &doc); + if (status.ok()) { + const auto& fields = doc.fields(); + assert(fields.contains(FIELD_ACCESS_KEY)); + std::string access_key = fields.at(FIELD_ACCESS_KEY).string_value(); + assert(fields.contains(FIELD_ACCESS_SECRET)); + std::string access_secret = fields.at(FIELD_ACCESS_SECRET).string_value(); + assert(fields.contains(FIELD_SESSION_TOKEN)); + std::string session_token = fields.at(FIELD_SESSION_TOKEN).string_value(); + assert(fields.contains(FIELD_EXPIRATION)); + std::string expiration_string = fields.at(FIELD_EXPIRATION).string_value(); + absl::Time expiration_instant; + std::string parse_error; + if (absl::ParseTime(EXPIRATION_DATE_TIME_FORMAT, expiration_string, absl::UTCTimeZone(), &expiration_instant, + &parse_error)) { + absl::MutexLock lk(&mtx_); + access_key_ = std::move(access_key); + access_secret_ = std::move(access_secret); + session_token_ = std::move(session_token); + expiration_ = absl::ToChronoTime(expiration_instant); + } else { + SPDLOG_WARN("Failed to parse expiration time. Message: {}", parse_error); + } + + } else { + SPDLOG_WARN("Failed to parse STS response. Message: {}", status.message().as_string()); + } + } else { + SPDLOG_WARN("STS response code is not OK. Code: {}", code); + } + + { + absl::MutexLock lk(&sync_mtx); + completed = true; + sync_cv.Signal(); + } + }; + + http_client_->get(HttpProtocol::HTTP, RAM_ROLE_HOST, 80, path, callback); + + while (!completed) { + absl::MutexLock lk(&sync_mtx); + sync_cv.Wait(&sync_mtx); + } +} + +const char* StsCredentialsProviderImpl::RAM_ROLE_HOST = "100.100.100.200"; +const char* StsCredentialsProviderImpl::RAM_ROLE_URL_PREFIX = "/latest/meta-data/Ram/security-credentials/"; +const char* StsCredentialsProviderImpl::FIELD_ACCESS_KEY = "AccessKeyId"; +const char* StsCredentialsProviderImpl::FIELD_ACCESS_SECRET = "AccessKeySecret"; +const char* StsCredentialsProviderImpl::FIELD_SESSION_TOKEN = "SecurityToken"; +const char* StsCredentialsProviderImpl::FIELD_EXPIRATION = "Expiration"; +const char* StsCredentialsProviderImpl::EXPIRATION_DATE_TIME_FORMAT = "%Y-%m-%d%ET%H:%H:%S%Ez"; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp new file mode 100644 index 000000000..e4beacb12 --- /dev/null +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQProducer.h" + +#include +#include +#include +#include + +#include "absl/strings/str_split.h" + +#include "DynamicNameServerResolver.h" +#include "MixAll.h" +#include "ProducerImpl.h" +#include "StaticNameServerResolver.h" +#include "rocketmq/Transaction.h" + +ROCKETMQ_NAMESPACE_BEGIN + +DefaultMQProducer::DefaultMQProducer(const std::string& group_name) + : impl_(std::make_shared(group_name)) { +} + +void DefaultMQProducer::start() { + impl_->start(); +} + +void DefaultMQProducer::shutdown() { + impl_->shutdown(); +} + +std::chrono::milliseconds DefaultMQProducer::getSendMsgTimeout() const { + return absl::ToChronoMilliseconds(impl_->getIoTimeout()); +} + +SendResult DefaultMQProducer::send(MQMessage& message, const std::string& message_group) { + message.bindMessageGroup(message_group); + std::error_code ec; + auto&& send_result = impl_->send(message, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return std::move(send_result); +} + +void DefaultMQProducer::setSendMsgTimeout(std::chrono::milliseconds timeout) { + impl_->setIoTimeout(absl::FromChrono(timeout)); +} + +void DefaultMQProducer::setNamesrvAddr(const std::string& name_server_address_list) { + auto name_server_resolver = std::make_shared(name_server_address_list); + impl_->withNameServerResolver(name_server_resolver); +} + +void DefaultMQProducer::setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint) { + auto name_server_resolver = std::make_shared(discovery_endpoint, std::chrono::seconds(10)); + impl_->withNameServerResolver(name_server_resolver); +} + +void DefaultMQProducer::setGroupName(const std::string& group_name) { + impl_->setGroupName(group_name); +} + +void DefaultMQProducer::setInstanceName(const std::string& instance_name) { + impl_->setInstanceName(instance_name); +} + +void DefaultMQProducer::enableTracing(bool enabled) { + impl_->enableTracing(enabled); +} + +bool DefaultMQProducer::isTracingEnabled() { + return impl_->isTracingEnabled(); +} + +SendResult DefaultMQProducer::send(const MQMessage& message, bool filter_active_broker) { + std::error_code ec; + auto&& send_result = impl_->send(message, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return std::move(send_result); +} + +SendResult DefaultMQProducer::send(const MQMessage& message, std::error_code& ec) noexcept { + return impl_->send(message, ec); +} + +SendResult DefaultMQProducer::send(MQMessage& msg, const MQMessageQueue& mq) { + msg.bindMessageQueue(mq); + std::error_code ec; + auto&& send_result = impl_->send(msg, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return std::move(send_result); +} + +SendResult DefaultMQProducer::send(MQMessage& msg, MessageQueueSelector* selector, void* arg) { + std::error_code ec; + auto&& list = impl_->listMessageQueue(msg.getTopic(), ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + + auto&& message_queue = selector->select(list, msg, arg); + msg.bindMessageQueue(message_queue); + + auto&& send_result = impl_->send(msg, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return std::move(send_result); +} + +SendResult DefaultMQProducer::send(MQMessage& message, MessageQueueSelector* selector, void* arg, int retry_times, + bool select_active_broker) { + return send(message, selector, arg); +} + +void DefaultMQProducer::send(const MQMessage& message, SendCallback* send_callback, bool select_active_broker) { + impl_->send(message, send_callback); +} + +void DefaultMQProducer::send(MQMessage& message, const MQMessageQueue& message_queue, SendCallback* send_callback) { + message.bindMessageQueue(message_queue); + impl_->send(message, send_callback); +} + +void DefaultMQProducer::send(MQMessage& message, MessageQueueSelector* selector, void* arg, + SendCallback* send_callback) { + std::error_code ec; + + // TODO: make querying route async + auto&& list = impl_->listMessageQueue(message.getTopic(), ec); + if (ec) { + send_callback->onFailure(ec); + } + + if (list.empty()) { + send_callback->onFailure(ErrorCode::ServiceUnavailable); + } + + auto&& message_queue = selector->select(list, message, arg); + message.bindMessageQueue(message_queue); + + impl_->send(message, send_callback); +} + +void DefaultMQProducer::sendOneway(const MQMessage& message, bool select_active_broker) { + std::error_code ec; + impl_->sendOneway(message, ec); +} + +void DefaultMQProducer::sendOneway(MQMessage& message, const MQMessageQueue& message_queue) { + message.bindMessageQueue(message_queue); + std::error_code ec; + impl_->sendOneway(message, ec); + if (ec) { + SPDLOG_INFO("Failed to send message in one-way: {}", ec.message()); + } +} + +void DefaultMQProducer::sendOneway(MQMessage& message, MessageQueueSelector* selector, void* arg) { + std::error_code ec; + auto&& list = impl_->listMessageQueue(message.getTopic(), ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + + if (list.empty()) { + ec = ErrorCode::ServiceUnavailable; + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + + auto&& message_queue = selector->select(list, message, arg); + message.bindMessageQueue(message_queue); + impl_->sendOneway(message, ec); +} + +void DefaultMQProducer::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { + impl_->setLocalTransactionStateChecker(std::move(checker)); +} + +void DefaultMQProducer::setMaxAttemptTimes(int max_attempt_times) { + impl_->maxAttemptTimes(max_attempt_times); +} + +int DefaultMQProducer::getMaxAttemptTimes() const { + return impl_->maxAttemptTimes(); +} + +std::vector DefaultMQProducer::getTopicMessageQueueInfo(const std::string& topic) { + std::error_code ec; + auto&& list = impl_->listMessageQueue(topic, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return std::move(list); +} + +void DefaultMQProducer::setUnitName(std::string unit_name) { + impl_->setUnitName(std::move(unit_name)); +} + +const std::string& DefaultMQProducer::getUnitName() { + return impl_->getUnitName(); +} + +uint32_t DefaultMQProducer::compressBodyThreshold() const { + return impl_->compressBodyThreshold(); +} + +void DefaultMQProducer::compressBodyThreshold(uint32_t threshold) { + impl_->compressBodyThreshold(threshold); +} + +void DefaultMQProducer::setResourceNamespace(const std::string& resource_namespace) { + impl_->resourceNamespace(resource_namespace); +} + +void DefaultMQProducer::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { + impl_->setCredentialsProvider(std::move(credentials_provider)); +} + +void DefaultMQProducer::setRegion(const std::string& region) { + impl_->region(region); +} + +TransactionPtr DefaultMQProducer::prepare(MQMessage& message) { + std::error_code ec; + auto transaction = impl_->prepare(message, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return transaction; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp new file mode 100644 index 000000000..56e22d391 --- /dev/null +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQPullConsumer.h" + +#include + +#include "absl/strings/str_split.h" + +#include "AwaitPullCallback.h" +#include "DynamicNameServerResolver.h" +#include "PullConsumerImpl.h" +#include "StaticNameServerResolver.h" + +ROCKETMQ_NAMESPACE_BEGIN + +DefaultMQPullConsumer::DefaultMQPullConsumer(const std::string& group_name) + : impl_(std::make_shared(group_name)) { +} + +void DefaultMQPullConsumer::start() { + impl_->start(); +} + +void DefaultMQPullConsumer::shutdown() { + impl_->shutdown(); +} + +std::future> DefaultMQPullConsumer::queuesFor(const std::string& topic) { + return impl_->queuesFor(topic); +} + +std::future DefaultMQPullConsumer::queryOffset(const OffsetQuery& query) { + return impl_->queryOffset(query); +} + +bool DefaultMQPullConsumer::pull(const PullMessageQuery& query, PullResult& pull_result) { + auto callback = absl::make_unique(pull_result); + pull(query, callback.get()); + return callback->await(); +} + +void DefaultMQPullConsumer::pull(const PullMessageQuery& query, PullCallback* callback) { + impl_->pull(query, callback); +} + +void DefaultMQPullConsumer::setResourceNamespace(const std::string& resource_namespace) { + impl_->resourceNamespace(resource_namespace); +} + +void DefaultMQPullConsumer::setCredentialsProvider(std::shared_ptr credentials_provider) { + impl_->setCredentialsProvider(std::move(credentials_provider)); +} + +void DefaultMQPullConsumer::setNamesrvAddr(const std::string& name_srv) { + auto name_server_resolver = std::make_shared(name_srv); + impl_->withNameServerResolver(name_server_resolver); +} + +void DefaultMQPullConsumer::setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint) { + auto name_server_resolver = std::make_shared(discovery_endpoint, std::chrono::seconds(10)); + impl_->withNameServerResolver(name_server_resolver); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp new file mode 100644 index 000000000..890221cf5 --- /dev/null +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "DynamicNameServerResolver.h" +#include "PushConsumerImpl.h" +#include "StaticNameServerResolver.h" +#include "rocketmq/DefaultMQPushConsumer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +DefaultMQPushConsumer::DefaultMQPushConsumer(const std::string& group_name) { + impl_ = std::make_shared(group_name); +} + +void DefaultMQPushConsumer::start() { + impl_->start(); +} + +void DefaultMQPushConsumer::shutdown() { + impl_->shutdown(); + SPDLOG_DEBUG("PushConsumerImpl shared_ptr use_count={}", impl_.use_count()); +} + +void DefaultMQPushConsumer::subscribe(const std::string& topic, const std::string& expression, + ExpressionType expression_type) { + impl_->subscribe(topic, expression, expression_type); +} + +void DefaultMQPushConsumer::setConsumeFromWhere(ConsumeFromWhere policy) { + impl_->setConsumeFromWhere(policy); +} + +void DefaultMQPushConsumer::registerMessageListener(MessageListener* listener) { + impl_->registerMessageListener(listener); +} + +void DefaultMQPushConsumer::setNamesrvAddr(const std::string& name_srv) { + auto name_server_resolver = std::make_shared(name_srv); + impl_->withNameServerResolver(name_server_resolver); +} + +void DefaultMQPushConsumer::setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint) { + if (discovery_endpoint.empty()) { + return; + } + + auto name_server_resolver = std::make_shared(discovery_endpoint, std::chrono::seconds(10)); + impl_->withNameServerResolver(name_server_resolver); +} + +void DefaultMQPushConsumer::setGroupName(const std::string& group_name) { + impl_->setGroupName(group_name); +} + +void DefaultMQPushConsumer::setConsumeThreadCount(int thread_count) { + impl_->consumeThreadPoolSize(thread_count); +} + +void DefaultMQPushConsumer::setInstanceName(const std::string& instance_name) { + impl_->setInstanceName(instance_name); +} + +int DefaultMQPushConsumer::getProcessQueueTableSize() { + return impl_->getProcessQueueTableSize(); +} + +void DefaultMQPushConsumer::setUnitName(std::string unit_name) { + impl_->setUnitName(std::move(unit_name)); +} + +const std::string& DefaultMQPushConsumer::getUnitName() const { + return impl_->getUnitName(); +} + +void DefaultMQPushConsumer::enableTracing(bool enabled) { + impl_->enableTracing(enabled); +} + +bool DefaultMQPushConsumer::isTracingEnabled() { + return impl_->isTracingEnabled(); +} + +void DefaultMQPushConsumer::setAsyncPull(bool) { +} + +void DefaultMQPushConsumer::setConsumeMessageBatchMaxSize(int batch_size) { + impl_->consumeBatchSize(batch_size); +} + +void DefaultMQPushConsumer::setCustomExecutor(const Executor& executor) { + impl_->setCustomExecutor(executor); +} + +void DefaultMQPushConsumer::setThrottle(const std::string& topic, uint32_t threshold) { + impl_->setThrottle(topic, threshold); +} + +void DefaultMQPushConsumer::setResourceNamespace(const std::string& resource_namespace) { + impl_->resourceNamespace(resource_namespace); +} + +void DefaultMQPushConsumer::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { + impl_->setCredentialsProvider(std::move(credentials_provider)); +} + +void DefaultMQPushConsumer::setMessageModel(MessageModel message_model) { + impl_->setMessageModel(message_model); +} + +std::string DefaultMQPushConsumer::groupName() const { + return impl_->getGroupName(); +} + +void DefaultMQPushConsumer::setOffsetStore(std::unique_ptr offset_store) { + impl_->setOffsetStore(std::move(offset_store)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp new file mode 100644 index 000000000..f9981bc15 --- /dev/null +++ b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "DynamicNameServerResolver.h" + +#include +#include +#include +#include +#include + +#include "absl/strings/str_join.h" + +#include "LoggerImpl.h" +#include "SchedulerImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +DynamicNameServerResolver::DynamicNameServerResolver(absl::string_view endpoint, + std::chrono::milliseconds refresh_interval) + : endpoint_(endpoint.data(), endpoint.length()), scheduler_(std::make_shared(1)), + refresh_interval_(refresh_interval) { + absl::string_view remains; + if (absl::StartsWith(endpoint_, "https://")) { + ssl_ = true; + remains = absl::StripPrefix(endpoint_, "https://"); + } else { + remains = absl::StripPrefix(endpoint_, "http://"); + } + + std::int32_t port = 80; + if (ssl_) { + port = 443; + } + + absl::string_view host; + if (absl::StrContains(remains, ':')) { + std::vector segments = absl::StrSplit(remains, ':'); + host = segments[0]; + remains = absl::StripPrefix(remains, host); + remains = absl::StripPrefix(remains, ":"); + + segments = absl::StrSplit(remains, '/'); + if (!absl::SimpleAtoi(segments[0], &port)) { + SPDLOG_WARN("Failed to parse port of name-server-list discovery service endpoint"); + abort(); + } + remains = absl::StripPrefix(remains, segments[0]); + } else { + std::vector segments = absl::StrSplit(remains, '/'); + host = segments[0]; + remains = absl::StripPrefix(remains, host); + } + + top_addressing_ = absl::make_unique(std::string(host.data(), host.length()), port, + std::string(remains.data(), remains.length())); +} + +std::string DynamicNameServerResolver::resolve() { + bool fetch_immediately = false; + { + absl::MutexLock lk(&name_server_list_mtx_); + if (name_server_list_.empty()) { + fetch_immediately = true; + } + } + + if (fetch_immediately) { + fetch(); + } + + { + absl::MutexLock lk(&name_server_list_mtx_); + return naming_scheme_.buildAddress(name_server_list_); + } +} + +void DynamicNameServerResolver::fetch() { + std::weak_ptr ptr(shared_from_this()); + auto callback = [ptr](bool success, const std::vector& name_server_list) { + if (success && !name_server_list.empty()) { + std::shared_ptr resolver = ptr.lock(); + if (resolver) { + resolver->onNameServerListFetched(name_server_list); + } + } + }; + top_addressing_->fetchNameServerAddresses(callback); +} + +void DynamicNameServerResolver::onNameServerListFetched(const std::vector& name_server_list) { + if (!name_server_list.empty()) { + absl::MutexLock lk(&name_server_list_mtx_); + if (name_server_list_ != name_server_list) { + SPDLOG_INFO("Name server list changed. {} --> {}", absl::StrJoin(name_server_list_, ";"), + absl::StrJoin(name_server_list, ";")); + name_server_list_ = name_server_list; + } + } +} + +void DynamicNameServerResolver::injectHttpClient(std::unique_ptr http_client) { + top_addressing_->injectHttpClient(std::move(http_client)); +} + +void DynamicNameServerResolver::start() { + scheduler_->start(); + scheduler_->schedule(std::bind(&DynamicNameServerResolver::fetch, this), "DynamicNameServerResolver", + std::chrono::milliseconds(0), refresh_interval_); +} + +void DynamicNameServerResolver::shutdown() { + scheduler_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/FilterExpression.cpp b/src/main/cpp/rocketmq/FilterExpression.cpp new file mode 100644 index 000000000..a60290b8d --- /dev/null +++ b/src/main/cpp/rocketmq/FilterExpression.cpp @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FilterExpression.h" + +ROCKETMQ_NAMESPACE_BEGIN + +bool FilterExpression::accept(const MQMessageExt& message) const { + + switch (type_) { + case ExpressionType::TAG: { + if (WILD_CARD_TAG == content_) { + return true; + } else { + return message.getTags() == content_; + } + } + + case ExpressionType::SQL92: { + // Server should have strictly filtered. + return true; + } + } + return true; +} + +const char* FilterExpression::WILD_CARD_TAG = "*"; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp new file mode 100644 index 000000000..6604f1134 --- /dev/null +++ b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "MessageGroupQueueSelector.h" + +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +MessageGroupQueueSelector::MessageGroupQueueSelector(std::string message_group) + : message_group_(std::move(message_group)) { +} + +MQMessageQueue MessageGroupQueueSelector::select(const std::vector& mqs, const MQMessage& msg, + void* arg) { + std::size_t hash_code = std::hash{}(message_group_); + assert(!mqs.empty()); + std::size_t len = mqs.size(); + return mqs[hash_code % len]; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/NamingScheme.cpp b/src/main/cpp/rocketmq/NamingScheme.cpp new file mode 100644 index 000000000..4646af971 --- /dev/null +++ b/src/main/cpp/rocketmq/NamingScheme.cpp @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "NamingScheme.h" + +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" + +ROCKETMQ_NAMESPACE_BEGIN + +const char* NamingScheme::DnsPrefix = "dns:"; +const char* NamingScheme::IPv4Prefix = "ipv4:"; +const char* NamingScheme::IPv6Prefix = "ipv6:"; + +const char* NamingScheme::IPv4Regex = + "(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"; + +const char* NamingScheme::IPv6Regex = "((([0-9a-fA-F]){1,4})\\:){7}([0-9a-fA-F]){1,4}"; + +NamingScheme::NamingScheme() : ipv4_pattern_(IPv4Regex), ipv6_pattern_(IPv6Regex) { +} + +std::string NamingScheme::buildAddress(const std::vector& list) { + absl::flat_hash_map ipv4; + absl::flat_hash_map ipv6; + + for (const auto& segment : list) { + std::vector host_port = absl::StrSplit(segment, ':'); + if (2 != host_port.size()) { + continue; + } + + if (re2::RE2::FullMatch(host_port[0], ipv4_pattern_)) { + std::uint32_t port; + if (absl::SimpleAtoi(host_port[1], &port)) { + ipv4.insert_or_assign(host_port[0], port); + } + continue; + } + + if (re2::RE2::FullMatch(host_port[0], ipv6_pattern_)) { + std::uint32_t port; + if (absl::SimpleAtoi(host_port[1], &port)) { + ipv6.insert_or_assign(host_port[0], port); + } + continue; + } + + // Once we find a domain name record, use it as the final result. + host_port.insert(host_port.begin(), "dns"); + return absl::StrJoin(host_port, ":"); + } + + if (!ipv4.empty()) { + return "ipv4:" + absl::StrJoin(ipv4, ",", absl::PairFormatter(":")); + } + + if (!ipv6.empty()) { + return "ipv6:" + absl::StrJoin(ipv4, ",", absl::PairFormatter(":")); + } + return std::string(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp new file mode 100644 index 000000000..d9ba8cefd --- /dev/null +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -0,0 +1,341 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ProcessQueueImpl.h" + +#include +#include +#include +#include +#include + +#include "ClientManagerImpl.h" +#include "MetadataConstants.h" +#include "Protocol.h" +#include "PushConsumer.h" +#include "Signature.h" +#include "rocketmq/MessageListener.h" +#include "rocketmq/MessageModel.h" + +using namespace std::chrono; + +ROCKETMQ_NAMESPACE_BEGIN + +ProcessQueueImpl::ProcessQueueImpl(MQMessageQueue message_queue, FilterExpression filter_expression, + std::weak_ptr consumer, std::shared_ptr client_instance) + : message_queue_(std::move(message_queue)), filter_expression_(std::move(filter_expression)), + invisible_time_(MixAll::millisecondsOf(MixAll::DEFAULT_INVISIBLE_TIME_)), + simple_name_(message_queue_.simpleName()), consumer_(std::move(consumer)), + client_manager_(std::move(client_instance)), cached_message_quantity_(0), cached_message_memory_(0) { + SPDLOG_DEBUG("Created ProcessQueue={}", simpleName()); +} + +ProcessQueueImpl::~ProcessQueueImpl() { + SPDLOG_INFO("ProcessQueue={} should have been re-balanced away, thus, is destructed", simpleName()); +} + +void ProcessQueueImpl::callback(std::shared_ptr callback) { + receive_callback_ = std::move(callback); +} + +bool ProcessQueueImpl::expired() const { + auto duration = std::chrono::steady_clock::now() - idle_since_; + if (duration > MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_) { + SPDLOG_WARN("ProcessQueue={} is expired. It remains idle for {}ms", simpleName(), MixAll::millisecondsOf(duration)); + return true; + } + return false; +} + +bool ProcessQueueImpl::shouldThrottle() const { + auto consumer = consumer_.lock(); + if (!consumer) { + return false; + } + + std::size_t quantity = cached_message_quantity_.load(std::memory_order_relaxed); + uint32_t quantity_threshold = consumer->maxCachedMessageQuantity(); + uint64_t memory_threshold = consumer->maxCachedMessageMemory(); + bool need_throttle = quantity >= quantity_threshold; + if (need_throttle) { + SPDLOG_INFO("{}: Number of locally cached messages is {}, which exceeds threshold={}", simple_name_, quantity, + quantity_threshold); + return true; + } + + if (memory_threshold) { + uint64_t bytes = cached_message_memory_.load(std::memory_order_relaxed); + need_throttle = bytes >= memory_threshold; + if (need_throttle) { + SPDLOG_INFO("{}: Locally cached messages take {} bytes, which exceeds threshold={}", simple_name_, bytes, + memory_threshold); + return true; + } + } + return false; +} + +void ProcessQueueImpl::receiveMessage() { + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + + switch (consumer->messageModel()) { + case MessageModel::CLUSTERING: { + popMessage(); + break; + } + case MessageModel::BROADCASTING: { + pullMessage(); + break; + } + } +} + +void ProcessQueueImpl::popMessage() { + rmq::ReceiveMessageRequest request; + absl::flat_hash_map metadata; + auto consumer_client = consumer_.lock(); + if (!consumer_client) { + return; + } + Signature::sign(consumer_client.get(), metadata); + wrapPopMessageRequest(metadata, request); + syncIdleState(); + SPDLOG_DEBUG("Try to pop message from {}", message_queue_.simpleName()); + client_manager_->receiveMessage(message_queue_.serviceAddress(), metadata, request, + absl::ToChronoMilliseconds(consumer_client->getLongPollingTimeout()), + receive_callback_); +} + +void ProcessQueueImpl::pullMessage() { + rmq::PullMessageRequest request; + absl::flat_hash_map metadata; + auto consumer = consumer_.lock(); + if (!consumer) { + SPDLOG_INFO("Owner consumer has destructed"); + return; + } + + Signature::sign(consumer.get(), metadata); + wrapPullMessageRequest(request); + syncIdleState(); + SPDLOG_DEBUG("Try to pull message from {}", message_queue_.simpleName()); + + auto timeout = consumer->getLongPollingTimeout(); + + auto callback = [this](const std::error_code& ec, const ReceiveMessageResult& result) { + receive_callback_->onCompletion(ec, result); + }; + + client_manager_->pullMessage(message_queue_.serviceAddress(), metadata, request, absl::ToChronoMilliseconds(timeout), + callback); +} + +bool ProcessQueueImpl::hasPendingMessages() const { + absl::MutexLock lk(&messages_mtx_); + return !cached_messages_.empty(); +} + +void ProcessQueueImpl::cacheMessages(const std::vector& messages) { + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + + { + absl::MutexLock messages_lock_guard(&messages_mtx_); + absl::MutexLock offsets_lock_guard(&offsets_mtx_); + for (const auto& message : messages) { + const std::string& msg_id = message.getMsgId(); + if (!filter_expression_.accept(message)) { + const std::string& topic = message.getTopic(); + auto callback = [topic, msg_id](const std::error_code& ec) { + if (ec) { + SPDLOG_WARN( + "Failed to ack message[Topic={}, MsgId={}] directly as it fails to pass filter expression. Cause: {}", + topic, msg_id, ec.message()); + } else { + SPDLOG_DEBUG("Ack message[Topic={}, MsgId={}] directly as it fails to pass filter expression", topic, + msg_id); + } + }; + consumer->ack(message, callback); + continue; + } + cached_messages_.emplace_back(message); + cached_message_quantity_.fetch_add(1, std::memory_order_relaxed); + cached_message_memory_.fetch_add(message.getBody().size(), std::memory_order_relaxed); + if (MessageModel::BROADCASTING == consumer->messageModel()) { + if (offsets_.size() == 1 && offsets_.begin()->released_) { + int64_t previously_released = offsets_.begin()->offset_; + offsets_.erase(OffsetRecord(previously_released)); + } + offsets_.emplace(message.getQueueOffset()); + } + } + } +} + +bool ProcessQueueImpl::take(uint32_t batch_size, std::vector& messages) { + absl::MutexLock lock(&messages_mtx_); + if (cached_messages_.empty()) { + return false; + } + + for (auto it = cached_messages_.begin(); it != cached_messages_.end();) { + if (0 == batch_size--) { + break; + } + + messages.push_back(*it); + it = cached_messages_.erase(it); + } + return !cached_messages_.empty(); +} + +bool ProcessQueueImpl::committedOffset(int64_t& offset) { + absl::MutexLock lk(&offsets_mtx_); + if (offsets_.empty()) { + return false; + } + if (offsets_.begin()->released_) { + offset = offsets_.begin()->offset_ + 1; + } else { + offset = offsets_.begin()->offset_; + } + return true; +} + +void ProcessQueueImpl::release(uint64_t body_size, int64_t offset) { + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + + cached_message_quantity_.fetch_sub(1); + cached_message_memory_.fetch_sub(body_size); + + if (MessageModel::BROADCASTING == consumer->messageModel()) { + absl::MutexLock lk(&offsets_mtx_); + if (offsets_.size() > 1) { + offsets_.erase(OffsetRecord(offset)); + } else { + assert(offsets_.begin()->offset_ == offset); + offsets_.erase(OffsetRecord(offset)); + offsets_.emplace(OffsetRecord(offset, true)); + } + } +} + +void ProcessQueueImpl::wrapFilterExpression(rmq::FilterExpression* filter_expression) { + assert(filter_expression); + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + auto&& optional = consumer->getFilterExpression(message_queue_.getTopic()); + if (optional.has_value()) { + auto expression = optional.value(); + switch (expression.type_) { + case TAG: + filter_expression->set_type(rmq::FilterType::TAG); + filter_expression->set_expression(expression.content_); + break; + case SQL92: + filter_expression->set_type(rmq::FilterType::SQL); + filter_expression->set_expression(expression.content_); + break; + } + } else { + filter_expression->set_type(rmq::FilterType::TAG); + filter_expression->set_expression("*"); + } +} + +void ProcessQueueImpl::wrapPopMessageRequest(absl::flat_hash_map& metadata, + rmq::ReceiveMessageRequest& request) { + std::shared_ptr consumer = consumer_.lock(); + assert(consumer); + request.set_client_id(consumer->clientId()); + request.mutable_group()->set_name(consumer->getGroupName()); + request.mutable_group()->set_resource_namespace(consumer->resourceNamespace()); + request.mutable_partition()->set_id(message_queue_.getQueueId()); + request.mutable_partition()->mutable_broker()->set_name(message_queue_.getBrokerName()); + request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); + request.mutable_partition()->mutable_topic()->set_resource_namespace(consumer->resourceNamespace()); + + wrapFilterExpression(request.mutable_filter_expression()); + + switch (consumer->getConsumeMessageService()->messageListenerType()) { + case MessageListenerType::STANDARD: { + request.set_fifo_flag(false); + break; + } + case MessageListenerType::FIFO: { + request.set_fifo_flag(true); + break; + } + } + + // Batch size + request.set_batch_size(consumer->receiveBatchSize()); + + // Consume policy + request.set_consume_policy(rmq::ConsumePolicy::RESUME); + + // Set invisible time + request.mutable_invisible_duration()->set_seconds( + std::chrono::duration_cast(invisible_time_).count()); + auto fraction = invisible_time_ - std::chrono::duration_cast(invisible_time_); + int32_t nano_seconds = static_cast(std::chrono::duration_cast(fraction).count()); + request.mutable_invisible_duration()->set_nanos(nano_seconds); +} + +void ProcessQueueImpl::wrapPullMessageRequest(rmq::PullMessageRequest& request) { + std::shared_ptr consumer = consumer_.lock(); + assert(consumer); + request.set_client_id(consumer->clientId()); + request.mutable_group()->set_name(consumer->getGroupName()); + request.mutable_group()->set_resource_namespace(consumer->resourceNamespace()); + request.mutable_partition()->set_id(message_queue_.getQueueId()); + request.mutable_partition()->mutable_broker()->set_name(message_queue_.getBrokerName()); + request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); + request.mutable_partition()->mutable_topic()->set_resource_namespace(consumer->resourceNamespace()); + request.set_offset(next_offset_); + request.set_batch_size(consumer->receiveBatchSize()); + + wrapFilterExpression(request.mutable_filter_expression()); +} + +std::weak_ptr ProcessQueueImpl::getConsumer() { + return consumer_; +} + +std::shared_ptr ProcessQueueImpl::getClientManager() { + return client_manager_; +} + +MQMessageQueue ProcessQueueImpl::getMQMessageQueue() { + return message_queue_; +} + +const FilterExpression& ProcessQueueImpl::getFilterExpression() const { + return filter_expression_; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp new file mode 100644 index 000000000..38bc99e6a --- /dev/null +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -0,0 +1,626 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ProducerImpl.h" + +#include +#include +#include +#include +#include + +#include "Client.h" +#include "absl/strings/str_join.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" + +#include "MessageAccessor.h" +#include "MessageGroupQueueSelector.h" +#include "MetadataConstants.h" +#include "MixAll.h" +#include "OtlpExporter.h" +#include "Protocol.h" +#include "RpcClient.h" +#include "SendCallbacks.h" +#include "SendMessageContext.h" +#include "Signature.h" +#include "TracingUtility.h" +#include "TransactionImpl.h" +#include "UniqueIdGenerator.h" +#include "UtilAll.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" +#include "rocketmq/Transaction.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ProducerImpl::ProducerImpl(absl::string_view group_name) + : ClientImpl(group_name), compress_body_threshold_(MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_) { + // TODO: initialize client_config_ and fault_strategy_ +} + +ProducerImpl::~ProducerImpl() { + SPDLOG_INFO("Producer instance is destructed"); +} + +void ProducerImpl::start() { + ClientImpl::start(); + + State expecting = State::STARTING; + if (!state_.compare_exchange_strong(expecting, State::STARTED)) { + SPDLOG_ERROR("Start with unexpected state. Expecting: {}, Actual: {}", State::STARTING, + state_.load(std::memory_order_relaxed)); + return; + } + + client_manager_->addClientObserver(shared_from_this()); +} + +void ProducerImpl::shutdown() { + State expected = State::STARTED; + if (!state_.compare_exchange_strong(expected, State::STOPPING)) { + SPDLOG_ERROR("Shutdown with unexpected state. Expecting: {}, Actual: {}", State::STOPPING, + state_.load(std::memory_order_relaxed)); + return; + } + + notifyClientTermination(); + + ClientImpl::shutdown(); + assert(State::STOPPED == state_.load()); + SPDLOG_INFO("Producer instance stopped"); +} + +void ProducerImpl::notifyClientTermination() { + NotifyClientTerminationRequest request; + request.mutable_producer_group()->set_resource_namespace(resource_namespace_); + request.mutable_producer_group()->set_name(group_name_); + request.set_client_id(clientId()); + ClientImpl::notifyClientTermination(request); +} + +bool ProducerImpl::isRunning() const { + return State::STARTED == state_.load(std::memory_order_relaxed); +} + +void ProducerImpl::ensureRunning(std::error_code& ec) const noexcept { + if (!isRunning()) { + ec = ErrorCode::IllegalState; + } +} + +bool ProducerImpl::validate(const MQMessage& message) { + return MixAll::validate(message); +} + +void ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, + const MQMessageQueue& message_queue) { + request.mutable_message()->mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_message()->mutable_topic()->set_name(message.getTopic()); + + auto system_attribute = request.mutable_message()->mutable_system_attribute(); + + // Handle Tag + auto&& tag = message.getTags(); + if (!tag.empty()) { + system_attribute->set_tag(tag); + } + + // Handle Key + const auto& keys = message.getKeys(); + if (!keys.empty()) { + system_attribute->mutable_keys()->Add(keys.begin(), keys.end()); + } + + // TraceContext + const auto& trace_context = message.traceContext(); + if (!trace_context.empty()) { + system_attribute->set_trace_context(trace_context); + } + + // Delivery Timestamp + auto delivery_timestamp = message.deliveryTimestamp(); + if (delivery_timestamp.time_since_epoch().count()) { + auto duration = delivery_timestamp.time_since_epoch(); + system_attribute->set_delivery_attempt(std::chrono::duration_cast(duration).count()); + } + + // Delay Level + if (message.getDelayTimeLevel()) { + system_attribute->set_delay_level(message.getDelayTimeLevel()); + } + + // Born-time + auto duration = absl::Now() - absl::UnixEpoch(); + int64_t seconds = absl::ToInt64Seconds(duration); + system_attribute->mutable_born_timestamp()->set_seconds(seconds); + system_attribute->mutable_born_timestamp()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); + + system_attribute->set_born_host(UtilAll::hostname()); + + system_attribute->mutable_producer_group()->set_resource_namespace(resource_namespace_); + system_attribute->mutable_producer_group()->set_name(group_name_); + + switch (message.messageType()) { + case MessageType::NORMAL: + system_attribute->set_message_type(rmq::MessageType::NORMAL); + break; + case MessageType::DELAY: + system_attribute->set_message_type(rmq::MessageType::DELAY); + break; + case MessageType::TRANSACTION: + system_attribute->set_message_type(rmq::MessageType::TRANSACTION); + break; + case MessageType::FIFO: + system_attribute->set_message_type(rmq::MessageType::FIFO); + break; + } + + if (message.bodyLength() >= compress_body_threshold_) { + std::string compressed_body; + UtilAll::compress(message.getBody(), compressed_body); + request.mutable_message()->set_body(compressed_body); + system_attribute->set_body_encoding(rmq::Encoding::GZIP); + } else { + request.mutable_message()->set_body(message.getBody()); + system_attribute->set_body_encoding(rmq::Encoding::IDENTITY); + } + + system_attribute->set_message_group(message.messageGroup()); + system_attribute->set_message_id(message.getMsgId()); + system_attribute->set_partition_id(message_queue.getQueueId()); + + // Forward user-defined-properties + for (auto& item : message.getProperties()) { + request.mutable_message()->mutable_user_attribute()->insert({item.first, item.second}); + } + SPDLOG_TRACE("SendMessageRequest: {}", request.DebugString()); +} + +SendResult ProducerImpl::send(const MQMessage& message, std::error_code& ec) noexcept { + ensureRunning(ec); + if (ec) { + return {}; + } + + auto topic_publish_info = getPublishInfo(message.getTopic()); + if (!topic_publish_info) { + ec = ErrorCode::NotFound; + return {}; + } + + std::vector message_queue_list; + + if (!message.messageGroup().empty() || message.messageQueue()) { + auto&& list = listMessageQueue(message.getTopic(), ec); + if (ec) { + return {}; + } + + if (list.empty()) { + ec = ErrorCode::ServiceUnavailable; + return {}; + } + + if (!message.messageGroup().empty()) { + std::size_t hash_code = std::hash{}(message.messageGroup()); + hash_code = hash_code & std::numeric_limits::max(); + std::size_t r = hash_code % list.size(); + message_queue_list.push_back(list[r]); + } else { + for (const auto& entry : list) { + if (entry == message.messageQueue()) { + message_queue_list.push_back(entry); + break; + } + } + } + } else { + takeMessageQueuesRoundRobin(topic_publish_info, message_queue_list, max_attempt_times_); + } + + if (message_queue_list.empty()) { + ec = ErrorCode::ServiceUnavailable; + return {}; + } + + AwaitSendCallback callback; + send0(message, &callback, message_queue_list, max_attempt_times_); + callback.await(); + + if (callback) { + return callback.sendResult(); + } + + ec = callback.errorCode(); + return {}; +} + +void ProducerImpl::send(const MQMessage& message, SendCallback* cb) { + std::error_code ec; + ensureRunning(ec); + if (ec) { + cb->onFailure(ec); + } + + auto callback = [this, message, cb](const std::error_code& ec, const TopicPublishInfoPtr& publish_info) { + if (ec) { + cb->onFailure(ec); + return; + } + + std::vector message_queue_list; + if (!message.messageGroup().empty() || message.messageQueue()) { + auto&& list = publish_info->getMessageQueueList(); + if (!message.messageGroup().empty()) { + std::size_t hash_code = std::hash{}(message.messageGroup()); + hash_code = hash_code & std::numeric_limits::max(); + std::size_t r = hash_code % list.size(); + message_queue_list.push_back(list[r]); + } else { + for (const auto& entry : list) { + if (entry == message.messageQueue()) { + message_queue_list.push_back(entry); + break; + } + } + } + } else { + takeMessageQueuesRoundRobin(publish_info, message_queue_list, max_attempt_times_); + } + + if (message_queue_list.empty()) { + cb->onFailure(ErrorCode::ServiceUnavailable); + return; + } + + send0(message, cb, message_queue_list, max_attempt_times_); + }; + + asyncPublishInfo(message.getTopic(), callback); +} + +void ProducerImpl::sendOneway(const MQMessage& message, std::error_code& ec) { + send(message, ec); +} + +void ProducerImpl::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { + transaction_state_checker_ = std::move(checker); +} + +void ProducerImpl::sendImpl(RetrySendCallback* callback) { + const std::string& target = callback->messageQueue().serviceAddress(); + if (target.empty()) { + SPDLOG_WARN("Failed to resolve broker address from MessageQueue"); + std::error_code ec = ErrorCode::BadGateway; + callback->onFailure(ec); + return; + } + + { + // Trace Send RPC + auto& message = callback->message(); + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); + + auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = + resourceNamespace() + "/" + message.getTopic() + " " + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION; + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, {&Samplers::always()}); + } else { + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION); + TracingUtility::addUniversalSpanAttributes(message, *this, span); + // Note: attempt-time is 0-based + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, 1 + callback->attemptTime()); + + if (message.deliveryTimestamp() != absl::ToChronoTime(absl::UnixEpoch())) { + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, + absl::FormatTime(absl::FromChrono(message.deliveryTimestamp()))); + } + callback->message().traceContext(opencensus::trace::propagation::ToTraceParentHeader(span.context())); + callback->span() = span; + } + + SendMessageRequest request; + wrapSendMessageRequest(callback->message(), request, callback->messageQueue()); + Metadata metadata; + Signature::sign(this, metadata); + + client_manager_->send(target, metadata, request, callback); +} + +void ProducerImpl::send0(const MQMessage& message, SendCallback* callback, std::vector list, + int max_attempt_times) { + assert(callback); + + if (!validate(message)) { + std::error_code ec = ErrorCode::BadRequest; + callback->onFailure(ec); + return; + } + + if (list.empty()) { + std::error_code ec = ErrorCode::NotFound; + callback->onFailure(ec); + return; + } + + if (max_attempt_times <= 0) { + std::error_code ec = ErrorCode::BadConfiguration; + callback->onFailure(ec); + return; + } + MQMessageQueue message_queue = list[0]; + auto retry_callback = + new RetrySendCallback(shared_from_this(), message, max_attempt_times, callback, std::move(list)); + sendImpl(retry_callback); + const_cast(message).traceContext( + opencensus::trace::propagation::ToTraceParentHeader(retry_callback->span().context())); +} + +bool ProducerImpl::endTransaction0(const std::string& target, const MQMessage& message, + const std::string& transaction_id, TransactionState resolution) { + + EndTransactionRequest request; + request.set_message_id(message.getMsgId()); + request.set_transaction_id(transaction_id); + request.mutable_group()->set_name(group_name_); + request.mutable_group()->set_resource_namespace(resource_namespace_); + + std::string action; + switch (resolution) { + case TransactionState::COMMIT: + request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_COMMIT); + action = "commit"; + break; + case TransactionState::ROLLBACK: + request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_ROLLBACK); + action = "rollback"; + break; + } + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + bool completed = false; + bool success = false; + // Trace transactional message + opencensus::trace::SpanContext span_context = + opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); + auto span = opencensus::trace::Span::BlankSpan(); + std::string trace_operation_name = TransactionState::COMMIT == resolution + ? MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_COMMIT_OPERATION + : MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ROLLBACK_OPERATION; + std::string span_name = resourceNamespace() + "/" + message.getTopic() + " " + trace_operation_name; + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, {&Samplers::always()}); + } else { + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, trace_operation_name); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, trace_operation_name); + TracingUtility::addUniversalSpanAttributes(message, *this, span); + + absl::Mutex mtx; + absl::CondVar cv; + auto cb = [&, span](const std::error_code& ec, const EndTransactionResponse& response) { + completed = true; + if (ec) { + { + span.SetStatus(opencensus::trace::StatusCode::ABORTED); + span.AddAnnotation(ec.message()); + span.End(); + } + SPDLOG_WARN("Failed to send {} transaction request to {}. Cause: ", action, target, ec.message()); + success = false; + } else { + { + span.SetStatus(opencensus::trace::StatusCode::OK); + span.End(); + } + success = true; + } + + { + absl::MutexLock lk(&mtx); + cv.SignalAll(); + } + }; + + client_manager_->endTransaction(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), cb); + { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + return success; +} + +void ProducerImpl::isolatedEndpoints(absl::flat_hash_set& endpoints) { + absl::MutexLock lk(&isolated_endpoints_mtx_); + endpoints.insert(isolated_endpoints_.begin(), isolated_endpoints_.end()); +} + +MQMessageQueue ProducerImpl::withServiceAddress(const MQMessageQueue& message_queue, std::error_code& ec) { + if (!message_queue.serviceAddress().empty()) { + return message_queue; + } + + if (message_queue.getTopic().empty() || message_queue.getBrokerName().empty() || message_queue.getQueueId() < 0) { + ec = ErrorCode::BadRequest; + return {}; + } + + std::vector list = listMessageQueue(message_queue.getTopic(), ec); + if (ec) { + return {}; + } + + for (const auto& item : list) { + if (item == message_queue) { + return item; + } + } + + if (list.empty()) { + ec = ErrorCode::NotFound; + return {}; + } else { + return *list.begin(); + } +} + +bool ProducerImpl::isEndpointIsolated(const std::string& target) { + absl::MutexLock lk(&isolated_endpoints_mtx_); + return isolated_endpoints_.contains(target); +} + +void ProducerImpl::isolateEndpoint(const std::string& target) { + absl::MutexLock lk(&isolated_endpoints_mtx_); + isolated_endpoints_.insert(target); +} + +std::unique_ptr ProducerImpl::prepare(MQMessage& message, std::error_code& ec) { + message.messageType(MessageType::TRANSACTION); + SendResult send_result = send(message, ec); + if (ec) { + return nullptr; + } + + return absl::make_unique(message, send_result.getTransactionId(), + send_result.getMessageQueue().serviceAddress(), send_result.traceContext(), + ProducerImpl::shared_from_this()); +} + +bool ProducerImpl::commit(const MQMessage& message, const std::string& transaction_id, const std::string& target) { + return endTransaction0(target, message, transaction_id, TransactionState::COMMIT); +} + +bool ProducerImpl::rollback(const MQMessage& message, const std::string& transaction_id, const std::string& target) { + return endTransaction0(target, message, transaction_id, TransactionState::ROLLBACK); +} + +void ProducerImpl::asyncPublishInfo(const std::string& topic, + const std::function& cb) { + TopicPublishInfoPtr ptr; + { + absl::MutexLock lock(&topic_publish_info_mtx_); + if (topic_publish_info_table_.contains(topic)) { + ptr = topic_publish_info_table_.at(topic); + } + } + std::error_code ec; + if (ptr) { + cb(ec, ptr); + } else { + auto callback = [this, topic, cb](const std::error_code& ec, const TopicRouteDataPtr& route) { + if (ec) { + cb(ec, nullptr); + return; + } + + auto publish_info = std::make_shared(topic, route); + { + absl::MutexLock lk(&topic_publish_info_mtx_); + topic_publish_info_table_.insert_or_assign(topic, publish_info); + } + cb(ec, publish_info); + }; + + getRouteFor(topic, callback); + } +} + +TopicPublishInfoPtr ProducerImpl::getPublishInfo(const std::string& topic) { + bool complete = false; + absl::Mutex mtx; + absl::CondVar cv; + TopicPublishInfoPtr topic_publish_info; + std::error_code error_code; + auto cb = [&](const std::error_code& ec, const TopicPublishInfoPtr& ptr) { + absl::MutexLock lk(&mtx); + topic_publish_info = ptr; + error_code = ec; + complete = true; + cv.SignalAll(); + }; + asyncPublishInfo(topic, cb); + + // Wait till acquiring topic publish info completes + while (!complete) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + + // TODO: propogate error_code to caller + return topic_publish_info; +} + +void ProducerImpl::takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, + std::vector& message_queues, int number) { + assert(publish_info); + absl::flat_hash_set isolated; + isolatedEndpoints(isolated); + publish_info->takeMessageQueues(isolated, message_queues, number); +} + +std::vector ProducerImpl::listMessageQueue(const std::string& topic, std::error_code& ec) { + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + TopicPublishInfoPtr ptr; + auto await_callback = [&](const std::error_code& error_code, const TopicPublishInfoPtr& publish_info) { + absl::MutexLock lk(&mtx); + ptr = publish_info; + ec = error_code; + completed = true; + cv.SignalAll(); + }; + + asyncPublishInfo(topic, await_callback); + + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + + if (ec) { + return {}; + } + + return ptr->getMessageQueueList(); +} + +void ProducerImpl::prepareHeartbeatData(HeartbeatRequest& request) { + request.set_client_id(clientId()); + request.mutable_producer_data()->mutable_group()->set_resource_namespace(resource_namespace_); + request.mutable_producer_data()->mutable_group()->set_name(group_name_); +} + +void ProducerImpl::resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) { + if (transaction_state_checker_) { + TransactionState state = transaction_state_checker_->checkLocalTransactionState(message); + const std::string& target_host = MessageAccessor::targetEndpoint(message); + endTransaction0(target_host, message, transaction_id, state); + } else { + SPDLOG_WARN("LocalTransactionStateChecker is unexpectedly nullptr"); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/PullConsumerImpl.cpp b/src/main/cpp/rocketmq/PullConsumerImpl.cpp new file mode 100644 index 000000000..530f2f3c8 --- /dev/null +++ b/src/main/cpp/rocketmq/PullConsumerImpl.cpp @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "PullConsumerImpl.h" +#include "ClientManagerFactory.h" +#include "InvocationContext.h" +#include "Signature.h" +#include "apache/rocketmq/v1/definition.pb.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/MQClientException.h" +#include "rocketmq/MessageModel.h" +#include "rocketmq/PullResult.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +void PullConsumerImpl::start() { + ClientImpl::start(); + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected state: {}", state_.load(std::memory_order_relaxed)); + return; + } + client_manager_->addClientObserver(shared_from_this()); +} + +void PullConsumerImpl::shutdown() { + // Shutdown services started by current tier + + notifyClientTermination(); + + // Shutdown services that are started by the parent + ClientImpl::shutdown(); + State expected = State::STOPPING; + if (state_.compare_exchange_strong(expected, State::STOPPED)) { + SPDLOG_INFO("DefaultMQPullConsumerImpl stopped"); + } +} + +std::future> PullConsumerImpl::queuesFor(const std::string& topic) { + auto promise = std::make_shared>>(); + { + absl::MutexLock lk(&topic_route_table_mtx_); + if (topic_route_table_.contains(topic)) { + TopicRouteDataPtr topic_route = topic_route_table_.at(topic); + auto partitions = topic_route->partitions(); + std::vector message_queues; + message_queues.reserve(partitions.size()); + for (const auto& partition : partitions) { + message_queues.emplace_back(partition.asMessageQueue()); + } + promise->set_value(std::move(message_queues)); + return promise->get_future(); + } + } + + auto callback = [promise](const std::error_code& ec, const TopicRouteDataPtr& route) { + if (ec) { + MQClientException e(ec.message(), ec.value(), __FILE__, __LINE__); + promise->set_exception(std::make_exception_ptr(e)); + return; + } + + std::vector message_queues; + for (const auto& partition : route->partitions()) { + message_queues.emplace_back(partition.asMessageQueue()); + } + promise->set_value(message_queues); + }; + + getRouteFor(topic, callback); + return promise->get_future(); +} + +std::future PullConsumerImpl::queryOffset(const OffsetQuery& query) { + QueryOffsetRequest request; + switch (query.policy) { + case QueryOffsetPolicy::BEGINNING: + request.set_policy(rmq::QueryOffsetPolicy::BEGINNING); + break; + case QueryOffsetPolicy::END: + request.set_policy(rmq::QueryOffsetPolicy::END); + break; + case QueryOffsetPolicy::TIME_POINT: + request.set_policy(rmq::QueryOffsetPolicy::TIME_POINT); + auto duration = absl::FromChrono(query.time_point.time_since_epoch()); + int64_t seconds = absl::ToInt64Seconds(duration); + request.mutable_time_point()->set_seconds(seconds); + request.mutable_time_point()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); + break; + } + + request.mutable_partition()->mutable_topic()->set_name(query.message_queue.getTopic()); + request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); + + request.mutable_partition()->set_id(query.message_queue.getQueueId()); + + absl::flat_hash_map metadata; + + Signature::sign(this, metadata); + + // TODO: Use std::unique_ptr if C++14 is adopted. + auto promise_ptr = std::make_shared>(); + auto callback = [promise_ptr](const std::error_code& ec, const QueryOffsetResponse& response) { + if (ec) { + MQClientException e(ec.message(), ec.value(), __FILE__, __LINE__); + promise_ptr->set_exception(std::make_exception_ptr(e)); + return; + } + promise_ptr->set_value(response.offset()); + }; + + client_manager_->queryOffset(query.message_queue.serviceAddress(), metadata, request, + absl::ToChronoMilliseconds(io_timeout_), callback); + return promise_ptr->get_future(); +} + +void PullConsumerImpl::pull(const PullMessageQuery& query, PullCallback* cb) { + PullMessageRequest request; + request.set_offset(query.offset); + auto duration = absl::FromChrono(query.await_time); + int64_t seconds = absl::ToInt64Seconds(duration); + request.mutable_await_time()->set_seconds(seconds); + request.mutable_await_time()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); + request.mutable_group()->set_name(group_name_); + request.mutable_group()->set_resource_namespace(resource_namespace_); + + request.mutable_partition()->mutable_topic()->set_name(query.message_queue.getTopic()); + request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_partition()->set_id(query.message_queue.getQueueId()); + request.set_client_id(clientId()); + + std::string target_host = query.message_queue.serviceAddress(); + assert(!target_host.empty()); + + auto callback = [target_host, cb](const std::error_code& ec, const ReceiveMessageResult& result) { + if (ec) { + cb->onFailure(ec); + return; + } + + PullResult pull_result(result.min_offset, result.max_offset, result.next_offset, result.messages); + cb->onSuccess(pull_result); + }; + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + + client_manager_->pullMessage(target_host, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), + callback); +} + +void PullConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { + request.set_client_id(clientId()); + auto consumer_data = request.mutable_consumer_data(); + consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); + consumer_data->mutable_group()->set_name(group_name_); + switch (message_model_) { + case MessageModel::BROADCASTING: + consumer_data->set_consume_model(rmq::ConsumeModel::BROADCASTING); + break; + case MessageModel::CLUSTERING: + consumer_data->set_consume_model(rmq::ConsumeModel::CLUSTERING); + break; + } +} + +void PullConsumerImpl::notifyClientTermination() { + NotifyClientTerminationRequest request; + request.mutable_consumer_group()->set_resource_namespace(resource_namespace_); + request.mutable_consumer_group()->set_name(group_name_); + request.set_client_id(clientId()); + ClientImpl::notifyClientTermination(request); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp new file mode 100644 index 000000000..6691716dc --- /dev/null +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -0,0 +1,657 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "PushConsumerImpl.h" + +#include +#include +#include +#include +#include + +#include "apache/rocketmq/v1/definition.pb.h" + +#include "AsyncReceiveMessageCallback.h" +#include "ClientManagerFactory.h" +#include "ConsumeFifoMessageService.h" +#include "ConsumeStandardMessageService.h" +#include "MessageAccessor.h" +#include "MixAll.h" +#include "ProcessQueueImpl.h" +#include "RpcClient.h" +#include "Signature.h" +#include "rocketmq/MQClientException.h" +#include "rocketmq/MessageListener.h" +#include "rocketmq/MessageModel.h" + +ROCKETMQ_NAMESPACE_BEGIN + +PushConsumerImpl::PushConsumerImpl(absl::string_view group_name) : ClientImpl(group_name) { +} + +PushConsumerImpl::~PushConsumerImpl() { + SPDLOG_DEBUG("DefaultMQPushConsumerImpl is destructed"); +} + +void PushConsumerImpl::start() { + ClientImpl::start(); + + State expecting = State::STARTING; + if (!state_.compare_exchange_strong(expecting, State::STARTED)) { + SPDLOG_ERROR("Unexpected consumer state. Expecting: {}, Actual: {}", State::STARTING, + state_.load(std::memory_order_relaxed)); + return; + } + + if (!message_listener_) { + SPDLOG_ERROR("Required message listener is nullptr"); + abort(); + return; + } + + client_manager_->addClientObserver(shared_from_this()); + + fetchRoutes(); + + if (message_listener_->listenerType() == MessageListenerType::FIFO) { + SPDLOG_INFO("start orderly consume service: {}", group_name_); + consume_message_service_ = + std::make_shared(shared_from_this(), consume_thread_pool_size_, message_listener_); + consume_batch_size_ = 1; + } else { + // For backward compatibility, by default, ConsumeMessageConcurrentlyService is assumed. + SPDLOG_INFO("start concurrently consume service: {}", group_name_); + consume_message_service_ = std::make_shared( + shared_from_this(), consume_thread_pool_size_, message_listener_); + } + consume_message_service_->start(); + + { + // Set consumer throttling + absl::MutexLock lock(&throttle_table_mtx_); + for (const auto& item : throttle_table_) { + consume_message_service_->throttle(item.first, item.second); + } + } + + // Heartbeat depends on initialization of consume-message-service + heartbeat(); + + std::weak_ptr consumer_weak_ptr(shared_from_this()); + auto scan_assignment_functor = [consumer_weak_ptr]() { + std::shared_ptr consumer = consumer_weak_ptr.lock(); + if (consumer) { + consumer->scanAssignments(); + } + }; + + scan_assignment_handle_ = client_manager_->getScheduler()->schedule( + scan_assignment_functor, SCAN_ASSIGNMENT_TASK_NAME, std::chrono::milliseconds(100), std::chrono::seconds(5)); + + SPDLOG_INFO("PushConsumer started, groupName={}", group_name_); +} + +const char* PushConsumerImpl::SCAN_ASSIGNMENT_TASK_NAME = "scan-assignment-task"; + +void PushConsumerImpl::shutdown() { + State expecting = State::STARTED; + if (state_.compare_exchange_strong(expecting, State::STOPPING)) { + if (scan_assignment_handle_) { + client_manager_->getScheduler()->cancel(scan_assignment_handle_); + SPDLOG_DEBUG("Scan assignment periodic task cancelled"); + } + + { + absl::MutexLock lock(&process_queue_table_mtx_); + process_queue_table_.clear(); + } + + if (consume_message_service_) { + consume_message_service_->shutdown(); + } + + // Shutdown services started by parent + ClientImpl::shutdown(); + + SPDLOG_INFO("PushConsumerImpl stopped"); + } else { + SPDLOG_ERROR("Shutdown with unexpected state. Expecting: {}, Actual: {}", State::STARTED, + state_.load(std::memory_order_relaxed)); + } +} + +void PushConsumerImpl::subscribe(const std::string& topic, const std::string& expression, + ExpressionType expression_type) { + absl::MutexLock lock(&topic_filter_expression_table_mtx_); + FilterExpression filter_expression{expression, expression_type}; + topic_filter_expression_table_.emplace(topic, filter_expression); +} + +void PushConsumerImpl::unsubscribe(const std::string& topic) { + absl::MutexLock lock(&topic_filter_expression_table_mtx_); + topic_filter_expression_table_.erase(topic); +} + +absl::optional PushConsumerImpl::getFilterExpression(const std::string& topic) const { + { + absl::MutexLock lock(&topic_filter_expression_table_mtx_); + if (topic_filter_expression_table_.contains(topic)) { + return absl::make_optional(topic_filter_expression_table_.at(topic)); + } else { + return absl::optional(); + } + } +} + +void PushConsumerImpl::setConsumeFromWhere(ConsumeFromWhere consume_from_where) { + consume_from_where_ = consume_from_where; +} + +void PushConsumerImpl::scanAssignments() { + SPDLOG_DEBUG("Start of assignment scanning"); + if (!active()) { + SPDLOG_INFO("Client has stopped. Abort scanning immediately."); + return; + } + + { + absl::MutexLock lk(&topic_filter_expression_table_mtx_); + for (auto& entry : topic_filter_expression_table_) { + std::string topic = entry.first; + const auto& filter_expression = entry.second; + SPDLOG_DEBUG("Scan assignments for {}", topic); + auto callback = [this, topic, filter_expression](const std::error_code& ec, + const TopicAssignmentPtr& assignments) { + if (ec) { + SPDLOG_WARN("Failed to acquire assignments for topic={} from load balancer. Cause: {}", topic, ec.message()); + } else if (assignments && !assignments->assignmentList().empty()) { + syncProcessQueue(topic, assignments, filter_expression); + } + }; + queryAssignment(topic, callback); + } // end of for-loop + } + SPDLOG_DEBUG("End of assignment scanning."); +} + +bool PushConsumerImpl::selectBroker(const TopicRouteDataPtr& topic_route_data, std::string& broker_host) { + if (topic_route_data && !topic_route_data->partitions().empty()) { + uint32_t index = TopicAssignment::getAndIncreaseQueryWhichBroker(); + for (uint32_t i = index; i < index + topic_route_data->partitions().size(); i++) { + auto partition = topic_route_data->partitions().at(i % topic_route_data->partitions().size()); + if (MixAll::MASTER_BROKER_ID != partition.broker().id() || Permission::NONE == partition.permission()) { + continue; + } + + if (partition.broker()) { + broker_host = partition.broker().serviceAddress(); + return true; + } + } + } + return false; +} + +void PushConsumerImpl::wrapQueryAssignmentRequest(const std::string& topic, const std::string& consumer_group, + const std::string& client_id, const std::string& strategy_name, + QueryAssignmentRequest& request) { + request.mutable_topic()->set_name(topic); + request.mutable_topic()->set_resource_namespace(resourceNamespace()); + request.mutable_group()->set_name(consumer_group); + request.mutable_group()->set_resource_namespace(resourceNamespace()); + request.set_client_id(client_id); +} + +void PushConsumerImpl::queryAssignment( + const std::string& topic, const std::function& cb) { + + auto callback = [this, topic, cb](const std::error_code& ec, const TopicRouteDataPtr& topic_route) { + TopicAssignmentPtr topic_assignment; + if (MessageModel::BROADCASTING == message_model_) { + if (ec) { + SPDLOG_WARN("Failed to get valid route entries for topic={}. Cause: {}", topic, ec.message()); + cb(ec, topic_assignment); + } + + std::vector assignments; + assignments.reserve(topic_route->partitions().size()); + for (const auto& partition : topic_route->partitions()) { + assignments.emplace_back(Assignment(partition.asMessageQueue())); + } + topic_assignment = std::make_shared(std::move(assignments)); + cb(ec, topic_assignment); + return; + } + + std::string broker_host; + if (!selectBroker(topic_route, broker_host)) { + SPDLOG_WARN("Failed to select a broker to query assignment for group={}, topic={}", group_name_, topic); + } + + QueryAssignmentRequest request; + setAccessPoint(request.mutable_endpoints()); + wrapQueryAssignmentRequest(topic, group_name_, clientId(), MixAll::DEFAULT_LOAD_BALANCER_STRATEGY_NAME_, request); + SPDLOG_DEBUG("QueryAssignmentRequest: {}", request.DebugString()); + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + auto assignment_callback = [this, cb, topic, broker_host](const std::error_code& ec, + const QueryAssignmentResponse& response) { + if (ec) { + SPDLOG_WARN("Failed to acquire queue assignment of topic={} from brokerAddress={}", topic, broker_host); + cb(ec, nullptr); + } else { + SPDLOG_DEBUG("Query topic assignment OK. Topic={}, group={}, assignment-size={}", topic, group_name_, + response.assignments().size()); + SPDLOG_TRACE("Query assignment response for {} is: {}", topic, response.DebugString()); + cb(ec, std::make_shared(response)); + } + }; + + client_manager_->queryAssignment(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), + assignment_callback); + }; + getRouteFor(topic, callback); +} + +/** + * + * @param topic Topic to process + * @param assignment Latest assignment from load balancer + * @param filter_expression Filter expression + */ +void PushConsumerImpl::syncProcessQueue(const std::string& topic, + const std::shared_ptr& topic_assignment, + const FilterExpression& filter_expression) { + const std::vector& assignment_list = topic_assignment->assignmentList(); + std::vector message_queue_list; + message_queue_list.reserve(assignment_list.size()); + for (const auto& assignment : assignment_list) { + message_queue_list.push_back(assignment.messageQueue()); + } + + std::vector current; + { + absl::MutexLock lock(&process_queue_table_mtx_); + for (auto it = process_queue_table_.begin(); it != process_queue_table_.end();) { + if (topic != it->first.getTopic()) { + it++; + continue; + } + + if (std::none_of(message_queue_list.cbegin(), message_queue_list.cend(), + [&](const MQMessageQueue& message_queue) { return it->first == message_queue; })) { + SPDLOG_INFO("Stop receiving messages from {} as it is not assigned to current client according to latest " + "assignment result from load balancer", + it->first.simpleName()); + process_queue_table_.erase(it++); + } else { + if (!it->second || it->second->expired()) { + SPDLOG_WARN("ProcessQueue={} is expired. Remove it for now.", it->first.simpleName()); + process_queue_table_.erase(it++); + continue; + } + current.push_back(it->first); + it++; + } + } + } + + for (const auto& message_queue : message_queue_list) { + if (std::none_of(current.cbegin(), current.cend(), + [&](const MQMessageQueue& item) { return item == message_queue; })) { + SPDLOG_INFO("Start to receive message from {} according to latest assignment info from load balancer", + message_queue.simpleName()); + if (!receiveMessage(message_queue, filter_expression)) { + if (!active()) { + SPDLOG_WARN("Failed to initiate receive message request-response-cycle for {}", message_queue.simpleName()); + // TODO: remove it from current assignment such that a second attempt will be made again in the next round. + } + } + } + } +} + +ProcessQueueSharedPtr PushConsumerImpl::getOrCreateProcessQueue(const MQMessageQueue& message_queue, + const FilterExpression& filter_expression) { + ProcessQueueSharedPtr process_queue; + { + absl::MutexLock lock(&process_queue_table_mtx_); + if (!active()) { + SPDLOG_INFO("PushConsumer has stopped. Drop creation of ProcessQueue"); + return process_queue; + } + + if (process_queue_table_.contains(message_queue)) { + process_queue = process_queue_table_.at(message_queue); + } else { + SPDLOG_INFO("Create ProcessQueue for message queue[{}]", message_queue.simpleName()); + // create ProcessQueue + process_queue = + std::make_shared(message_queue, filter_expression, shared_from_this(), client_manager_); + std::shared_ptr receive_callback = + std::make_shared(process_queue); + process_queue->callback(receive_callback); + process_queue_table_.emplace(std::make_pair(message_queue, process_queue)); + } + } + return process_queue; +} + +bool PushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) { + if (!active()) { + SPDLOG_INFO("PushConsumer has stopped. Drop further receive message request"); + return false; + } + + ProcessQueueSharedPtr process_queue_ptr = getOrCreateProcessQueue(message_queue, filter_expression); + if (!process_queue_ptr) { + SPDLOG_INFO("Consumer has stopped. Stop creating processQueue"); + return false; + } + + const std::string& broker_host = message_queue.serviceAddress(); + if (broker_host.empty()) { + SPDLOG_ERROR("Failed to resolve address for brokerName={}", message_queue.getBrokerName()); + return false; + } + + switch (message_model_) { + case MessageModel::BROADCASTING: { + int64_t offset = -1; + if (!offset_store_ || !offset_store_->readOffset(message_queue, offset)) { + // Query latest offset from server. + QueryOffsetRequest request; + request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_partition()->mutable_topic()->set_name(message_queue.getTopic()); + request.mutable_partition()->set_id(message_queue.getQueueId()); + request.mutable_partition()->mutable_broker()->set_name(message_queue.getBrokerName()); + request.set_policy(rmq::QueryOffsetPolicy::END); + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + auto callback = [broker_host, message_queue, process_queue_ptr](const std::error_code& ec, + const QueryOffsetResponse& response) { + if (ec) { + SPDLOG_WARN("Failed to acquire latest offset for partition[{}] from server[host={}]. Cause: {}", + message_queue.simpleName(), broker_host, ec.message()); + } else { + assert(response.offset() >= 0); + process_queue_ptr->nextOffset(response.offset()); + process_queue_ptr->receiveMessage(); + } + }; + client_manager_->queryOffset(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + } + break; + } + case MessageModel::CLUSTERING: + process_queue_ptr->receiveMessage(); + break; + } + return true; +} + +std::shared_ptr PushConsumerImpl::getConsumeMessageService() { + return consume_message_service_; +} + +void PushConsumerImpl::ack(const MQMessageExt& msg, const std::function& callback) { + const std::string& target_host = MessageAccessor::targetEndpoint(msg); + assert(!target_host.empty()); + SPDLOG_DEBUG("Prepare to send ack to broker. BrokerAddress={}, topic={}, queueId={}, msgId={}", target_host, + msg.getTopic(), msg.getQueueId(), msg.getMsgId()); + AckMessageRequest request; + wrapAckMessageRequest(msg, request); + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + client_manager_->ack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); +} + +void PushConsumerImpl::nack(const MQMessageExt& msg, const std::function& callback) { + std::string target_host = MessageAccessor::targetEndpoint(msg); + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + + rmq::NackMessageRequest request; + + // Group + request.mutable_group()->set_resource_namespace(resource_namespace_); + request.mutable_group()->set_name(group_name_); + // Topic + request.mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_topic()->set_name(msg.getTopic()); + request.set_client_id(clientId()); + request.set_receipt_handle(msg.receiptHandle()); + request.set_message_id(msg.getMsgId()); + request.set_delivery_attempt(msg.getDeliveryAttempt() + 1); + request.set_max_delivery_attempts(max_delivery_attempts_); + + client_manager_->nack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + SPDLOG_DEBUG("Send message nack to broker server[host={}]", target_host); +} + +void PushConsumerImpl::forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) { + std::string target_host = MessageAccessor::targetEndpoint(message); + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + + ForwardMessageToDeadLetterQueueRequest request; + request.mutable_group()->set_resource_namespace(resource_namespace_); + request.mutable_group()->set_name(group_name_); + + request.mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_topic()->set_name(message.getTopic()); + + request.set_client_id(clientId()); + request.set_message_id(message.getMsgId()); + + request.set_delivery_attempt(message.getDeliveryAttempt()); + request.set_max_delivery_attempts(max_delivery_attempts_); + + client_manager_->forwardMessageToDeadLetterQueue(target_host, metadata, request, + absl::ToChronoMilliseconds(io_timeout_), cb); +} + +void PushConsumerImpl::wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request) { + request.mutable_group()->set_resource_namespace(resource_namespace_); + request.mutable_group()->set_name(group_name_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_topic()->set_name(msg.getTopic()); + request.set_client_id(clientId()); + request.set_message_id(msg.getMsgId()); + request.set_receipt_handle(msg.receiptHandle()); +} + +uint32_t PushConsumerImpl::consumeThreadPoolSize() const { + return consume_thread_pool_size_; +} + +void PushConsumerImpl::consumeThreadPoolSize(int thread_pool_size) { + if (thread_pool_size >= 1) { + consume_thread_pool_size_ = thread_pool_size; + } +} + +uint32_t PushConsumerImpl::consumeBatchSize() const { + return consume_batch_size_; +} + +void PushConsumerImpl::consumeBatchSize(uint32_t consume_batch_size) { + + // For FIFO messages, consume batch size should always be 1. + if (message_listener_ && message_listener_->listenerType() == MessageListenerType::FIFO) { + return; + } + + if (consume_batch_size >= 1) { + consume_batch_size_ = consume_batch_size; + } +} + +void PushConsumerImpl::registerMessageListener(MessageListener* message_listener) { + message_listener_ = message_listener; +} + +std::size_t PushConsumerImpl::getProcessQueueTableSize() { + absl::MutexLock lock(&process_queue_table_mtx_); + return process_queue_table_.size(); +} + +void PushConsumerImpl::setThrottle(const std::string& topic, uint32_t threshold) { + absl::MutexLock lock(&throttle_table_mtx_); + throttle_table_.emplace(topic, threshold); + // If consumer has started, update it dynamically. + if (getConsumeMessageService()) { + getConsumeMessageService()->throttle(topic, threshold); + } +} + +void PushConsumerImpl::iterateProcessQueue(const std::function& callback) { + absl::MutexLock lock(&process_queue_table_mtx_); + for (const auto& item : process_queue_table_) { + if (item.second->hasPendingMessages()) { + callback(item.second); + } + } +} + +void PushConsumerImpl::fetchRoutes() { + std::vector topics; + { + absl::MutexLock lk(&topic_filter_expression_table_mtx_); + for (const auto& item : topic_filter_expression_table_) { + topics.emplace_back(item.first); + } + } + + if (topics.empty()) { + return; + } + + std::vector::size_type countdown = topics.size(); + absl::Mutex mtx; + absl::CondVar cv; + int acquired = 0; + auto callback = [&](const std::error_code& ec, const TopicRouteDataPtr& route) { + absl::MutexLock lk(&mtx); + countdown--; + cv.SignalAll(); + if (!ec) { + acquired++; + } + }; + + for (const auto& topic : topics) { + getRouteFor(topic, callback); + } + + while (countdown) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + SPDLOG_INFO("Fetched route for {} out of {} topics", acquired, topics.size()); +} + +void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { + request.set_client_id(clientId()); + + if (message_listener_) { + switch (message_listener_->listenerType()) { + case MessageListenerType::FIFO: + request.set_fifo_flag(true); + break; + case MessageListenerType::STANDARD: + request.set_fifo_flag(false); + break; + } + } + + auto consumer_data = request.mutable_consumer_data(); + consumer_data->mutable_group()->set_name(group_name_); + consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); + + switch (message_model_) { + case MessageModel::BROADCASTING: + consumer_data->set_consume_model(rmq::ConsumeModel::BROADCASTING); + break; + case MessageModel::CLUSTERING: + consumer_data->set_consume_model(rmq::ConsumeModel::CLUSTERING); + break; + default: + break; + } + + auto subscriptions = consumer_data->mutable_subscriptions(); + { + absl::MutexLock lk(&topic_filter_expression_table_mtx_); + for (const auto& entry : topic_filter_expression_table_) { + auto subscription = new rmq::SubscriptionEntry; + subscription->mutable_topic()->set_resource_namespace(resource_namespace_); + subscription->mutable_topic()->set_name(entry.first); + subscription->mutable_expression()->set_expression(entry.second.content_); + switch (entry.second.type_) { + case ExpressionType::TAG: + subscription->mutable_expression()->set_type(rmq::FilterType::TAG); + break; + case ExpressionType::SQL92: + subscription->mutable_expression()->set_type(rmq::FilterType::SQL); + break; + } + subscriptions->AddAllocated(subscription); + } + } + + assert(consume_message_service_); + switch (consume_message_service_->messageListenerType()) { + case MessageListenerType::FIFO: { + // TODO: Use enumeration in the protocol buffer specification? + request.set_fifo_flag(true); + break; + } + case MessageListenerType::STANDARD: { + request.set_fifo_flag(false); + break; + } + } + + consumer_data->set_consume_policy(rmq::ConsumePolicy::RESUME); + consumer_data->mutable_dead_letter_policy()->set_max_delivery_attempts(maxDeliveryAttempts()); + consumer_data->set_consume_type(rmq::ConsumeMessageType::PASSIVE); +} + +ClientResourceBundle PushConsumerImpl::resourceBundle() { + auto&& resource_bundle = ClientImpl::resourceBundle(); + resource_bundle.group_type = GroupType::SUBSCRIBER; + { + absl::MutexLock lk(&topic_filter_expression_table_mtx_); + for (auto& item : topic_filter_expression_table_) { + resource_bundle.topics.emplace_back(item.first); + } + } + return std::move(resource_bundle); +} + +void PushConsumerImpl::notifyClientTermination() { + NotifyClientTerminationRequest request; + request.mutable_consumer_group()->set_resource_namespace(resource_namespace_); + request.mutable_consumer_group()->set_name(group_name_); + request.set_client_id(clientId()); + ClientImpl::notifyClientTermination(request); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/SendCallbacks.cpp b/src/main/cpp/rocketmq/SendCallbacks.cpp new file mode 100644 index 000000000..cddf1fc5a --- /dev/null +++ b/src/main/cpp/rocketmq/SendCallbacks.cpp @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "SendCallbacks.h" + +#include "ProducerImpl.h" +#include "TransactionImpl.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" +#include "rocketmq/Logger.h" +#include "rocketmq/MQMessageQueue.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void OnewaySendCallback::onFailure(const std::error_code& ec) noexcept { + SPDLOG_WARN("Failed to one-way send message. Message: {}", ec.message()); +} + +void OnewaySendCallback::onSuccess(SendResult& send_result) noexcept { + SPDLOG_DEBUG("Send message in one-way OK. MessageId: {}", send_result.getMsgId()); +} + +OnewaySendCallback* onewaySendCallback() { + static OnewaySendCallback callback; + return &callback; +} + +void AwaitSendCallback::await() { + absl::MutexLock lk(&mtx_); + while (!completed_) { + cv_.Wait(&mtx_); + } +} + +void AwaitSendCallback::onSuccess(SendResult& send_result) noexcept { + send_result_ = send_result; + completed_ = true; + absl::MutexLock lk(&mtx_); + cv_.SignalAll(); +} + +void AwaitSendCallback::onFailure(const std::error_code& ec) noexcept { + completed_ = true; + ec_ = ec; + absl::MutexLock lk(&mtx_); + cv_.SignalAll(); +} + +void RetrySendCallback::onSuccess(SendResult& send_result) noexcept { + { + // Mark end of send-message span. + span_.SetStatus(opencensus::trace::StatusCode::OK); + span_.End(); + } + send_result.setMessageQueue(messageQueue()); + send_result.traceContext(opencensus::trace::propagation::ToTraceParentHeader(span_.context())); + callback_->onSuccess(send_result); + delete this; +} + +void RetrySendCallback::onFailure(const std::error_code& ec) noexcept { + { + // Mark end of the send-message span. + span_.SetStatus(opencensus::trace::StatusCode::INTERNAL); + span_.End(); + } + + if (++attempt_times_ >= max_attempt_times_) { + SPDLOG_WARN("Retried {} times, which exceeds the limit: {}", attempt_times_, max_attempt_times_); + callback_->onFailure(ec); + delete this; + return; + } + + std::shared_ptr producer = producer_.lock(); + if (!producer) { + SPDLOG_WARN("Producer has been destructed"); + callback_->onFailure(ec); + delete this; + return; + } + + if (candidates_.empty()) { + SPDLOG_WARN("No alternative hosts to perform additional retries"); + callback_->onFailure(ec); + delete this; + return; + } + + MQMessageQueue message_queue = candidates_[attempt_times_ % candidates_.size()]; + producer->sendImpl(this); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp new file mode 100644 index 000000000..c7f0804e5 --- /dev/null +++ b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "StaticNameServerResolver.h" + +#include "absl/strings/str_split.h" + +#include "LoggerImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +StaticNameServerResolver::StaticNameServerResolver(absl::string_view name_server_list) { + std::vector segments = absl::StrSplit(name_server_list, ';'); + name_server_address_ = naming_scheme_.buildAddress(segments); + if (name_server_address_.empty()) { + SPDLOG_WARN("Failed to create gRPC naming scheme compliant address from {}", + std::string(name_server_list.data(), name_server_list.length())); + } +} + +std::string StaticNameServerResolver::resolve() { + return name_server_address_; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp new file mode 100644 index 000000000..11f9ea3c9 --- /dev/null +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TransactionImpl.h" +#include "ProducerImpl.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +bool TransactionImpl::commit() { + std::shared_ptr producer = producer_.lock(); + if (!producer) { + return false; + } + + return producer->commit(message_, transaction_id_, endpoint_); +} + +bool TransactionImpl::rollback() { + std::shared_ptr producer = producer_.lock(); + if (!producer) { + return false; + } + return producer->rollback(message_, transaction_id_, endpoint_); +} + +std::string TransactionImpl::messageId() const { + return message_.getMsgId(); +} + +std::string TransactionImpl::transactionId() const { + return transaction_id_; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h new file mode 100644 index 000000000..77df4d576 --- /dev/null +++ b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "ProcessQueue.h" +#include "ReceiveMessageCallback.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class AsyncReceiveMessageCallback : public ReceiveMessageCallback, + public std::enable_shared_from_this { +public: + explicit AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue); + + ~AsyncReceiveMessageCallback() override = default; + + void onCompletion(const std::error_code& ec, const ReceiveMessageResult& result) override; + + void receiveMessageLater(); + + void receiveMessageImmediately(); + +private: + /** + * Hold a weak_ptr to ProcessQueue. Once ProcessQueue was released, stop the + * pop-cycle immediately. + */ + ProcessQueueWeakPtr process_queue_; + + std::function receive_message_later_; + + void checkThrottleThenReceive(); + + static const char* RECEIVE_LATER_TASK_NAME; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/AwaitPullCallback.h b/src/main/cpp/rocketmq/include/AwaitPullCallback.h new file mode 100644 index 000000000..c3fd373ee --- /dev/null +++ b/src/main/cpp/rocketmq/include/AwaitPullCallback.h @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "absl/synchronization/mutex.h" + +#include "rocketmq/AsyncCallback.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class AwaitPullCallback : public PullCallback { +public: + explicit AwaitPullCallback(PullResult& pull_result) : pull_result_(pull_result) { + } + + void onSuccess(const PullResult& pull_result) noexcept override; + + void onFailure(const std::error_code& ec) noexcept override; + + bool await(); + + bool hasFailure() const { + return ec_.operator bool(); + } + + bool isCompleted() const { + return completed_; + } + + const std::error_code& errorCode() const noexcept { + return ec_; + } + +private: + PullResult& pull_result_; + absl::Mutex mtx_; + absl::CondVar cv_; + bool completed_{false}; + std::error_code ec_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h new file mode 100644 index 000000000..036179bfc --- /dev/null +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "RpcClient.h" +#include "absl/strings/string_view.h" +#include "apache/rocketmq/v1/definition.pb.h" + +#include "Client.h" +#include "ClientConfigImpl.h" +#include "ClientManager.h" +#include "ClientResourceBundle.h" +#include "InvocationContext.h" +#include "NameServerResolver.h" +#include "OtlpExporter.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/MessageListener.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientImpl : public ClientConfigImpl, virtual public Client { +public: + explicit ClientImpl(absl::string_view group_name); + + ~ClientImpl() override = default; + + virtual void start(); + + virtual void shutdown(); + + void getRouteFor(const std::string& topic, const std::function& cb) + LOCKS_EXCLUDED(inflight_route_requests_mtx_, topic_route_table_mtx_); + + /** + * Gather collection of endpoints that are reachable from latest topic route + * table. + * + * @param endpoints + */ + void endpointsInUse(absl::flat_hash_set& endpoints) override LOCKS_EXCLUDED(topic_route_table_mtx_); + + void heartbeat() override; + + bool active() override { + State state = state_.load(std::memory_order_relaxed); + return State::STARTING == state || State::STARTED == state; + } + + void onRemoteEndpointRemoval(const std::vector& hosts) override LOCKS_EXCLUDED(isolated_endpoints_mtx_); + + void healthCheck() override LOCKS_EXCLUDED(isolated_endpoints_mtx_); + + void schedule(const std::string& task_name, const std::function& task, + std::chrono::milliseconds delay) override; + + void withNameServerResolver(std::shared_ptr name_server_resolver) { + name_server_resolver_ = std::move(name_server_resolver); + } + + /** + * Expose for test purpose only. + */ + void state(State state) { + state_.store(state, std::memory_order_relaxed); + } + +protected: + ClientManagerPtr client_manager_; + std::shared_ptr exporter_; + std::atomic state_; + + absl::flat_hash_map topic_route_table_ GUARDED_BY(topic_route_table_mtx_); + absl::Mutex topic_route_table_mtx_ ACQUIRED_AFTER(inflight_route_requests_mtx_); // protects topic_route_table_ + + absl::flat_hash_map>> + inflight_route_requests_ GUARDED_BY(inflight_route_requests_mtx_); + absl::Mutex inflight_route_requests_mtx_ ACQUIRED_BEFORE(topic_route_table_mtx_); // Protects inflight_route_requests_ + static const char* UPDATE_ROUTE_TASK_NAME; + std::uint32_t route_update_handle_{0}; + + // Set Name Server Resolver + std::shared_ptr name_server_resolver_; + + absl::flat_hash_map multiplexing_requests_; + absl::Mutex multiplexing_requests_mtx_; + + absl::flat_hash_set isolated_endpoints_ GUARDED_BY(isolated_endpoints_mtx_); + absl::Mutex isolated_endpoints_mtx_; + + void updateRouteInfo() LOCKS_EXCLUDED(topic_route_table_mtx_); + + /** + * Sub-class is supposed to inherit from std::enable_shared_from_this. + */ + virtual std::shared_ptr self() = 0; + + virtual void prepareHeartbeatData(HeartbeatRequest& request) = 0; + + virtual void verifyMessageConsumption(std::string remote_address, std::string command_id, MQMessageExt message); + + /** + * @brief Execute transaction-state-checker to commit or roll-back the orphan transactional message. + * + * It is no-op by default and Producer-subclass is supposed to override it. + * + * @param transaction_id + * @param message + */ + virtual void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) { + } + + /** + * Concrete publisher/subscriber client is expected to fill other + * type-specific resources. + */ + virtual ClientResourceBundle resourceBundle() { + ClientResourceBundle resource_bundle; + resource_bundle.client_id = clientId(); + resource_bundle.resource_namespace = resource_namespace_; + return resource_bundle; + } + + void setAccessPoint(rmq::Endpoints* endpoints); + + void notifyClientTermination() override; + + void notifyClientTermination(const NotifyClientTerminationRequest& request); + + /** + * @brief Return application developer provided message listener if this client is of PushConsumer type. + * + * By default, it returns nullptr such that error messages are generated and directed to server immediately. + * + * @return nullptr by default. + */ + virtual MessageListener* messageListener() { + return nullptr; + } + +private: + /** + * This is a low-level API that fetches route data from name server through + * gRPC unary request/response. Once request/response is completed, either + * timeout or response arrival in time, callback would get invoked. + * @param topic + * @param cb + */ + void fetchRouteFor(const std::string& topic, + const std::function& cb); + + /** + * Callback to execute once route data is fetched from name server. + * @param topic + * @param route + */ + void onTopicRouteReady(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) + LOCKS_EXCLUDED(inflight_route_requests_mtx_); + + /** + * Update Trace candidate hosts. + */ + void updateTraceHosts() LOCKS_EXCLUDED(topic_route_table_mtx_); + + /** + * Update local cache for the topic. Note, route differences are logged in + * INFO level since route bears fundamental importance. + * + * @param topic + * @param route + */ + void updateRouteCache(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) + LOCKS_EXCLUDED(topic_route_table_mtx_); + + void pollCommand(const std::string& target); + + void onPollCommandResponse(const InvocationContext* ctx); + + void onHealthCheckResponse(const std::error_code& endpoint, const InvocationContext* ctx) + LOCKS_EXCLUDED(isolated_endpoints_mtx_); + + void doVerify(std::string target, std::string command_id, MQMessageExt message); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientResourceBundle.h b/src/main/cpp/rocketmq/include/ClientResourceBundle.h new file mode 100644 index 000000000..0cabca736 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ClientResourceBundle.h @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/RocketMQ.h" + +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +enum class GroupType : int8_t +{ + PUBLISHER = 0, + SUBSCRIBER = 1, +}; + +struct ClientResourceBundle { + std::string client_id; + std::vector topics; + std::string resource_namespace; + std::string group; + GroupType group_type{GroupType::PUBLISHER}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h new file mode 100644 index 000000000..ea85888cf --- /dev/null +++ b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ConsumeMessageServiceBase.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeFifoMessageService : public ConsumeMessageServiceBase, + public std::enable_shared_from_this { +public: + ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener); + void start() override; + + void shutdown() override; + + /** + * @brief Entry of ConsumeMessageService + * + * @param process_queue + */ + void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; + + MessageListenerType messageListenerType() override; + +private: + void consumeTask(const ProcessQueueWeakPtr& process_queue, MQMessageExt& message); + + void submitConsumeTask0(const std::shared_ptr& consumer, const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message); + + void scheduleAckTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); + + void onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, const std::error_code& ec); + + void scheduleConsumeTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); + + void onForwardToDeadLetterQueue(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, bool ok); + + void scheduleForwardDeadLetterQueueTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h new file mode 100644 index 000000000..e6980e4f1 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "ProcessQueue.h" +#include "rocketmq/MessageListener.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeMessageService { +public: + virtual ~ConsumeMessageService() = default; + + /** + * Start the dispatcher thread, which will dispatch messages in process queue to thread pool in form of runnable + * functor. + */ + virtual void start() = 0; + + /** + * Stop the dispatcher thread and then reset the thread pool. + */ + virtual void shutdown() = 0; + + virtual void submitConsumeTask(const ProcessQueueWeakPtr& process_queue_ptr) = 0; + + virtual MessageListenerType messageListenerType() = 0; + + virtual void signalDispatcher() = 0; + + virtual void throttle(const std::string& topic, std::uint32_t threshold) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h new file mode 100644 index 000000000..50173a578 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" + +#include "ConsumeMessageService.h" +#include "RateLimiter.h" +#include "ThreadPool.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumer; + +class ConsumeMessageServiceBase : public ConsumeMessageService { +public: + ConsumeMessageServiceBase(std::weak_ptr consumer, int thread_count, MessageListener* message_listener); + + ~ConsumeMessageServiceBase() override = default; + + /** + * Make it noncopyable. + */ + ConsumeMessageServiceBase(const ConsumeMessageServiceBase& other) = delete; + ConsumeMessageServiceBase& operator=(const ConsumeMessageServiceBase& other) = delete; + + /** + * Start the dispatcher thread, which will dispatch messages in process queue to thread pool in form of runnable + * functor. + */ + void start() override; + + /** + * Stop the dispatcher thread and then reset the thread pool. + */ + void shutdown() override; + + /** + * Signal dispatcher thread to check new pending messages. + */ + void signalDispatcher() override; + + /** + * Set throttle threshold per topic. + * + * @param topic + * @param threshold + */ + void throttle(const std::string& topic, std::uint32_t threshold) override; + + bool hasConsumeRateLimiter(const std::string& topic) const LOCKS_EXCLUDED(rate_limiter_table_mtx_); + + std::shared_ptr> rateLimiter(const std::string& topic) const LOCKS_EXCLUDED(rate_limiter_table_mtx_); + +protected: + RateLimiterObserver rate_limiter_observer_; + + mutable absl::flat_hash_map>> + rate_limiter_table_ GUARDED_BY(rate_limiter_table_mtx_); + mutable absl::Mutex rate_limiter_table_mtx_; // Protects rate_limiter_table_ + + std::atomic state_; + + int thread_count_; + std::unique_ptr pool_; + std::weak_ptr consumer_; + + absl::Mutex dispatch_mtx_; + std::thread dispatch_thread_; + absl::CondVar dispatch_cv_; + + MessageListener* message_listener_; + + /** + * Dispatch messages to thread pool. Implementation of this function should be sub-class specific. + */ + void dispatch(); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h new file mode 100644 index 000000000..52bad1406 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "ConsumeMessageServiceBase.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeStandardMessageService : public ConsumeMessageServiceBase { +public: + ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, + MessageListener* message_listener_ptr); + + ~ConsumeStandardMessageService() override = default; + + void start() override; + + void shutdown() override; + + void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; + + MessageListenerType messageListenerType() override; + +private: + void consumeTask(const ProcessQueueWeakPtr& process_queue, const std::vector& msgs); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/Consumer.h b/src/main/cpp/rocketmq/include/Consumer.h new file mode 100644 index 000000000..ad9cdad52 --- /dev/null +++ b/src/main/cpp/rocketmq/include/Consumer.h @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "absl/container/flat_hash_map.h" +#include "absl/types/optional.h" + +#include "Client.h" +#include "ConsumeMessageService.h" +#include "FilterExpression.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Consumer : virtual public Client { +public: + ~Consumer() override = default; + + virtual absl::optional getFilterExpression(const std::string& topic) const = 0; + + virtual uint32_t maxCachedMessageQuantity() const = 0; + + virtual uint64_t maxCachedMessageMemory() const = 0; + + virtual int32_t receiveBatchSize() const = 0; + + virtual std::shared_ptr getConsumeMessageService() = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h new file mode 100644 index 000000000..5bc2c53f8 --- /dev/null +++ b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/memory/memory.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +#include "NameServerResolver.h" +#include "NamingScheme.h" +#include "Scheduler.h" +#include "TopAddressing.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DynamicNameServerResolver : public NameServerResolver, + public std::enable_shared_from_this { +public: + DynamicNameServerResolver(absl::string_view endpoint, std::chrono::milliseconds refresh_interval); + + void start() override; + + void shutdown() override; + + std::string resolve() override LOCKS_EXCLUDED(name_server_list_mtx_); + + void injectHttpClient(std::unique_ptr http_client); + +private: + std::string endpoint_; + + SchedulerSharedPtr scheduler_; + std::chrono::milliseconds refresh_interval_; + + void fetch(); + + void onNameServerListFetched(const std::vector& name_server_list) LOCKS_EXCLUDED(name_server_list_mtx_); + + std::vector name_server_list_ GUARDED_BY(name_server_list_mtx_); + absl::Mutex name_server_list_mtx_; + + std::atomic index_{0}; + + bool ssl_{false}; + std::unique_ptr top_addressing_; + + NamingScheme naming_scheme_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/FilterExpression.h b/src/main/cpp/rocketmq/include/FilterExpression.h new file mode 100644 index 000000000..51cd89a00 --- /dev/null +++ b/src/main/cpp/rocketmq/include/FilterExpression.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "rocketmq/ExpressionType.h" +#include "rocketmq/MQMessageExt.h" + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * Server supported message filtering expression. At present, two types are supported: tag and SQL92. + */ +struct FilterExpression { + explicit FilterExpression(std::string expression, ExpressionType expression_type = ExpressionType::TAG) + : content_(std::move(expression)), type_(expression_type), version_(std::chrono::steady_clock::now()) { + if (ExpressionType::TAG == type_ && content_.empty()) { + content_ = WILD_CARD_TAG; + } + } + + bool accept(const MQMessageExt& message) const; + + std::string content_; + ExpressionType type_; + std::chrono::steady_clock::time_point version_; + + static const char* WILD_CARD_TAG; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h b/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h new file mode 100644 index 000000000..270c494a8 --- /dev/null +++ b/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "rocketmq/MQSelector.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageGroupQueueSelector : public MessageQueueSelector { +public: + explicit MessageGroupQueueSelector(std::string message_group); + + MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) override; + +private: + std::string message_group_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/NameServerResolver.h b/src/main/cpp/rocketmq/include/NameServerResolver.h new file mode 100644 index 000000000..00ec0d91e --- /dev/null +++ b/src/main/cpp/rocketmq/include/NameServerResolver.h @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class NameServerResolver { +public: + virtual ~NameServerResolver() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual std::string resolve() = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/NamingScheme.h b/src/main/cpp/rocketmq/include/NamingScheme.h new file mode 100644 index 000000000..d9b4f62e7 --- /dev/null +++ b/src/main/cpp/rocketmq/include/NamingScheme.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "re2/re2.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class NamingScheme { +public: + NamingScheme(); + + std::string buildAddress(const std::vector& list); + + static const char* DnsPrefix; + static const char* IPv4Prefix; + static const char* IPv6Prefix; + +private: + static const char* IPv4Regex; + static const char* IPv6Regex; + + re2::RE2 ipv4_pattern_; + re2::RE2 ipv6_pattern_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ProcessQueue.h b/src/main/cpp/rocketmq/include/ProcessQueue.h new file mode 100644 index 000000000..e53edb76c --- /dev/null +++ b/src/main/cpp/rocketmq/include/ProcessQueue.h @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "ConsumeMessageType.h" +#include "FilterExpression.h" +#include "ReceiveMessageCallback.h" +#include "rocketmq/MQMessageExt.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumer; + +class ClientManager; + +class ProcessQueue { +public: + virtual ~ProcessQueue() = default; + + virtual bool expired() const = 0; + + virtual void callback(std::shared_ptr callback) = 0; + + virtual void receiveMessage() = 0; + + virtual void nextOffset(int64_t next_offset) = 0; + + virtual bool hasPendingMessages() const = 0; + + virtual std::string topic() const = 0; + + virtual bool take(uint32_t batch_size, std::vector& messages) = 0; + + virtual std::weak_ptr getConsumer() = 0; + + virtual const std::string& simpleName() const = 0; + + virtual MQMessageQueue getMQMessageQueue() = 0; + + virtual bool committedOffset(int64_t& offset) = 0; + + virtual void release(uint64_t body_size, int64_t offset) = 0; + + virtual void cacheMessages(const std::vector& messages) = 0; + + virtual bool shouldThrottle() const = 0; + + virtual std::shared_ptr getClientManager() = 0; + + virtual void syncIdleState() = 0; + + virtual const FilterExpression& getFilterExpression() const = 0; + + virtual bool bindFifoConsumeTask() = 0; + + virtual bool unbindFifoConsumeTask() = 0; +}; + +using ProcessQueueSharedPtr = std::shared_ptr; +using ProcessQueueWeakPtr = std::weak_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h new file mode 100644 index 000000000..10a032112 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "Assignment.h" +#include "ClientManager.h" +#include "FilterExpression.h" +#include "MixAll.h" +#include "ProcessQueue.h" +#include "ReceiveMessageCallback.h" +#include "TopicAssignmentInfo.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "apache/rocketmq/v1/service.pb.h" +#include "rocketmq/ConsumeType.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct OffsetRecord { + explicit OffsetRecord(int64_t offset) : offset_(offset), released_(false) { + } + OffsetRecord(int64_t offset, bool released) : offset_(offset), released_(released) { + } + int64_t offset_; + bool released_; +}; + +ROCKETMQ_NAMESPACE_END + +namespace std { + +template <> +struct less { + bool operator()(const ROCKETMQ_NAMESPACE::OffsetRecord& lhs, const ROCKETMQ_NAMESPACE::OffsetRecord& rhs) const { + return lhs.offset_ < rhs.offset_; + } +}; + +} // namespace std + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumer; + +/** + * @brief Once messages are fetched(either pulled or popped) from remote server, they are firstly put into cache. + * Dispatcher thread, after waking up, will submit them into thread-pool. Messages at this phase are called "inflight" + * state. Once messages are processed by user-passed-in callback, their quota will be released for future incoming + * messages. + */ +class ProcessQueueImpl : virtual public ProcessQueue { +public: + ProcessQueueImpl(MQMessageQueue message_queue, FilterExpression filter_expression, + std::weak_ptr consumer, std::shared_ptr client_instance); + + ~ProcessQueueImpl() override; + + void callback(std::shared_ptr callback) override; + + MQMessageQueue getMQMessageQueue() override; + + bool expired() const override; + + bool shouldThrottle() const override LOCKS_EXCLUDED(messages_mtx_); + + const FilterExpression& getFilterExpression() const override; + + std::weak_ptr getConsumer() override; + + std::shared_ptr getClientManager() override; + + void receiveMessage() override; + + const std::string& simpleName() const override { + return simple_name_; + } + + std::string topic() const override { + return message_queue_.getTopic(); + } + + bool hasPendingMessages() const override LOCKS_EXCLUDED(messages_mtx_); + + /** + * Put message fetched from broker into cache. + * + * @param messages + */ + void cacheMessages(const std::vector& messages) override LOCKS_EXCLUDED(messages_mtx_, offsets_mtx_); + + /** + * @return Number of messages that is not yet dispatched to thread pool, likely, due to topic-rate-limiting. + */ + uint32_t cachedMessagesSize() const LOCKS_EXCLUDED(messages_mtx_) { + absl::MutexLock lk(&messages_mtx_); + return cached_messages_.size(); + } + + /** + * Dispatch messages from cache to thread pool in form of consumeTask. + * @param batch_size + * @param messages + * @return true if there are more messages to consume in cache + */ + bool take(uint32_t batch_size, std::vector& messages) override LOCKS_EXCLUDED(messages_mtx_); + + void syncIdleState() override { + idle_since_ = std::chrono::steady_clock::now(); + } + + void nextOffset(int64_t next_offset) override { + assert(next_offset >= 0); + next_offset_ = next_offset; + } + + int64_t nextOffset() const { + return next_offset_; + } + + bool committedOffset(int64_t& offset) override LOCKS_EXCLUDED(offsets_mtx_); + + void release(uint64_t body_size, int64_t offset) override LOCKS_EXCLUDED(messages_mtx_, offsets_mtx_); + + bool unbindFifoConsumeTask() override { + bool expected = true; + return has_fifo_task_bound_.compare_exchange_strong(expected, false, std::memory_order_relaxed); + } + + bool bindFifoConsumeTask() override { + bool expected = false; + return has_fifo_task_bound_.compare_exchange_strong(expected, true, std::memory_order_relaxed); + } + +private: + MQMessageQueue message_queue_; + + /** + * Expression used to filter message in the server side. + */ + const FilterExpression filter_expression_; + + std::chrono::milliseconds invisible_time_; + + std::chrono::steady_clock::time_point idle_since_{std::chrono::steady_clock::now()}; + + absl::Time create_timestamp_{absl::Now()}; + + std::string simple_name_; + + std::weak_ptr consumer_; + std::shared_ptr client_manager_; + + std::shared_ptr receive_callback_; + + /** + * Messages that are pending to be submitted to thread pool. + */ + mutable std::vector cached_messages_ GUARDED_BY(messages_mtx_); + + mutable absl::Mutex messages_mtx_; + + /** + * @brief Quantity of the cached messages. + * + */ + std::atomic cached_message_quantity_; + + /** + * @brief Total body memory size of the cached messages. + * + */ + std::atomic cached_message_memory_; + + int64_t next_offset_{0}; + + /** + * If this process queue is used in FIFO scenario, this field marks if there is an task in thread pool. + */ + std::atomic_bool has_fifo_task_bound_{false}; + + std::set offsets_ GUARDED_BY(offsets_mtx_); + absl::Mutex offsets_mtx_; + + void popMessage(); + void wrapPopMessageRequest(absl::flat_hash_map& metadata, + rmq::ReceiveMessageRequest& request); + + void pullMessage(); + void wrapPullMessageRequest(rmq::PullMessageRequest& request); + + void wrapFilterExpression(rmq::FilterExpression* filter_expression); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h new file mode 100644 index 000000000..a2172cef6 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" + +#include "ClientImpl.h" +#include "ClientManagerImpl.h" +#include "MixAll.h" +#include "SendCallbacks.h" +#include "TopicPublishInfo.h" +#include "TransactionImpl.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/LocalTransactionStateChecker.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" +#include "rocketmq/MQSelector.h" +#include "rocketmq/SendResult.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_this { +public: + explicit ProducerImpl(absl::string_view group_name); + + ~ProducerImpl() override; + + void prepareHeartbeatData(HeartbeatRequest& request) override; + + void start() override; + + void shutdown() override; + + SendResult send(const MQMessage& message, std::error_code& ec) noexcept; + + void send(const MQMessage& message, SendCallback* callback); + + void sendOneway(const MQMessage& message, std::error_code& ec); + + void setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker); + + std::unique_ptr prepare(MQMessage& message, std::error_code& ec); + + bool commit(const MQMessage& message, const std::string& transaction_id, const std::string& target); + + bool rollback(const MQMessage& message, const std::string& transaction_id, const std::string& target); + + /** + * Check if the RPC client for the target host is isolated or not + * @param endpoint Address of target host. + * @return true if client is active; false otherwise. + */ + bool isEndpointIsolated(const std::string& endpoint) LOCKS_EXCLUDED(isolated_endpoints_mtx_); + + /** + * Note: This function is purpose-made public such that the whole isolate/add-back mechanism can be properly tested. + * @param target Endpoint of the target host + */ + void isolateEndpoint(const std::string& target) LOCKS_EXCLUDED(isolated_endpoints_mtx_); + + int maxAttemptTimes() const { + return max_attempt_times_; + } + + void maxAttemptTimes(int times) { + max_attempt_times_ = times; + } + + int getFailedTimes() const { + return failed_times_; + } + + void setFailedTimes(int times) { + failed_times_ = times; + } + + std::vector listMessageQueue(const std::string& topic, std::error_code& ec); + + uint32_t compressBodyThreshold() const { + return compress_body_threshold_; + } + + void compressBodyThreshold(uint32_t threshold) { + compress_body_threshold_ = threshold; + } + + /** + * @brief Send message with tracing. + * + * @param message + * @param callback + * @param message_queue + * @param attempt_time current attempt times, which starts from 0. + */ + void sendImpl(RetrySendCallback* callback); + +protected: + std::shared_ptr self() override { + return shared_from_this(); + } + + void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) override; + + void notifyClientTermination() override; + +private: + absl::flat_hash_map topic_publish_info_table_ GUARDED_BY(topic_publish_info_mtx_); + absl::Mutex topic_publish_info_mtx_; // protects topic_publish_info_ + + int32_t max_attempt_times_{MixAll::MAX_SEND_MESSAGE_ATTEMPT_TIMES_}; + int32_t failed_times_{0}; // only for test + uint32_t compress_body_threshold_; + + LocalTransactionStateCheckerPtr transaction_state_checker_; + + void asyncPublishInfo(const std::string& topic, + const std::function& cb) + LOCKS_EXCLUDED(topic_publish_info_mtx_); + + TopicPublishInfoPtr getPublishInfo(const std::string& topic); + + void takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, std::vector& message_queues, + int number); + + void wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, + const MQMessageQueue& message_queue); + + bool isRunning() const; + + void ensureRunning(std::error_code& ec) const noexcept; + + bool validate(const MQMessage& message); + + void send0(const MQMessage& message, SendCallback* callback, std::vector list, int max_attempt_times); + + bool endTransaction0(const std::string& target, const MQMessage& message, const std::string& transaction_id, + TransactionState resolution); + + void isolatedEndpoints(absl::flat_hash_set& endpoints) LOCKS_EXCLUDED(isolated_endpoints_mtx_); + + MQMessageQueue withServiceAddress(const MQMessageQueue& message_queue, std::error_code& ec); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PullConsumerImpl.h b/src/main/cpp/rocketmq/include/PullConsumerImpl.h new file mode 100644 index 000000000..30c7cc019 --- /dev/null +++ b/src/main/cpp/rocketmq/include/PullConsumerImpl.h @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "absl/strings/string_view.h" + +#include "ClientConfig.h" +#include "ClientImpl.h" +#include "ClientManagerImpl.h" +#include "rocketmq/ConsumeType.h" +#include "rocketmq/MQMessageQueue.h" +#include "rocketmq/MessageModel.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PullConsumerImpl : public ClientImpl, public std::enable_shared_from_this { +public: + explicit PullConsumerImpl(absl::string_view group_name) : ClientImpl(group_name) { + } + + void start() override; + + void shutdown() override; + + std::future> queuesFor(const std::string& topic); + + std::future queryOffset(const OffsetQuery& query); + + void pull(const PullMessageQuery& query, PullCallback* callback); + + void prepareHeartbeatData(HeartbeatRequest& request) override; + +protected: + std::shared_ptr self() override { + return shared_from_this(); + } + + void notifyClientTermination() override; + + MessageModel message_model_{MessageModel::CLUSTERING}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PushConsumer.h b/src/main/cpp/rocketmq/include/PushConsumer.h new file mode 100644 index 000000000..cf2da65b7 --- /dev/null +++ b/src/main/cpp/rocketmq/include/PushConsumer.h @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "Consumer.h" +#include "ProcessQueue.h" +#include "rocketmq/Executor.h" +#include "rocketmq/MessageListener.h" +#include "rocketmq/MessageModel.h" +#include "rocketmq/OffsetStore.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumer : virtual public Consumer { +public: + ~PushConsumer() override = default; + + virtual void iterateProcessQueue(const std::function& cb) = 0; + + virtual MessageModel messageModel() const = 0; + + virtual void ack(const MQMessageExt& msg, const std::function& callback) = 0; + + virtual void forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) = 0; + + virtual const Executor& customExecutor() const = 0; + + virtual uint32_t consumeBatchSize() const = 0; + + virtual int32_t maxDeliveryAttempts() const = 0; + + virtual void updateOffset(const MQMessageQueue& message_queue, int64_t offset) = 0; + + virtual void nack(const MQMessageExt& message, const std::function& callback) = 0; + + virtual bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) = 0; + + virtual MessageListener* messageListener() = 0; + + virtual void setOffsetStore(std::unique_ptr offset_store) = 0; +}; + +using PushConsumerSharedPtr = std::shared_ptr; +using PushConsumerWeakPtr = std::weak_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h new file mode 100644 index 000000000..399b87ed6 --- /dev/null +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" + +#include "ClientConfigImpl.h" +#include "ClientImpl.h" +#include "ClientManagerImpl.h" +#include "ConsumeMessageService.h" +#include "FilterExpression.h" +#include "ProcessQueue.h" +#include "PushConsumer.h" +#include "Scheduler.h" +#include "TopicAssignmentInfo.h" +#include "TopicPublishInfo.h" +#include "UtilAll.h" +#include "apache/rocketmq/v1/service.pb.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/OffsetStore.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeMessageService; +class ConsumeFifoMessageService; +class ConsumeStandardMessageService; + +class PushConsumerImpl : virtual public ClientImpl, + virtual public PushConsumer, + public std::enable_shared_from_this { +public: + explicit PushConsumerImpl(absl::string_view group_name); + + ~PushConsumerImpl() override; + + void prepareHeartbeatData(HeartbeatRequest& request) override LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + void start() override; + + void shutdown() override; + + void subscribe(const std::string& topic, const std::string& expression, + ExpressionType expression_type = ExpressionType::TAG) + LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + void unsubscribe(const std::string& topic) LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + absl::optional getFilterExpression(const std::string& topic) const override + LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + void setConsumeFromWhere(ConsumeFromWhere consume_from_where); + + void registerMessageListener(MessageListener* message_listener); + + void scanAssignments() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + static bool selectBroker(const TopicRouteDataPtr& route, std::string& broker_host); + + void wrapQueryAssignmentRequest(const std::string& topic, const std::string& consumer_group, + const std::string& client_id, const std::string& strategy_name, + QueryAssignmentRequest& request); + + /** + * Query assignment of the specified topic from load balancer directly if + * message consuming mode is clustering. In case current client is operating + * in the broadcasting mode, assignments are constructed locally from topic + * route entries. + * + * @param topic Topic to query + * @return shared pointer to topic assignment info + */ + void queryAssignment(const std::string& topic, + const std::function& cb); + + void syncProcessQueue(const std::string& topic, const TopicAssignmentPtr& topic_assignment, + const FilterExpression& filter_expression) LOCKS_EXCLUDED(process_queue_table_mtx_); + + ProcessQueueSharedPtr getOrCreateProcessQueue(const MQMessageQueue& message_queue, + const FilterExpression& filter_expression) + LOCKS_EXCLUDED(process_queue_table_mtx_); + + bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) override + LOCKS_EXCLUDED(process_queue_table_mtx_); + + uint32_t consumeThreadPoolSize() const; + + void consumeThreadPoolSize(int thread_pool_size); + + int32_t maxDeliveryAttempts() const override { + return max_delivery_attempts_; + } + + uint32_t consumeBatchSize() const override; + + void consumeBatchSize(uint32_t consume_batch_size); + + int32_t receiveBatchSize() const override { + return receive_batch_size_; + } + + std::shared_ptr getConsumeMessageService() override; + + void ack(const MQMessageExt& msg, const std::function& callback) override; + + /** + * Negative acknowledge the given message; Refer to + * https://en.wikipedia.org/wiki/Acknowledgement_(data_networks) for + * background info. + * + * Current implementation is to change invisible time of the given message. + * + * @param message Message to negate on the broker side. + */ + void nack(const MQMessageExt& message, const std::function& callback) override; + + void forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) override; + + void wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request); + + // only for test + std::size_t getProcessQueueTableSize() LOCKS_EXCLUDED(process_queue_table_mtx_); + + void setCustomExecutor(const Executor& executor) { + custom_executor_ = executor; + } + + const Executor& customExecutor() const override { + return custom_executor_; + } + + void setThrottle(const std::string& topic, uint32_t threshold); + + MessageModel messageModel() const override { + return message_model_; + } + + void setMessageModel(MessageModel message_model) { + message_model_ = message_model; + } + + void setOffsetStore(std::unique_ptr offset_store) override { + offset_store_ = std::move(offset_store); + } + + void updateOffset(const MQMessageQueue& message_queue, int64_t offset) override { + if (offset_store_) { + offset_store_->updateOffset(message_queue, offset); + } + } + + /** + * Max number of messages that may be cached per queue before applying + * back-pressure. + * @return + */ + uint32_t maxCachedMessageQuantity() const override { + return MixAll::DEFAULT_CACHED_MESSAGE_COUNT; + } + + /** + * Threshold of total cached message body size by queue before applying + * back-pressure. + * @return + */ + uint64_t maxCachedMessageMemory() const override { + return MixAll::DEFAULT_CACHED_MESSAGE_MEMORY; + } + + void iterateProcessQueue(const std::function& callback) override; + + MessageListener* messageListener() override { + return message_listener_; + } + +protected: + std::shared_ptr self() override { + return shared_from_this(); + } + + ClientResourceBundle resourceBundle() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_) override; + + void notifyClientTermination() override; + +private: + absl::flat_hash_map + topic_filter_expression_table_ GUARDED_BY(topic_filter_expression_table_mtx_); + mutable absl::Mutex topic_filter_expression_table_mtx_; + + /** + * Consume message thread pool size. + */ + uint32_t consume_thread_pool_size_{MixAll::DEFAULT_CONSUME_THREAD_POOL_SIZE}; + + MessageListener* message_listener_{nullptr}; + + std::shared_ptr consume_message_service_; + uint32_t consume_batch_size_{MixAll::DEFAULT_CONSUME_MESSAGE_BATCH_SIZE}; + + int32_t receive_batch_size_{MixAll::DEFAULT_RECEIVE_MESSAGE_BATCH_SIZE}; + + std::uintptr_t scan_assignment_handle_{0}; + static const char* SCAN_ASSIGNMENT_TASK_NAME; + + absl::flat_hash_map process_queue_table_ GUARDED_BY(process_queue_table_mtx_); + absl::Mutex process_queue_table_mtx_; + + ConsumeFromWhere consume_from_where_{ConsumeFromWhere::CONSUME_FROM_LAST_OFFSET}; + Executor custom_executor_; + + absl::flat_hash_map + throttle_table_ GUARDED_BY(throttle_table_mtx_); + absl::Mutex throttle_table_mtx_; + + int32_t max_delivery_attempts_{MixAll::DEFAULT_MAX_DELIVERY_ATTEMPTS}; + + MessageModel message_model_{MessageModel::CLUSTERING}; + + mutable std::unique_ptr offset_store_; + + void fetchRoutes() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + friend class ConsumeMessageService; + friend class ConsumeFifoMessageService; + friend class ConsumeStandardMessageService; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h new file mode 100644 index 000000000..d7cc68608 --- /dev/null +++ b/src/main/cpp/rocketmq/include/SendCallbacks.h @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "apache/rocketmq/v1/service.grpc.pb.h" +#include "opencensus/trace/span.h" + +#include "TransactionImpl.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +using SendMessageRequest = apache::rocketmq::v1::SendMessageRequest; + +class OnewaySendCallback : public SendCallback { +public: + void onSuccess(SendResult& send_result) noexcept override; + + void onFailure(const std::error_code& ec) noexcept override; +}; + +OnewaySendCallback* onewaySendCallback(); + +class AwaitSendCallback : public SendCallback { +public: + void onSuccess(SendResult& send_result) noexcept override; + + void onFailure(const std::error_code& ec) noexcept override; + + void await(); + + explicit operator bool() const { + return !ec_.operator bool(); + } + + const SendResult& sendResult() const { + return send_result_; + } + + const std::error_code& errorCode() const { + return ec_; + } + +private: + absl::Mutex mtx_; + absl::CondVar cv_; + bool completed_{false}; + SendResult send_result_; + std::error_code ec_; +}; + +class ProducerImpl; + +class RetrySendCallback : public SendCallback { +public: + RetrySendCallback(std::weak_ptr producer, MQMessage message, int max_attempt_times, + SendCallback* callback, std::vector candidates) + : producer_(std::move(producer)), message_(std::move(message)), max_attempt_times_(max_attempt_times), + callback_(callback), candidates_(std::move(candidates)), span_(opencensus::trace::Span::BlankSpan()) { + } + + void onSuccess(SendResult& send_result) noexcept override; + + void onFailure(const std::error_code& ec) noexcept override; + + MQMessage& message() { + return message_; + } + + int attemptTime() const { + return attempt_times_; + } + + const MQMessageQueue& messageQueue() const { + int index = attempt_times_ % candidates_.size(); + return candidates_[index]; + } + + opencensus::trace::Span& span() { + return span_; + } + +private: + std::weak_ptr producer_; + MQMessage message_; + int attempt_times_{0}; + int max_attempt_times_; + SendCallback* callback_{nullptr}; + + /** + * @brief Once the first publish attempt failed, the following routable + * message queues are employed. + * + */ + std::vector candidates_; + + /** + * @brief The on-going span. Should be terminated in the callback functions. + */ + opencensus::trace::Span span_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h new file mode 100644 index 000000000..b38a86e6a --- /dev/null +++ b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "absl/strings/string_view.h" + +#include "NameServerResolver.h" +#include "NamingScheme.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class StaticNameServerResolver : public NameServerResolver { +public: + explicit StaticNameServerResolver(absl::string_view name_server_list); + + void start() override { + } + + void shutdown() override { + } + + std::string resolve() override; + +private: + /** + * @brief Name server addresses, following gRPC URI schemes described in + * https://github.com/grpc/grpc/blob/master/doc/naming.md + * + * Sample values are: + * dns:[//authority/]host[:port] + * ipv4:address[:port][,address[:port],...] + * ipv6:address[:port][,address[:port],...] + */ + std::string name_server_address_; + + NamingScheme naming_scheme_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h new file mode 100644 index 000000000..0789e4ae6 --- /dev/null +++ b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" + +#include "HttpClient.h" +#include "rocketmq/CredentialsProvider.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class StsCredentialsProviderImpl : public CredentialsProvider { +public: + explicit StsCredentialsProviderImpl(std::string ram_role_name); + + ~StsCredentialsProviderImpl() override; + + Credentials getCredentials() override; + + void withHttpClient(std::unique_ptr http_client) { + http_client_ = std::move(http_client); + } + +private: + static const char* RAM_ROLE_HOST; + static const char* RAM_ROLE_URL_PREFIX; + static const char* FIELD_ACCESS_KEY; + static const char* FIELD_ACCESS_SECRET; + static const char* FIELD_SESSION_TOKEN; + static const char* FIELD_EXPIRATION; + static const char* EXPIRATION_DATE_TIME_FORMAT; + + std::string ram_role_name_; + + std::string access_key_ GUARDED_BY(mtx_); + std::string access_secret_ GUARDED_BY(mtx_); + std::string session_token_ GUARDED_BY(mtx_); + std::chrono::system_clock::time_point expiration_; + + absl::Mutex mtx_; + void refresh() LOCKS_EXCLUDED(mtx_); + + std::unique_ptr http_client_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h new file mode 100644 index 000000000..844c82796 --- /dev/null +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "rocketmq/MQMessage.h" +#include "rocketmq/Transaction.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ProducerImpl; + +class TransactionImpl : public Transaction { +public: + TransactionImpl(MQMessage message, std::string transaction_id, std::string endpoint, std::string trace_context, + const std::shared_ptr& producer) + : message_(std::move(message)), transaction_id_(std::move(transaction_id)), endpoint_(std::move(endpoint)), + trace_context_(std::move(trace_context)), producer_(producer) { + } + + ~TransactionImpl() override = default; + + bool commit() override; + + bool rollback() override; + + std::string messageId() const override; + + std::string transactionId() const override; + +private: + MQMessage message_; + std::string transaction_id_; + std::string endpoint_; + std::string trace_context_; + std::weak_ptr producer_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/BUILD.bazel b/src/main/cpp/rocketmq/mocks/BUILD.bazel new file mode 100644 index 000000000..21262fabc --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/BUILD.bazel @@ -0,0 +1,28 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "rocketmq_mocks", + hdrs = glob(["include/*.h"]), + strip_include_prefix = "//src/main/cpp/rocketmq/mocks/include", + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "//src/main/cpp/client/mocks:client_mocks", + ], +) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h new file mode 100644 index 000000000..ee9251937 --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "ConsumeMessageService.h" + +#include "gmock/gmock.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeMessageServiceMock : public ConsumeMessageService { +public: + MOCK_METHOD(void, start, (), (override)); + + MOCK_METHOD(void, shutdown, (), (override)); + + MOCK_METHOD(void, submitConsumeTask, (const ProcessQueueWeakPtr&), (override)); + + MOCK_METHOD(MessageListenerType, messageListenerType, (), (override)); + + MOCK_METHOD(void, signalDispatcher, (), (override)); + + MOCK_METHOD(void, throttle, (const std::string&, std::uint32_t), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h new file mode 100644 index 000000000..9ed217ef0 --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "ClientMock.h" +#include "Consumer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumerMock : virtual public Consumer, virtual public ClientMock { +public: + MOCK_METHOD((absl::optional), getFilterExpression, (const std::string&), (const override)); + + MOCK_METHOD(uint32_t, maxCachedMessageQuantity, (), (const override)); + + MOCK_METHOD(uint64_t, maxCachedMessageMemory, (), (const override)); + + MOCK_METHOD(int32_t, receiveBatchSize, (), (const override)); + + MOCK_METHOD(std::shared_ptr, getConsumeMessageService, (), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h new file mode 100644 index 000000000..4f8f7f3fd --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "NameServerResolver.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class NameServerResolverMock : public NameServerResolver { +public: + MOCK_METHOD(void, start, (), (override)); + + MOCK_METHOD(void, shutdown, (), (override)); + + MOCK_METHOD(std::string, resolve, (), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h b/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h new file mode 100644 index 000000000..4ae20860d --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ProcessQueue.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ProcessQueueMock : public ProcessQueue { +public: + MOCK_METHOD(bool, expired, (), (const override)); + + MOCK_METHOD(void, callback, (std::shared_ptr), (override)); + + MOCK_METHOD(void, receiveMessage, (), (override)); + + MOCK_METHOD(void, nextOffset, (int64_t), (override)); + + MOCK_METHOD(bool, hasPendingMessages, (), (const override)); + + MOCK_METHOD(std::string, topic, (), (const override)); + + MOCK_METHOD(bool, take, (uint32_t, (std::vector&)), (override)); + + MOCK_METHOD(std::weak_ptr, getConsumer, (), (override)); + + MOCK_METHOD(const std::string&, simpleName, (), (const override)); + + MOCK_METHOD(bool, committedOffset, (int64_t&), (override)); + + MOCK_METHOD(void, release, (uint64_t, int64_t), (override)); + + MOCK_METHOD(void, cacheMessages, (const std::vector&), (override)); + + MOCK_METHOD(bool, shouldThrottle, (), (const override)); + + MOCK_METHOD((std::shared_ptr), getClientManager, (), (override)); + + MOCK_METHOD(void, syncIdleState, (), (override)); + + MOCK_METHOD(const FilterExpression&, getFilterExpression, (), (const override)); + + MOCK_METHOD(bool, bindFifoConsumeTask, (), (override)); + + MOCK_METHOD(bool, unbindFifoConsumeTask, (), (override)); + + MOCK_METHOD(MQMessageQueue, getMQMessageQueue, (), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h new file mode 100644 index 000000000..e4508baa6 --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "ConsumerMock.h" +#include "PushConsumer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumerMock : virtual public PushConsumer, virtual public ConsumerMock { +public: + MOCK_METHOD(void, iterateProcessQueue, (const std::function&), (override)); + + MOCK_METHOD(MessageModel, messageModel, (), (const override)); + + MOCK_METHOD(void, ack, (const MQMessageExt&, const std::function&), (override)); + + MOCK_METHOD(void, forwardToDeadLetterQueue, (const MQMessageExt&, const std::function&), (override)); + + MOCK_METHOD(const Executor&, customExecutor, (), (const override)); + + MOCK_METHOD(uint32_t, consumeBatchSize, (), (const override)); + + MOCK_METHOD(int32_t, maxDeliveryAttempts, (), (const override)); + + MOCK_METHOD(void, updateOffset, (const MQMessageQueue&, int64_t), (override)); + + MOCK_METHOD(void, nack, (const MQMessageExt&, const std::function&), (override)); + + MOCK_METHOD(bool, receiveMessage, (const MQMessageQueue&, const FilterExpression&), (override)); + + MOCK_METHOD(MessageListener*, messageListener, (), (override)); + + MOCK_METHOD(void, setOffsetStore, (std::unique_ptr), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/scheduler/BUILD.bazel b/src/main/cpp/scheduler/BUILD.bazel new file mode 100644 index 000000000..9997c630a --- /dev/null +++ b/src/main/cpp/scheduler/BUILD.bazel @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "scheduler_library", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/scheduler/include", + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/log:log_library", + "@com_google_absl//absl/base", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/container:flat_hash_map", + "@asio//:asio", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/src/main/cpp/scheduler/CMakeLists.txt b/src/main/cpp/scheduler/CMakeLists.txt new file mode 100644 index 000000000..22eb4353b --- /dev/null +++ b/src/main/cpp/scheduler/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(scheduler OBJECT SchedulerImpl.cpp) +target_include_directories(scheduler + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(scheduler + PRIVATE + absl::base + api + asio + spdlog) diff --git a/src/main/cpp/scheduler/SchedulerImpl.cpp b/src/main/cpp/scheduler/SchedulerImpl.cpp new file mode 100644 index 000000000..7eb4da384 --- /dev/null +++ b/src/main/cpp/scheduler/SchedulerImpl.cpp @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "SchedulerImpl.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "asio/error_code.hpp" +#include "asio/executor_work_guard.hpp" +#include "asio/io_context.hpp" +#include "asio/steady_timer.hpp" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +SchedulerImpl::SchedulerImpl(std::uint32_t worker_num) + : work_guard_( + absl::make_unique>(context_.get_executor())), + worker_num_(worker_num) { +} + +SchedulerImpl::SchedulerImpl() : SchedulerImpl(std::thread::hardware_concurrency()) { +} + +SchedulerImpl::~SchedulerImpl() { + shutdown0(); +} + +void SchedulerImpl::start() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTING, std::memory_order_relaxed)) { + for (std::uint32_t i = 0; i < worker_num_; i++) { + auto worker = std::thread([this]() { + { + State expect = State::STARTING; + if (state_.compare_exchange_strong(expect, State::STARTED, std::memory_order_relaxed)) { + absl::MutexLock lk(&start_mtx_); + start_cv_.SignalAll(); + } + } + + while (true) { +#ifdef __EXCEPTIONS + try { +#endif + std::error_code ec; + context_.run(ec); + if (ec) { + SPDLOG_WARN("Error raised from thread-pool: {}", ec.message()); + } +#ifdef __EXCEPTIONS + } catch (std::exception& e) { + SPDLOG_WARN("Exception raised from thread-pool: {}", e.what()); + } +#endif + + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_INFO("One scheduler worker thread quit"); + break; + } + } + }); + threads_.emplace_back(std::move(worker)); + } + + { + absl::MutexLock lk(&start_mtx_); + if (State::STARTING == state_.load(std::memory_order_relaxed)) { + start_cv_.Wait(&start_mtx_); + SPDLOG_INFO("Scheduler threads start to loop"); + } + } + } +} + +void SchedulerImpl::shutdown() { + shutdown0(); +} + +void SchedulerImpl::shutdown0() { + State expected = State::STARTED; + if (state_.compare_exchange_strong(expected, State::STOPPING, std::memory_order_relaxed)) { + work_guard_->reset(); + { + absl::MutexLock lk(&tasks_mtx_); + tasks_.clear(); + } + context_.stop(); + + for (auto& worker : threads_) { + if (worker.joinable()) { + worker.join(); + } + } + threads_.clear(); + + state_.store(State::STOPPED); + } +} + +std::uint32_t SchedulerImpl::schedule(const std::function& functor, const std::string& task_name, + std::chrono::milliseconds delay, std::chrono::milliseconds interval) { + static std::uint32_t task_id = 0; + + auto task = std::make_shared(); + task->task_name = task_name; + task->callback = functor; + task->interval = interval; + + std::uint32_t id; + { + absl::MutexLock lk(&tasks_mtx_); + id = ++task_id; + tasks_.insert({id, task}); + } + task->task_id = id; + task->timer = absl::make_unique(context_, delay); + task->scheduler = shared_from_this(); + SPDLOG_DEBUG("Timer-task[name={}] to fire in {}ms", task_name, delay.count()); + auto timer_task_weak_ptr = std::weak_ptr(task); + task->timer->async_wait(std::bind(&SchedulerImpl::execute, std::placeholders::_1, timer_task_weak_ptr)); + return id; +} + +void SchedulerImpl::cancel(std::uint32_t task_id) { + absl::MutexLock lk(&tasks_mtx_); + if (!tasks_.contains(task_id)) { + SPDLOG_ERROR("Scheduler does not have the task to delete. Task-ID specified is: {}", task_id); + return; + } + auto search = tasks_.find(task_id); + assert(search != tasks_.end()); + SPDLOG_INFO("Cancel task[task-id={}, name={}]", task_id, search->second->task_name); + tasks_.erase(search); +} + +void SchedulerImpl::execute(const asio::error_code& ec, std::weak_ptr task) { + std::shared_ptr timer_task = task.lock(); + if (!timer_task) { + return; + } + + SPDLOG_INFO("Execute task: {}. Use-count: {}", timer_task->task_name, timer_task.use_count()); + + // Execute the actual callback. +#ifdef __EXCEPTIONS + try { +#endif + timer_task->callback(); +#ifdef __EXCEPTIONS + } catch (std::exception& e) { + SPDLOG_WARN("Exception raised: {}", e.what()); + } catch (std::string& e) { + SPDLOG_WARN("Exception raised: {}", e); + } catch (...) { + SPDLOG_WARN("Unknown exception type raised"); + } +#endif + + if (timer_task->interval.count()) { + auto& timer = timer_task->timer; + timer->expires_at(timer->expiry() + timer_task->interval); + timer->async_wait(std::bind(&SchedulerImpl::execute, std::placeholders::_1, task)); + SPDLOG_DEBUG("Repeated timer-task {} to fire in {}ms", timer_task->task_name, timer_task->interval.count()); + } else { + auto scheduler = timer_task->scheduler.lock(); + if (scheduler) { + scheduler->cancel(timer_task->task_id); + } + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/scheduler/include/Scheduler.h b/src/main/cpp/scheduler/include/Scheduler.h new file mode 100644 index 000000000..65feabb85 --- /dev/null +++ b/src/main/cpp/scheduler/include/Scheduler.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Scheduler { +public: + virtual ~Scheduler() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual std::uint32_t schedule(const std::function& functor, const std::string& task_name, + std::chrono::milliseconds delay, std::chrono::milliseconds interval) = 0; + + virtual void cancel(std::uint32_t task_id) = 0; +}; + +using SchedulerPtr = std::weak_ptr; +using SchedulerSharedPtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/scheduler/include/SchedulerImpl.h b/src/main/cpp/scheduler/include/SchedulerImpl.h new file mode 100644 index 000000000..00066eb4b --- /dev/null +++ b/src/main/cpp/scheduler/include/SchedulerImpl.h @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "asio.hpp" + +#include "Scheduler.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct TimerTask { + std::uint32_t task_id; + std::string task_name; + std::function callback; + std::chrono::milliseconds interval; + std::unique_ptr timer; + SchedulerPtr scheduler; +}; + +class SchedulerImpl : public std::enable_shared_from_this, public Scheduler { +public: + SchedulerImpl(); + + explicit SchedulerImpl(std::uint32_t worker_num); + + ~SchedulerImpl() override; + + void start() override; + + void shutdown() override LOCKS_EXCLUDED(tasks_mtx_); + + /** + * @functor Pointer to the functor. Lifecycle of this functor should be maintained by the caller. + * @task_name Name of the task. Task name would be very helpful at debug site. + * @delay The amount of time to wait before the first shot. + * @interval The interval between each fire-shot. If it is 0ms, callable will just fired once. + */ + std::uint32_t schedule(const std::function& functor, const std::string& task_name, + std::chrono::milliseconds delay, std::chrono::milliseconds interval) override + LOCKS_EXCLUDED(tasks_mtx_); + + /** + * Note: + * Periodic tasks should be explicitly cancelled once they are no longer needed. + */ + void cancel(std::uint32_t task_id) override LOCKS_EXCLUDED(tasks_mtx_); + +private: + asio::io_context context_; + std::unique_ptr> work_guard_; + absl::Mutex start_mtx_; + absl::CondVar start_cv_; + std::uint32_t worker_num_{std::thread::hardware_concurrency()}; + std::vector threads_; + std::atomic state_{State::CREATED}; + + absl::flat_hash_map> tasks_ GUARDED_BY(tasks_mtx_); + absl::Mutex tasks_mtx_; + + static void execute(const asio::error_code& ec, std::weak_ptr task); + + void shutdown0(); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/tracing/BUILD.bazel b/src/main/cpp/tracing/BUILD.bazel new file mode 100644 index 000000000..0fdb6b095 --- /dev/null +++ b/src/main/cpp/tracing/BUILD.bazel @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "tracing_utility", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/tracing/include", + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/client:client_library", + "@io_opencensus_cpp//opencensus/trace", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/src/main/cpp/tracing/CMakeLists.txt b/src/main/cpp/tracing/CMakeLists.txt new file mode 100644 index 000000000..220343fd3 --- /dev/null +++ b/src/main/cpp/tracing/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(rocketmq_trace OBJECT TracingUtility.cpp) +target_include_directories(rocketmq_trace + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(rocketmq_trace + PRIVATE + api + base + client + fmt + opencensus::stats + proto + spdlog) + +add_subdirectory(exporters) \ No newline at end of file diff --git a/src/main/cpp/tracing/TracingUtility.cpp b/src/main/cpp/tracing/TracingUtility.cpp new file mode 100644 index 000000000..d5b7be0d2 --- /dev/null +++ b/src/main/cpp/tracing/TracingUtility.cpp @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TracingUtility.h" +#include "MixAll.h" +#include "absl/strings/str_join.h" +#include "rocketmq/CredentialsProvider.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void TracingUtility::addUniversalSpanAttributes(const MQMessage& message, ClientConfig& client_config, + opencensus::trace::Span& span) { + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message.getMsgId()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES, message.getBody().length()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, message.getTags()); + const std::vector& keys = message.getKeys(); + if (!keys.empty()) { + span.AddAnnotation(MixAll::SPAN_ANNOTATION_MESSAGE_KEYS, + {{MixAll::SPAN_ANNOTATION_MESSAGE_KEYS, + absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)}}); + } + switch (message.messageType()) { + case MessageType::FIFO: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_FIFO_MESSAGE); + break; + case MessageType::DELAY: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DELAY_MESSAGE); + break; + case MessageType::TRANSACTION: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_TRANSACTION_MESSAGE); + break; + default: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NORMAL_MESSAGE); + break; + } + std::chrono::system_clock::time_point timestamp = message.deliveryTimestamp(); + auto duration = absl::FromChrono(timestamp.time_since_epoch()); + int64_t timestamp_millis = absl::ToInt64Milliseconds(duration); + if (timestamp_millis > 0) { + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, timestamp_millis); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_SYSTEM, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_MESSAGING_SYSTEM); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, message.getTopic()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION_KIND, + MixAll::SPAN_ATTRIBUTE_VALUE_DESTINATION_KIND); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL, MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL_VERSION, + MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL_VERSION); + // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_URL, "abc") + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, client_config.resourceNamespace()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_ID, client_config.clientId()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, client_config.getGroupName()); + + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, + client_config.credentialsProvider()->getCredentials().accessKey()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, client_config.resourceNamespace()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/tracing/exporters/BUILD.bazel b/src/main/cpp/tracing/exporters/BUILD.bazel new file mode 100644 index 000000000..0fee74515 --- /dev/null +++ b/src/main/cpp/tracing/exporters/BUILD.bazel @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "otlp_exporter", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/tracing/exporters/include", + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/client:client_library", + "@com_google_absl//absl/base", + "@com_google_absl//absl/memory", + "@io_opencensus_cpp//opencensus/trace", + "@com_github_opentelemetry_proto//:trace_service_grpc_cc", + "@com_github_grpc_grpc//:grpc++", + "@com_github_fmtlib_fmt//:fmtlib" + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/src/main/cpp/tracing/exporters/CMakeLists.txt b/src/main/cpp/tracing/exporters/CMakeLists.txt new file mode 100644 index 000000000..e7d344d74 --- /dev/null +++ b/src/main/cpp/tracing/exporters/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(otlp_exporter OBJECT OtlpExporter.cpp) +target_include_directories(otlp_exporter + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(otlp_exporter + PRIVATE + absl::flat_hash_map + api + base + client + fmt + httplib + opencensus::stats + proto + scheduler + spdlog) \ No newline at end of file diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp new file mode 100644 index 000000000..1d24de4a1 --- /dev/null +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -0,0 +1,428 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "OtlpExporter.h" +#include "ClientConfigImpl.h" +#include "InvocationContext.h" +#include "MixAll.h" +#include "Signature.h" +#include "UtilAll.h" +#include "absl/strings/str_split.h" +#include "fmt/format.h" +#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h" +#include "opentelemetry/proto/common/v1/common.pb.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +namespace trace = opentelemetry::proto::trace::v1; +namespace common = opentelemetry::proto::common::v1; + +opencensus::trace::Sampler& Samplers::always() { + static opencensus::trace::AlwaysSampler sampler; + return sampler; +} + +void OtlpExporter::start() { + std::shared_ptr self = shared_from_this(); + auto handler = absl::make_unique(self); + handler->start(); + opencensus::trace::exporter::SpanExporter::RegisterHandler(std::move(handler)); +} + +void ExportClient::asyncExport(const collector_trace::ExportTraceServiceRequest& request, + InvocationContext* invocation_context) { + auto completion_queue = completion_queue_.lock(); + if (!completion_queue) { + // Server should have shutdown + return; + } + + invocation_context->response_reader = + stub_->PrepareAsyncExport(&invocation_context->context, request, completion_queue.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +const int OtlpExporterHandler::SPAN_ID_SIZE = 8; +const int OtlpExporterHandler::TRACE_ID_SIZE = 16; + +OtlpExporterHandler::OtlpExporterHandler(std::weak_ptr exporter) + : exporter_(std::move(exporter)), completion_queue_(std::make_shared()) { + auto exp = exporter_.lock(); +} + +void OtlpExporterHandler::start() { + poll_thread_ = std::thread(std::bind(&OtlpExporterHandler::poll, this)); + { + absl::MutexLock lk(&start_mtx_); + start_cv_.Wait(&start_mtx_); + } +} + +void OtlpExporterHandler::shutdown() { + stopped_.store(true, std::memory_order_relaxed); + if (poll_thread_.joinable()) { + poll_thread_.join(); + } +} + +void OtlpExporterHandler::syncExportClients() { + auto exp = exporter_.lock(); + if (!exp) { + return; + } + + std::vector&& hosts = exp->hosts(); + { + absl::MutexLock lk(&clients_map_mtx_); + for (auto i = clients_map_.begin(); i != clients_map_.end();) { + if (std::none_of(hosts.cbegin(), hosts.cend(), [&](const std::string& host) { return i->first == host; })) { + clients_map_.erase(i++); + } else { + i++; + } + } + + auto client_manager = exp->clientManager().lock(); + for (const auto& host : hosts) { + if (!clients_map_.contains(host)) { + if (client_manager) { + auto channel = client_manager->createChannel(host); + auto export_client = absl::make_unique(completion_queue_, channel); + clients_map_.emplace(host, std::move(export_client)); + } + } + } + } +} + +void OtlpExporterHandler::poll() { + { + // Notify main thread that the poller thread has started + absl::MutexLock lk(&start_mtx_); + start_cv_.SignalAll(); + } + + while (!stopped_.load(std::memory_order_relaxed)) { + bool ok = false; + void* opaque_invocation_context; + while (completion_queue_->Next(&opaque_invocation_context, &ok)) { + auto invocation_context = static_cast(opaque_invocation_context); + if (!ok) { + // the call is dead + SPDLOG_WARN("CompletionQueue#Next assigned ok false, indicating the call is dead"); + } + invocation_context->onCompletion(ok); + } + SPDLOG_INFO("CompletionQueue is fully drained and shut down"); + } + SPDLOG_INFO("poll completed and quit"); +} + +void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter::SpanData>& spans) { + auto exp = exporter_.lock(); + if (!exp) { + return; + } + + switch (exp->traceMode()) { + case TraceMode::Off: + return; + case TraceMode::Develop: { + { + for (const auto& span : spans) { + SPDLOG_INFO("trace span {} --> {}: {}", absl::FormatTime(span.start_time()), + absl::FormatTime(span.end_time()), span.name().data()); + for (const auto& event : span.annotations().events()) { + for (const auto& attr : event.event().attributes()) { + switch (attr.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, + attr.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, + attr.second.int_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, + attr.second.bool_value()); + break; + } + } + } + SPDLOG_INFO("Attributes size={}", span.attributes().size()); + for (const auto& attribute : span.attributes()) { + switch (attribute.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + SPDLOG_INFO("Span attribute: {} --> {}", attribute.first, attribute.second.string_value()); + break; + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + SPDLOG_INFO("Span attribute: {} --> {}", attribute.first, attribute.second.bool_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + SPDLOG_INFO("Span attribute: {} --> {}", attribute.first, attribute.second.int_value()); + break; + } + } + } + } + return; + } + case TraceMode::Grpc: + break; + } + + syncExportClients(); + absl::MutexLock lk(&clients_map_mtx_); + if (clients_map_.empty()) { + SPDLOG_WARN("No exporter client is available"); + return; + } + + uint32_t client_index = round_robin_++ % clients_map_.size(); + + auto iterator = clients_map_.begin(); + for (uint32_t i = 0; i < client_index; i++) { + iterator++; + } + + auto& exporter_client = iterator->second; + + collector_trace::ExportTraceServiceRequest request; + + auto resource = new trace::ResourceSpans(); + auto instrument_library_span = new trace::InstrumentationLibrarySpans(); + + uint8_t span_id_buf[SPAN_ID_SIZE]; + uint8_t trace_id_buf[TRACE_ID_SIZE]; + for (const auto& span : spans) { + auto item = new trace::Span(); + + span.context().trace_id().CopyTo(trace_id_buf); + item->set_trace_id(&trace_id_buf, TRACE_ID_SIZE); + + span.context().span_id().CopyTo(span_id_buf); + item->set_span_id(&span_id_buf, SPAN_ID_SIZE); + + span.parent_span_id().CopyTo(span_id_buf); + item->set_parent_span_id(&span_id_buf, SPAN_ID_SIZE); + + item->set_name(span.name().data()); + + item->set_start_time_unix_nano(absl::ToUnixNanos(span.start_time())); + item->set_end_time_unix_nano(absl::ToUnixNanos(span.end_time())); + + item->set_kind(trace::Span_SpanKind::Span_SpanKind_SPAN_KIND_CLIENT); + + // OpenCensus has annotations or message_events, which maps to events in OpenTelemetry. + if (!span.message_events().events().empty()) { + for (const auto& event : span.message_events().events()) { + auto ev = new trace::Span::Event(); + ev->set_time_unix_nano(absl::ToUnixNanos(event.timestamp())); + } + item->set_dropped_events_count(span.message_events().dropped_events_count()); + } + + if (!span.annotations().events().empty()) { + for (const auto& annotation : span.annotations().events()) { + // Specialized annotation to adjust span start-time. + // OpenCensus does not expose function to modify span start time. As as result, we need to apply this system + // annotation duration transforming opencensus span to OpenTelemetry span. + if (annotation.event().description() == MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION) { + for (const auto& attr : annotation.event().attributes()) { + if (attr.first == MixAll::SPAN_ANNOTATION_ATTR_START_TIME) { + assert(attr.second.type() == opencensus::trace::AttributeValueRef::Type::kInt); + item->set_start_time_unix_nano(attr.second.int_value() * 1e6); + } + } + continue; + } + if (annotation.event().description() == MixAll::SPAN_ANNOTATION_MESSAGE_KEYS) { + for (const auto& attr : annotation.event().attributes()) { + if (attr.first == MixAll::SPAN_ANNOTATION_MESSAGE_KEYS) { + assert(attr.second.type() == opencensus::trace::AttributeValueRef::Type::kString); + std::string message_keys = attr.second.string_value(); + std::vector key_list = absl::StrSplit(message_keys, MixAll::MESSAGE_KEY_SEPARATOR); + auto key_kv = new common::KeyValue(); + key_kv->set_key(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS); + auto key_value = new common::AnyValue(); + auto key_array_value = new common::ArrayValue(); + for (const auto& key : key_list) { + auto value = new common::AnyValue(); + value->set_string_value(key); + key_array_value->mutable_values()->AddAllocated(value); + } + key_value->set_allocated_array_value(key_array_value); + key_kv->set_allocated_value(key_value); + item->mutable_attributes()->AddAllocated(key_kv); + } + } + continue; + } + auto ev = new trace::Span::Event(); + ev->set_time_unix_nano(absl::ToUnixNanos(annotation.timestamp())); + auto attrs = annotation.event().attributes(); + for (const auto& attr : attrs) { + auto kv = new common::KeyValue(); + kv->set_key(attr.first); + auto value = new common::AnyValue(); + switch (attr.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + value->set_string_value(attr.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + value->set_int_value(attr.second.int_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + value->set_bool_value(attr.second.bool_value()); + break; + } + ev->mutable_attributes()->AddAllocated(kv); + } + item->mutable_events()->AddAllocated(ev); + } + item->set_dropped_events_count(span.annotations().dropped_events_count()); + } + + for (const auto& link : span.links()) { + auto span_link = new trace::Span::Link(); + + link.trace_id().CopyTo(trace_id_buf); + item->set_trace_id(&trace_id_buf, TRACE_ID_SIZE); + + link.span_id().CopyTo(span_id_buf); + item->set_trace_id(&span_id_buf, SPAN_ID_SIZE); + + for (const auto& attribute : link.attributes()) { + auto kv = new common::KeyValue(); + kv->set_key(attribute.first); + auto value = new common::AnyValue(); + switch (attribute.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + value->set_string_value(attribute.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + value->set_bool_value(attribute.second.bool_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + value->set_int_value(attribute.second.int_value()); + break; + } + kv->set_allocated_value(value); + span_link->mutable_attributes()->AddAllocated(kv); + } + + item->mutable_links()->AddAllocated(span_link); + } + item->set_dropped_links_count(span.num_attributes_dropped()); + + for (const auto& attribute : span.attributes()) { + auto kv = new common::KeyValue(); + kv->set_key(attribute.first); + auto value = new common::AnyValue(); + switch (attribute.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + value->set_string_value(attribute.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + value->set_bool_value(attribute.second.bool_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + value->set_int_value(attribute.second.int_value()); + break; + } + kv->set_allocated_value(value); + item->mutable_attributes()->AddAllocated(kv); + } + + if (span.status().ok()) { + item->mutable_status()->set_code(trace::Status_StatusCode::Status_StatusCode_STATUS_CODE_OK); + } else { + item->mutable_status()->set_code(trace::Status_StatusCode::Status_StatusCode_STATUS_CODE_ERROR); + item->mutable_status()->set_message(span.status().error_message()); + } + + instrument_library_span->mutable_instrumentation_library()->mutable_name()->assign(MixAll::OTLP_NAME_VALUE); + instrument_library_span->mutable_instrumentation_library()->mutable_version()->assign( + ClientConfigImpl::CLIENT_VERSION); + instrument_library_span->mutable_spans()->AddAllocated(item); + } + resource->mutable_instrumentation_library_spans()->AddAllocated(instrument_library_span); + + auto telemetry_sdk_language_kv = new common::KeyValue(); + telemetry_sdk_language_kv->set_key(MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_TELEMETRY_SDK_LANGUAGE); + auto telemetry_sdk_language_value = new common::AnyValue(); + telemetry_sdk_language_value->set_string_value(MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_TELEMETRY_SDK_LANGUAGE); + telemetry_sdk_language_kv->set_allocated_value(telemetry_sdk_language_value); + resource->mutable_resource()->mutable_attributes()->AddAllocated(telemetry_sdk_language_kv); + + auto host_name_kv = new common::KeyValue(); + host_name_kv->set_key(MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_HOST_NAME); + auto host_name_value = new common::AnyValue(); + host_name_value->set_string_value(UtilAll::hostname()); + host_name_kv->set_allocated_value(host_name_value); + resource->mutable_resource()->mutable_attributes()->AddAllocated(host_name_kv); + + auto service_name_kv = new common::KeyValue(); + service_name_kv->set_key(MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME); + auto service_name_value = new common::AnyValue(); + service_name_value->set_string_value(MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME); + service_name_kv->set_allocated_value(service_name_value); + resource->mutable_resource()->mutable_attributes()->AddAllocated(service_name_kv); + + request.mutable_resource_spans()->AddAllocated(resource); + + auto invocation_context = new InvocationContext(); + invocation_context->remote_address = iterator->first; + auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(3); + invocation_context->context.set_deadline(deadline); + + absl::flat_hash_map metadata; + Signature::sign(exp->clientConfig(), metadata); + for (const auto& entry : metadata) { + invocation_context->context.AddMetadata(entry.first, entry.second); + } + auto callback = [](const InvocationContext* invocation_context) { + if (invocation_context->status.ok()) { + SPDLOG_DEBUG("Export tracing spans OK, target={}", invocation_context->remote_address); + } else { + SPDLOG_WARN("Failed to export tracing spans to {}, gRPC code:{}, gRPC error message: {}", + invocation_context->remote_address, invocation_context->status.error_code(), + invocation_context->status.error_message()); + } + }; + invocation_context->callback = callback; + + exporter_client->asyncExport(request, invocation_context); +} + +thread_local std::uint32_t OtlpExporterHandler::round_robin_ = 0; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/tracing/exporters/include/OtlpExporter.h b/src/main/cpp/tracing/exporters/include/OtlpExporter.h new file mode 100644 index 000000000..f947a3886 --- /dev/null +++ b/src/main/cpp/tracing/exporters/include/OtlpExporter.h @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/memory/memory.h" +#include "absl/synchronization/mutex.h" +#include "opencensus/trace/exporter/span_data.h" +#include "opencensus/trace/exporter/span_exporter.h" +#include "opencensus/trace/sampler.h" +#include "opentelemetry/proto/collector/trace/v1/trace_service.grpc.pb.h" + +#include "ClientConfig.h" +#include "ClientManager.h" +#include "InvocationContext.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +namespace collector = opentelemetry::proto::collector; +namespace collector_trace = collector::trace::v1; + +enum class TraceMode : std::uint8_t +{ + Off = 0, + Develop = 1, + Grpc = 2 +}; + +class Samplers { +public: + static opencensus::trace::Sampler& always(); +}; + +class OtlpExporter : public std::enable_shared_from_this { +public: + OtlpExporter(std::weak_ptr client_manager, ClientConfig* client_config) + : client_manager_(std::move(client_manager)), client_config_(client_config) { + if (client_config_->isTracingEnabled()) { + mode_ = TraceMode::Grpc; + } + } + + void updateHosts(std::vector hosts) LOCKS_EXCLUDED(hosts_mtx_) { + if (hosts.empty()) { + return; + } + absl::MutexLock lk(&hosts_mtx_); + hosts_ = std::move(hosts); + } + + void start(); + + void shutdown(); + + std::vector hosts() LOCKS_EXCLUDED(hosts_mtx_) { + absl::MutexLock lk(&hosts_mtx_); + return hosts_; + } + + std::weak_ptr& clientManager() { + return client_manager_; + } + + ClientConfig* clientConfig() { + return client_config_; + } + + void traceMode(TraceMode mode) { + mode_ = mode; + } + + TraceMode traceMode() const { + return mode_; + } + +private: + std::weak_ptr client_manager_; + ClientConfig* client_config_; + + std::vector hosts_; + absl::Mutex hosts_mtx_; + + TraceMode mode_{TraceMode::Off}; +}; + +class ExportClient { +public: + ExportClient(std::shared_ptr completion_queue, std::shared_ptr channel) + : completion_queue_(std::move(completion_queue)), stub_(collector_trace::TraceService::NewStub(channel)) { + } + + void asyncExport(const collector_trace::ExportTraceServiceRequest& request, + InvocationContext* invocation_context); + +private: + std::weak_ptr completion_queue_; + std::unique_ptr stub_; +}; + +class OtlpExporterHandler : public ::opencensus::trace::exporter::SpanExporter::Handler { +public: + static const int SPAN_ID_SIZE; + static const int TRACE_ID_SIZE; + + OtlpExporterHandler(std::weak_ptr exporter); + + void Export(const std::vector<::opencensus::trace::exporter::SpanData>& spans) override; + + void start(); + + void shutdown(); + +private: + std::weak_ptr exporter_; + std::shared_ptr completion_queue_; + std::thread poll_thread_; + absl::Mutex start_mtx_; + absl::CondVar start_cv_; + + absl::flat_hash_map> clients_map_ GUARDED_BY(clients_map_mtx_); + absl::Mutex clients_map_mtx_; + + thread_local static std::uint32_t round_robin_; + + std::atomic_bool stopped_{false}; + + void poll(); + + void syncExportClients() LOCKS_EXCLUDED(clients_map_mtx_); +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/tracing/include/TracingUtility.h b/src/main/cpp/tracing/include/TracingUtility.h new file mode 100644 index 000000000..f97478068 --- /dev/null +++ b/src/main/cpp/tracing/include/TracingUtility.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "opencensus/trace/span.h" + +#include "ClientConfig.h" +#include "rocketmq/MQMessage.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TracingUtility { +public: + static void addUniversalSpanAttributes(const MQMessage& message, ClientConfig&, opencensus::trace::Span& span); +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/message/BatchMessage.cpp b/src/message/BatchMessage.cpp deleted file mode 100644 index 9bcca1cf0..000000000 --- a/src/message/BatchMessage.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "include/BatchMessage.h" - -#include "MQDecoder.h" -#include "StringIdMaker.h" - -namespace rocketmq { - -std::string BatchMessage::encode(std::vector& msgs) { - std::string encodedBody; - for (auto message : msgs) { - std::string unique_id = StringIdMaker::getInstance().createUniqID(); - message.setProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, unique_id); - encodedBody.append(encode(message)); - } - return encodedBody; -} - -std::string BatchMessage::encode(MQMessage& message) { - string encodeMsg; - const string& body = message.getBody(); - int bodyLen = body.length(); - string properties = MQDecoder::messageProperties2String(message.getProperties()); - short propertiesLength = (short)properties.length(); - int storeSize = 20 + bodyLen + 2 + propertiesLength; - // TOTALSIZE|MAGICCOD|BODYCRC|FLAG|BODYLen|Body|propertiesLength|properties - int magicCode = 0; - int bodyCrc = 0; - int flag = message.getFlag(); - int storeSize_net = htonl(storeSize); - int magicCode_net = htonl(magicCode); - int bodyCrc_net = htonl(bodyCrc); - int flag_net = htonl(flag); - int bodyLen_net = htonl(bodyLen); - int propertiesLength_net = htons(propertiesLength); - encodeMsg.append((char*)&storeSize_net, sizeof(int)); - encodeMsg.append((char*)&magicCode_net, sizeof(int)); - encodeMsg.append((char*)&bodyCrc_net, sizeof(int)); - encodeMsg.append((char*)&flag_net, sizeof(int)); - encodeMsg.append((char*)&bodyLen_net, sizeof(int)); - encodeMsg.append(body.c_str(), body.length()); - encodeMsg.append((char*)&propertiesLength_net, sizeof(short)); - encodeMsg.append(properties.c_str(), propertiesLength); - return encodeMsg; -} - -} // namespace rocketmq diff --git a/src/message/MQDecoder.cpp b/src/message/MQDecoder.cpp deleted file mode 100644 index 1ba8a951f..000000000 --- a/src/message/MQDecoder.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "MQDecoder.h" -#include -#include -#include -#include -#include "Logging.h" -#include "MemoryOutputStream.h" -#include "MessageSysFlag.h" -#include "UtilAll.h" - -namespace rocketmq { - -//(outputmen.getData()); - int len = outputmen.getDataSize(); - - return UtilAll::bytes2string(bytes, len); -} - -MQMessageId MQDecoder::decodeMessageId(const string& msgId) { - string ipStr = msgId.substr(0, 8); - string portStr = msgId.substr(8, 8); - string offsetStr = msgId.substr(16, 16); - - int num = strspn(offsetStr.c_str(), "F"); - offsetStr = offsetStr.substr(num, 16 - num); - - char* end; - int ipInt = strtoul(ipStr.c_str(), &end, 16); - int portInt = strtoul(portStr.c_str(), &end, 16); - uint64 offset = strtoul(offsetStr.c_str(), &end, 16); - // int64 offset = UtilAll::hexstr2ull(offsetStr.c_str()); - - struct sockaddr_in sa; - sa.sin_family = AF_INET; - sa.sin_port = htons(portInt); - sa.sin_addr.s_addr = htonl(ipInt); - sockaddr addr; - memcpy(&addr, &sa, sizeof(sockaddr)); - - MQMessageId id(addr, offset); - return id; -} - -MQMessageExt* MQDecoder::decode(MemoryInputStream& byteBuffer) { - return decode(byteBuffer, true); -} - -MQMessageExt* MQDecoder::decode(MemoryInputStream& byteBuffer, bool readBody) { - MQMessageExt* msgExt = new MQMessageExt(); - - // 1 TOTALSIZE - int storeSize = byteBuffer.readIntBigEndian(); - msgExt->setStoreSize(storeSize); - - // 2 MAGICCODE sizeof(int) - byteBuffer.skipNextBytes(sizeof(int)); - - // 3 BODYCRC - int bodyCRC = byteBuffer.readIntBigEndian(); - msgExt->setBodyCRC(bodyCRC); - - // 4 QUEUEID - int queueId = byteBuffer.readIntBigEndian(); - msgExt->setQueueId(queueId); - - // 5 FLAG - int flag = byteBuffer.readIntBigEndian(); - msgExt->setFlag(flag); - - // 6 QUEUEOFFSET - int64 queueOffset = byteBuffer.readInt64BigEndian(); - msgExt->setQueueOffset(queueOffset); - - // 7 PHYSICALOFFSET - int64 physicOffset = byteBuffer.readInt64BigEndian(); - msgExt->setCommitLogOffset(physicOffset); - - // 8 SYSFLAG - int sysFlag = byteBuffer.readIntBigEndian(); - msgExt->setSysFlag(sysFlag); - - // 9 BORNTIMESTAMP - int64 bornTimeStamp = byteBuffer.readInt64BigEndian(); - msgExt->setBornTimestamp(bornTimeStamp); - - // 10 BORNHOST - int bornHost = byteBuffer.readIntBigEndian(); - int port = byteBuffer.readIntBigEndian(); - sockaddr bornAddr = IPPort2socketAddress(bornHost, port); - msgExt->setBornHost(bornAddr); - - // 11 STORETIMESTAMP - int64 storeTimestamp = byteBuffer.readInt64BigEndian(); - msgExt->setStoreTimestamp(storeTimestamp); - - // // 12 STOREHOST - int storeHost = byteBuffer.readIntBigEndian(); - port = byteBuffer.readIntBigEndian(); - sockaddr storeAddr = IPPort2socketAddress(storeHost, port); - msgExt->setStoreHost(storeAddr); - - // 13 RECONSUMETIMES - int reconsumeTimes = byteBuffer.readIntBigEndian(); - msgExt->setReconsumeTimes(reconsumeTimes); - - // 14 Prepared Transaction Offset - int64 preparedTransactionOffset = byteBuffer.readInt64BigEndian(); - msgExt->setPreparedTransactionOffset(preparedTransactionOffset); - - // 15 BODY - int bodyLen = byteBuffer.readIntBigEndian(); - if (bodyLen > 0) { - if (readBody) { - MemoryBlock block; - byteBuffer.readIntoMemoryBlock(block, bodyLen); - - const char* const pBody = static_cast(block.getData()); - int len = block.getSize(); - string msgbody(pBody, len); - - // decompress body - if ((sysFlag & MessageSysFlag::CompressedFlag) == MessageSysFlag::CompressedFlag) { - string outbody; - if (UtilAll::inflate(msgbody, outbody)) { - msgExt->setBody(outbody); - } - } else { - msgExt->setBody(msgbody); - } - } else { - byteBuffer.skipNextBytes(bodyLen); - } - } - - // 16 TOPIC - int topicLen = (int)byteBuffer.readByte(); - MemoryBlock block; - byteBuffer.readIntoMemoryBlock(block, topicLen); - const char* const pTopic = static_cast(block.getData()); - topicLen = block.getSize(); - msgExt->setTopic(pTopic, topicLen); - - // 17 properties - short propertiesLen = byteBuffer.readShortBigEndian(); - if (propertiesLen > 0) { - MemoryBlock block; - byteBuffer.readIntoMemoryBlock(block, propertiesLen); - const char* const pProperty = static_cast(block.getData()); - int len = block.getSize(); - string propertiesString(pProperty, len); - - map propertiesMap; - string2messageProperties(propertiesString, propertiesMap); - msgExt->setPropertiesInternal(propertiesMap); - propertiesMap.clear(); - } - - // 18 msg ID - string offsetMsgId = createMessageId(msgExt->getStoreHost(), (int64)msgExt->getCommitLogOffset()); - msgExt->setOffsetMsgId(offsetMsgId); - - string msgId = msgExt->getProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX); - if (msgId.empty()) { - msgId = offsetMsgId; - } - msgExt->setMsgId(msgId); - - // LOG_INFO("get msgExt from remote server, its contents are:%s", msgExt->toString().c_str()); - return msgExt; -} - -void MQDecoder::decodes(const MemoryBlock* mem, vector& mqvec) { - mqvec.clear(); - decodes(mem, mqvec, true); -} - -void MQDecoder::decodes(const MemoryBlock* mem, vector& mqvec, bool readBody) { - MemoryInputStream rawInput(*mem, true); - - while (rawInput.getNumBytesRemaining() > 0) { - unique_ptr msg(decode(rawInput, readBody)); - mqvec.push_back(*msg); - } -} - -string MQDecoder::messageProperties2String(const map& properties) { - string os; - - for (const auto& it : properties) { - // os << it->first << NAME_VALUE_SEPARATOR << it->second << PROPERTY_SEPARATOR; - os.append(it.first); - os += NAME_VALUE_SEPARATOR; - os.append(it.second); - os += PROPERTY_SEPARATOR; - } - - return os; -} - -void MQDecoder::string2messageProperties(const string& propertiesString, map& properties) { - vector out; - UtilAll::Split(out, propertiesString, PROPERTY_SEPARATOR); - - for (size_t i = 0; i < out.size(); i++) { - vector outValue; - UtilAll::Split(outValue, out[i], NAME_VALUE_SEPARATOR); - - if (outValue.size() == 2) { - properties[outValue[0]] = outValue[1]; - } - } -} -} // namespace rocketmq diff --git a/src/message/MQDecoder.h b/src/message/MQDecoder.h deleted file mode 100644 index d9c94ad48..000000000 --- a/src/message/MQDecoder.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __MESSAGEDECODER_H__ -#define __MESSAGEDECODER_H__ - -#include "MQClientException.h" -#include "MQMessageExt.h" -#include "MQMessageId.h" -#include "MemoryInputStream.h" -#include "SocketUtil.h" - -namespace rocketmq { -//& mqvec); - - static void decodes(const MemoryBlock* mem, vector& mqvec, bool readBody); - - static string messageProperties2String(const map& properties); - static void string2messageProperties(const string& propertiesString, map& properties); - - private: - static MQMessageExt* decode(MemoryInputStream& byteBuffer); - static MQMessageExt* decode(MemoryInputStream& byteBuffer, bool readBody); - - public: - static const char NAME_VALUE_SEPARATOR; - static const char PROPERTY_SEPARATOR; - static const int MSG_ID_LENGTH; - static int MessageMagicCodePostion; - static int MessageFlagPostion; - static int MessagePhysicOffsetPostion; - static int MessageStoreTimestampPostion; -}; -} //::const_iterator it = m_properties.find(name); - if (it == m_properties.end()) { - return EMPTY_STRING; - } else { - return it->second; - } -} - -const string& MQMessage::getTopic() const { - return m_topic; -} - -void MQMessage::setTopic(const string& topic) { - m_topic = topic; -} - -void MQMessage::setTopic(const char* body, int len) { - m_topic.clear(); - m_topic.append(body, len); -} - -const string& MQMessage::getTags() const { - return getProperty(PROPERTY_TAGS); -} - -void MQMessage::setTags(const string& tags) { - setPropertyInternal(PROPERTY_TAGS, tags); -} - -const string& MQMessage::getKeys() const { - return getProperty(PROPERTY_KEYS); -} - -void MQMessage::setKeys(const string& keys) { - setPropertyInternal(PROPERTY_KEYS, keys); -} - -void MQMessage::setKeys(const vector& keys) { - if (keys.empty()) { - return; - } - - vector::const_iterator it = keys.begin(); - string str; - str += *it; - it++; - - for (; it != keys.end(); it++) { - str += KEY_SEPARATOR; - str += *it; - } - - setKeys(str); -} - -int MQMessage::getDelayTimeLevel() const { - string tmp = getProperty(PROPERTY_DELAY_TIME_LEVEL); - if (!tmp.empty()) { - return atoi(tmp.c_str()); - } - return 0; -} - -void MQMessage::setDelayTimeLevel(int level) { - char tmp[16]; - sprintf(tmp, "%d", level); - - setPropertyInternal(PROPERTY_DELAY_TIME_LEVEL, tmp); -} - -bool MQMessage::isWaitStoreMsgOK() const { - string tmp = getProperty(PROPERTY_WAIT_STORE_MSG_OK); - if (tmp.empty()) { - return true; - } else { - return (tmp == "true") ? true : false; - } -} - -void MQMessage::setWaitStoreMsgOK(bool waitStoreMsgOK) { - if (waitStoreMsgOK) { - setPropertyInternal(PROPERTY_WAIT_STORE_MSG_OK, "true"); - } else { - setPropertyInternal(PROPERTY_WAIT_STORE_MSG_OK, "false"); - } -} - -int MQMessage::getFlag() const { - return m_flag; -} - -void MQMessage::setFlag(int flag) { - m_flag = flag; -} - -int MQMessage::getSysFlag() const { - return m_sysFlag; -} - -void MQMessage::setSysFlag(int sysFlag) { - m_sysFlag = sysFlag; -} - -const string& MQMessage::getBody() const { - return m_body; -} - -void MQMessage::setBody(const char* body, int len) { - m_body.clear(); - m_body.append(body, len); -} - -void MQMessage::setBody(const string& body) { - m_body.clear(); - m_body.append(body); -} - -map MQMessage::getProperties() const { - return m_properties; -} - -void MQMessage::setProperties(map& properties) { - m_properties = properties; - - map::const_iterator it = m_properties.find(PROPERTY_TRANSACTION_PREPARED); - if (it != m_properties.end()) { - string tranMsg = it->second; - if (!tranMsg.empty() && tranMsg == "true") { - m_sysFlag |= MessageSysFlag::TransactionPreparedType; - } else { - m_sysFlag &= ~MessageSysFlag::TransactionPreparedType; - } - } -} - -void MQMessage::setPropertiesInternal(map& properties) { - m_properties = properties; -} - -void MQMessage::Init(const string& topic, - const string& tags, - const string& keys, - const int flag, - const string& body, - bool waitStoreMsgOK) { - m_topic = topic; - m_flag = flag; - m_sysFlag = 0; - m_body = body; - - if (tags.length() > 0) { - setTags(tags); - } - - if (keys.length() > 0) { - setKeys(keys); - } - - setWaitStoreMsgOK(waitStoreMsgOK); -} -} //m_address = id.m_address; - this->m_offset = id.m_offset; - return *this; - } - - sockaddr getAddress() const { return m_address; } - - void setAddress(sockaddr address) { m_address = address; } - - int64 getOffset() const { return m_offset; } - - void setOffset(int64 offset) { m_offset = offset; } - - private: - sockaddr m_address; - int64 m_offset; -}; - -} // namespace rocketmq - -#endif diff --git a/src/message/MQMessageQueue.cpp b/src/message/MQMessageQueue.cpp deleted file mode 100644 index 22b52f438..000000000 --- a/src/message/MQMessageQueue.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "MQMessageQueue.h" - -namespace rocketmq { -// - -#include "DefaultMQProducerImpl.h" - -namespace rocketmq { - -DefaultMQProducer::DefaultMQProducer(const std::string& groupName) { - impl = new DefaultMQProducerImpl(groupName); -} - -DefaultMQProducer::~DefaultMQProducer() { - delete impl; -} -void DefaultMQProducer::start() { - impl->start(); -} - -void DefaultMQProducer::shutdown() { - impl->shutdown(); -} - -std::string DefaultMQProducer::version() { - std::string versions = impl->getClientVersionString(); - /* - versions.append(", PROTOCOL VERSION: ") - .append(MQVersion::GetVersionDesc(MQVersion::s_CurrentVersion)) - .append(", LANGUAGE: ") - .append(MQVersion::s_CurrentLanguage); - */ - return versions; -} - -// start mqclient set -const std::string& DefaultMQProducer::getNamesrvAddr() const { - return impl->getNamesrvAddr(); -} - -void DefaultMQProducer::setNamesrvAddr(const std::string& namesrvAddr) { - impl->setNamesrvAddr(namesrvAddr); -} - -const std::string& DefaultMQProducer::getNamesrvDomain() const { - return impl->getNamesrvDomain(); -} - -void DefaultMQProducer::setNamesrvDomain(const std::string& namesrvDomain) { - impl->setNamesrvDomain(namesrvDomain); -} -void DefaultMQProducer::setSessionCredentials(const std::string& accessKey, - const std::string& secretKey, - const std::string& accessChannel) { - impl->setSessionCredentials(accessKey, secretKey, accessChannel); -} - -const SessionCredentials& DefaultMQProducer::getSessionCredentials() const { - return impl->getSessionCredentials(); -} -const std::string& DefaultMQProducer::getInstanceName() const { - return impl->getInstanceName(); -} - -void DefaultMQProducer::setInstanceName(const std::string& instanceName) { - impl->setInstanceName(instanceName); -} - -const std::string& DefaultMQProducer::getNameSpace() const { - return impl->getNameSpace(); -} - -void DefaultMQProducer::setNameSpace(const std::string& nameSpace) { - impl->setNameSpace(nameSpace); -} -const std::string& DefaultMQProducer::getGroupName() const { - return impl->getGroupName(); -} - -void DefaultMQProducer::setGroupName(const std::string& groupName) { - impl->setGroupName(groupName); -} -void DefaultMQProducer::setSendMsgTimeout(int sendMsgTimeout) { - impl->setSendMsgTimeout(sendMsgTimeout); -} - -int DefaultMQProducer::getSendMsgTimeout() const { - return impl->getSendMsgTimeout(); -} - -void DefaultMQProducer::setRetryTimes(int times) { - impl->setRetryTimes(times); -} - -int DefaultMQProducer::getRetryTimes() const { - return impl->getRetryTimes(); -} - -int DefaultMQProducer::getCompressMsgBodyOverHowmuch() const { - return impl->getCompressMsgBodyOverHowmuch(); -} - -void DefaultMQProducer::setCompressMsgBodyOverHowmuch(int compressMsgBodyThreshold) { - impl->setCompressMsgBodyOverHowmuch(compressMsgBodyThreshold); -} - -int DefaultMQProducer::getCompressLevel() const { - return impl->getCompressLevel(); -} - -void DefaultMQProducer::setCompressLevel(int compressLevel) { - impl->setCompressLevel(compressLevel); -} - -void DefaultMQProducer::setMaxMessageSize(int maxMessageSize) { - impl->setMaxMessageSize(maxMessageSize); -} - -int DefaultMQProducer::getMaxMessageSize() const { - return impl->getMaxMessageSize(); -} - -void DefaultMQProducer::setRetryTimes4Async(int times) { - impl->setRetryTimes4Async(times); -} - -int DefaultMQProducer::getRetryTimes4Async() const { - return impl->getRetryTimes4Async(); -} - -void DefaultMQProducer::setLogLevel(elogLevel inputLevel) { - impl->setLogLevel(inputLevel); -} - -elogLevel DefaultMQProducer::getLogLevel() { - return impl->getLogLevel(); -} -void DefaultMQProducer::setLogFileSizeAndNum(int fileNum, long perFileSize) { - impl->setLogFileSizeAndNum(fileNum, perFileSize); -} - -void DefaultMQProducer::setTcpTransportPullThreadNum(int num) { - impl->setTcpTransportPullThreadNum(num); -} -int DefaultMQProducer::getTcpTransportPullThreadNum() const { - return impl->getTcpTransportPullThreadNum(); -} - -void DefaultMQProducer::setTcpTransportConnectTimeout(uint64_t timeout) { - impl->setTcpTransportConnectTimeout(timeout); -} -uint64_t DefaultMQProducer::getTcpTransportConnectTimeout() const { - return impl->getTcpTransportConnectTimeout(); -} -void DefaultMQProducer::setTcpTransportTryLockTimeout(uint64_t timeout) { - impl->setTcpTransportTryLockTimeout(timeout); -} -uint64_t DefaultMQProducer::getTcpTransportTryLockTimeout() const { - return impl->getTcpTransportTryLockTimeout(); -} - -void DefaultMQProducer::setUnitName(std::string unitName) { - impl->setUnitName(unitName); -} -const std::string& DefaultMQProducer::getUnitName() const { - return impl->getUnitName(); -} -void DefaultMQProducer::setMessageTrace(bool messageTrace) { - impl->setMessageTrace(messageTrace); -} -bool DefaultMQProducer::getMessageTrace() const { - return impl->getMessageTrace(); -} -SendResult DefaultMQProducer::send(MQMessage& msg, bool bSelectActiveBroker) { - return impl->send(msg, bSelectActiveBroker); -} - -SendResult DefaultMQProducer::send(MQMessage& msg, const MQMessageQueue& mq) { - return impl->send(msg, mq); -} - -SendResult DefaultMQProducer::send(MQMessage& msg, MessageQueueSelector* selector, void* arg) { - return impl->send(msg, selector, arg); -} - -SendResult DefaultMQProducer::send(MQMessage& msg, - MessageQueueSelector* selector, - void* arg, - int autoRetryTimes, - bool bActiveBroker) { - return impl->send(msg, selector, arg, autoRetryTimes, bActiveBroker); -} - -SendResult DefaultMQProducer::send(std::vector& msgs) { - return impl->send(msgs); -} - -SendResult DefaultMQProducer::send(std::vector& msgs, const MQMessageQueue& mq) { - return impl->send(msgs, mq); -} - -void DefaultMQProducer::send(MQMessage& msg, SendCallback* pSendCallback, bool bSelectActiveBroker) { - impl->send(msg, pSendCallback, bSelectActiveBroker); -} - -void DefaultMQProducer::send(MQMessage& msg, const MQMessageQueue& mq, SendCallback* pSendCallback) { - impl->send(msg, mq, pSendCallback); -} - -void DefaultMQProducer::send(MQMessage& msg, MessageQueueSelector* selector, void* arg, SendCallback* pSendCallback) { - impl->send(msg, selector, arg, pSendCallback); -} - -void DefaultMQProducer::sendOneway(MQMessage& msg, bool bSelectActiveBroker) { - impl->sendOneway(msg, bSelectActiveBroker); -} - -void DefaultMQProducer::sendOneway(MQMessage& msg, const MQMessageQueue& mq) { - impl->sendOneway(msg, mq); -} - -void DefaultMQProducer::sendOneway(MQMessage& msg, MessageQueueSelector* selector, void* arg) { - impl->sendOneway(msg, selector, arg); -} - -void DefaultMQProducer::setEnableSsl(bool enableSsl) { - impl->setEnableSsl(enableSsl); -} - -bool DefaultMQProducer::getEnableSsl() const { - return impl->getEnableSsl(); -} - -void DefaultMQProducer::setSslPropertyFile(const std::string& sslPropertyFile) { - impl->setSslPropertyFile(sslPropertyFile); -} - -const std::string& DefaultMQProducer::getSslPropertyFile() const { - return impl->getSslPropertyFile(); -} - -} // namespace rocketmq \ No newline at end of file diff --git a/src/producer/DefaultMQProducerImpl.cpp b/src/producer/DefaultMQProducerImpl.cpp deleted file mode 100644 index 20a9dc3a4..000000000 --- a/src/producer/DefaultMQProducerImpl.cpp +++ /dev/null @@ -1,777 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "DefaultMQProducerImpl.h" - -#include -#include - -#include "BatchMessage.h" -#include "CommandHeader.h" -#include "CommunicationMode.h" -#include "Logging.h" -#include "MQClientException.h" -#include "MQClientFactory.h" -#include "MQDecoder.h" -#include "MessageAccessor.h" -#include "NameSpaceUtil.h" -#include "SendMessageHookImpl.h" -#include "StringIdMaker.h" -#include "TopicPublishInfo.h" -#include "TraceConstant.h" -#include "Validators.h" - -namespace rocketmq { - -DefaultMQProducerImpl::DefaultMQProducerImpl(const string& groupname) - : m_sendMsgTimeout(3000), - m_compressMsgBodyOverHowmuch(4 * 1024), - m_maxMessageSize(1024 * 128), - // m_retryAnotherBrokerWhenNotStoreOK(false), - m_compressLevel(5), - m_retryTimes(5), - m_retryTimes4Async(1), - m_trace_ioService_work(m_trace_ioService) { - //registerProducer(this); - if (!registerOK) { - m_serviceState = CREATE_JUST; - THROW_MQEXCEPTION( - MQClientException, - "The producer group[" + getGroupName() + "] has been created before, specify another name please.", -1); - } - if (factoryStart) { - getFactory()->start(); - getFactory()->sendHeartbeatToAllBroker(); - } - m_serviceState = RUNNING; - break; - } - case RUNNING: - case START_FAILED: - case SHUTDOWN_ALREADY: - break; - default: - break; - } -} - -void DefaultMQProducerImpl::shutdown() { - shutdown(true); -} -void DefaultMQProducerImpl::shutdown(bool factoryStart) { - switch (m_serviceState) { - case RUNNING: { - LOG_INFO("DefaultMQProducerImpl shutdown"); - if (getMessageTrace()) { - LOG_INFO("DefaultMQProducerImpl message trace thread pool shutdown."); - m_trace_ioService.stop(); - m_trace_threadpool.join_all(); - } - getFactory()->unregisterProducer(this); - if (factoryStart) { - getFactory()->shutdown(); - } - m_serviceState = SHUTDOWN_ALREADY; - break; - } - case SHUTDOWN_ALREADY: - case CREATE_JUST: - break; - default: - break; - } -} - -SendResult DefaultMQProducerImpl::send(MQMessage& msg, bool bSelectActiveBroker) { - Validators::checkMessage(msg, getMaxMessageSize()); - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - try { - return sendDefaultImpl(msg, ComMode_SYNC, NULL, bSelectActiveBroker); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - throw e; - } - return SendResult(); -} - -void DefaultMQProducerImpl::send(MQMessage& msg, SendCallback* pSendCallback, bool bSelectActiveBroker) { - Validators::checkMessage(msg, getMaxMessageSize()); - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - try { - sendDefaultImpl(msg, ComMode_ASYNC, pSendCallback, bSelectActiveBroker); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - throw e; - } -} - -SendResult DefaultMQProducerImpl::send(std::vector& msgs) { - SendResult result; - try { - BatchMessage batchMessage = buildBatchMessage(msgs); - result = sendDefaultImpl(batchMessage, ComMode_SYNC, NULL); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - throw e; - } - return result; -} - -SendResult DefaultMQProducerImpl::send(std::vector& msgs, const MQMessageQueue& mq) { - SendResult result; - try { - BatchMessage batchMessage = buildBatchMessage(msgs); - result = sendKernelImpl(batchMessage, mq, ComMode_SYNC, NULL); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - throw e; - } - return result; -} - -BatchMessage DefaultMQProducerImpl::buildBatchMessage(std::vector& msgs) { - if (msgs.size() < 1) { - THROW_MQEXCEPTION(MQClientException, "msgs need one message at least", -1); - } - BatchMessage batchMessage; - bool firstFlag = true; - string topic; - bool waitStoreMsgOK = false; - for (auto& msg : msgs) { - Validators::checkMessage(msg, getMaxMessageSize()); - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - if (firstFlag) { - topic = msg.getTopic(); - waitStoreMsgOK = msg.isWaitStoreMsgOK(); - firstFlag = false; - - if (UtilAll::startsWith_retry(topic)) { - THROW_MQEXCEPTION(MQClientException, "Retry Group is not supported for batching", -1); - } - } else { - if (msg.getDelayTimeLevel() > 0) { - THROW_MQEXCEPTION(MQClientException, "TimeDelayLevel in not supported for batching", -1); - } - if (msg.getTopic() != topic) { - THROW_MQEXCEPTION(MQClientException, "msgs need one message at least", -1); - } - if (msg.isWaitStoreMsgOK() != waitStoreMsgOK) { - THROW_MQEXCEPTION(MQClientException, "msgs need one message at least", -2); - } - } - } - batchMessage.setBody(BatchMessage::encode(msgs)); - batchMessage.setTopic(topic); - batchMessage.setWaitStoreMsgOK(waitStoreMsgOK); - return batchMessage; -} - -SendResult DefaultMQProducerImpl::send(MQMessage& msg, const MQMessageQueue& mq) { - Validators::checkMessage(msg, getMaxMessageSize()); - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - if (msg.getTopic() != mq.getTopic()) { - LOG_WARN("message's topic not equal mq's topic"); - } - try { - return sendKernelImpl(msg, mq, ComMode_SYNC, NULL); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - throw e; - } - return SendResult(); -} - -void DefaultMQProducerImpl::send(MQMessage& msg, const MQMessageQueue& mq, SendCallback* pSendCallback) { - Validators::checkMessage(msg, getMaxMessageSize()); - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - if (msg.getTopic() != mq.getTopic()) { - LOG_WARN("message's topic not equal mq's topic"); - } - try { - sendKernelImpl(msg, mq, ComMode_ASYNC, pSendCallback); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - throw e; - } -} - -void DefaultMQProducerImpl::sendOneway(MQMessage& msg, bool bSelectActiveBroker) { - Validators::checkMessage(msg, getMaxMessageSize()); - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - try { - sendDefaultImpl(msg, ComMode_ONEWAY, NULL, bSelectActiveBroker); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - throw e; - } -} - -void DefaultMQProducerImpl::sendOneway(MQMessage& msg, const MQMessageQueue& mq) { - Validators::checkMessage(msg, getMaxMessageSize()); - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - if (msg.getTopic() != mq.getTopic()) { - LOG_WARN("message's topic not equal mq's topic"); - } - try { - sendKernelImpl(msg, mq, ComMode_ONEWAY, NULL); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - throw e; - } -} - -SendResult DefaultMQProducerImpl::send(MQMessage& msg, MessageQueueSelector* pSelector, void* arg) { - try { - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - return sendSelectImpl(msg, pSelector, arg, ComMode_SYNC, NULL); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - throw e; - } - return SendResult(); -} - -SendResult DefaultMQProducerImpl::send(MQMessage& msg, - MessageQueueSelector* pSelector, - void* arg, - int autoRetryTimes, - bool bActiveBroker) { - try { - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - return sendAutoRetrySelectImpl(msg, pSelector, arg, ComMode_SYNC, NULL, autoRetryTimes, bActiveBroker); - } catch (MQException& e) { - LOG_ERROR("%s", e.what()); - throw e; - } - return SendResult(); -} - -void DefaultMQProducerImpl::send(MQMessage& msg, - MessageQueueSelector* pSelector, - void* arg, - SendCallback* pSendCallback) { - try { - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - sendSelectImpl(msg, pSelector, arg, ComMode_ASYNC, pSendCallback); - } catch (MQException& e) { - LOG_ERROR(e.what()); - throw e; - } -} - -void DefaultMQProducerImpl::sendOneway(MQMessage& msg, MessageQueueSelector* pSelector, void* arg) { - try { - if (!NameSpaceUtil::hasNameSpace(msg.getTopic(), getNameSpace())) { - MessageAccessor::withNameSpace(msg, getNameSpace()); - } - sendSelectImpl(msg, pSelector, arg, ComMode_ONEWAY, NULL); - } catch (MQException& e) { - LOG_ERROR(e.what()); - throw e; - } -} - -int DefaultMQProducerImpl::getSendMsgTimeout() const { - return m_sendMsgTimeout; -} - -void DefaultMQProducerImpl::setSendMsgTimeout(int sendMsgTimeout) { - m_sendMsgTimeout = sendMsgTimeout; -} - -int DefaultMQProducerImpl::getCompressMsgBodyOverHowmuch() const { - return m_compressMsgBodyOverHowmuch; -} - -void DefaultMQProducerImpl::setCompressMsgBodyOverHowmuch(int compressMsgBodyOverHowmuch) { - m_compressMsgBodyOverHowmuch = compressMsgBodyOverHowmuch; -} - -int DefaultMQProducerImpl::getMaxMessageSize() const { - return m_maxMessageSize; -} - -void DefaultMQProducerImpl::setMaxMessageSize(int maxMessageSize) { - m_maxMessageSize = maxMessageSize; -} - -int DefaultMQProducerImpl::getCompressLevel() const { - return m_compressLevel; -} - -void DefaultMQProducerImpl::setCompressLevel(int compressLevel) { - assert((compressLevel >= 0 && compressLevel <= 9) || compressLevel == -1); - - m_compressLevel = compressLevel; -} - -// weak_topicPublishInfo( - getFactory()->tryToFindTopicPublishInfo(msg.getTopic(), getSessionCredentials())); - boost::shared_ptr topicPublishInfo(weak_topicPublishInfo.lock()); - if (topicPublishInfo) { - if (times == 1) { - mq_index = topicPublishInfo->getWhichQueue(); - } else { - mq_index++; - } - - SendResult sendResult; - MQMessageQueue mq; - if (bActiveMQ) - mq = topicPublishInfo->selectOneActiveMessageQueue(lastmq, mq_index); - else - mq = topicPublishInfo->selectOneMessageQueue(lastmq, mq_index); - - lastmq = mq; - if (mq.getQueueId() == -1) { - // THROW_MQEXCEPTION(MQClientException, "the MQMessageQueue is - // invalide", -1); - continue; - } - - try { - LOG_DEBUG("send to mq:%s", mq.toString().data()); - sendResult = sendKernelImpl(msg, mq, communicationMode, pSendCallback); - switch (communicationMode) { - case ComMode_ASYNC: - return sendResult; - case ComMode_ONEWAY: - return sendResult; - case ComMode_SYNC: - if (sendResult.getSendStatus() != SEND_OK) { - if (bActiveMQ) { - topicPublishInfo->updateNonServiceMessageQueue(mq, getSendMsgTimeout()); - } - continue; - } - return sendResult; - default: - break; - } - } catch (std::exception& e) { - send_failed = true; - failed_detail = e.what(); - LOG_ERROR("send failed of times:%d,brokerName:%s,details:%s", times, mq.getBrokerName().c_str(), e.what()); - if (bActiveMQ) { - topicPublishInfo->updateNonServiceMessageQueue(mq, getSendMsgTimeout()); - } - continue; - } catch (...) { - LOG_ERROR("Unknown error, send failed of times:%d, brokerName:%s", times, mq.getBrokerName().c_str()); - } - } // end of for - LOG_WARN("Retry many times, still failed"); - } - if (send_failed) { - THROW_MQEXCEPTION(MQClientException, failed_detail, -1); - } - string info = "No route info of this topic: " + msg.getTopic(); - THROW_MQEXCEPTION(MQClientException, info, -1); -} - -SendResult DefaultMQProducerImpl::sendKernelImpl(MQMessage& msg, - const MQMessageQueue& mq, - int communicationMode, - SendCallback* sendCallback) { - string brokerAddr = getFactory()->findBrokerAddressInPublish(mq.getBrokerName()); - - if (brokerAddr.empty()) { - getFactory()->tryToFindTopicPublishInfo(mq.getTopic(), getSessionCredentials()); - brokerAddr = getFactory()->findBrokerAddressInPublish(mq.getBrokerName()); - } - - if (!brokerAddr.empty()) { - boost::scoped_ptr pSendMesgContext(new SendMessageContext()); - try { - bool isBatchMsg = std::type_index(typeid(msg)) == std::type_index(typeid(BatchMessage)); - // msgId is produced by client, offsetMsgId produced by broker. (same with java sdk) - if (!isBatchMsg) { - string unique_id = StringIdMaker::getInstance().createUniqID(); - msg.setProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, unique_id); - - // batch does not support compressing right now, - tryToCompressMessage(msg); - } - - LOG_DEBUG("produce before:%s to %s", msg.toString().c_str(), mq.toString().c_str()); - if (!isMessageTraceTopic(msg.getTopic()) && getMessageTrace() && hasSendMessageHook()) { - pSendMesgContext.reset(new SendMessageContext); - pSendMesgContext->setDefaultMqProducer(this); - pSendMesgContext->setProducerGroup(NameSpaceUtil::withoutNameSpace(getGroupName(), getNameSpace())); - pSendMesgContext->setCommunicationMode(static_cast(communicationMode)); - pSendMesgContext->setBornHost(UtilAll::getLocalAddress()); - pSendMesgContext->setBrokerAddr(brokerAddr); - pSendMesgContext->setMessage(msg); - pSendMesgContext->setMessageQueue(mq); - pSendMesgContext->setMsgType(TRACE_NORMAL_MSG); - pSendMesgContext->setNameSpace(getNameSpace()); - string tranMsg = msg.getProperty(MQMessage::PROPERTY_TRANSACTION_PREPARED); - if (!tranMsg.empty() && tranMsg == "true") { - pSendMesgContext->setMsgType(TRACE_TRANS_HALF_MSG); - } - - if (msg.getProperty("__STARTDELIVERTIME") != "" || - msg.getProperty(MQMessage::PROPERTY_DELAY_TIME_LEVEL) != "") { - pSendMesgContext->setMsgType(TRACE_DELAY_MSG); - } - executeSendMessageHookBefore(pSendMesgContext.get()); - } - SendMessageRequestHeader requestHeader; - requestHeader.producerGroup = getGroupName(); - requestHeader.topic = (msg.getTopic()); - requestHeader.defaultTopic = DEFAULT_TOPIC; - requestHeader.defaultTopicQueueNums = 4; - requestHeader.queueId = (mq.getQueueId()); - requestHeader.sysFlag = (msg.getSysFlag()); - requestHeader.bornTimestamp = UtilAll::currentTimeMillis(); - requestHeader.flag = (msg.getFlag()); - requestHeader.consumeRetryTimes = 16; - requestHeader.batch = isBatchMsg; - requestHeader.properties = (MQDecoder::messageProperties2String(msg.getProperties())); - - SendResult sendResult = getFactory()->getMQClientAPIImpl()->sendMessage( - brokerAddr, mq.getBrokerName(), msg, requestHeader, getSendMsgTimeout(), getRetryTimes4Async(), - communicationMode, sendCallback, getSessionCredentials()); - if (!isMessageTraceTopic(msg.getTopic()) && getMessageTrace() && hasSendMessageHook() && sendCallback == NULL && - communicationMode == ComMode_SYNC) { - pSendMesgContext->setSendResult(sendResult); - executeSendMessageHookAfter(pSendMesgContext.get()); - } - return sendResult; - } catch (MQException& e) { - throw e; - } - } - THROW_MQEXCEPTION(MQClientException, "The broker[" + mq.getBrokerName() + "] not exist", -1); -} - -SendResult DefaultMQProducerImpl::sendSelectImpl(MQMessage& msg, - MessageQueueSelector* pSelector, - void* pArg, - int communicationMode, - SendCallback* sendCallback) { - Validators::checkMessage(msg, getMaxMessageSize()); - - boost::weak_ptr weak_topicPublishInfo( - getFactory()->tryToFindTopicPublishInfo(msg.getTopic(), getSessionCredentials())); - boost::shared_ptr topicPublishInfo(weak_topicPublishInfo.lock()); - if (topicPublishInfo) //&& topicPublishInfo->ok()) - { - MQMessageQueue mq = pSelector->select(topicPublishInfo->getMessageQueueList(), msg, pArg); - return sendKernelImpl(msg, mq, communicationMode, sendCallback); - } - THROW_MQEXCEPTION(MQClientException, "No route info for this topic", -1); -} - -SendResult DefaultMQProducerImpl::sendAutoRetrySelectImpl(MQMessage& msg, - MessageQueueSelector* pSelector, - void* pArg, - int communicationMode, - SendCallback* pSendCallback, - int autoRetryTimes, - bool bActiveMQ) { - Validators::checkMessage(msg, getMaxMessageSize()); - - MQMessageQueue lastmq; - MQMessageQueue mq; - int mq_index = 0; - bool send_failed = false; - string failed_detail; - for (int times = 1; times <= autoRetryTimes + 1; times++) { - boost::weak_ptr weak_topicPublishInfo( - getFactory()->tryToFindTopicPublishInfo(msg.getTopic(), getSessionCredentials())); - boost::shared_ptr topicPublishInfo(weak_topicPublishInfo.lock()); - if (topicPublishInfo) { - SendResult sendResult; - if (times == 1) { - // always send to selected MQ firstly, evenif bActiveMQ was setted to true - mq = pSelector->select(topicPublishInfo->getMessageQueueList(), msg, pArg); - lastmq = mq; - } else { - LOG_INFO("sendAutoRetrySelectImpl with times:%d", times); - std::vector mqs(topicPublishInfo->getMessageQueueList()); - for (size_t i = 0; i < mqs.size(); i++) { - if (mqs[i] == lastmq) - mq_index = i; - } - if (bActiveMQ) - mq = topicPublishInfo->selectOneActiveMessageQueue(lastmq, mq_index); - else - mq = topicPublishInfo->selectOneMessageQueue(lastmq, mq_index); - lastmq = mq; - if (mq.getQueueId() == -1) { - // THROW_MQEXCEPTION(MQClientException, "the MQMessageQueue is - // invalide", -1); - continue; - } - } - - try { - LOG_DEBUG("send to broker:%s", mq.toString().c_str()); - sendResult = sendKernelImpl(msg, mq, communicationMode, pSendCallback); - switch (communicationMode) { - case ComMode_ASYNC: - return sendResult; - case ComMode_ONEWAY: - return sendResult; - case ComMode_SYNC: - if (sendResult.getSendStatus() != SEND_OK) { - if (bActiveMQ) { - topicPublishInfo->updateNonServiceMessageQueue(mq, getSendMsgTimeout()); - } - continue; - } - return sendResult; - default: - break; - } - } catch (std::exception& e) { - send_failed = true; - failed_detail = e.what(); - LOG_ERROR("send failed of times:%d,mq:%s,details:%s", times, mq.toString().c_str(), e.what()); - if (bActiveMQ) { - topicPublishInfo->updateNonServiceMessageQueue(mq, getSendMsgTimeout()); - } - continue; - } catch (...) { - LOG_ERROR("An unknown exception occurred,send failed of times:%d,mq:%s", times, mq.toString().c_str()); - } - } // end of for - LOG_WARN("Retry many times, still failed"); - if (send_failed) { - THROW_MQEXCEPTION(MQClientException, failed_detail, -1); - } - } - THROW_MQEXCEPTION(MQClientException, "No route info of this topic, ", -1); -} - -bool DefaultMQProducerImpl::tryToCompressMessage(MQMessage& msg) { - int sysFlag = msg.getSysFlag(); - if ((sysFlag & MessageSysFlag::CompressedFlag) == MessageSysFlag::CompressedFlag) { - return true; - } - - string body = msg.getBody(); - if ((int)body.length() >= getCompressMsgBodyOverHowmuch()) { - string outBody; - if (UtilAll::deflate(body, outBody, getCompressLevel())) { - msg.setBody(outBody); - msg.setSysFlag(sysFlag | MessageSysFlag::CompressedFlag); - return true; - } - } - - return false; -} - -int DefaultMQProducerImpl::getRetryTimes() const { - return m_retryTimes; -} - -void DefaultMQProducerImpl::setRetryTimes(int times) { - if (times <= 0) { - LOG_WARN("set retry times illegal, use default value:5"); - return; - } - - if (times > 15) { - LOG_WARN("set retry times illegal, use max value:15"); - m_retryTimes = 15; - return; - } - LOG_WARN("set retry times to:%d", times); - m_retryTimes = times; -} - -int DefaultMQProducerImpl::getRetryTimes4Async() const { - return m_retryTimes4Async; -} - -void DefaultMQProducerImpl::setRetryTimes4Async(int times) { - if (times <= 0) { - LOG_WARN("set retry times illegal, use default value:1"); - m_retryTimes4Async = 1; - return; - } - - if (times > 15) { - LOG_WARN("set retry times illegal, use max value:15"); - m_retryTimes4Async = 15; - return; - } - LOG_INFO("set retry times to:%d", times); - m_retryTimes4Async = times; -} - -// we should deal with name space before producer start. -bool DefaultMQProducerImpl::dealWithNameSpace() { - string ns = getNameSpace(); - if (ns.empty()) { - string nsAddr = getNamesrvAddr(); - if (!NameSpaceUtil::checkNameSpaceExistInNameServer(nsAddr)) { - return true; - } - ns = NameSpaceUtil::getNameSpaceFromNsURL(nsAddr); - // reset namespace - setNameSpace(ns); - } - // reset group name - if (!NameSpaceUtil::hasNameSpace(getGroupName(), ns)) { - string fullGID = NameSpaceUtil::withNameSpace(getGroupName(), ns); - setGroupName(fullGID); - } - return true; -} - -void DefaultMQProducerImpl::logConfigs() { - showClientConfigs(); - - LOG_WARN("SendMsgTimeout:%d ms", m_sendMsgTimeout); - LOG_WARN("CompressMsgBodyOverHowmuch:%d", m_compressMsgBodyOverHowmuch); - LOG_WARN("MaxMessageSize:%d", m_maxMessageSize); - LOG_WARN("CompressLevel:%d", m_compressLevel); - LOG_WARN("RetryTimes:%d", m_retryTimes); - LOG_WARN("RetryTimes4Async:%d", m_retryTimes4Async); -} - -// we should create trace message poll before producer send messages. -bool DefaultMQProducerImpl::dealWithMessageTrace() { - if (!getMessageTrace()) { - LOG_INFO("Message Trace set to false, Will not send trace messages."); - return false; - } - size_t threadpool_size = boost::thread::hardware_concurrency(); - LOG_INFO("Create send message trace threadpool: %d", threadpool_size); - for (size_t i = 0; i < threadpool_size; ++i) { - m_trace_threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &m_trace_ioService)); - } - LOG_INFO("DefaultMQProducer Open meassage trace.."); - std::shared_ptr hook(new SendMessageHookImpl); - registerSendMessageHook(hook); - return true; -} -bool DefaultMQProducerImpl::isMessageTraceTopic(const string& source) { - return source.find(TraceConstant::TRACE_TOPIC) != string::npos; -} -bool DefaultMQProducerImpl::hasSendMessageHook() { - return !m_sendMessageHookList.empty(); -} - -void DefaultMQProducerImpl::registerSendMessageHook(std::shared_ptr& hook) { - m_sendMessageHookList.push_back(hook); - LOG_INFO("Register sendMessageHook success,hookname is %s", hook->getHookName().c_str()); -} - -void DefaultMQProducerImpl::executeSendMessageHookBefore(SendMessageContext* context) { - if (!m_sendMessageHookList.empty()) { - std::vector >::iterator it = m_sendMessageHookList.begin(); - for (; it != m_sendMessageHookList.end(); ++it) { - try { - (*it)->executeHookBefore(context); - } catch (exception e) { - } - } - } -} - -void DefaultMQProducerImpl::executeSendMessageHookAfter(SendMessageContext* context) { - if (!m_sendMessageHookList.empty()) { - std::vector >::iterator it = m_sendMessageHookList.begin(); - for (; it != m_sendMessageHookList.end(); ++it) { - try { - (*it)->executeHookAfter(context); - } catch (exception e) { - } - } - } -} - -void DefaultMQProducerImpl::submitSendTraceRequest(const MQMessage& msg, SendCallback* pSendCallback) { - m_trace_ioService.post(boost::bind(&DefaultMQProducerImpl::sendTraceMessage, this, msg, pSendCallback)); -} - -void DefaultMQProducerImpl::sendTraceMessage(MQMessage& msg, SendCallback* pSendCallback) { - try { - LOG_DEBUG("=====Send Trace Messages,Topic[%s],Key[%s],Body[%s]", msg.getTopic().c_str(), msg.getKeys().c_str(), - msg.getBody().c_str()); - send(msg, pSendCallback, true); - } catch (MQException e) { - LOG_ERROR(e.what()); - // throw e; - } -} -} // namespace rocketmq diff --git a/src/producer/DefaultMQProducerImpl.h b/src/producer/DefaultMQProducerImpl.h deleted file mode 100644 index d2eb1879d..000000000 --- a/src/producer/DefaultMQProducerImpl.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEFAULTMQPRODUCERIMPL_H__ -#define __DEFAULTMQPRODUCERIMPL_H__ - -#include "BatchMessage.h" -#include "MQMessageQueue.h" -#include "MQProducer.h" -#include "RocketMQClient.h" -#include "SendMessageContext.h" -#include "SendMessageHook.h" -#include "SendResult.h" - -namespace rocketmq { -//& msgs); - virtual SendResult send(std::vector& msgs, const MQMessageQueue& mq); - virtual void send(MQMessage& msg, SendCallback* pSendCallback, bool bSelectActiveBroker = false); - virtual void send(MQMessage& msg, const MQMessageQueue& mq, SendCallback* pSendCallback); - virtual void send(MQMessage& msg, MessageQueueSelector* selector, void* arg, SendCallback* pSendCallback); - virtual void sendOneway(MQMessage& msg, bool bSelectActiveBroker = false); - virtual void sendOneway(MQMessage& msg, const MQMessageQueue& mq); - virtual void sendOneway(MQMessage& msg, MessageQueueSelector* selector, void* arg); - //& msgs); - bool dealWithNameSpace(); - void logConfigs(); - bool dealWithMessageTrace(); - bool isMessageTraceTopic(const std::string& topic); - bool hasSendMessageHook(); - void registerSendMessageHook(std::shared_ptr& hook); - void executeSendMessageHookBefore(SendMessageContext* context); - void executeSendMessageHookAfter(SendMessageContext* context); - - void sendTraceMessage(MQMessage& msg, SendCallback* pSendCallback); - - private: - int m_sendMsgTimeout; - int m_compressMsgBodyOverHowmuch; - int m_maxMessageSize; // > m_sendMessageHookList; - boost::asio::io_service m_trace_ioService; - boost::thread_group m_trace_threadpool; - boost::asio::io_service::work m_trace_ioService_work; -}; -// -#include -#include "DefaultMQProducerImpl.h" -#include "Logging.h" -#include "MQClientException.h" -#include "SendMessageContext.h" -#include "TraceConstant.h" -#include "TraceTransferBean.h" -#include "TraceUtil.h" -#include "UtilAll.h" - -using namespace std; -namespace rocketmq { - -class TraceMessageSendCallback : public SendCallback { - virtual void onSuccess(SendResult& sendResult) { - LOG_DEBUG("TraceMessageSendCallback, MsgId:[%s],OffsetMsgId[%s]", sendResult.getMsgId().c_str(), - sendResult.getOffsetMsgId().c_str()); - } - virtual void onException(MQException& e) {} -}; -static TraceMessageSendCallback* callback = new TraceMessageSendCallback(); -std::string SendMessageHookImpl::getHookName() { - return "RocketMQSendMessageHookImpl"; -} - -void SendMessageHookImpl::executeHookBefore(SendMessageContext* context) { - if (context != NULL) { - string topic = context->getMessage()->getTopic(); - // Check if contains TraceConstants::TRACE_TOPIC - if (topic.find(TraceConstant::TRACE_TOPIC) != string::npos) { - // trace message itself - return; - } - TraceContext* traceContext = new TraceContext(); - context->setTraceContext(traceContext); - } - return; -} - -void SendMessageHookImpl::executeHookAfter(SendMessageContext* context) { - if (context == NULL || context->getSendResult() == NULL) { - return; - } - string topic = context->getMessage()->getTopic(); - // Check if contains TraceConstants::TRACE_TOPIC - if (topic.find(TraceConstant::TRACE_TOPIC) != string::npos - || context->getSendResult()->getTraceOn() == false) { - // trace message itself - return; - } - std::shared_ptr traceContext; - traceContext.reset(context->getTraceContext()); - - // OnsTraceContext* onsContext = context->getMqTraceContext(); - traceContext->setTraceType(Pub); - traceContext->setGroupName(context->getProducerGroup()); - // boost::scoped_ptr traceBean(new OnsTraceBean()); - TraceBean traceBean; - traceBean.setTopic(context->getMessage()->getTopic()); - traceBean.setTags(context->getMessage()->getTags()); - traceBean.setKeys(context->getMessage()->getKeys()); - traceBean.setStoreHost(context->getBrokerAddr()); - traceBean.setBodyLength(context->getMessage()->getBody().size()); - traceBean.setMsgType(context->getMsgType()); - - int costTime = static_cast(UtilAll::currentTimeMillis() - traceContext->getTimeStamp()); - traceContext->setCostTime(costTime); - if (context->getSendResult()->getSendStatus() == SEND_OK) { - traceContext->setStatus(true); - } else { - traceContext->setStatus(false); - } - - traceContext->setRegionId(context->getSendResult()->getRegionId()); - traceBean.setMsgId(context->getMessage()->getProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX)); - traceBean.setOffsetMsgId(context->getSendResult()->getOffsetMsgId()); - traceBean.setStoreTime(traceContext->getTimeStamp() + (costTime / 2)); - - traceContext->setTraceBean(traceBean); - - topic = TraceConstant::TRACE_TOPIC + traceContext->getRegionId(); - TraceTransferBean ben = TraceUtil::CovertTraceContextToTransferBean(traceContext.get()); - // encode data - MQMessage message(topic, ben.getTransData()); - message.setKeys(ben.getTransKey()); - // send trace message. - context->getDefaultMqProducer()->submitSendTraceRequest(message, callback); - return; -} -} // namespace rocketmq diff --git a/src/producer/SendMessageHookImpl.h b/src/producer/SendMessageHookImpl.h deleted file mode 100644 index 54a2ebcfd..000000000 --- a/src/producer/SendMessageHookImpl.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __ROCKETMQ_SEND_MESSAGE_RPC_HOOK_IMPL_H__ -#define __ROCKETMQ_SEND_MESSAGE_RPC_HOOK_IMPL_H__ - -#include -#include "SendMessageContext.h" -#include "SendMessageHook.h" -namespace rocketmq { -class SendMessageHookImpl : public SendMessageHook { - public: - virtual ~SendMessageHookImpl() {} - virtual std::string getHookName(); - virtual void executeHookBefore(SendMessageContext* context); - virtual void executeHookAfter(SendMessageContext* context); -}; -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/src/producer/SendResult.cpp b/src/producer/SendResult.cpp deleted file mode 100644 index f6926d500..000000000 --- a/src/producer/SendResult.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "SendResult.h" -#include -#include "UtilAll.h" -#include "VirtualEnvUtil.h" - -namespace rocketmq { -// -#include -#include -#include - -#include "ByteOrder.h" -#include "UtilAll.h" - -namespace rocketmq { - -const char StringIdMaker::sHexAlphabet[16] = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - -StringIdMaker::StringIdMaker() { - std::srand((uint32_t)std::time(NULL)); - - uint32_t pid = ByteOrder::swapIfLittleEndian(static_cast(getpid())); - uint32_t ip = ByteOrder::swapIfLittleEndian(getIP()); - uint32_t random_num = ByteOrder::swapIfLittleEndian(static_cast(std::rand())); - - unsigned char bin_buf[10]; - std::memcpy(bin_buf + 2, &pid, 4); - std::memcpy(bin_buf, &ip, 4); - std::memcpy(bin_buf + 6, &random_num, 4); - - hexdump(bin_buf, kFixString, 10); - kFixString[20] = '\0'; - - setStartTime(UtilAll::currentTimeMillis()); - - mCounter = 0; -} - -StringIdMaker::~StringIdMaker() {} - -uint32_t StringIdMaker::getIP() { - std::string ip = UtilAll::getLocalAddress(); - if (ip.empty()) { - return 0; - } - - char* ip_str = new char[ip.length() + 1]; - std::strncpy(ip_str, ip.c_str(), ip.length()); - ip_str[ip.length()] = '\0'; - - int i = 3; - uint32_t nResult = 0; - for (char* token = std::strtok(ip_str, "."); token != nullptr && i >= 0; token = std::strtok(nullptr, ".")) { - uint32_t n = std::atoi(token); - nResult |= n << (8 * i--); - } - - delete[] ip_str; - - return nResult; -} - -void StringIdMaker::setStartTime(uint64_t millis) { - // std::time_t - // Although not defined, this is almost always an integral value holding the number of seconds - // (not counting leap seconds) since 00:00, Jan 1 1970 UTC, corresponding to POSIX time. - std::time_t tmNow = millis / 1000; - std::tm* ptmNow = std::localtime(&tmNow); // may not be thread-safe - - std::tm curMonthBegin = {0}; - curMonthBegin.tm_year = ptmNow->tm_year; // since 1900 - curMonthBegin.tm_mon = ptmNow->tm_mon; // [0, 11] - curMonthBegin.tm_mday = 1; // [1, 31] - curMonthBegin.tm_hour = 0; // [0, 23] - curMonthBegin.tm_min = 0; // [0, 59] - curMonthBegin.tm_sec = 0; // [0, 60] - - std::tm nextMonthBegin = {0}; - if (ptmNow->tm_mon >= 11) { - nextMonthBegin.tm_year = ptmNow->tm_year + 1; - nextMonthBegin.tm_mon = 0; - } else { - nextMonthBegin.tm_year = ptmNow->tm_year; - nextMonthBegin.tm_mon = ptmNow->tm_mon + 1; - } - nextMonthBegin.tm_mday = 1; - nextMonthBegin.tm_hour = 0; - nextMonthBegin.tm_min = 0; - nextMonthBegin.tm_sec = 0; - - mStartTime = std::mktime(&curMonthBegin) * 1000; - mNextStartTime = std::mktime(&nextMonthBegin) * 1000; -} - -std::string StringIdMaker::createUniqID() { - uint64_t current = UtilAll::currentTimeMillis(); - if (current >= mNextStartTime) { - setStartTime(current); - current = UtilAll::currentTimeMillis(); - } - - uint32_t period = ByteOrder::swapIfLittleEndian(static_cast(current - mStartTime)); - uint16_t seqid = ByteOrder::swapIfLittleEndian(mCounter++); - - unsigned char bin_buf[6]; - std::memcpy(bin_buf, &period, 4); - std::memcpy(bin_buf + 4, &seqid, 2); - - char hex_buf[12]; - hexdump(bin_buf, hex_buf, 6); - - return std::string(kFixString, 20) + std::string(hex_buf, 12); -} - -void StringIdMaker::hexdump(unsigned char* buffer, char* out_buff, std::size_t index) { - for (std::size_t i = 0; i < index; i++) { - unsigned char v = buffer[i]; - out_buff[i * 2] = sHexAlphabet[v >> 4]; - out_buff[i * 2 + 1] = sHexAlphabet[v & 0x0FU]; - } -} - -} // namespace rocketmq diff --git a/src/producer/StringIdMaker.h b/src/producer/StringIdMaker.h deleted file mode 100644 index 0433b41d5..000000000 --- a/src/producer/StringIdMaker.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __STRINGID_MAKER_H__ -#define __STRINGID_MAKER_H__ - -#include -#include -#include -#include - -namespace rocketmq { - -class StringIdMaker { - private: - StringIdMaker(); - ~StringIdMaker(); - - public: - static StringIdMaker& getInstance() { - // After c++11, the initialization occurs exactly once - static StringIdMaker singleton_; - return singleton_; - } - - /* ID format: - * ip: 4 bytes - * pid: 2 bytes - * random: 4 bytes - * time: 4 bytes - * auto num: 2 bytes - */ - std::string createUniqID(); - - private: - void setStartTime(uint64_t millis); - - static uint32_t getIP(); - static void hexdump(unsigned char* buffer, char* out_buff, std::size_t index); - - private: - uint64_t mStartTime; - uint64_t mNextStartTime; - std::atomic mCounter; - - char kFixString[21]; - - static const char sHexAlphabet[16]; -}; - -} // namespace rocketmq -#endif diff --git a/src/producer/TopicPublishInfo.h b/src/producer/TopicPublishInfo.h deleted file mode 100644 index c57253508..000000000 --- a/src/producer/TopicPublishInfo.h +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __TOPICPUBLISHINFO_H__ -#define __TOPICPUBLISHINFO_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "Logging.h" -#include "MQMessageQueue.h" -#include "UtilAll.h" - -namespace rocketmq { -//interrupt(); - m_async_service_thread->join(); - - m_nonSerivceQueues.clear(); - m_onSerivceQueues.clear(); - m_brokerTimerMap.clear(); - m_queues.clear(); - } - - bool ok() { - boost::lock_guard lock(m_queuelock); - return !m_queues.empty(); - } - - void updateMessageQueueList(const MQMessageQueue& mq) { - boost::lock_guard lock(m_queuelock); - m_queues.push_back(mq); - string key = mq.getBrokerName() + UtilAll::to_string(mq.getQueueId()); - m_onSerivceQueues[key] = mq; - if (m_nonSerivceQueues.find(key) != m_nonSerivceQueues.end()) { - m_nonSerivceQueues.erase(key); // if topicPublishInfo changed, erase this - // mq from m_nonSerivceQueues to avoid 2 - // copies both in m_onSerivceQueues and - // m_nonSerivceQueues - } - } - - void op_resumeNonServiceMessageQueueList(boost::system::error_code& ec, boost::asio::deadline_timer* t) { - resumeNonServiceMessageQueueList(); - boost::system::error_code e; - t->expires_from_now(t->expires_from_now() + boost::posix_time::seconds(60), e); - t->async_wait(boost::bind(&TopicPublishInfo::op_resumeNonServiceMessageQueueList, this, e, t)); - } - - void resumeNonServiceMessageQueueList() { - boost::lock_guard lock(m_queuelock); - for (map::iterator it = m_brokerTimerMap.begin(); it != m_brokerTimerMap.end(); ++it) { - if (UtilAll::currentTimeMillis() - it->second >= 1000 * 60 * 5) { - string key = it->first.getBrokerName() + UtilAll::to_string(it->first.getQueueId()); - if (m_nonSerivceQueues.find(key) != m_nonSerivceQueues.end()) { - m_nonSerivceQueues.erase(key); - } - m_onSerivceQueues[key] = it->first; - } - } - } - - void updateNonServiceMessageQueue(const MQMessageQueue& mq, int timeoutMilliseconds) { - boost::lock_guard lock(m_queuelock); - - string key = mq.getBrokerName() + UtilAll::to_string(mq.getQueueId()); - if (m_nonSerivceQueues.find(key) != m_nonSerivceQueues.end()) { - return; - } - LOG_INFO("updateNonServiceMessageQueue of mq:%s", mq.toString().c_str()); - m_brokerTimerMap[mq] = UtilAll::currentTimeMillis(); - m_nonSerivceQueues[key] = mq; - if (m_onSerivceQueues.find(key) != m_onSerivceQueues.end()) { - m_onSerivceQueues.erase(key); - } - } - - vector& getMessageQueueList() { - boost::lock_guard lock(m_queuelock); - return m_queues; - } - - int getWhichQueue() { return m_sendWhichQueue.load(boost::memory_order_acquire); } - - MQMessageQueue selectOneMessageQueue(const MQMessageQueue& lastmq, int& mq_index) { - boost::lock_guard lock(m_queuelock); - - if (m_queues.size() > 0) { - LOG_DEBUG("selectOneMessageQueue Enter, queue size:" SIZET_FMT "", m_queues.size()); - unsigned int pos = 0; - if (mq_index >= 0) { - pos = mq_index % m_queues.size(); - } else { - LOG_ERROR("mq_index is negative"); - return MQMessageQueue(); - } - if (!lastmq.getBrokerName().empty()) { - for (size_t i = 0; i < m_queues.size(); i++) { - if (m_sendWhichQueue.load(boost::memory_order_acquire) == (numeric_limits::max)()) { - m_sendWhichQueue.store(0, boost::memory_order_release); - } - - if (pos >= m_queues.size()) - pos = pos % m_queues.size(); - - ++m_sendWhichQueue; - MQMessageQueue mq = m_queues.at(pos); - LOG_DEBUG("lastmq broker not empty, m_sendWhichQueue:%d, pos:%d", - m_sendWhichQueue.load(boost::memory_order_acquire), pos); - if (mq.getBrokerName().compare(lastmq.getBrokerName()) != 0) { - mq_index = pos; - return mq; - } - ++pos; - } - LOG_ERROR("could not find property mq"); - return MQMessageQueue(); - } else { - if (m_sendWhichQueue.load(boost::memory_order_acquire) == (numeric_limits::max)()) { - m_sendWhichQueue.store(0, boost::memory_order_release); - } - - ++m_sendWhichQueue; - LOG_DEBUG("lastmq broker empty, m_sendWhichQueue:%d, pos:%d", - m_sendWhichQueue.load(boost::memory_order_acquire), pos); - mq_index = pos; - return m_queues.at(pos); - } - } else { - LOG_ERROR("m_queues empty"); - return MQMessageQueue(); - } - } - - MQMessageQueue selectOneActiveMessageQueue(const MQMessageQueue& lastmq, int& mq_index) { - boost::lock_guard lock(m_queuelock); - - if (m_queues.size() > 0) { - unsigned int pos = 0; - if (mq_index >= 0) { - pos = mq_index % m_queues.size(); - } else { - LOG_ERROR("mq_index is negative"); - return MQMessageQueue(); - } - if (!lastmq.getBrokerName().empty()) { - for (size_t i = 0; i < m_queues.size(); i++) { - if (m_sendWhichQueue.load(boost::memory_order_acquire) == (numeric_limits::max)()) { - m_sendWhichQueue.store(0, boost::memory_order_release); - } - - if (pos >= m_queues.size()) - pos = pos % m_queues.size(); - - ++m_sendWhichQueue; - MQMessageQueue mq = m_queues.at(pos); - string key = mq.getBrokerName() + UtilAll::to_string(mq.getQueueId()); - if ((mq.getBrokerName().compare(lastmq.getBrokerName()) != 0) && - (m_onSerivceQueues.find(key) != m_onSerivceQueues.end())) { - mq_index = pos; - return mq; - } - ++pos; - } - - for (MQMAP::iterator it = m_nonSerivceQueues.begin(); it != m_nonSerivceQueues.end(); - ++it) { // if no MQMessageQueue(except lastmq) in - // m_onSerivceQueues, search m_nonSerivceQueues - if (it->second.getBrokerName().compare(lastmq.getBrokerName()) != 0) - return it->second; - } - LOG_ERROR("can not find property mq"); - return MQMessageQueue(); - } else { - for (size_t i = 0; i < m_queues.size(); i++) { - if (m_sendWhichQueue.load(boost::memory_order_acquire) == (numeric_limits::max)()) { - m_sendWhichQueue.store(0, boost::memory_order_release); - } - if (pos >= m_queues.size()) - pos = pos % m_queues.size(); - - ++m_sendWhichQueue; - LOG_DEBUG("lastmq broker empty, m_sendWhichQueue:%d, pos:%d", - m_sendWhichQueue.load(boost::memory_order_acquire), pos); - mq_index = pos; - MQMessageQueue mq = m_queues.at(pos); - string key = mq.getBrokerName() + UtilAll::to_string(mq.getQueueId()); - if (m_onSerivceQueues.find(key) != m_onSerivceQueues.end()) { - return mq; - } else { - ++pos; - } - } - - for (MQMAP::iterator it = m_nonSerivceQueues.begin(); it != m_nonSerivceQueues.end(); - ++it) { // if no MQMessageQueue(except lastmq) in - // m_onSerivceQueues, search m_nonSerivceQueues - if (it->second.getBrokerName().compare(lastmq.getBrokerName()) != 0) - return it->second; - } - LOG_ERROR("can not find property mq"); - return MQMessageQueue(); - } - } else { - LOG_ERROR("m_queues empty"); - return MQMessageQueue(); - } - } - - private: - boost::mutex m_queuelock; - typedef vector QueuesVec; - QueuesVec m_queues; - typedef map MQMAP; - MQMAP m_onSerivceQueues; - MQMAP m_nonSerivceQueues; - boost::atomic m_sendWhichQueue; - map m_brokerTimerMap; - boost::asio::io_service m_async_ioService; - boost::scoped_ptr m_async_service_thread; -}; - -// - -#include "TransactionMQProducerImpl.h" - -namespace rocketmq { - -TransactionMQProducer::TransactionMQProducer(const std::string& groupName) { - impl = new TransactionMQProducerImpl(groupName); -} - -TransactionMQProducer::~TransactionMQProducer() { - delete impl; -} -void TransactionMQProducer::start() { - impl->start(); -} - -void TransactionMQProducer::shutdown() { - impl->shutdown(); -} -std::string TransactionMQProducer::version() { - std::string versions = impl->getClientVersionString(); - /* - versions.append(", PROTOCOL VERSION: ") - .append(MQVersion::GetVersionDesc(MQVersion::s_CurrentVersion)) - .append(", LANGUAGE: ") - .append(MQVersion::s_CurrentLanguage); - */ - return versions; -} -// start mqclient set -const std::string& TransactionMQProducer::getNamesrvAddr() const { - return impl->getNamesrvAddr(); -} - -void TransactionMQProducer::setNamesrvAddr(const std::string& namesrvAddr) { - impl->setNamesrvAddr(namesrvAddr); -} - -const std::string& TransactionMQProducer::getNamesrvDomain() const { - return impl->getNamesrvDomain(); -} - -void TransactionMQProducer::setNamesrvDomain(const std::string& namesrvDomain) { - impl->setNamesrvDomain(namesrvDomain); -} -void TransactionMQProducer::setSessionCredentials(const std::string& accessKey, - const std::string& secretKey, - const std::string& accessChannel) { - impl->setSessionCredentials(accessKey, secretKey, accessChannel); -} - -const SessionCredentials& TransactionMQProducer::getSessionCredentials() const { - return impl->getSessionCredentials(); -} -const std::string& TransactionMQProducer::getInstanceName() const { - return impl->getInstanceName(); -} - -void TransactionMQProducer::setInstanceName(const std::string& instanceName) { - impl->setInstanceName(instanceName); -} - -const std::string& TransactionMQProducer::getNameSpace() const { - return impl->getNameSpace(); -} - -void TransactionMQProducer::setNameSpace(const std::string& nameSpace) { - impl->setNameSpace(nameSpace); -} -const std::string& TransactionMQProducer::getGroupName() const { - return impl->getGroupName(); -} - -void TransactionMQProducer::setGroupName(const std::string& groupName) { - impl->setGroupName(groupName); -} -void TransactionMQProducer::setSendMsgTimeout(int sendMsgTimeout) { - impl->setSendMsgTimeout(sendMsgTimeout); -} - -int TransactionMQProducer::getSendMsgTimeout() const { - return impl->getSendMsgTimeout(); -} -void TransactionMQProducer::setTcpTransportPullThreadNum(int num) { - impl->setTcpTransportPullThreadNum(num); -} -int TransactionMQProducer::getTcpTransportPullThreadNum() const { - return impl->getTcpTransportPullThreadNum(); -} - -void TransactionMQProducer::setTcpTransportConnectTimeout(uint64_t timeout) { - impl->setTcpTransportConnectTimeout(timeout); -} -uint64_t TransactionMQProducer::getTcpTransportConnectTimeout() const { - return impl->getTcpTransportConnectTimeout(); -} -void TransactionMQProducer::setTcpTransportTryLockTimeout(uint64_t timeout) { - impl->setTcpTransportTryLockTimeout(timeout); -} -uint64_t TransactionMQProducer::getTcpTransportTryLockTimeout() const { - return impl->getTcpTransportTryLockTimeout(); -} -int TransactionMQProducer::getCompressMsgBodyOverHowmuch() const { - return impl->getCompressMsgBodyOverHowmuch(); -} - -void TransactionMQProducer::setCompressMsgBodyOverHowmuch(int compressMsgBodyThreshold) { - impl->setCompressMsgBodyOverHowmuch(compressMsgBodyThreshold); -} - -int TransactionMQProducer::getCompressLevel() const { - return impl->getCompressLevel(); -} - -void TransactionMQProducer::setCompressLevel(int compressLevel) { - impl->setCompressLevel(compressLevel); -} - -void TransactionMQProducer::setMaxMessageSize(int maxMessageSize) { - impl->setMaxMessageSize(maxMessageSize); -} - -int TransactionMQProducer::getMaxMessageSize() const { - return impl->getMaxMessageSize(); -} - -void TransactionMQProducer::setLogLevel(elogLevel inputLevel) { - impl->setLogLevel(inputLevel); -} - -elogLevel TransactionMQProducer::getLogLevel() { - return impl->getLogLevel(); -} -void TransactionMQProducer::setLogFileSizeAndNum(int fileNum, long perFileSize) { - impl->setLogFileSizeAndNum(fileNum, perFileSize); -} - -void TransactionMQProducer::setUnitName(std::string unitName) { - impl->setUnitName(unitName); -} -const std::string& TransactionMQProducer::getUnitName() const { - return impl->getUnitName(); -} -void TransactionMQProducer::setMessageTrace(bool messageTrace) { - impl->setMessageTrace(messageTrace); -} -bool TransactionMQProducer::getMessageTrace() const { - return impl->getMessageTrace(); -} -void TransactionMQProducer::setEnableSsl(bool enableSsl) { - impl->setEnableSsl(enableSsl); -} -bool TransactionMQProducer::getEnableSsl() const { - return impl->getEnableSsl(); -} -void TransactionMQProducer::setSslPropertyFile(const std::string& sslPropertyFile) { - impl->setSslPropertyFile(sslPropertyFile); -} -const std::string& TransactionMQProducer::getSslPropertyFile() const { - return impl->getSslPropertyFile(); -} -std::shared_ptr TransactionMQProducer::getTransactionListener() { - return impl->getTransactionListener(); -} -void TransactionMQProducer::setTransactionListener(TransactionListener* listener) { - impl->setTransactionListener(listener); -} -TransactionSendResult TransactionMQProducer::sendMessageInTransaction(MQMessage& msg, void* arg) { - return impl->sendMessageInTransaction(msg, arg); -} -void TransactionMQProducer::checkTransactionState(const std::string& addr, - const MQMessageExt& message, - long tranStateTableOffset, - long commitLogOffset, - const std::string& msgId, - const std::string& transactionId, - const std::string& offsetMsgId) { - impl->checkTransactionState(addr, message, tranStateTableOffset, commitLogOffset, msgId, transactionId, offsetMsgId); -} -} // namespace rocketmq \ No newline at end of file diff --git a/src/producer/TransactionMQProducerImpl.cpp b/src/producer/TransactionMQProducerImpl.cpp deleted file mode 100644 index 849dd55b6..000000000 --- a/src/producer/TransactionMQProducerImpl.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "TransactionMQProducerImpl.h" -#include -#include "CommandHeader.h" -#include "Logging.h" -#include "MQClientFactory.h" -#include "MQDecoder.h" -#include "MessageSysFlag.h" -#include "TransactionListener.h" -#include "TransactionSendResult.h" - -using namespace std; -namespace rocketmq { - -void TransactionMQProducerImpl::initTransactionEnv() { - for (int i = 0; i < m_thread_num; ++i) { - m_threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &m_ioService)); - } -} - -void TransactionMQProducerImpl::destroyTransactionEnv() { - m_ioService.stop(); - m_threadpool.join_all(); -} - -TransactionSendResult TransactionMQProducerImpl::sendMessageInTransaction(MQMessage& msg, void* arg) { - if (!m_transactionListener) { - THROW_MQEXCEPTION(MQClientException, "transactionListener is null", -1); - } - - SendResult sendResult; - msg.setProperty(MQMessage::PROPERTY_TRANSACTION_PREPARED, "true"); - msg.setProperty(MQMessage::PROPERTY_PRODUCER_GROUP, getGroupName()); - try { - sendResult = send(msg); - } catch (MQException& e) { - THROW_MQEXCEPTION(MQClientException, e.what(), -1); - } - - LocalTransactionState localTransactionState = LocalTransactionState::UNKNOWN; - switch (sendResult.getSendStatus()) { - case SendStatus::SEND_OK: - try { - if (sendResult.getTransactionId() != "") { - msg.setProperty("__transactionId__", sendResult.getTransactionId()); - } - string transactionId = msg.getProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX); - if (transactionId != "") { - msg.setTransactionId(transactionId); - } - LOG_DEBUG("sendMessageInTransaction, msgId:%s, transactionId:%s", sendResult.getMsgId().data(), - transactionId.data()); - localTransactionState = m_transactionListener->executeLocalTransaction(msg, arg); - if (localTransactionState != LocalTransactionState::COMMIT_MESSAGE) { - LOG_WARN("executeLocalTransaction ret not LocalTransactionState::commit, msg:%s", msg.toString().data()); - } - } catch (MQException& e) { - THROW_MQEXCEPTION(MQClientException, e.what(), -1); - } - break; - case SendStatus::SEND_FLUSH_DISK_TIMEOUT: - case SendStatus::SEND_FLUSH_SLAVE_TIMEOUT: - case SendStatus::SEND_SLAVE_NOT_AVAILABLE: - localTransactionState = LocalTransactionState::ROLLBACK_MESSAGE; - LOG_WARN("sendMessageInTransaction, send not ok, rollback, result:%s", sendResult.toString().data()); - break; - default: - break; - } - - try { - endTransaction(sendResult, localTransactionState); - } catch (MQException& e) { - LOG_WARN("endTransaction exception:%s", e.what()); - } - - TransactionSendResult transactionSendResult(sendResult.getSendStatus(), sendResult.getMsgId(), - sendResult.getOffsetMsgId(), sendResult.getMessageQueue(), - sendResult.getQueueOffset()); - transactionSendResult.setTransactionId(msg.getTransactionId()); - transactionSendResult.setLocalTransactionState(localTransactionState); - return transactionSendResult; -} - -void TransactionMQProducerImpl::endTransaction(SendResult& sendResult, LocalTransactionState& localTransactionState) { - MQMessageId id; - if (sendResult.getOffsetMsgId() != "") { - id = MQDecoder::decodeMessageId(sendResult.getOffsetMsgId()); - } else { - id = MQDecoder::decodeMessageId(sendResult.getMsgId()); - } - string transId = sendResult.getTransactionId(); - - int commitOrRollback = MessageSysFlag::TransactionNotType; - switch (localTransactionState) { - case COMMIT_MESSAGE: - commitOrRollback = MessageSysFlag::TransactionCommitType; - break; - case ROLLBACK_MESSAGE: - commitOrRollback = MessageSysFlag::TransactionRollbackType; - break; - case UNKNOWN: - commitOrRollback = MessageSysFlag::TransactionNotType; - break; - default: - break; - } - - bool fromTransCheck = false; - EndTransactionRequestHeader* requestHeader = - new EndTransactionRequestHeader(getGroupName(), sendResult.getQueueOffset(), id.getOffset(), commitOrRollback, - fromTransCheck, sendResult.getMsgId(), transId); - LOG_DEBUG("endTransaction: msg:%s", requestHeader->toString().data()); - getFactory()->endTransactionOneway(sendResult.getMessageQueue(), requestHeader, getSessionCredentials()); -} - -void TransactionMQProducerImpl::checkTransactionState(const std::string& addr, - const MQMessageExt& message, - long tranStateTableOffset, - long commitLogOffset, - const std::string& msgId, - const std::string& transactionId, - const std::string& offsetMsgId) { - LOG_DEBUG("checkTransactionState: msgId:%s, transactionId:%s", msgId.data(), transactionId.data()); - if (!m_transactionListener) { - LOG_WARN("checkTransactionState, transactionListener null"); - THROW_MQEXCEPTION(MQClientException, "checkTransactionState, transactionListener null", -1); - } - - m_ioService.post(boost::bind(&TransactionMQProducerImpl::checkTransactionStateImpl, this, addr, message, - tranStateTableOffset, commitLogOffset, msgId, transactionId, offsetMsgId)); -} - -void TransactionMQProducerImpl::checkTransactionStateImpl(const std::string& addr, - const MQMessageExt& message, - long tranStateTableOffset, - long commitLogOffset, - const std::string& msgId, - const std::string& transactionId, - const std::string& offsetMsgId) { - LOG_DEBUG("checkTransactionStateImpl: msgId:%s, transactionId:%s", msgId.data(), transactionId.data()); - LocalTransactionState localTransactionState = UNKNOWN; - try { - localTransactionState = m_transactionListener->checkLocalTransaction(message); - } catch (MQException& e) { - LOG_INFO("checkTransactionState, checkLocalTransaction exception: %s", e.what()); - } - - EndTransactionRequestHeader* endHeader = new EndTransactionRequestHeader(); - endHeader->m_commitLogOffset = commitLogOffset; - endHeader->m_producerGroup = getGroupName(); - endHeader->m_tranStateTableOffset = tranStateTableOffset; - endHeader->m_fromTransactionCheck = true; - - string uniqueKey = transactionId; - if (transactionId.empty()) { - uniqueKey = message.getMsgId(); - } - - endHeader->m_msgId = uniqueKey; - endHeader->m_transactionId = transactionId; - switch (localTransactionState) { - case COMMIT_MESSAGE: - endHeader->m_commitOrRollback = MessageSysFlag::TransactionCommitType; - break; - case ROLLBACK_MESSAGE: - endHeader->m_commitOrRollback = MessageSysFlag::TransactionRollbackType; - LOG_WARN("when broker check, client rollback this transaction, %s", endHeader->toString().data()); - break; - case UNKNOWN: - endHeader->m_commitOrRollback = MessageSysFlag::TransactionNotType; - LOG_WARN("when broker check, client does not know this transaction state, %s", endHeader->toString().data()); - break; - default: - break; - } - - LOG_INFO("checkTransactionState, endTransactionOneway: uniqueKey:%s, client state:%d, end header: %s", - uniqueKey.data(), localTransactionState, endHeader->toString().data()); - - string remark; - try { - getFactory()->getMQClientAPIImpl()->endTransactionOneway(addr, endHeader, remark, getSessionCredentials()); - } catch (MQException& e) { - LOG_ERROR("endTransactionOneway exception:%s", e.what()); - throw e; - } -} - -void TransactionMQProducerImpl::start() { - initTransactionEnv(); - DefaultMQProducerImpl::start(); -} - -void TransactionMQProducerImpl::shutdown() { - DefaultMQProducerImpl::shutdown(); - destroyTransactionEnv(); -} - -} // namespace rocketmq diff --git a/src/producer/TransactionMQProducerImpl.h b/src/producer/TransactionMQProducerImpl.h deleted file mode 100644 index 319ee885a..000000000 --- a/src/producer/TransactionMQProducerImpl.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __TRANSACTIONMQPRODUCERIMPL_H__ -#define __TRANSACTIONMQPRODUCERIMPL_H__ - -#include -#include -#include -#include -#include -#include -#include -#include "DefaultMQProducerImpl.h" -#include "MQMessageExt.h" -#include "TransactionListener.h" -#include "TransactionSendResult.h" - -namespace rocketmq { - -class TransactionMQProducerImpl : public DefaultMQProducerImpl { - public: - TransactionMQProducerImpl(const std::string& producerGroup) - : DefaultMQProducerImpl(producerGroup), m_thread_num(1), m_ioServiceWork(m_ioService) {} - virtual ~TransactionMQProducerImpl() {} - void start(); - void shutdown(); - std::shared_ptr getTransactionListener() { return m_transactionListener; } - void setTransactionListener(TransactionListener* listener) { m_transactionListener.reset(listener); } - TransactionSendResult sendMessageInTransaction(MQMessage& msg, void* arg); - void checkTransactionState(const std::string& addr, - const MQMessageExt& message, - long tranStateTableOffset, - long commitLogOffset, - const std::string& msgId, - const std::string& transactionId, - const std::string& offsetMsgId); - - private: - void initTransactionEnv(); - void destroyTransactionEnv(); - void endTransaction(SendResult& sendResult, LocalTransactionState& localTransactionState); - void checkTransactionStateImpl(const std::string& addr, - const MQMessageExt& message, - long tranStateTableOffset, - long commitLogOffset, - const std::string& msgId, - const std::string& transactionId, - const std::string& offsetMsgId); - - private: - std::shared_ptr m_transactionListener; - int m_thread_num; - boost::thread_group m_threadpool; - boost::asio::io_service m_ioService; - boost::asio::io_service::work m_ioServiceWork; -}; -} // namespace rocketmq - -#endif diff --git a/src/protocol/CommandHeader.cpp b/src/protocol/CommandHeader.cpp deleted file mode 100644 index 20477a3fd..000000000 --- a/src/protocol/CommandHeader.cpp +++ /dev/null @@ -1,683 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CommandHeader.h" -#include -#include -#include "Logging.h" -#include "UtilAll.h" - -namespace rocketmq { -//& requestMap) { - requestMap.insert(pair("topic", topic)); -} -//& requestMap) { - requestMap.insert(pair("clientID", clientID)); - requestMap.insert(pair("producerGroup", producerGroup)); - requestMap.insert(pair("consumerGroup", consumerGroup)); -} -//& requestMap) { - requestMap.insert(pair("topic", topic)); - requestMap.insert(pair("defaultTopic", defaultTopic)); - requestMap.insert(pair("readQueueNums", UtilAll::to_string(readQueueNums))); - requestMap.insert(pair("writeQueueNums", UtilAll::to_string(writeQueueNums))); - requestMap.insert(pair("perm", UtilAll::to_string(perm))); - requestMap.insert(pair("topicFilterType", topicFilterType)); -} - -void CheckTransactionStateRequestHeader::Encode(Json::Value& outData) {} - -CommandHeader* CheckTransactionStateRequestHeader::Decode(Json::Value& ext) { - CheckTransactionStateRequestHeader* h = new CheckTransactionStateRequestHeader(); - Json::Value& tempValue = ext["msgId"]; - if (tempValue.isString()) { - h->m_msgId = tempValue.asString(); - } - - tempValue = ext["transactionId"]; - if (tempValue.isString()) { - h->m_transactionId = tempValue.asString(); - } - - tempValue = ext["offsetMsgId"]; - if (tempValue.isString()) { - h->m_offsetMsgId = tempValue.asString(); - } - - tempValue = ext["tranStateTableOffset"]; - if (tempValue.isString()) { - h->m_tranStateTableOffset = UtilAll::str2ll(tempValue.asCString()); - } - - tempValue = ext["commitLogOffset"]; - if (tempValue.isString()) { - h->m_commitLogOffset = UtilAll::str2ll(tempValue.asCString()); - } - - return h; -} - -void CheckTransactionStateRequestHeader::SetDeclaredFieldOfCommandHeader(map& requestMap) { - requestMap.insert(pair("msgId", m_msgId)); - requestMap.insert(pair("transactionId", m_transactionId)); - requestMap.insert(pair("offsetMsgId", m_offsetMsgId)); - requestMap.insert(pair("commitLogOffset", UtilAll::to_string(m_commitLogOffset))); - requestMap.insert(pair("tranStateTableOffset", UtilAll::to_string(m_tranStateTableOffset))); -} - -std::string CheckTransactionStateRequestHeader::toString() { - stringstream ss; - ss << "CheckTransactionStateRequestHeader:"; - ss << " msgId:" << m_msgId; - ss << " transactionId:" << m_transactionId; - ss << " offsetMsgId:" << m_offsetMsgId; - ss << " commitLogOffset:" << m_commitLogOffset; - ss << " tranStateTableOffset:" << m_tranStateTableOffset; - return ss.str(); -} - -void EndTransactionRequestHeader::Encode(Json::Value& outData) { - outData["msgId"] = m_msgId; - outData["transactionId"] = m_transactionId; - outData["producerGroup"] = m_producerGroup; - outData["tranStateTableOffset"] = UtilAll::to_string(m_tranStateTableOffset); - outData["commitLogOffset"] = UtilAll::to_string(m_commitLogOffset); - outData["commitOrRollback"] = UtilAll::to_string(m_commitOrRollback); - outData["fromTransactionCheck"] = UtilAll::to_string(m_fromTransactionCheck); -} - -void EndTransactionRequestHeader::SetDeclaredFieldOfCommandHeader(map& requestMap) { - requestMap.insert(pair("msgId", m_msgId)); - requestMap.insert(pair("transactionId", m_transactionId)); - requestMap.insert(pair("producerGroup", m_producerGroup)); - requestMap.insert(pair("tranStateTableOffset", UtilAll::to_string(m_tranStateTableOffset))); - requestMap.insert(pair("commitLogOffset", UtilAll::to_string(m_commitLogOffset))); - requestMap.insert(pair("commitOrRollback", UtilAll::to_string(m_commitOrRollback))); - requestMap.insert(pair("fromTransactionCheck", UtilAll::to_string(m_fromTransactionCheck))); -} - -std::string EndTransactionRequestHeader::toString() { - stringstream ss; - ss << "EndTransactionRequestHeader:"; - ss << " m_msgId:" << m_msgId; - ss << " m_transactionId:" << m_transactionId; - ss << " m_producerGroup:" << m_producerGroup; - ss << " m_tranStateTableOffset:" << m_tranStateTableOffset; - ss << " m_commitLogOffset:" << m_commitLogOffset; - ss << " m_commitOrRollback:" << m_commitOrRollback; - ss << " m_fromTransactionCheck:" << m_fromTransactionCheck; - return ss.str(); -} - -//& requestMap) { - LOG_DEBUG( - "SendMessageRequestHeader producerGroup is:%s,topic is:%s, defaulttopic " - "is:%s, properties is:%s,UtilAll::to_string( defaultTopicQueueNums) " - "is:%s,UtilAll::to_string( queueId):%s, UtilAll::to_string( sysFlag) " - "is:%s, UtilAll::to_string( bornTimestamp) is:%s,UtilAll::to_string( " - "flag) is:%s", - producerGroup.c_str(), topic.c_str(), defaultTopic.c_str(), properties.c_str(), - UtilAll::to_string(defaultTopicQueueNums).c_str(), UtilAll::to_string(queueId).c_str(), - UtilAll::to_string(sysFlag).c_str(), UtilAll::to_string(bornTimestamp).c_str(), UtilAll::to_string(flag).c_str()); - - requestMap.insert(pair("producerGroup", producerGroup)); - requestMap.insert(pair("topic", topic)); - requestMap.insert(pair("defaultTopic", defaultTopic)); - requestMap.insert(pair("defaultTopicQueueNums", UtilAll::to_string(defaultTopicQueueNums))); - requestMap.insert(pair("queueId", UtilAll::to_string(queueId))); - requestMap.insert(pair("sysFlag", UtilAll::to_string(sysFlag))); - requestMap.insert(pair("bornTimestamp", UtilAll::to_string(bornTimestamp))); - requestMap.insert(pair("flag", UtilAll::to_string(flag))); - requestMap.insert(pair("properties", properties)); - requestMap.insert(pair("reconsumeTimes", UtilAll::to_string(reconsumeTimes))); - requestMap.insert(pair("unitMode", UtilAll::to_string(unitMode))); - requestMap.insert(pair("batch", UtilAll::to_string(batch))); -} - -//& requestMap) { - LOG_DEBUG( - "SendMessageRequestHeaderV2 producerGroup is:%s,topic is:%s, defaulttopic " - "is:%s, properties is:%s,UtilAll::to_string( defaultTopicQueueNums) " - "is:%s,UtilAll::to_string( queueId):%s, UtilAll::to_string( sysFlag) " - "is:%s, UtilAll::to_string( bornTimestamp) is:%s,UtilAll::to_string( " - "flag) is:%s,UtilAll::to_string( reconsumeTimes) is:%s,UtilAll::to_string( unitMode) is:%s,UtilAll::to_string( " - "batch) is:%s", - a.c_str(), b.c_str(), c.c_str(), i.c_str(), UtilAll::to_string(d).c_str(), UtilAll::to_string(e).c_str(), - UtilAll::to_string(f).c_str(), UtilAll::to_string(g).c_str(), UtilAll::to_string(g).c_str(), - UtilAll::to_string(j).c_str(), UtilAll::to_string(k).c_str(), UtilAll::to_string(m).c_str()); - - requestMap.insert(pair("a", a)); - requestMap.insert(pair("b", b)); - requestMap.insert(pair("c", c)); - requestMap.insert(pair("d", UtilAll::to_string(d))); - requestMap.insert(pair("e", UtilAll::to_string(e))); - requestMap.insert(pair("f", UtilAll::to_string(f))); - requestMap.insert(pair("g", UtilAll::to_string(g))); - requestMap.insert(pair("h", UtilAll::to_string(h))); - requestMap.insert(pair("i", i)); - requestMap.insert(pair("j", UtilAll::to_string(j))); - requestMap.insert(pair("k", UtilAll::to_string(k))); - requestMap.insert(pair("l", UtilAll::to_string(l))); - requestMap.insert(pair("m", UtilAll::to_string(m))); -} -void SendMessageRequestHeaderV2::CreateSendMessageRequestHeaderV1(SendMessageRequestHeader& v1) { - v1.producerGroup = a; - v1.topic = b; - v1.defaultTopic = c; - v1.defaultTopicQueueNums = d; - v1.queueId = e; - v1.sysFlag = f; - v1.bornTimestamp = g; - v1.flag = h; - v1.properties = i; - v1.reconsumeTimes = j; - v1.unitMode = k; - v1.consumeRetryTimes = l; - v1.batch = m; -} -//msgId = tempValue.asString(); - } - - tempValue = ext["queueId"]; - if (tempValue.isString()) { - h->queueId = atoi(tempValue.asCString()); - } - - tempValue = ext["queueOffset"]; - if (tempValue.isString()) { - h->queueOffset = UtilAll::str2ll(tempValue.asCString()); - } - - tempValue = ext["transactionId"]; - if (tempValue.isString()) { - h->transactionId = tempValue.asCString(); - } - - tempValue = ext["MSG_REGION"]; - if (tempValue.isString()) { - h->regionId = tempValue.asCString(); - } - return h; -} - -void SendMessageResponseHeader::SetDeclaredFieldOfCommandHeader(map& requestMap) { - requestMap.insert(pair("msgId", msgId)); - requestMap.insert(pair("queueId", UtilAll::to_string(queueId))); - requestMap.insert(pair("queueOffset", UtilAll::to_string(queueOffset))); - requestMap.insert(pair("transactionId", transactionId)); - requestMap.insert(pair("MSG_REGION", regionId)); -} -//& requestMap) { - requestMap.insert(pair("consumerGroup", consumerGroup)); - requestMap.insert(pair("topic", topic)); - requestMap.insert(pair("queueId", UtilAll::to_string(queueId))); - requestMap.insert(pair("queueOffset", UtilAll::to_string(queueOffset))); - requestMap.insert(pair("maxMsgNums", UtilAll::to_string(maxMsgNums))); - requestMap.insert(pair("sysFlag", UtilAll::to_string(sysFlag))); - requestMap.insert(pair("commitOffset", UtilAll::to_string(commitOffset))); - requestMap.insert(pair("subVersion", UtilAll::to_string(subVersion))); - requestMap.insert(pair("suspendTimeoutMillis", UtilAll::to_string(suspendTimeoutMillis))); - requestMap.insert(pair("subscription", subscription)); -} -//suggestWhichBrokerId = UtilAll::str2ll(tempValue.asCString()); - } - - tempValue = ext["nextBeginOffset"]; - if (tempValue.isString()) { - h->nextBeginOffset = UtilAll::str2ll(tempValue.asCString()); - } - - tempValue = ext["minOffset"]; - if (tempValue.isString()) { - h->minOffset = UtilAll::str2ll(tempValue.asCString()); - } - - tempValue = ext["maxOffset"]; - if (tempValue.isString()) { - h->maxOffset = UtilAll::str2ll(tempValue.asCString()); - } - - return h; -} - -void PullMessageResponseHeader::SetDeclaredFieldOfCommandHeader(map& requestMap) { - requestMap.insert(pair("suggestWhichBrokerId", UtilAll::to_string(suggestWhichBrokerId))); - requestMap.insert(pair("nextBeginOffset", UtilAll::to_string(nextBeginOffset))); - requestMap.insert(pair("minOffset", UtilAll::to_string(minOffset))); - requestMap.insert(pair("maxOffset", UtilAll::to_string(maxOffset))); -} -//& requestMap) {} -//& requestMap) { - requestMap.insert(pair("topic", topic)); - requestMap.insert(pair("queueId", UtilAll::to_string(queueId))); -} -//offset = UtilAll::str2ll(tempValue.asCString()); - } - return h; -} - -void GetMinOffsetResponseHeader::SetDeclaredFieldOfCommandHeader(map& requestMap) { - requestMap.insert(pair("offset", UtilAll::to_string(offset))); -} -//& requestMap) { - requestMap.insert(pair("topic", topic)); - requestMap.insert(pair("queueId", UtilAll::to_string(queueId))); -} -//offset = UtilAll::str2ll(tempValue.asCString()); - } - return h; -} - -void GetMaxOffsetResponseHeader::SetDeclaredFieldOfCommandHeader(map& requestMap) { - requestMap.insert(pair("offset", UtilAll::to_string(offset))); -} -//& requestMap) { - requestMap.insert(pair("topic", topic)); - requestMap.insert(pair("queueId", UtilAll::to_string(queueId))); - requestMap.insert(pair("timestamp", UtilAll::to_string(timestamp))); -} -//offset = UtilAll::str2ll(tempValue.asCString()); - } - return h; -} - -void SearchOffsetResponseHeader::SetDeclaredFieldOfCommandHeader(map& requestMap) { - requestMap.insert(pair("offset", UtilAll::to_string(offset))); -} -//& requestMap) { - requestMap.insert(pair("offset", UtilAll::to_string(offset))); -} -//& requestMap) { - requestMap.insert(pair("topic", topic)); - requestMap.insert(pair("queueId", UtilAll::to_string(queueId))); -} -//timestamp = UtilAll::str2ll(tempValue.asCString()); - } - return h; -} - -void GetEarliestMsgStoretimeResponseHeader::SetDeclaredFieldOfCommandHeader(map& requestMap) { - requestMap.insert(pair("timestamp", UtilAll::to_string(timestamp))); -} -//& requestMap) { - requestMap.insert(pair("consumerGroup", consumerGroup)); -} -//& requestMap) { - requestMap.insert(pair("consumerGroup", consumerGroup)); - requestMap.insert(pair("topic", topic)); - requestMap.insert(pair("queueId", UtilAll::to_string(queueId))); -} -//offset = UtilAll::str2ll(tempValue.asCString()); - } - return h; -} - -void QueryConsumerOffsetResponseHeader::SetDeclaredFieldOfCommandHeader(map& requestMap) { - requestMap.insert(pair("offset", UtilAll::to_string(offset))); -} -//& requestMap) { - requestMap.insert(pair("consumerGroup", consumerGroup)); - requestMap.insert(pair("topic", topic)); - requestMap.insert(pair("queueId", UtilAll::to_string(queueId))); - requestMap.insert(pair("commitOffset", UtilAll::to_string(commitOffset))); -} -//& requestMap) { - requestMap.insert(pair("group", group)); - requestMap.insert(pair("delayLevel", UtilAll::to_string(delayLevel))); - requestMap.insert(pair("offset", UtilAll::to_string(offset))); - requestMap.insert(pair("unitMode", UtilAll::to_string(unitMode))); - requestMap.insert(pair("originMsgId", originMsgId)); - requestMap.insert(pair("originTopic", originTopic)); - requestMap.insert(pair("maxReconsumeTimes", UtilAll::to_string(maxReconsumeTimes))); -} -//& cids) { - cids.clear(); - //getData(), mem->getSize()); - - Json::Reader reader; - Json::Value root; - if (!reader.parse(pData, root)) { - LOG_ERROR("GetConsumerListByGroupResponse error"); - return; - } - - Json::Value ids = root["consumerIdList"]; - for (unsigned int i = 0; i < ids.size(); i++) { - if (ids[i].isString()) { - cids.push_back(ids[i].asString()); - } - } -} - -void GetConsumerListByGroupResponseBody::SetDeclaredFieldOfCommandHeader(map& requestMap) {} - -void ResetOffsetRequestHeader::setTopic(const string& tmp) { - topic = tmp; -} - -void ResetOffsetRequestHeader::setGroup(const string& tmp) { - group = tmp; -} - -void ResetOffsetRequestHeader::setTimeStamp(const int64& tmp) { - timestamp = tmp; -} - -void ResetOffsetRequestHeader::setForceFlag(const bool& tmp) { - isForce = tmp; -} - -const string ResetOffsetRequestHeader::getTopic() const { - return topic; -} - -const string ResetOffsetRequestHeader::getGroup() const { - return group; -} - -const int64 ResetOffsetRequestHeader::getTimeStamp() const { - return timestamp; -} - -const bool ResetOffsetRequestHeader::getForceFlag() const { - return isForce; -} - -CommandHeader* ResetOffsetRequestHeader::Decode(Json::Value& ext) { - ResetOffsetRequestHeader* h = new ResetOffsetRequestHeader(); - - Json::Value& tempValue = ext["topic"]; - if (tempValue.isString()) { - h->topic = tempValue.asString(); - } - - tempValue = ext["group"]; - if (tempValue.isString()) { - h->group = tempValue.asString(); - } - - tempValue = ext["timestamp"]; - if (tempValue.isString()) { - h->timestamp = UtilAll::str2ll(tempValue.asCString()); - } - - tempValue = ext["isForce"]; - if (tempValue.isString()) { - h->isForce = UtilAll::to_bool(tempValue.asCString()); - } - LOG_INFO("topic:%s, group:%s, timestamp:%lld, isForce:%d", h->topic.c_str(), h->group.c_str(), h->timestamp, - h->isForce); - return h; -} - -CommandHeader* GetConsumerRunningInfoRequestHeader::Decode(Json::Value& ext) { - GetConsumerRunningInfoRequestHeader* h = new GetConsumerRunningInfoRequestHeader(); - - Json::Value& tempValue = ext["consumerGroup"]; - if (tempValue.isString()) { - h->consumerGroup = tempValue.asString(); - } - - tempValue = ext["clientId"]; - if (tempValue.isString()) { - h->clientId = tempValue.asString(); - } - - tempValue = ext["jstackEnable"]; - if (tempValue.isBool()) { - h->jstackEnable = tempValue.asBool(); - } else if (tempValue.isString()) { - h->jstackEnable = UtilAll::to_bool(tempValue.asCString()); - } - LOG_INFO("consumerGroup:%s, clientId:%s, jstackEnable:%d", h->consumerGroup.c_str(), h->clientId.c_str(), - h->jstackEnable); - return h; -} - -void GetConsumerRunningInfoRequestHeader::Encode(Json::Value& outData) { - outData["consumerGroup"] = consumerGroup; - outData["clientId"] = clientId; - outData["jstackEnable"] = jstackEnable; -} - -void GetConsumerRunningInfoRequestHeader::SetDeclaredFieldOfCommandHeader(map& requestMap) { - requestMap.insert(pair("consumerGroup", consumerGroup)); - requestMap.insert(pair("clientId", clientId)); - requestMap.insert(pair("jstackEnable", UtilAll::to_string(jstackEnable))); -} - -const string GetConsumerRunningInfoRequestHeader::getConsumerGroup() const { - return consumerGroup; -} - -void GetConsumerRunningInfoRequestHeader::setConsumerGroup(const string& Group) { - consumerGroup = Group; -} - -const string GetConsumerRunningInfoRequestHeader::getClientId() const { - return clientId; -} - -void GetConsumerRunningInfoRequestHeader::setClientId(const string& input_clientId) { - clientId = input_clientId; -} - -const bool GetConsumerRunningInfoRequestHeader::isJstackEnable() const { - return jstackEnable; -} - -void GetConsumerRunningInfoRequestHeader::setJstackEnable(const bool& input_jstackEnable) { - jstackEnable = input_jstackEnable; -} - -CommandHeader* NotifyConsumerIdsChangedRequestHeader::Decode(Json::Value& ext) { - NotifyConsumerIdsChangedRequestHeader* h = new NotifyConsumerIdsChangedRequestHeader(); - - Json::Value& tempValue = ext["consumerGroup"]; - if (tempValue.isString()) { - h->consumerGroup = tempValue.asString(); - } - - return h; -} - -void NotifyConsumerIdsChangedRequestHeader::setGroup(const string& tmp) { - consumerGroup = tmp; -} -const string NotifyConsumerIdsChangedRequestHeader::getGroup() const { - return consumerGroup; -} - -// -#include -#include "MQClientException.h" -#include "MessageSysFlag.h" -#include "UtilAll.h" -#include "dataBlock.h" -#include "json/json.h" - -namespace rocketmq { -//& requestMap) {} -}; - -class CheckTransactionStateRequestHeader : public CommandHeader { - public: - CheckTransactionStateRequestHeader() {} - CheckTransactionStateRequestHeader(long tableOffset, - long commLogOffset, - const std::string& msgid, - const std::string& transactionId, - const std::string& offsetMsgId) - : m_tranStateTableOffset(tableOffset), - m_commitLogOffset(commLogOffset), - m_msgId(msgid), - m_transactionId(transactionId), - m_offsetMsgId(offsetMsgId) {} - virtual ~CheckTransactionStateRequestHeader() {} - virtual void Encode(Json::Value& outData); - static CommandHeader* Decode(Json::Value& ext); - virtual void SetDeclaredFieldOfCommandHeader(std::map& requestMap); - std::string toString(); - - public: - long m_tranStateTableOffset; - long m_commitLogOffset; - std::string m_msgId; - std::string m_transactionId; - std::string m_offsetMsgId; -}; - -class EndTransactionRequestHeader : public CommandHeader { - public: - EndTransactionRequestHeader() {} - EndTransactionRequestHeader(const std::string& groupName, - long tableOffset, - long commLogOffset, - int commitOrRoll, - bool fromTransCheck, - const std::string& msgid, - const std::string& transId) - : m_producerGroup(groupName), - m_tranStateTableOffset(tableOffset), - m_commitLogOffset(commLogOffset), - m_commitOrRollback(commitOrRoll), - m_fromTransactionCheck(fromTransCheck), - m_msgId(msgid), - m_transactionId(transId) {} - virtual ~EndTransactionRequestHeader() {} - virtual void Encode(Json::Value& outData); - virtual void SetDeclaredFieldOfCommandHeader(std::map& requestMap); - std::string toString(); - - public: - std::string m_producerGroup; - long m_tranStateTableOffset; - long m_commitLogOffset; - int m_commitOrRollback; - bool m_fromTransactionCheck; - std::string m_msgId; - std::string m_transactionId; -}; - -//& requestMap); - - private: - string topic; -}; - -//& requestMap); - - private: - string clientID; - string producerGroup; - string consumerGroup; -}; - -//& requestMap); - - public: - string topic; - string defaultTopic; - int readQueueNums; - int writeQueueNums; - int perm; - string topicFilterType; -}; - -//& requestMap); - - public: - string producerGroup; - string topic; - string defaultTopic; - int defaultTopicQueueNums; - int queueId; - int sysFlag; - int64 bornTimestamp; - int flag; - string properties; - int reconsumeTimes; - bool unitMode; - int consumeRetryTimes; - bool batch; -}; - -//& requestMap); - virtual void CreateSendMessageRequestHeaderV1(SendMessageRequestHeader& v1); - - public: - string a; // producerGroup - string b; // topic; - string c; // defaultTopic; - int d; // defaultTopicQueueNums; - int e; // queueId; - int f; // sysFlag; - int64 g; // bornTimestamp; - int h; // flag; - string i; // properties; - int j; // reconsumeTimes; - bool k; // unitMode; - int l; // consumeRetryTimes; - bool m; // batch; -}; - -//& requestMap); - - public: - string msgId; - int queueId; - int64 queueOffset; - string regionId; - string transactionId; -}; - -//& requestMap); - - public: - string consumerGroup; - string topic; - int queueId; - int maxMsgNums; - int sysFlag; - string subscription; - int64 queueOffset; - int64 commitOffset; - int64 suspendTimeoutMillis; - int64 subVersion; -}; - -//& requestMap); - - public: - int64 suggestWhichBrokerId; - int64 nextBeginOffset; - int64 minOffset; - int64 maxOffset; -}; - -//& requestMap); -}; - -//& requestMap); - - public: - string topic; - int queueId; -}; - -//& requestMap); - - public: - int64 offset; -}; - -//& requestMap); - - public: - string topic; - int queueId; -}; - -//& requestMap); - - public: - int64 offset; -}; - -//& requestMap); - - public: - string topic; - int queueId; - int64 timestamp; -}; - -//& requestMap); - - public: - int64 offset; -}; - -//& requestMap); - - public: - int64 offset; -}; - -//& requestMap); - - public: - string topic; - int queueId; -}; - -//& requestMap); - - public: - int64 timestamp; -}; - -//& requestMap); - - public: - string consumerGroup; -}; - -//& requestMap); - - public: - string consumerGroup; - string topic; - int queueId; -}; - -//& requestMap); - - public: - int64 offset; -}; - -//& requestMap); - - public: - string consumerGroup; - string topic; - int queueId; - int64 commitOffset; -}; - -//& requestMap); - - public: - string group; - int delayLevel; - int64 offset; - bool unitMode = false; - string originMsgId; - string originTopic; - int maxReconsumeTimes = 16; -}; - -//& requestMap); - - public: - static void Decode(const MemoryBlock* mem, vector& cids); -}; - -class ResetOffsetRequestHeader : public CommandHeader { - public: - ResetOffsetRequestHeader() {} - ~ResetOffsetRequestHeader() {} - static CommandHeader* Decode(Json::Value& ext); - void setTopic(const string& tmp); - void setGroup(const string& tmp); - void setTimeStamp(const int64& tmp); - void setForceFlag(const bool& tmp); - const string getTopic() const; - const string getGroup() const; - const int64 getTimeStamp() const; - const bool getForceFlag() const; - - private: - string topic; - string group; - int64 timestamp; - bool isForce; -}; - -class GetConsumerRunningInfoRequestHeader : public CommandHeader { - public: - GetConsumerRunningInfoRequestHeader() {} - virtual ~GetConsumerRunningInfoRequestHeader() {} - virtual void Encode(Json::Value& outData); - virtual void SetDeclaredFieldOfCommandHeader(map& requestMap); - static CommandHeader* Decode(Json::Value& ext); - const string getConsumerGroup() const; - void setConsumerGroup(const string& consumerGroup); - const string getClientId() const; - void setClientId(const string& clientId); - const bool isJstackEnable() const; - void setJstackEnable(const bool& jstackEnable); - - private: - string consumerGroup; - string clientId; - bool jstackEnable; -}; - -class NotifyConsumerIdsChangedRequestHeader : public CommandHeader { - public: - NotifyConsumerIdsChangedRequestHeader() {} - virtual ~NotifyConsumerIdsChangedRequestHeader() {} - static CommandHeader* Decode(Json::Value& ext); - void setGroup(const string& tmp); - const string getGroup() const; - - private: - string consumerGroup; -}; - -// ConsumerRunningInfo::getProperties() const { - return properties; -} - -void ConsumerRunningInfo::setProperties(const map& input_properties) { - properties = input_properties; -} - -void ConsumerRunningInfo::setProperty(const string& key, const string& value) { - properties[key] = value; -} - -const map ConsumerRunningInfo::getMqTable() const { - return mqTable; -} - -void ConsumerRunningInfo::setMqTable(const MessageQueue& queue, const ProcessQueueInfo& queueInfo) { - mqTable[queue] = queueInfo; -} - -const map ConsumerRunningInfo::getStatusTable() const { - return statusTable; -} - -void ConsumerRunningInfo::setStatusTable(const string& topic, const ConsumeStats& consumeStats) { - statusTable[topic] = consumeStats; -} - -const vector ConsumerRunningInfo::getSubscriptionSet() const { - return subscriptionSet; -} - -void ConsumerRunningInfo::setSubscriptionSet(const vector& input_subscriptionSet) { - subscriptionSet = input_subscriptionSet; -} - -const string ConsumerRunningInfo::getJstack() const { - return jstack; -} - -void ConsumerRunningInfo::setJstack(const string& input_jstack) { - jstack = input_jstack; -} - -string ConsumerRunningInfo::encode() { - Json::Value outData; - - outData[PROP_NAMESERVER_ADDR] = properties[PROP_NAMESERVER_ADDR]; - outData[PROP_CONSUME_TYPE] = properties[PROP_CONSUME_TYPE]; - outData[PROP_CLIENT_VERSION] = properties[PROP_CLIENT_VERSION]; - outData[PROP_CONSUMER_START_TIMESTAMP] = properties[PROP_CONSUMER_START_TIMESTAMP]; - outData[PROP_CONSUME_ORDERLY] = properties[PROP_CONSUME_ORDERLY]; - outData[PROP_THREADPOOL_CORE_SIZE] = properties[PROP_THREADPOOL_CORE_SIZE]; - outData[PROP_CLIENT_SDK_VERSION] = properties[PROP_CLIENT_SDK_VERSION]; - - Json::Value root; - root["jstack"] = jstack; - root["properties"] = outData; - - { - vector::const_iterator it = subscriptionSet.begin(); - for (; it != subscriptionSet.end(); it++) { - root["subscriptionSet"].append(it->toJson()); - } - } - { - Json::Value stats; - for (map::iterator it = statusTable.begin(); it != statusTable.end(); ++it) { - stats[it->first] = it->second.toJson(); - } - if (!stats.isNull()) { - root["statusTable"] = stats; - } - } - - Json::FastWriter fastwrite; - string finals = fastwrite.write(root); - string key = "\"mqTable\":"; - key.append("{"); - for (map::iterator it = mqTable.begin(); it != mqTable.end(); ++it) { - key.append((it->first).toJson().toStyledString()); - key.erase(key.end() - 1); - key.append(":"); - key.append((it->second).toJson().toStyledString()); - key.append(","); - } - key.erase(key.end() - 1); - key.append("}"); - - // insert mqTable to final string - key.append(","); - finals.insert(1, key); - - return finals; -} -} // namespace rocketmq diff --git a/src/protocol/ConsumerRunningInfo.h b/src/protocol/ConsumerRunningInfo.h deleted file mode 100644 index e61c43da7..000000000 --- a/src/protocol/ConsumerRunningInfo.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __CONSUMERRUNNINGINFO_H__ -#define __CONSUMERRUNNINGINFO_H__ - -#include "ConsumeStats.h" -#include "MessageQueue.h" -#include "ProcessQueueInfo.h" -#include "SubscriptionData.h" - -namespace rocketmq { - -class ConsumerRunningInfo { - public: - ConsumerRunningInfo() {} - virtual ~ConsumerRunningInfo() { - properties.clear(); - mqTable.clear(); - subscriptionSet.clear(); - } - - public: - static const string PROP_NAMESERVER_ADDR; - static const string PROP_THREADPOOL_CORE_SIZE; - static const string PROP_CONSUME_ORDERLY; - static const string PROP_CONSUME_TYPE; - static const string PROP_CLIENT_VERSION; - static const string PROP_CLIENT_SDK_VERSION; - static const string PROP_CONSUMER_START_TIMESTAMP; - - public: - const map getProperties() const; - void setProperties(const map& input_properties); - void setProperty(const string& key, const string& value); - const map getMqTable() const; - void setMqTable(const MessageQueue& queue, const ProcessQueueInfo& queueInfo); - const map getStatusTable() const; - void setStatusTable(const string& topic, const ConsumeStats& consumeStats); - const vector getSubscriptionSet() const; - void setSubscriptionSet(const vector& input_subscriptionSet); - const string getJstack() const; - void setJstack(const string& input_jstack); - string encode(); - - private: - map properties; - vector subscriptionSet; - map mqTable; - map statusTable; - string jstack; -}; -} // namespace rocketmq -#endif diff --git a/src/protocol/HeartbeatData.h b/src/protocol/HeartbeatData.h deleted file mode 100644 index 2bd4aa71a..000000000 --- a/src/protocol/HeartbeatData.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __HEARTBEATDATA_H__ -#define __HEARTBEATDATA_H__ -#include -#include -#include -#include -#include "ConsumeType.h" -#include "SubscriptionData.h" - -namespace rocketmq { -//::const_iterator it = subscriptionDataSet.begin(); - for (; it != subscriptionDataSet.end(); it++) { - outJson["subscriptionDataSet"].append((*it).toJson()); - } - - return outJson; - } - - public: - string groupName; - ConsumeType consumeType; - MessageModel messageModel; - ConsumeFromWhere consumeFromWhere; - vector subscriptionDataSet; -}; - -// lock(m_consumerDataMutex); - vector::iterator itc = m_consumerDataSet.begin(); - for (; itc != m_consumerDataSet.end(); itc++) { - root["consumerDataSet"].append((*itc).toJson()); - } - } - - // lock(m_producerDataMutex); - vector::iterator itp = m_producerDataSet.begin(); - for (; itp != m_producerDataSet.end(); itp++) { - root["producerDataSet"].append((*itp).toJson()); - } - } - // lock(m_producerDataMutex); - return m_producerDataSet.empty(); - } - - void insertDataToProducerDataSet(ProducerData& producerData) { - boost::lock_guard lock(m_producerDataMutex); - m_producerDataSet.push_back(producerData); - } - - bool isConsumerDataSetEmpty() { - boost::lock_guard lock(m_consumerDataMutex); - return m_consumerDataSet.empty(); - } - - void insertDataToConsumerDataSet(ConsumerData& consumerData) { - boost::lock_guard lock(m_consumerDataMutex); - m_consumerDataSet.push_back(consumerData); - } - - private: - string m_clientID; - vector m_producerDataSet; - vector m_consumerDataSet; - boost::mutex m_producerDataMutex; - boost::mutex m_consumerDataMutex; -}; -} // -#include -#include "RemotingSerializable.h" - -using std::map; -using std::string; - -namespace rocketmq { -//& getTable() { return m_table; } - - void setTable(const map& table) { m_table = table; } - - private: - map m_table; -}; -} // namespace rocketmq - -#endif diff --git a/src/protocol/LockBatchBody.cpp b/src/protocol/LockBatchBody.cpp deleted file mode 100644 index 5ed2bcf27..000000000 --- a/src/protocol/LockBatchBody.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "LockBatchBody.h" -#include "Logging.h" -namespace rocketmq { // LockBatchRequestBody::getMqSet() { - return mqSet; -} -void LockBatchRequestBody::setMqSet(const vector& in_mqSet) { - mqSet = in_mqSet; -} -void LockBatchRequestBody::Encode(string& outData) { - Json::Value root; - root["consumerGroup"] = consumerGroup; - root["clientId"] = clientId; - - vector::const_iterator it = mqSet.begin(); - for (; it != mqSet.end(); it++) { - root["mqSet"].append(toJson(*it)); - } - - Json::FastWriter fastwrite; - outData = fastwrite.write(root); -} - -Json::Value LockBatchRequestBody::toJson(const MQMessageQueue& mq) const { - Json::Value outJson; - outJson["topic"] = mq.getTopic(); - outJson["brokerName"] = mq.getBrokerName(); - outJson["queueId"] = mq.getQueueId(); - return outJson; -} - -vector LockBatchResponseBody::getLockOKMQSet() { - return lockOKMQSet; -} -void LockBatchResponseBody::setLockOKMQSet(vector in_lockOKMQSet) { - lockOKMQSet.swap(in_lockOKMQSet); -} - -void LockBatchResponseBody::Decode(const MemoryBlock* mem, vector& messageQueues) { - messageQueues.clear(); - //getData(), mem->getSize()); - - Json::Reader reader; - Json::Value root; - if (!reader.parse(pData, root)) { - LOG_WARN("decode LockBatchResponseBody error"); - return; - } - - Json::Value mqs = root["lockOKMQSet"]; - LOG_DEBUG("LockBatchResponseBody mqs size:%d", mqs.size()); - for (unsigned int i = 0; i < mqs.size(); i++) { - MQMessageQueue mq; - Json::Value qd = mqs[i]; - mq.setTopic(qd["topic"].asString()); - mq.setBrokerName(qd["brokerName"].asString()); - mq.setQueueId(qd["queueId"].asInt()); - LOG_INFO("LockBatchResponseBody MQ:%s", mq.toString().c_str()); - messageQueues.push_back(mq); - } -} - -string UnlockBatchRequestBody::getConsumerGroup() { - return consumerGroup; -} -void UnlockBatchRequestBody::setConsumerGroup(const string& in_consumerGroup) { - consumerGroup = in_consumerGroup; -} -string UnlockBatchRequestBody::getClientId() { - return clientId; -} -void UnlockBatchRequestBody::setClientId(const string& in_clientId) { - clientId = in_clientId; -} -vector UnlockBatchRequestBody::getMqSet() { - return mqSet; -} -void UnlockBatchRequestBody::setMqSet(const vector& in_mqSet) { - mqSet = in_mqSet; -} -void UnlockBatchRequestBody::Encode(string& outData) { - Json::Value root; - root["consumerGroup"] = consumerGroup; - root["clientId"] = clientId; - - vector::const_iterator it = mqSet.begin(); - for (; it != mqSet.end(); it++) { - root["mqSet"].append(toJson(*it)); - } - - Json::FastWriter fastwrite; - outData = fastwrite.write(root); -} - -Json::Value UnlockBatchRequestBody::toJson(const MQMessageQueue& mq) const { - Json::Value outJson; - outJson["topic"] = mq.getTopic(); - outJson["brokerName"] = mq.getBrokerName(); - outJson["queueId"] = mq.getQueueId(); - return outJson; -} -} diff --git a/src/protocol/LockBatchBody.h b/src/protocol/LockBatchBody.h deleted file mode 100644 index fd60c28d2..000000000 --- a/src/protocol/LockBatchBody.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __LOCKBATCHBODY_H__ -#define __LOCKBATCHBODY_H__ -#include -#include -#include "MQMessageQueue.h" -#include "RemotingSerializable.h" -#include "dataBlock.h" -#include "json/json.h" -#include "UtilAll.h" - -namespace rocketmq { -// getMqSet(); - void setMqSet(const vector& mqSet); - void Encode(string& outData); - Json::Value toJson(const MQMessageQueue& mq) const; - - private: - string consumerGroup; - string clientId; - vector mqSet; -}; - -class LockBatchResponseBody { - public: - virtual ~LockBatchResponseBody() { lockOKMQSet.clear(); } - vector getLockOKMQSet(); - void setLockOKMQSet(vector lockOKMQSet); - static void Decode(const MemoryBlock* mem, vector& messageQueues); - - private: - vector lockOKMQSet; -}; - -class UnlockBatchRequestBody { - public: - virtual ~UnlockBatchRequestBody() { mqSet.clear(); } - string getConsumerGroup(); - void setConsumerGroup(const string& consumerGroup); - string getClientId(); - void setClientId(const string& clientId); - vector getMqSet(); - void setMqSet(const vector& mqSet); - void Encode(string& outData); - Json::Value toJson(const MQMessageQueue& mq) const; - - private: - string consumerGroup; - string clientId; - vector mqSet; -}; - -} // -#include "json/json.h" - -namespace rocketmq { -// RemotingCommand::s_seqNumber; - -//::max(); -} - -RemotingCommand::RemotingCommand(int code, - string language, - int version, - int opaque, - int flag, - string remark, - CommandHeader* pExtHeader) - : m_code(code), - m_language(language), - m_version(version), - m_opaque(opaque), - m_flag(flag), - m_remark(remark), - m_pExtHeader(pExtHeader) {} - -RemotingCommand::RemotingCommand(const RemotingCommand& command) { - Assign(command); -} - -RemotingCommand& RemotingCommand::operator=(const RemotingCommand& command) { - if (this != &command) { - Assign(command); - } - return *this; -} - -RemotingCommand::~RemotingCommand() { - m_pExtHeader = NULL; -} - -void RemotingCommand::Assign(const RemotingCommand& command) { - m_code = command.m_code; - m_language = command.m_language; - m_version = command.m_version; - m_opaque = command.m_opaque; - m_flag = command.m_flag; - m_remark = command.m_remark; - m_msgBody = command.m_msgBody; - - for (auto& it : command.m_extFields) { - m_extFields[it.first] = it.second; - } - - m_head = command.m_head; - m_body = command.m_body; - m_parsedJson = command.m_parsedJson; - // m_pExtHeader = command.m_pExtHeader; //ignore this filed at this moment, if need please add it -} - -void RemotingCommand::Encode() { - Json::Value root; - root["code"] = m_code; - root["language"] = m_language; - root["version"] = m_version; - root["opaque"] = m_opaque; - root["flag"] = m_flag; - root["remark"] = m_remark; - - if (m_pExtHeader) { - Json::Value extJson; - m_pExtHeader->Encode(extJson); - - extJson[SessionCredentials::Signature] = m_extFields[SessionCredentials::Signature]; - extJson[SessionCredentials::AccessKey] = m_extFields[SessionCredentials::AccessKey]; - extJson[SessionCredentials::ONSChannelKey] = m_extFields[SessionCredentials::ONSChannelKey]; - - root["extFields"] = extJson; - } else { // for heartbeat - Json::Value extJson; - extJson[SessionCredentials::Signature] = m_extFields[SessionCredentials::Signature]; - extJson[SessionCredentials::AccessKey] = m_extFields[SessionCredentials::AccessKey]; - extJson[SessionCredentials::ONSChannelKey] = m_extFields[SessionCredentials::ONSChannelKey]; - root["extFields"] = extJson; - } - - Json::FastWriter fastwrite; - string data = fastwrite.write(root); - - uint32 headLen = data.size(); - uint32 totalLen = 4 + headLen + m_body.getSize(); - - uint32 messageHeader[2]; - messageHeader[0] = htonl(totalLen); - messageHeader[1] = htonl(headLen); - - //(mem.getData()); - Json::Reader reader; - Json::Value object; - const char* begin = pData + 4; - const char* end = pData + 4 + headLen; - - if (!reader.parse(begin, end, object)) { - THROW_MQEXCEPTION(MQClientException, "conn't parse json", -1); - } - - int code = object["code"].asInt(); - - string language = object["language"].asString(); - int version = object["version"].asInt(); - int opaque = object["opaque"].asInt(); - int flag = object["flag"].asInt(); - Json::Value v = object["remark"]; - string remark = ""; - if (!v.isNull()) { - remark = object["remark"].asString(); - } - LOG_DEBUG( - "code:%d, language:%s, version:%d, opaque:%d, flag:%d, remark:%s, " - "headLen:%d, bodyLen:%d ", - code, language.c_str(), version, opaque, flag, remark.c_str(), headLen, bodyLen); - RemotingCommand* cmd = new RemotingCommand(code, language, version, opaque, flag, remark, NULL); - cmd->setParsedJson(object); - if (bodyLen > 0) { - cmd->SetBody(pData + 4 + headLen, bodyLen); - } - if (object.isMember("extFields")) { - Json::Value& extFields = object["extFields"]; - for (auto& it : extFields.getMemberNames()) { - cmd->m_extFields[it] = extFields[it].asString(); - } - } - return cmd; -} - -void RemotingCommand::markResponseType() { - int bits = 1 << RPC_TYPE; - m_flag |= bits; -} - -bool RemotingCommand::isResponseType() { - int bits = 1 << RPC_TYPE; - return (m_flag & bits) == bits; -} - -void RemotingCommand::markOnewayRPC() { - int bits = 1 << RPC_ONEWAY; - m_flag |= bits; -} - -bool RemotingCommand::isOnewayRPC() { - int bits = 1 << RPC_ONEWAY; - return (m_flag & bits) == bits; -} - -void RemotingCommand::setOpaque(const int opa) { - m_opaque = opa; -} - -void RemotingCommand::SetExtHeader(int code) { - try { - Json::Value ext = m_parsedJson["extFields"]; - if (!ext.isNull()) { - m_pExtHeader = NULL; - switch (code) { - case SEND_MESSAGE: - case SEND_MESSAGE_V2: - m_pExtHeader.reset(SendMessageResponseHeader::Decode(ext)); - break; - case PULL_MESSAGE: - m_pExtHeader.reset(PullMessageResponseHeader::Decode(ext)); - break; - case GET_MIN_OFFSET: - m_pExtHeader.reset(GetMinOffsetResponseHeader::Decode(ext)); - break; - case GET_MAX_OFFSET: - m_pExtHeader.reset(GetMaxOffsetResponseHeader::Decode(ext)); - break; - case SEARCH_OFFSET_BY_TIMESTAMP: - m_pExtHeader.reset(SearchOffsetResponseHeader::Decode(ext)); - break; - case GET_EARLIEST_MSG_STORETIME: - m_pExtHeader.reset(GetEarliestMsgStoretimeResponseHeader::Decode(ext)); - break; - case QUERY_CONSUMER_OFFSET: - m_pExtHeader.reset(QueryConsumerOffsetResponseHeader::Decode(ext)); - break; - case RESET_CONSUMER_CLIENT_OFFSET: - m_pExtHeader.reset(ResetOffsetRequestHeader::Decode(ext)); - break; - case GET_CONSUMER_RUNNING_INFO: - m_pExtHeader.reset(GetConsumerRunningInfoRequestHeader::Decode(ext)); - break; - case NOTIFY_CONSUMER_IDS_CHANGED: - m_pExtHeader.reset(NotifyConsumerIdsChangedRequestHeader::Decode(ext)); - break; - case CHECK_TRANSACTION_STATE: - m_pExtHeader.reset(CheckTransactionStateRequestHeader::Decode(ext)); - break; - default: - break; - } - } - } catch (MQException& e) { - LOG_ERROR("set response head error"); - } -} - -void RemotingCommand::setCode(int code) { - m_code = code; -} - -int RemotingCommand::getCode() const { - return m_code; -} - -int RemotingCommand::getOpaque() const { - return m_opaque; -} - -string RemotingCommand::getRemark() const { - return m_remark; -} - -void RemotingCommand::setRemark(string mark) { - m_remark = mark; -} - -CommandHeader* RemotingCommand::getCommandHeader() const { - return m_pExtHeader.get(); -} - -void RemotingCommand::setParsedJson(const Json::Value& json) { - m_parsedJson = json; -} - -const int RemotingCommand::getFlag() const { - return m_flag; -} - -const int RemotingCommand::getVersion() const { - return m_version; -} - -void RemotingCommand::setMsgBody(const string& body) { - m_msgBody = body; -} - -string RemotingCommand::getMsgBody() const { - return m_msgBody; -} - -void RemotingCommand::addExtField(const string& key, const string& value) { - m_extFields[key] = value; -} - -const unordered_map* RemotingCommand::getExtFields() const{ - return &m_extFields; -} - -std::string RemotingCommand::ToString() const { - std::stringstream ss; - ss << "code:" << m_code << ",opaque:" << m_opaque << ",flag:" << m_flag << ",body.size:" << m_body.getSize() - << ",header.size:" << m_head.getSize(); - return ss.str(); -} - -} // namespace rocketmq diff --git a/src/protocol/RemotingCommand.h b/src/protocol/RemotingCommand.h deleted file mode 100644 index b0525b72a..000000000 --- a/src/protocol/RemotingCommand.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __REMOTINGCOMMAND_H__ -#define __REMOTINGCOMMAND_H__ -#include -#include -#include -#include -#include -#include -#include "CommandHeader.h" -#include "dataBlock.h" - -namespace rocketmq { -//* getExtFields() const; - string getMsgBody() const; - void setMsgBody(const string& body); - - public: - void Encode(); - static RemotingCommand* Decode(const MemoryBlock& mem); - std::string ToString() const; - - private: - void Assign(const RemotingCommand& command); - - private: - int m_code; - string m_language; - int m_version; - int m_opaque; - int m_flag; - string m_remark; - string m_msgBody; - unordered_map m_extFields; - - MemoryBlock m_head; - MemoryBlock m_body; - // m_pExtHeader; - - static boost::atomic s_seqNumber; -}; - -} // -#include -#include "dataBlock.h" - -namespace rocketmq { -// m_topicList; -}; -// -#include -#include "Logging.h" -#include "UtilAll.h" -#include "dataBlock.h" -#include "json/json.h" - -namespace rocketmq { - -// brokerAddrs; //(mem->getData()); - string data(pData, mem->getSize()); - - Json::CharReaderBuilder charReaderBuilder; - charReaderBuilder.settings_["allowNumericKeys"] = true; - unique_ptr pCharReaderPtr(charReaderBuilder.newCharReader()); - - const char* begin = pData; - const char* end = pData + mem->getSize(); - Json::Value root; - string errs; - - if (!pCharReaderPtr->parse(begin, end, &root, &errs)) { - LOG_ERROR("parse json error:%s, value isArray:%d, isObject:%d", errs.c_str(), root.isArray(), root.isObject()); - return nullptr; - } - - auto* trd = new TopicRouteData(); - trd->setOrderTopicConf(root["orderTopicConf"].asString()); - - Json::Value qds = root["queueDatas"]; - for (auto qd : qds) { - QueueData d; - d.brokerName = qd["brokerName"].asString(); - d.readQueueNums = qd["readQueueNums"].asInt(); - d.writeQueueNums = qd["writeQueueNums"].asInt(); - d.perm = qd["perm"].asInt(); - trd->getQueueDatas().push_back(d); - } - sort(trd->getQueueDatas().begin(), trd->getQueueDatas().end()); - - Json::Value bds = root["brokerDatas"]; - for (auto bd : bds) { - BrokerData d; - d.brokerName = bd["brokerName"].asString(); - LOG_DEBUG("brokerName:%s", d.brokerName.c_str()); - Json::Value bas = bd["brokerAddrs"]; - Json::Value::Members mbs = bas.getMemberNames(); - for (const auto& key : mbs) { - int id = atoi(key.c_str()); - string addr = bas[key].asString(); - d.brokerAddrs[id] = addr; - LOG_DEBUG("brokerId:%d, brokerAddr:%s", id, addr.c_str()); - } - trd->getBrokerDatas().push_back(d); - } - sort(trd->getBrokerDatas().begin(), trd->getBrokerDatas().end()); - - return trd; - } - - /** - * Selects a (preferably master) broker address from the registered list. - * If the master's address cannot be found, a slave broker address is selected in a random manner. - * - * @return Broker address. - */ - std::string selectBrokerAddr() { - int bdSize = m_brokerDatas.size(); - if (bdSize > 0) { - int bdIndex = std::rand() % bdSize; - auto bd = m_brokerDatas[bdIndex]; - auto iter = bd.brokerAddrs.find(MASTER_ID); - if (iter == bd.brokerAddrs.end()) { - int baSize = bd.brokerAddrs.size(); - int baIndex = std::rand() % baSize; - iter = bd.brokerAddrs.begin(); - for (; baIndex > 0; baIndex--) { - iter++; - } - } - return iter->second; - } - return ""; - } - - std::vector& getQueueDatas() { return m_queueDatas; } - - std::vector& getBrokerDatas() { return m_brokerDatas; } - - const std::string& getOrderTopicConf() const { return m_orderTopicConf; } - - void setOrderTopicConf(const string& orderTopicConf) { m_orderTopicConf = orderTopicConf; } - - bool operator==(const TopicRouteData& other) const { - return m_brokerDatas == other.m_brokerDatas && m_orderTopicConf == other.m_orderTopicConf && - m_queueDatas == other.m_queueDatas; - } - - private: - std::string m_orderTopicConf; - std::vector m_queueDatas; - std::vector m_brokerDatas; -}; - -} // namespace rocketmq - -#endif diff --git a/src/status/ConsumeStats.cpp b/src/status/ConsumeStats.cpp deleted file mode 100644 index 3272347df..000000000 --- a/src/status/ConsumeStats.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ConsumeStats.h" - -namespace rocketmq { -ConsumeStats::ConsumeStats() { - pullRT = 0.0; - pullTPS = 0.0; - consumeRT = 0.0; - consumeOKTPS = 0.0; - consumeFailedTPS = 0.0; - consumeFailedMsgs = 0; -} -Json::Value ConsumeStats::toJson() const { - Json::Value outJson; - outJson["pullRT"] = pullRT; - outJson["pullTPS"] = pullTPS; - outJson["consumeRT"] = consumeRT; - outJson["consumeOKTPS"] = consumeOKTPS; - outJson["consumeFailedTPS"] = consumeFailedTPS; - outJson["consumeFailedMsgs"] = consumeFailedMsgs; - return outJson; -} -} // namespace rocketmq diff --git a/src/status/ConsumeStats.h b/src/status/ConsumeStats.h deleted file mode 100644 index e6f76a500..000000000 --- a/src/status/ConsumeStats.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __CONSUMER_STATUS_H__ -#define __CONSUMER_STATUS_H__ - -#include "UtilAll.h" -#include "json/json.h" - -namespace rocketmq { -class ConsumeStats { - public: - ConsumeStats(); - virtual ~ConsumeStats() {} - Json::Value toJson() const; - - public: - double pullRT; - double pullTPS; - double consumeRT; - double consumeOKTPS; - double consumeFailedTPS; - uint64 consumeFailedMsgs; -}; - -} // namespace rocketmq - -#endif diff --git a/src/status/StatsItem.h b/src/status/StatsItem.h deleted file mode 100644 index 5fd3f3dec..000000000 --- a/src/status/StatsItem.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __CONSUMER_STATUS_ITEM_H__ -#define __CONSUMER_STATUS_ITEM_H__ - -#include "RocketMQClient.h" - -namespace rocketmq { -class StatsItem { - public: - StatsItem() { - pullRT = 0; - pullRTCount = 0; - pullCount = 0; - consumeRT = 0; - consumeRTCount = 0; - consumeOKCount = 0; - consumeFailedCount = 0; - consumeFailedMsgs = 0; - }; - virtual ~StatsItem() {} - - public: - uint64 pullRT; - uint64 pullRTCount; - uint64 pullCount; - uint64 consumeRT; - uint64 consumeRTCount; - uint64 consumeOKCount; - uint64 consumeFailedCount; - uint64 consumeFailedMsgs; -}; - -} // namespace rocketmq - -#endif diff --git a/src/status/StatsServer.cpp b/src/status/StatsServer.cpp deleted file mode 100644 index 0ac306379..000000000 --- a/src/status/StatsServer.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "StatsServer.h" -#include -#include -#include "Logging.h" -#include "RocketMQClient.h" -#include "StatsItem.h" - -namespace rocketmq { -const std::string StatsServer::TOPIC_AND_GROUP_CONSUME_OK_TPS = "CONSUME_OK_TPS"; -const std::string StatsServer::TOPIC_AND_GROUP_CONSUME_FAILED_TPS = "CONSUME_FAILED_TPS"; -const std::string StatsServer::TOPIC_AND_GROUP_CONSUME_RT = "CONSUME_RT"; -const std::string StatsServer::TOPIC_AND_GROUP_PULL_TPS = "PULL_TPS"; -const std::string StatsServer::TOPIC_AND_GROUP_PULL_RT = "PULL_RT"; -const int StatsServer::SAMPLING_PERIOD = 10; -StatsServer::StatsServer() { - m_status = CREATE_JUST; - serverName = "default"; -} -StatsServer::StatsServer(std::string sName) { - m_status = CREATE_JUST; - serverName = sName; -} -StatsServer::~StatsServer() {} -void StatsServer::start() { - switch (m_status) { - case CREATE_JUST: - m_status = START_FAILED; - startScheduledTask(); - LOG_INFO("Default Status Service Start."); - m_status = RUNNING; - break; - case RUNNING: - case SHUTDOWN_ALREADY: - case START_FAILED: - break; - default: - break; - } -} -void StatsServer::shutdown() { - switch (m_status) { - case CREATE_JUST: - case RUNNING: - LOG_INFO("Default Status Service ShutDown."); - stopScheduledTask(); - m_status = SHUTDOWN_ALREADY; - break; - case SHUTDOWN_ALREADY: - case START_FAILED: - break; - default: - break; - } -} -ConsumeStats StatsServer::getConsumeStats(std::string topic, std::string groupName) { - ConsumeStats consumeStats; - LOG_DEBUG("getConsumeStats Topic:%s, Group:%s", topic.c_str(), groupName.c_str()); - if (m_status == RUNNING) { - std::string key = topic + "@" + groupName; - LOG_DEBUG("getConsumeStats Key:%s", key.c_str()); - return getConsumeStats(key); - } - return consumeStats; -} -void StatsServer::incPullRT(std::string topic, std::string groupName, uint64 rt) { - std::string key = topic + "@" + groupName; - std::lock_guard lock(m_consumeStatsItemMutex); - if (m_consumeStatsItems.find(key) == m_consumeStatsItems.end()) { - StatsItem item; - m_consumeStatsItems[key] = item; - } - m_consumeStatsItems[key].pullRT += rt; - m_consumeStatsItems[key].pullRTCount += 1; -} -void StatsServer::incPullTPS(std::string topic, std::string groupName, uint64 msgCount) { - std::string key = topic + "@" + groupName; - std::lock_guard lock(m_consumeStatsItemMutex); - if (m_consumeStatsItems.find(key) == m_consumeStatsItems.end()) { - StatsItem item; - m_consumeStatsItems[key] = item; - } - m_consumeStatsItems[key].pullCount += msgCount; -} -void StatsServer::incConsumeRT(std::string topic, std::string groupName, uint64 rt, uint64 msgCount) { - std::string key = topic + "@" + groupName; - LOG_DEBUG("incConsumeRT before Key:%s, RT:%lld, Count: %lld", key.c_str(), rt, msgCount); - - std::lock_guard lock(m_consumeStatsItemMutex); - if (m_consumeStatsItems.find(key) == m_consumeStatsItems.end()) { - StatsItem item; - m_consumeStatsItems[key] = item; - } - m_consumeStatsItems[key].consumeRT += rt; - m_consumeStatsItems[key].consumeRTCount += msgCount; - LOG_DEBUG("incConsumeRT After Key:%s, RT:%lld, Count: %lld", key.c_str(), m_consumeStatsItems[key].consumeRT, - m_consumeStatsItems[key].consumeRTCount); -} -void StatsServer::incConsumeOKTPS(std::string topic, std::string groupName, uint64 msgCount) { - std::string key = topic + "@" + groupName; - LOG_DEBUG("incConsumeOKTPS Before Key:%s, Count: %lld", key.c_str(), msgCount); - std::lock_guard lock(m_consumeStatsItemMutex); - if (m_consumeStatsItems.find(key) == m_consumeStatsItems.end()) { - StatsItem item; - m_consumeStatsItems[key] = item; - } - m_consumeStatsItems[key].consumeOKCount += msgCount; - LOG_DEBUG("incConsumeOKTPS After Key:%s, Count: %lld", key.c_str(), m_consumeStatsItems[key].consumeOKCount); -} -void StatsServer::incConsumeFailedTPS(std::string topic, std::string groupName, uint64 msgCount) { - std::string key = topic + "@" + groupName; - LOG_DEBUG("incConsumeFailedTPS Key:%s, Count: %lld", key.c_str(), msgCount); - std::lock_guard lock(m_consumeStatsItemMutex); - if (m_consumeStatsItems.find(key) == m_consumeStatsItems.end()) { - StatsItem item; - m_consumeStatsItems[key] = item; - } - m_consumeStatsItems[key].consumeFailedCount += msgCount; - m_consumeStatsItems[key].consumeFailedMsgs += msgCount; -} -void StatsServer::incConsumeFailedMsgs(std::string topic, std::string groupName, uint64 msgCount) { - std::string key = topic + "@" + groupName; - LOG_DEBUG("incConsumeFailedTPS Key:%s, Count: %lld", key.c_str(), msgCount); - std::lock_guard lock(m_consumeStatsItemMutex); - if (m_consumeStatsItems.find(key) == m_consumeStatsItems.end()) { - StatsItem item; - m_consumeStatsItems[key] = item; - } - m_consumeStatsItems[key].consumeFailedMsgs += msgCount; -} -void StatsServer::startScheduledTask() { - m_consumer_status_service_thread.reset(new boost::thread(boost::bind(&StatsServer::doStartScheduledTask, this))); -} -void StatsServer::stopScheduledTask() { - if (m_consumer_status_service_thread) { - m_consumerStatus_ioService.stop(); - m_consumer_status_service_thread->interrupt(); - m_consumer_status_service_thread->join(); - m_consumer_status_service_thread.reset(); - } -} -void StatsServer::doStartScheduledTask() { - boost::asio::io_service::work work(m_consumerStatus_ioService); - boost::system::error_code ec1; - std::shared_ptr t1 = std::make_shared( - m_consumerStatus_ioService, boost::posix_time::seconds(SAMPLING_PERIOD)); - t1->async_wait(boost::bind(&StatsServer::scheduledTaskInSeconds, this, ec1, t1)); - - boost::system::error_code errorCode; - m_consumerStatus_ioService.run(errorCode); -} -void StatsServer::scheduledTaskInSeconds(boost::system::error_code& ec, - std::shared_ptr t) { - samplingInSeconds(); - - boost::system::error_code e; - t->expires_from_now(t->expires_from_now() + boost::posix_time::seconds(SAMPLING_PERIOD), e); - t->async_wait(boost::bind(&StatsServer::scheduledTaskInSeconds, this, ec, t)); -} -void StatsServer::samplingInSeconds() { - LOG_DEBUG("samplingInSeconds=="); - - // do samplings - std::lock_guard lock(m_consumeStatsItemMutex); - for (std::map::iterator it = m_consumeStatsItems.begin(); it != m_consumeStatsItems.end(); - ++it) { - ConsumeStats consumeStats; - if (it->second.pullRTCount != 0) { - consumeStats.pullRT = (1.0 * (it->second.pullRT)) / (it->second.pullRTCount); - } - it->second.pullRT = 0; - it->second.pullRTCount = 0; - consumeStats.pullTPS = (1.0 * (it->second.pullCount)) / SAMPLING_PERIOD; - it->second.pullCount = 0; - if (it->second.consumeRTCount != 0) { - consumeStats.consumeRT = (1.0 * (it->second.consumeRT)) / (it->second.consumeRTCount); - LOG_DEBUG("samplingInSeconds Key[%s], consumeRT:%.2f,Total RT:%lld, Count: %lld", it->first.c_str(), - consumeStats.consumeRT, it->second.consumeRT, it->second.consumeRTCount); - } - it->second.consumeRT = 0; - it->second.consumeRTCount = 0; - consumeStats.consumeOKTPS = (1.0 * (it->second.consumeOKCount)) / SAMPLING_PERIOD; - LOG_DEBUG("samplingInSeconds Key[%s], consumeOKTPS:%.2f, Count: %lld", it->first.c_str(), consumeStats.consumeOKTPS, - it->second.consumeOKCount); - it->second.consumeOKCount = 0; - consumeStats.consumeFailedTPS = (1.0 * (it->second.consumeFailedCount)) / SAMPLING_PERIOD; - it->second.consumeFailedCount = 0; - LOG_DEBUG("samplingInSeconds Key[%s], consumeFailedTPS:%.2f, Count: %lld", it->first.c_str(), - consumeStats.consumeFailedTPS, it->second.consumeFailedCount); - consumeStats.consumeFailedMsgs = it->second.consumeFailedMsgs; - // it->second.consumeFailedMsgs = 0; - updateConsumeStats(it->first, consumeStats); - } -} - -void StatsServer::updateConsumeStats(std::string topic, std::string groupName, ConsumeStats consumeStats) { - if (m_status == RUNNING) { - std::string key = topic + "@" + groupName; - updateConsumeStats(key, consumeStats); - } -} -void StatsServer::updateConsumeStats(std::string key, ConsumeStats consumeStats) { - LOG_DEBUG("updateConsumeStats Key:%s, Count: %lld", key.c_str(), consumeStats.consumeOKTPS); - - std::lock_guard lock(m_consumeStatusMutex); - m_consumeStatus[key] = consumeStats; -} -ConsumeStats StatsServer::getConsumeStats(std::string key) { - ConsumeStats consumeStats; - std::lock_guard lock(m_consumeStatusMutex); - if (m_consumeStatus.find(key) != m_consumeStatus.end()) { - return m_consumeStatus[key]; - } - return consumeStats; -} -} // namespace rocketmq diff --git a/src/status/StatsServer.h b/src/status/StatsServer.h deleted file mode 100644 index e105f23df..000000000 --- a/src/status/StatsServer.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __CONSUMER_STATUS_SERVICE_H__ -#define __CONSUMER_STATUS_SERVICE_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ConsumeStats.h" -#include "RocketMQClient.h" -#include "ServiceState.h" -#include "StatsItem.h" - -namespace rocketmq { -class StatsServer { - public: - static const std::string TOPIC_AND_GROUP_CONSUME_OK_TPS; - static const std::string TOPIC_AND_GROUP_CONSUME_FAILED_TPS; - static const std::string TOPIC_AND_GROUP_CONSUME_RT; - static const std::string TOPIC_AND_GROUP_PULL_TPS; - static const std::string TOPIC_AND_GROUP_PULL_RT; - static const int SAMPLING_PERIOD; - StatsServer(); - StatsServer(std::string serverName); - virtual ~StatsServer(); - void start(); - void shutdown(); - ConsumeStats getConsumeStats(std::string topic, std::string groupName); - void incPullRT(std::string topic, std::string groupName, uint64 rt); - void incPullTPS(std::string topic, std::string groupName, uint64 msgCount); - void incConsumeRT(std::string topic, std::string groupName, uint64 rt, uint64 msgCount = 1); - void incConsumeOKTPS(std::string topic, std::string groupName, uint64 msgCount); - void incConsumeFailedTPS(std::string topic, std::string groupName, uint64 msgCount); - void incConsumeFailedMsgs(std::string topic, std::string groupName, uint64 msgCount); - - private: - void startScheduledTask(); - void stopScheduledTask(); - void doStartScheduledTask(); - void scheduledTaskInSeconds(boost::system::error_code& ec, std::shared_ptr t); - void samplingInSeconds(); - void updateConsumeStats(std::string topic, std::string groupName, ConsumeStats consumeStats); - void updateConsumeStats(std::string key, ConsumeStats consumeStats); - ConsumeStats getConsumeStats(std::string key); - - public: - std::string serverName; - - private: - ServiceState m_status; - std::mutex m_consumeStatusMutex; - std::map m_consumeStatus; - boost::asio::io_service m_consumerStatus_ioService; - // boost::asio::io_service::work m_consumerStatus_work; - std::unique_ptr m_consumer_status_service_thread; - std::mutex m_consumeStatsItemMutex; - std::map m_consumeStatsItems; -}; - -} // namespace rocketmq - -#endif diff --git a/src/status/StatsServerManager.cpp b/src/status/StatsServerManager.cpp deleted file mode 100644 index f3a6d1cdb..000000000 --- a/src/status/StatsServerManager.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "StatsServerManager.h" -#include "RocketMQClient.h" -#include "StatsServer.h" -#include "string" - -namespace rocketmq { -StatsServerManager::StatsServerManager() { - serverName = "default"; -} - -StatsServerManager::~StatsServerManager() { - m_consumeStatusServers.clear(); -} -std::shared_ptr StatsServerManager::getConsumeStatServer() { - return getConsumeStatServer(serverName); -} -std::shared_ptr StatsServerManager::getConsumeStatServer(std::string serverName) { - std::map>::iterator it = m_consumeStatusServers.find(serverName); - if (it != m_consumeStatusServers.end()) { - return it->second; - } else { - std::shared_ptr server = std::make_shared(); - m_consumeStatusServers[serverName] = server; - return server; - } -} -void StatsServerManager::removeConsumeStatServer(std::string serverName) { - std::map>::iterator it = m_consumeStatusServers.find(serverName); - if (it != m_consumeStatusServers.end()) { - m_consumeStatusServers.erase(it); - } -} - -StatsServerManager* StatsServerManager::getInstance() { - static StatsServerManager instance; - return &instance; -} -} // namespace rocketmq diff --git a/src/status/StatsServerManager.h b/src/status/StatsServerManager.h deleted file mode 100644 index 923ea6110..000000000 --- a/src/status/StatsServerManager.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __CONSUMER_STATUS_SERVICE_MANAGER_H__ -#define __CONSUMER_STATUS_SERVICE_MANAGER_H__ - -#include -#include -#include -#include "ConsumeStats.h" -#include "RocketMQClient.h" -#include "ServiceState.h" -#include "StatsServer.h" - -namespace rocketmq { -class StatsServerManager { - public: - virtual ~StatsServerManager(); - // void start(); - // void shutdown(); - std::shared_ptr getConsumeStatServer(); - std::shared_ptr getConsumeStatServer(std::string serverName); - void removeConsumeStatServer(std::string serverName); - - static StatsServerManager* getInstance(); - - private: - StatsServerManager(); - - public: - std::string serverName; - - private: - std::map> m_consumeStatusServers; -}; - -} // namespace rocketmq - -#endif diff --git a/src/test/cpp/benchmark/BUILD.bazel b/src/test/cpp/benchmark/BUILD.bazel new file mode 100644 index 000000000..c0cabf8f3 --- /dev/null +++ b/src/test/cpp/benchmark/BUILD.bazel @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") + +cc_test( + name = "benchmark_test", + srcs = [ + "BenchmarkTest.cpp", + ], + deps = [ + "//external:benchmark", + ], +) \ No newline at end of file diff --git a/src/test/cpp/benchmark/BenchmarkTest.cpp b/src/test/cpp/benchmark/BenchmarkTest.cpp new file mode 100644 index 000000000..421541d2b --- /dev/null +++ b/src/test/cpp/benchmark/BenchmarkTest.cpp @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "benchmark/benchmark.h" + +static void BM_StringCreation(benchmark::State& state) { + for (auto _ : state) + std::string empty_string; +} +// Register the function as a benchmark +BENCHMARK(BM_StringCreation); + +// Define another benchmark +static void BM_StringCopy(benchmark::State& state) { + std::string x = "hello"; + for (auto _ : state) + std::string copy(x); +} +BENCHMARK(BM_StringCopy); + +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/src/test/cpp/it/BUILD.bazel b/src/test/cpp/it/BUILD.bazel new file mode 100644 index 000000000..b4cabee09 --- /dev/null +++ b/src/test/cpp/it/BUILD.bazel @@ -0,0 +1,51 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "it_top_addressing", + srcs = [ + "TopAddressingTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "it_rpc_client", + srcs = [ + "RpcClientTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "it_topic_publish_info", + srcs = [ + "TopicPublishInfoTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/it/README.md b/src/test/cpp/it/README.md new file mode 100644 index 000000000..d9bac84d3 --- /dev/null +++ b/src/test/cpp/it/README.md @@ -0,0 +1,4 @@ +## Introduction + +Execution of integration test relies on presence of daily environment, which provides dependent services. Dependent +services are discovered through configured DNS records in hosts file. \ No newline at end of file diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp new file mode 100644 index 000000000..fe3920420 --- /dev/null +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -0,0 +1,374 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include "absl/container/flat_hash_set.h" +#include "apache/rocketmq/v1/service.pb.h" +#include "google/rpc/code.pb.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" + +#include "ClientConfigImpl.h" +#include "InvocationContext.h" +#include "LogInterceptorFactory.h" +#include "MixAll.h" +#include "RpcClientImpl.h" +#include "Signature.h" +#include "TlsHelper.h" +#include "UniqueIdGenerator.h" +#include "UtilAll.h" +#include "rocketmq/CredentialsProvider.h" +#include "rocketmq/Logger.h" + +using namespace testing; + +ROCKETMQ_NAMESPACE_BEGIN + +class RpcClientTest : public ::testing::Test { +protected: + RpcClientTest() : completion_queue_(std::make_shared()) { + + server_authorization_check_config_ = std::make_shared( + std::make_shared()); + std::vector pem_list; + grpc::experimental::IdentityKeyCertPair pair{}; + pair.private_key = TlsHelper::client_private_key; + pair.certificate_chain = TlsHelper::client_certificate_chain; + pem_list.emplace_back(pair); + certificate_provider_ = + std::make_shared(TlsHelper::CA, pem_list); + tls_channel_credential_options_.set_certificate_provider(certificate_provider_); + tls_channel_credential_options_.set_server_verification_option(GRPC_TLS_SKIP_ALL_SERVER_VERIFICATION); + tls_channel_credential_options_.set_server_authorization_check_config(server_authorization_check_config_); + tls_channel_credential_options_.watch_root_certs(); + tls_channel_credential_options_.watch_identity_key_cert_pairs(); + channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); + credentials_provider_ = std::make_shared(); + client_config_.tenantId(tenant_id_); + client_config_.setCredentialsProvider(credentials_provider_); + } + + void SetUp() override { + getLogger().setLevel(Level::Debug); + client_config_.setCredentialsProvider(std::make_shared()); + client_config_.resourceNamespace(resource_namespace_); + client_config_.region(region_id_); + client_config_.tenantId(tenant_id_); + + Signature::sign(&client_config_, metadata_); + + std::vector> interceptor_factories; + interceptor_factories.emplace_back(absl::make_unique()); + name_server_channel_ = grpc::experimental::CreateCustomChannelWithInterceptors( + name_server_target_, channel_credential_, channel_arguments_, std::move(interceptor_factories)); + } + + void TearDown() override { + } + + ~RpcClientTest() override { + completion_queue_->Shutdown(); + } + + bool brokerEndpoint(const std::string& topic, std::string& endpoint) { + QueryRouteRequest route_request; + route_request.mutable_topic()->set_name(topic.data()); + QueryRouteResponse route_response; + RpcClientImpl name_server_client(completion_queue_, name_server_channel_); + + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + + auto callback = [&](const InvocationContext* invocation_context) { + ASSERT_TRUE(invocation_context->status.ok()); + std::cout << "Route Response:" << route_response.DebugString() << std::endl; + absl::MutexLock lk(&mtx); + route_response = invocation_context->response; + completed = true; + cv.SignalAll(); + }; + + auto invocation_context = new InvocationContext(); + invocation_context->callback = callback; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(3)); + + for (const auto& item : metadata_) { + invocation_context->context.AddMetadata(item.first, item.second); + } + + name_server_client.asyncQueryRoute(route_request, invocation_context); + + absl::flat_hash_set broker_addresses; + for (auto& partition : route_response.partitions()) { + auto& broker = partition.broker(); + if (MixAll::MASTER_BROKER_ID == broker.id()) { + for (auto& item : broker.endpoints().addresses()) { + std::string connect_string(fmt::format("{}:{}", item.host(), item.port())); + if (!broker_addresses.contains(connect_string)) { + broker_addresses.insert(connect_string); + } + } + } else { + for (const auto& item : broker.endpoints().addresses()) { + SPDLOG_WARN("Unexpected endpoint[{}:{}] with brokerId={}", item.host(), item.port(), broker.id()); + } + } + } + if (broker_addresses.empty()) { + return false; + } + + endpoint = *broker_addresses.begin(); + return true; + } + + void fillSendMessageRequest(SendMessageRequest& send_message_request) { + send_message_request.mutable_message()->mutable_topic()->set_resource_namespace(resource_namespace_); + send_message_request.mutable_message()->mutable_topic()->set_name(topic_); + std::unordered_map props; + props["key"] = "value"; + props["Jack"] = "Bauer"; + send_message_request.mutable_message()->mutable_user_attribute()->insert(props.begin(), props.end()); + auto system_attribute = send_message_request.mutable_message()->mutable_system_attribute(); + system_attribute->set_message_id(message_id_); + system_attribute->set_message_type(rmq::MessageType::NORMAL); + system_attribute->set_body_encoding(rmq::Encoding::IDENTITY); + system_attribute->set_born_host(UtilAll::hostname()); + system_attribute->set_tag("TagA"); + send_message_request.mutable_message()->set_body("Example data"); + } + + RpcClientSharedPtr brokerRpcClient() { + std::string broker_endpoint; + bool success = brokerEndpoint(topic_, broker_endpoint); + if (!success) { + return nullptr; + } + SPDLOG_INFO("Target broker address: {}", broker_endpoint); + + std::vector> interceptor_factories; + interceptor_factories.emplace_back(absl::make_unique()); + auto broker_channel = grpc::experimental::CreateCustomChannelWithInterceptors( + broker_endpoint, channel_credential_, channel_arguments_, std::move(interceptor_factories)); + auto client = std::make_shared(completion_queue_, broker_channel); + return client; + } + + void fillHeartbeatRequest(HeartbeatRequest& heartbeat_request) { + heartbeat_request.set_client_id("client_id_0"); + auto consumer_data = heartbeat_request.mutable_consumer_data(); + consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); + consumer_data->mutable_group()->set_name(topic_); + + auto subscription_entry = new rmq::SubscriptionEntry; + subscription_entry->mutable_topic()->set_name(topic_); + subscription_entry->mutable_topic()->set_resource_namespace(resource_namespace_); + subscription_entry->mutable_expression()->set_type(rmq::FilterType::TAG); + subscription_entry->mutable_expression()->set_expression("*"); + consumer_data->mutable_subscriptions()->AddAllocated(subscription_entry); + heartbeat_request.set_fifo_flag(false); + } + + std::string name_server_target_{"47.98.116.189:80"}; + std::shared_ptr completion_queue_; + std::shared_ptr name_server_channel_; + std::string topic_{"cpp_sdk_standard"}; + std::string group_{"GID_cpp_sdk_standard"}; + std::string resource_namespace_{"MQ_INST_1080056302921134_BXuIbML7"}; + std::string tenant_id_{"sample-tenant"}; + std::string region_id_{"cn-hangzhou"}; + std::string service_name_{"MQ"}; + absl::flat_hash_map metadata_; + ClientConfigImpl client_config_{group_}; + CredentialsProviderPtr credentials_provider_; + std::shared_ptr certificate_provider_; + grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_options_; + std::shared_ptr server_authorization_check_config_; + std::shared_ptr channel_credential_; + grpc::ChannelArguments channel_arguments_; + std::string message_id_{UniqueIdGenerator::instance().next()}; +}; + +TEST_F(RpcClientTest, testRouteInfo) { + RpcClientImpl client(completion_queue_, name_server_channel_); + QueryRouteRequest request; + request.mutable_topic()->set_name(topic_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); + auto invocation_context = new InvocationContext(); + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + auto callback = [&](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_ERROR("Status not OK"); + } + ASSERT_TRUE(invocation_context->status.ok()); + EXPECT_TRUE(google::rpc::Code::OK == invocation_context->response.common().status().code()); + EXPECT_FALSE(invocation_context->response.partitions().empty()); + { + absl::MutexLock lk(&mtx); + cv.SignalAll(); + } + }; + + invocation_context->callback = callback; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(3)); + for (const auto& item : metadata_) { + invocation_context->context.AddMetadata(item.first, item.second); + } + client.asyncQueryRoute(request, invocation_context); + + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +TEST_F(RpcClientTest, DISABLED_testSendMessageAsync) { + SendMessageRequest request; + fillSendMessageRequest(request); + auto client = brokerRpcClient(); + auto context = new InvocationContext(); + + for (const auto& entry : metadata_) { + context->context.AddMetadata(entry.first, entry.second); + } + + context->callback = [](const InvocationContext* invocation_context) { + if ((!invocation_context->status.ok())) { + std::cout << "error code: " << invocation_context->status.error_code() + << ", error message: " << invocation_context->status.error_message() << std::endl; + } + ASSERT_TRUE(invocation_context->status.ok()); + }; + client->asyncSend(request, context); + std::thread th([&]() { + InvocationContext* ctx; + bool ok = false; + completion_queue_->Next(reinterpret_cast(&ctx), &ok); + if (ok) { + ctx->onCompletion(ok); + } + }); + + if (th.joinable()) { + th.join(); + } +} + +TEST_F(RpcClientTest, DISABLED_testHeartbeat) { + auto client = brokerRpcClient(); + HeartbeatRequest heartbeat_request; + HeartbeatResponse response; + fillHeartbeatRequest(heartbeat_request); + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + auto invocation_context = new InvocationContext(); + + for (const auto& entry : metadata_) { + invocation_context->context.AddMetadata(entry.first, entry.second); + } + + invocation_context->callback = [&](const InvocationContext* invocation_context) { + ASSERT_TRUE(invocation_context->status.ok()); + EXPECT_TRUE(google::rpc::Code::OK == invocation_context->response.common().status().code()); + { + completed = true; + absl::MutexLock lk(&mtx); + cv.SignalAll(); + } + }; + + client->asyncHeartbeat(heartbeat_request, invocation_context); + + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +TEST_F(RpcClientTest, DISABLED_testQueryAssignment) { + auto client = brokerRpcClient(); + QueryAssignmentRequest request; + request.mutable_topic()->set_name(topic_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_group()->set_resource_namespace(resource_namespace_); + request.mutable_group()->set_name(group_); + QueryAssignmentResponse response; + + auto invocation_context = new InvocationContext(); + + for (const auto& entry : metadata_) { + invocation_context->context.AddMetadata(entry.first, entry.second); + } + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + auto callback = [&](const InvocationContext* invocation_context) { + ASSERT_TRUE(invocation_context->status.ok()); + ASSERT_FALSE(invocation_context->response.assignments().empty()); + completed = true; + { + absl::MutexLock lk(&mtx); + cv.SignalAll(); + } + }; + invocation_context->callback = callback; + client->asyncQueryAssignment(request, invocation_context); + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +TEST_F(RpcClientTest, DISABLED_testHealthCheck) { + auto client = brokerRpcClient(); + HealthCheckRequest request; + const std::string& client_host = UtilAll::hostname(); + request.set_client_host(client_host); + HealthCheckResponse response; + auto invocation_context = new InvocationContext(); + + for (const auto& entry : metadata_) { + invocation_context->context.AddMetadata(entry.first, entry.second); + } + + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + invocation_context->callback = [&](const InvocationContext* invocation_context) { + { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + } + EXPECT_TRUE(invocation_context->status.ok()); + EXPECT_EQ(google::rpc::Code::OK, invocation_context->response.common().status().code()); + }; + client->asyncHealthCheck(request, invocation_context); + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/TopAddressingTest.cpp b/src/test/cpp/it/TopAddressingTest.cpp new file mode 100644 index 000000000..42c7a13e8 --- /dev/null +++ b/src/test/cpp/it/TopAddressingTest.cpp @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "absl/synchronization/mutex.h" +#include "grpc/grpc.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" + +#include "RateLimiter.h" +#include "TopAddressing.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TopAddressingTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + spdlog::set_level(spdlog::level::debug); + } + + void TearDown() override { + grpc_shutdown(); + } + + void SetEnv(const char* key, const char* value) { + int overwrite = 1; +#ifdef _WIN32 + std::string env; + env.append(key); + env.push_back('='); + env.append(value); + _putenv(env.c_str()); +#else + setenv(key, value, overwrite); +#endif + } +}; + +TEST_F(TopAddressingTest, testFetchNameServerAddresses) { + std::vector list; + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + + bool success = false; + auto callback = [&](bool ok, const std::vector& name_server_list) { + success = ok; + list.insert(list.end(), name_server_list.begin(), name_server_list.end()); + { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + } + }; + TopAddressing top_addressing; + top_addressing.fetchNameServerAddresses(callback); + + while (!completed) { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithTimeout(&mtx, absl::Seconds(3)); + } + } + + ASSERT_TRUE(success); + EXPECT_FALSE(list.empty()); +} + +TEST_F(TopAddressingTest, testFetchNameServerAddresses_env) { + SetEnv(HostInfo::ENV_LABEL_UNIT, "CENTER_UNIT.center"); + SetEnv(HostInfo::ENV_LABEL_STAGE, "DAILY"); + std::vector list; + + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + + bool success = false; + auto callback = [&](bool ok, const std::vector& name_server_list) { + success = ok; + list.insert(list.end(), name_server_list.begin(), name_server_list.end()); + { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + } + }; + TopAddressing top_addressing; + top_addressing.fetchNameServerAddresses(callback); + + while (!completed) { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithTimeout(&mtx, absl::Seconds(3)); + } + } + + ASSERT_TRUE(success); + EXPECT_FALSE(list.empty()); +} + +TEST_F(TopAddressingTest, testPerf) { + TopAddressing top_addressing; + RateLimiter<10> rate_limiter(100); + + std::atomic_bool stopped(false); + std::atomic_long qps(0); + auto callback = [&](bool ok, const std::vector& name_sever_list) { + if (ok) { + qps++; + } else { + SPDLOG_WARN("Yuck, Bad HTTP response"); + } + }; + + auto benchmark = [&]() { + while (!stopped) { + // rate_limiter.acquire(); + SPDLOG_DEBUG("Submit a fetch request"); + top_addressing.fetchNameServerAddresses(callback); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }; + + auto stats = [&]() { + while (!stopped) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + long value = qps.load(std::memory_order_relaxed); + qps.fetch_sub(value, std::memory_order_relaxed); + SPDLOG_INFO("QPS: {}", value); + } + }; + + std::thread benchmark_thread(benchmark); + std::thread stats_thread(stats); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + stopped.store(true); + + if (stats_thread.joinable()) { + stats_thread.join(); + } + + if (benchmark_thread.joinable()) { + benchmark_thread.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp new file mode 100644 index 000000000..1edfea028 --- /dev/null +++ b/src/test/cpp/it/TopicPublishInfoTest.cpp @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TopicPublishInfo.h" +#include "ClientConfigImpl.h" +#include "LogInterceptorFactory.h" +#include "RpcClientImpl.h" +#include "Signature.h" +#include "TlsHelper.h" +#include "grpcpp/security/tls_credentials_options.h" +#include "rocketmq/MQMessageQueue.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TopicPublishInfoTest : public ::testing::Test { +protected: + TopicPublishInfoTest() : completion_queue_(std::make_shared()) { + server_authorization_check_config_ = std::make_shared( + std::make_shared()); + tls_channel_credential_option_.set_server_verification_option(GRPC_TLS_SKIP_HOSTNAME_VERIFICATION); + + std::vector pem_list; + grpc::experimental::IdentityKeyCertPair pair{}; + pair.private_key = TlsHelper::client_private_key; + pair.certificate_chain = TlsHelper::client_certificate_chain; + pem_list.emplace_back(pair); + certificate_provider_ = + std::make_shared(TlsHelper::CA, pem_list); + tls_channel_credential_option_.set_certificate_provider(certificate_provider_); + tls_channel_credential_option_.set_server_authorization_check_config(server_authorization_check_config_); + tls_channel_credential_option_.watch_root_certs(); + tls_channel_credential_option_.watch_identity_key_cert_pairs(); + channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_option_); + credentials_provider_ = std::make_shared(); + } + + void SetUp() override { + std::vector name_server_list; + + if (!name_server_list.empty()) { + target_ = *name_server_list.begin(); + } + std::vector> interceptor_factories; + client_config_.tenantId(tenant_id_); + client_config_.setCredentialsProvider(credentials_provider_); + interceptor_factories.emplace_back(absl::make_unique()); + auto channel = grpc::experimental::CreateCustomChannelWithInterceptors( + target_, channel_credential_, channel_arguments_, std::move(interceptor_factories)); + client_ = std::make_shared(completion_queue_, channel); + + client_config_.resourceNamespace(resource_namespace_); + client_config_.setCredentialsProvider(std::make_shared()); + client_config_.setIoTimeout(absl::Seconds(3)); + + Signature::sign(&client_config_, metadata_); + } + + void TearDown() override { + completion_queue_->Shutdown(); + } + + std::string topic_{"yc001"}; + std::string group_{"yc001"}; + std::string resource_namespace_{"MQ_INST_1973281269661160_BXmPlOA6"}; + std::string tenant_id_{"sample-tenant"}; + std::string region_id_{"cn-hangzhou"}; + std::string service_name_{"MQ"}; + std::string target_{"dns:grpc.dev:9876"}; + ClientConfigImpl client_config_{group_}; + absl::flat_hash_map metadata_; + std::shared_ptr completion_queue_; + std::shared_ptr client_; + CredentialsProviderPtr credentials_provider_; + std::shared_ptr certificate_provider_; + grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_option_; + std::shared_ptr server_authorization_check_config_; + std::shared_ptr channel_credential_; + grpc::ChannelArguments channel_arguments_; +}; + +TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { + rmq::QueryRouteResponse response; + rmq::QueryRouteRequest request; + request.mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_topic()->set_name(topic_); + auto invocation_context = new InvocationContext(); + invocation_context->context.set_deadline(std::chrono::system_clock::now() + + absl::ToChronoMilliseconds(client_config_.getIoTimeout())); + for (const auto& item : metadata_) { + invocation_context->context.AddMetadata(item.first, item.second); + } + + auto callback = [this](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + std::cout << "code: " << invocation_context->status.error_code() + << ", message: " << invocation_context->status.error_message() << std::endl; + } else { + std::cout << "Response debug string:" << invocation_context->response.DebugString() << std::endl; + } + ASSERT_TRUE(invocation_context->status.ok()); + + std::vector partitions; + for (const auto& item : invocation_context->response.partitions()) { + Topic topic(resource_namespace_, topic_); + Permission permission; + switch (item.permission()) { + case rmq::Permission::READ: + permission = Permission::READ; + break; + case rmq::Permission::WRITE: + permission = Permission::WRITE; + break; + case rmq::Permission::READ_WRITE: + permission = Permission::READ_WRITE; + break; + default: + permission = Permission::NONE; + break; + } + + AddressScheme scheme; + switch (item.broker().endpoints().scheme()) { + case rmq::AddressScheme::IPv4: + scheme = AddressScheme::IPv4; + break; + case rmq::AddressScheme::IPv6: + scheme = AddressScheme::IPv6; + break; + case rmq::AddressScheme::DOMAIN_NAME: + scheme = AddressScheme::DOMAIN_NAME; + break; + default: + scheme = AddressScheme::IPv4; + } + + std::vector
addresses; + for (const auto& host_port : item.broker().endpoints().addresses()) { + Address address(host_port.host(), host_port.port()); + addresses.emplace_back(address); + } + ServiceAddress service_address(scheme, addresses); + + Broker broker(item.broker().name(), item.broker().id(), service_address); + Partition partition(topic, item.id(), permission, broker); + partitions.emplace_back(partition); + } + auto topic_route_data = + std::make_shared(partitions, invocation_context->response.common().DebugString()); + rocketmq::TopicPublishInfo topic_publish_info(topic_, topic_route_data); + rocketmq::MQMessageQueue message_queue; + EXPECT_TRUE(topic_publish_info.selectOneMessageQueue(message_queue)); + + EXPECT_STREQ(topic_.c_str(), message_queue.getTopic().c_str()); + }; + + invocation_context->callback = callback; + + client_->asyncQueryRoute(request, invocation_context); +} + +ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/ut/BUILD b/src/test/cpp/ut/BUILD new file mode 100644 index 000000000..9bf771b53 --- /dev/null +++ b/src/test/cpp/ut/BUILD @@ -0,0 +1 @@ +load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") \ No newline at end of file diff --git a/src/test/cpp/ut/BUILD.bazel b/src/test/cpp/ut/BUILD.bazel new file mode 100644 index 000000000..2695ae24a --- /dev/null +++ b/src/test/cpp/ut/BUILD.bazel @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") \ No newline at end of file diff --git a/src/test/cpp/ut/README.md b/src/test/cpp/ut/README.md new file mode 100644 index 000000000..f16f14eea --- /dev/null +++ b/src/test/cpp/ut/README.md @@ -0,0 +1,3 @@ +## Introduction +This directory is supposed to contain all unit tests for this project. File hierarchy of unit test files should resemble +those of main sources. Aka, Unit test file for src/main/cpp/base/UtilAll.cpp should be src/test/cpp/base/UtilAllTest.cpp \ No newline at end of file diff --git a/src/test/cpp/ut/admin/AdminServerTest.cpp b/src/test/cpp/ut/admin/AdminServerTest.cpp new file mode 100644 index 000000000..eb988cc64 --- /dev/null +++ b/src/test/cpp/ut/admin/AdminServerTest.cpp @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include "AdminServerImpl.h" +#include "apache/rocketmq/v1/admin.grpc.pb.h" + +#include "rocketmq/RocketMQ.h" +#include "spdlog/sinks/basic_file_sink.h" +#include "spdlog/spdlog.h" + +#include + +namespace rmq = apache::rocketmq::v1; + +ROCKETMQ_NAMESPACE_BEGIN +namespace admin { + +TEST(AdminServerTest, testSetUp) { + + auto logger = spdlog::basic_logger_mt("rocketmq_logger", "logs/test.log"); + logger->set_level(spdlog::level::debug); + spdlog::set_default_logger(logger); + + AdminServer* admin_server = new AdminServerImpl; + admin_server->start(); + + std::string address("127.0.0.1:"); + address.append(std::to_string(admin_server->port())); + auto channel = grpc::CreateChannel(address, grpc::InsecureChannelCredentials()); + + auto stub = rmq::Admin::NewStub(channel); + + rmq::ChangeLogLevelRequest request; + request.set_level(rmq::ChangeLogLevelRequest_Level_INFO); + rmq::ChangeLogLevelResponse response; + + grpc::ClientContext context; + context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(10)); + + auto status = stub->ChangeLogLevel(&context, request, &response); + + EXPECT_TRUE(status.ok()); + EXPECT_STREQ("OK", response.remark().c_str()); + EXPECT_EQ(spdlog::level::info, logger->level()); + + admin_server->stop(); + + delete admin_server; +} + +} // namespace admin +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/admin/BUILD.bazel b/src/test/cpp/ut/admin/BUILD.bazel new file mode 100644 index 000000000..8fe13fcb6 --- /dev/null +++ b/src/test/cpp/ut/admin/BUILD.bazel @@ -0,0 +1,28 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "admin_server_test", + srcs = [ + "AdminServerTest.cpp", + ], + deps = [ + "//src/main/cpp/admin:admin_server_library", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/api/BUILD.bazel b/src/test/cpp/ut/api/BUILD.bazel new file mode 100644 index 000000000..78f575ff8 --- /dev/null +++ b/src/test/cpp/ut/api/BUILD.bazel @@ -0,0 +1,28 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "error_code_test", + srcs = [ + "ErrorCodeTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/api/ErrorCodeTest.cpp b/src/test/cpp/ut/api/ErrorCodeTest.cpp new file mode 100644 index 000000000..c63df2f33 --- /dev/null +++ b/src/test/cpp/ut/api/ErrorCodeTest.cpp @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/ErrorCode.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(ErrorCodeTest, testErrorCode) { + std::error_code ec = ErrorCode::BadRequest; + if (ec) { + std::cout << ec.message() << std::endl; + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel new file mode 100644 index 000000000..092ef7860 --- /dev/null +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -0,0 +1,153 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "histogram_test", + srcs = [ + "HistogramTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "mix_all_test", + srcs = [ + "MixAllTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "rate_limiter_test", + srcs = [ + "RateLimiterTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "util_all_test", + srcs = [ + "UtilAllTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + "@zlib//:zlib", + "@asio//:asio", + ], +) + +cc_test( + name = "http_client_test", + srcs = [ + "HttpClientTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "unique_id_generator_test", + srcs = [ + "UniqueIdGeneratorTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "host_info_test", + srcs = [ + "HostInfoTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "message_test", + srcs = [ + "MQMessageTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "message_ext_test", + srcs = [ + "MQMessageExtTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "top_addressing_test", + srcs = [ + "TopAddressingTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "//src/main/cpp/base/mocks:base_mocks", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "invocation_context_test", + srcs = [ + "InvocationContextTest.cpp", + ], + deps = [ + "//src/main/cpp/client:client_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "thread_pool_test", + srcs = [ + "ThreadPoolTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/base/HistogramTest.cpp b/src/test/cpp/ut/base/HistogramTest.cpp new file mode 100644 index 000000000..388a2c43f --- /dev/null +++ b/src/test/cpp/ut/base/HistogramTest.cpp @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Histogram.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class HistogramTest : public testing::Test { +public: + HistogramTest() : histogram_("Test", 3) { + } + + void SetUp() override { + histogram_.labels().emplace_back("Foo: "); + histogram_.labels().emplace_back("Bar: "); + histogram_.labels().emplace_back("Baz: "); + } + +protected: + Histogram histogram_; +}; + +TEST_F(HistogramTest, testHistogram_no_count) { + + std::string result; + histogram_.reportAndReset(result); + + std::string expect("Test:Foo: 0, Bar: 0, Baz: 0"); + EXPECT_STREQ(result.c_str(), expect.c_str()); +} + +TEST_F(HistogramTest, testHistogram) { + histogram_.countIn(0); + histogram_.countIn(-1); + histogram_.countIn(4); + histogram_.countIn(2); + + std::string result; + histogram_.reportAndReset(result); + + std::string expect("Test:Foo: 1, Bar: 0, Baz: 2"); + EXPECT_STREQ(result.c_str(), expect.c_str()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/HostInfoTest.cpp b/src/test/cpp/ut/base/HostInfoTest.cpp new file mode 100644 index 000000000..3b75f0d38 --- /dev/null +++ b/src/test/cpp/ut/base/HostInfoTest.cpp @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "HostInfo.h" +#include "fmt/format.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class HostInfoTest : public testing::Test { +public: + void SetUp() override { + } + + void TearDown() override { + } + +protected: + std::string site_{"site"}; + std::string unit_{"unit"}; + std::string app_{"app"}; + std::string stage_{"stage"}; + + void SetEnv(const char* key, const char* value) { + int overwrite = 1; +#ifdef _WIN32 + std::string env; + env.append(key); + env.push_back('='); + env.append(value); + _putenv(env.c_str()); +#else + setenv(key, value, overwrite); +#endif + } +}; + +TEST_F(HostInfoTest, testQueryString) { + SetEnv(HostInfo::ENV_LABEL_SITE, site_.c_str()); + SetEnv(HostInfo::ENV_LABEL_UNIT, unit_.c_str()); + SetEnv(HostInfo::ENV_LABEL_APP, app_.c_str()); + SetEnv(HostInfo::ENV_LABEL_STAGE, stage_.c_str()); + + HostInfo host_info; + std::string query_string = host_info.queryString(); + std::string query_string_template("labels=site:{},unit:{},app:{},stage:{}"); + EXPECT_EQ(query_string, fmt::format(query_string_template, site_, unit_, app_, stage_)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/HttpClientTest.cpp b/src/test/cpp/ut/base/HttpClientTest.cpp new file mode 100644 index 000000000..5ea118516 --- /dev/null +++ b/src/test/cpp/ut/base/HttpClientTest.cpp @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#include "HttpClientImpl.h" +#include "LoggerImpl.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class HttpClientTest : public testing::Test { +public: + void SetUp() override { + SPDLOG_DEBUG("GHttpClient::SetUp() starts"); + http_client.start(); + SPDLOG_DEBUG("GHttpClient::SetUp() completed"); + } + + void TearDown() override { + SPDLOG_DEBUG("GHttpClientTest::TearDown() starts"); + http_client.shutdown(); + SPDLOG_DEBUG("GHttpClientTest::TearDown() completed"); + } + +protected: + HttpClientImpl http_client; +}; + +TEST_F(HttpClientTest, testBasics) { +} + +TEST_F(HttpClientTest, testGet) { + auto cb = [](int code, const std::multimap& headers, const std::string& body) { + SPDLOG_INFO("Response received. Status-code: {}, Body: {}", code, body); + }; + + http_client.get(HttpProtocol::HTTP, "www.baidu.com", 80, "/", cb); +} + +TEST_F(HttpClientTest, DISABLED_testJMEnv) { + auto cb = [](int code, const std::multimap& headers, const std::string& body) { + SPDLOG_INFO("Response received. Status-code: {}, Body: {}", code, body); + }; + + http_client.get(HttpProtocol::HTTP, "jmenv.tbsite.net", 8080, "/rocketmq/nsaddr", cb); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/InvocationContextTest.cpp b/src/test/cpp/ut/base/InvocationContextTest.cpp new file mode 100644 index 000000000..91212e5ef --- /dev/null +++ b/src/test/cpp/ut/base/InvocationContextTest.cpp @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "InvocationContext.h" +#include "RpcClient.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(InvocationContextTest, testOnCompletion_OK) { + bool cb_invoked = false; + auto callback = [&](const InvocationContext* invocation_context) { + cb_invoked = true; + throw 1; + }; + + auto invocation_context = new InvocationContext(); + invocation_context->status = grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "Mock deadline exceeded"); + invocation_context->callback = callback; + invocation_context->onCompletion(true); + EXPECT_TRUE(cb_invoked); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/MQMessageExtTest.cpp b/src/test/cpp/ut/base/MQMessageExtTest.cpp new file mode 100644 index 000000000..894cfe50f --- /dev/null +++ b/src/test/cpp/ut/base/MQMessageExtTest.cpp @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/MQMessageExt.h" +#include "MessageAccessor.h" +#include "rocketmq/MQMessage.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class MQMessageExtTest : public testing::Test { +public: + void SetUp() override { + MessageAccessor::setMessageId(message_, msg_id_); + } + + void TearDown() override { + } + +protected: + std::string msg_id_{"msg-0"}; + std::string topic_{"test"}; + MQMessageExt message_; +}; + +TEST_F(MQMessageExtTest, testGetQueueId) { + EXPECT_EQ(message_.getQueueId(), 0); +} + +TEST_F(MQMessageExtTest, testBornTimestamp) { + auto born_timestamp = message_.bornTimestamp(); + EXPECT_TRUE(std::chrono::system_clock::now() - born_timestamp < std::chrono::seconds(1)); +} + +TEST_F(MQMessageExtTest, testGetDeliveryAttempt) { + EXPECT_EQ(message_.getDeliveryAttempt(), 0); +} + +TEST_F(MQMessageExtTest, testGetBornTimestamp) { + int64_t born_timestamp = message_.getBornTimestamp(); + uint64_t last_second = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch() - std::chrono::seconds(1)) + .count(); + EXPECT_TRUE(static_cast(born_timestamp) > last_second); +} + +TEST_F(MQMessageExtTest, testEqual) { + MQMessageExt other; + other.setTopic("test2"); + MessageAccessor::setMessageId(other, msg_id_); + EXPECT_TRUE(message_ == other); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/MQMessageTest.cpp b/src/test/cpp/ut/base/MQMessageTest.cpp new file mode 100644 index 000000000..c42ef2219 --- /dev/null +++ b/src/test/cpp/ut/base/MQMessageTest.cpp @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/MQMessage.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class MQMessageTest : public testing::Test { +public: + void SetUp() override { + message.setTopic(topic_); + message.setKey(key_); + message.setBody(body_data_, strlen(body_data_)); + message.setDelayTimeLevel(delay_level_); + } + + void TearDown() override { + } + +protected: + std::string topic_{"Test"}; + std::string key_{"key0"}; + const char* body_data_{"body content"}; + int delay_level_{1}; + MQMessage message; +}; + +TEST_F(MQMessageTest, testAssignment) { + MQMessage msg; + msg = message; + EXPECT_EQ(msg.getTopic(), topic_); + EXPECT_EQ(msg.getDelayTimeLevel(), delay_level_); + EXPECT_EQ(*msg.getKeys().begin(), key_); + EXPECT_EQ(msg.getBody(), body_data_); +} + +TEST_F(MQMessageTest, testProperty) { + std::string key{"k"}; + std::string value{"value"}; + message.setProperty(key, value); + auto prop_value = message.getProperty(key); + EXPECT_EQ(value, prop_value); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/MixAllTest.cpp b/src/test/cpp/ut/base/MixAllTest.cpp new file mode 100644 index 000000000..89b3312b5 --- /dev/null +++ b/src/test/cpp/ut/base/MixAllTest.cpp @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "MixAll.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN +class MixAllTest : public testing::Test { +public: + static std::string toUpperCase(const std::string& s) { + std::string result; + for (const char& c : s) { + if ('a' <= c && 'z' >= c) { + result.push_back(static_cast('A' + (c - 'a'))); + } else { + result.push_back(c); + } + } + return result; + } +}; + +TEST_F(MixAllTest, testValidate_empty_topic) { + MQMessage message; + ASSERT_FALSE(MixAll::validate(message)); +} + +TEST_F(MixAllTest, testValidate_normal_topic) { + MQMessage message("T_abc-123", "sample_body"); + ASSERT_TRUE(MixAll::validate(message)); +} + +TEST_F(MixAllTest, testValidate_topic_too_long) { + + std::string topic("T"); + for (int i = 0;; ++i) { + topic.append(std::to_string(i)); + if (topic.length() > 64) { + break; + } + } + MQMessage message(topic, "sample_body"); + ASSERT_FALSE(MixAll::validate(message)); +} + +TEST_F(MixAllTest, testValidate_body_too_large) { + std::string topic("TestTopic"); + std::string body; + + body.reserve(MixAll::MAX_MESSAGE_BODY_SIZE + 1); + for (uint32_t i = 0; i <= MixAll::MAX_MESSAGE_BODY_SIZE; ++i) { + body.append("a"); + } + ASSERT_FALSE(MixAll::validate(MQMessage(topic, body))); +} + +TEST_F(MixAllTest, testRandom) { + uint32_t left = 1; + uint32_t right = 100; + uint32_t random_number = MixAll::random(left, right); + EXPECT_TRUE(random_number >= left && random_number <= right); +} + +TEST_F(MixAllTest, testHex) { + const char* data = "abc"; + std::string hex = MixAll::hex(data, strlen(data)); + std::vector bin; + EXPECT_TRUE(MixAll::hexToBinary(hex, bin)); + EXPECT_EQ(hex, MixAll::hex(bin.data(), bin.size())); +} + +TEST_F(MixAllTest, testMD5) { + std::string data("abc"); + std::string digest; + bool success = MixAll::md5(data, digest); + std::string expect("900150983CD24FB0D6963F7D28E17F72"); + EXPECT_TRUE(success); + EXPECT_EQ(digest, expect); +} + +TEST_F(MixAllTest, testSHA1) { + std::string data("abc"); + std::string digest; + bool ok = MixAll::sha1(data, digest); + EXPECT_TRUE(ok); + std::string expect("a9993e364706816aba3e25717850c26c9cd0d89d"); + EXPECT_EQ(digest, toUpperCase(expect)); +} + +TEST_F(MixAllTest, testCrc32) { + std::string data("abc"); + std::string digest; + bool success = MixAll::crc32(data, digest); + EXPECT_TRUE(success); + std::cout << digest << std::endl; +} + +TEST_F(MixAllTest, testIsIpv4) { + const char* ip = "8.8.8.8"; + const char* host = "www.taobao.com"; + EXPECT_TRUE(MixAll::isIPv4(ip)); + EXPECT_FALSE(MixAll::isIPv4(host)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/RateLimiterTest.cpp b/src/test/cpp/ut/base/RateLimiterTest.cpp new file mode 100644 index 000000000..ca6c047f3 --- /dev/null +++ b/src/test/cpp/ut/base/RateLimiterTest.cpp @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "RateLimiter.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN +class RateLimiterTest : public ::testing::Test { +public: + void TearDown() override { + observer.stop(); + } + +protected: + RateLimiterObserver observer; +}; + +TEST_F(RateLimiterTest, basicTest) { + std::shared_ptr> limiter(new RateLimiter<10>(1000)); + observer.subscribe(limiter); + + std::atomic_bool stopped(false); + std::atomic_long acquired(0); + + std::thread timer([&] { + std::this_thread::sleep_for(std::chrono::seconds(10)); + stopped.store(true); + }); + + std::thread report([&] { + while (!stopped) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + long qps = acquired.load(); + while (!acquired.compare_exchange_weak(qps, 0)) { + qps = acquired.load(); + } + std::cout << "QPS: " << qps << ", available=" << limiter->available() << std::endl; + } + }); + + std::thread t([&] { + while (!stopped.load()) { + limiter->acquire(); + acquired++; + } + }); + + timer.join(); + t.join(); + report.join(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/ThreadPoolTest.cpp b/src/test/cpp/ut/base/ThreadPoolTest.cpp new file mode 100644 index 000000000..9cbde6cfa --- /dev/null +++ b/src/test/cpp/ut/base/ThreadPoolTest.cpp @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ThreadPoolImpl.h" +#include "absl/memory/memory.h" +#include "absl/synchronization/mutex.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ThreadPoolTest : public testing::Test { +public: + ThreadPoolTest() = default; + + void SetUp() override { + pool_ = absl::make_unique(2); + pool_->start(); + completed = false; + } + + void TearDown() override { + pool_->shutdown(); + } + +protected: + std::unique_ptr pool_; + absl::Mutex mtx; + absl::CondVar cv; + bool completed{false}; +}; + +TEST_F(ThreadPoolTest, testBasics) { + + auto task = [this](int cnt) { + for (int i = 0; i < cnt; i++) { + std::cout << std::this_thread::get_id() << ": It works" << std::endl; + } + { + absl::MutexLock lk(&mtx); + if (!completed) { + completed = true; + cv.SignalAll(); + } + } + }; + + for (int i = 0; i < 3; i++) { + pool_->submit(std::bind(task, 3)); + } + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.Wait(&mtx); + } + } +} + +ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/ut/base/TopAddressingTest.cpp b/src/test/cpp/ut/base/TopAddressingTest.cpp new file mode 100644 index 000000000..cfb6e7879 --- /dev/null +++ b/src/test/cpp/ut/base/TopAddressingTest.cpp @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "absl/synchronization/mutex.h" +#include "grpc/grpc.h" +#include "gtest/gtest.h" + +#include "HttpClientMock.h" +#include "TopAddressing.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TopAddressingTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + top_addressing_ = absl::make_unique(); + auto http_client_ = absl::make_unique>(); + auto mock_get = + [](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) { + std::multimap headers; + std::string body("10.0.0.1:9876"); + cb(200, headers, body); + }; + ON_CALL(*http_client_, get).WillByDefault(testing::Invoke(mock_get)); + top_addressing_->injectHttpClient(std::move(http_client_)); + } + + void TearDown() override { + grpc_shutdown(); + } + +protected: + std::unique_ptr top_addressing_; +}; + +TEST_F(TopAddressingTest, testFetchNameServerAddresses) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto callback = [&](bool ok, const std::vector& name_server_list) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + top_addressing_->fetchNameServerAddresses(callback); + { + absl::MutexLock lk(&mtx); + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + EXPECT_TRUE(completed); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp new file mode 100644 index 000000000..d78e1347b --- /dev/null +++ b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "UniqueIdGenerator.h" +#include "absl/container/flat_hash_set.h" +#include "rocketmq/RocketMQ.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" + +#include +ROCKETMQ_NAMESPACE_BEGIN + +TEST(UniqueIdGeneratorTest, testOutputSampleId) { + std::cout << "A sample unique ID: " << UniqueIdGenerator::instance().next() << std::endl; +} + +TEST(UniqueIdGeneratorTest, testNext) { + absl::flat_hash_set id_set; + uint32_t total = 500000; + uint32_t count = 0; + while (total--) { + std::string id = UniqueIdGenerator::instance().next(); + if (id_set.contains(id)) { + SPDLOG_WARN("Yuck, found an duplicated ID: {}", id); + } else { + id_set.insert(id); + } + ++count; + } + EXPECT_EQ(count, id_set.size()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/UtilAllTest.cpp b/src/test/cpp/ut/base/UtilAllTest.cpp new file mode 100644 index 000000000..9ef8c83c7 --- /dev/null +++ b/src/test/cpp/ut/base/UtilAllTest.cpp @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "UtilAll.h" +#include "MixAll.h" +#include "absl/strings/str_split.h" +#include "asio.hpp" +#include "rocketmq/RocketMQ.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class UtilAllTest : public testing::Test { +public: + void SetUp() override { + } + + void TearDown() override { + } +}; + +TEST_F(UtilAllTest, testCompress) { + std::string src("How are you doing?"); + std::string dst; + bool success = UtilAll::compress(src, dst); + EXPECT_TRUE(success); + EXPECT_FALSE(dst.empty()); +} + +TEST_F(UtilAllTest, testUncompress) { + std::string raw("What is your favorite color?"); + std::string compressed; + EXPECT_TRUE(UtilAll::compress(raw, compressed)); + std::string uncompressed; + EXPECT_TRUE(UtilAll::uncompress(compressed, uncompressed)); + EXPECT_EQ(raw.length(), uncompressed.length()); + EXPECT_EQ(raw, uncompressed); +} + +TEST_F(UtilAllTest, benchmarkTest) { + std::string raw; + uint32_t len = 1024 * 1024; + raw.reserve(len); + for (uint32_t i = 0; i < len; i++) { + raw.push_back(i % 128); + } + + std::string compressed; + auto now = std::chrono::steady_clock::now(); + UtilAll::compress(raw, compressed); + auto elapsed = std::chrono::steady_clock::now() - now; + EXPECT_TRUE(elapsed < std::chrono::milliseconds(100)); + EXPECT_TRUE(len / compressed.length() >= 5); +} + +TEST_F(UtilAllTest, split) { + std::string ip("8.8.8.8"); + std::vector segments = absl::StrSplit(ip, '.'); + std::vector expected = {"8", "8", "8", "8"}; + EXPECT_EQ(expected, segments); +} + +TEST_F(UtilAllTest, macAddrss) { + std::vector mac; + bool success = UtilAll::macAddress(mac); + ASSERT_TRUE(success); + std::cout << MixAll::hex(mac.data(), mac.size()) << std::endl; +} + +TEST_F(UtilAllTest, testAsioGetHostName) { + auto&& host_name = asio::ip::host_name(); + std::cout << host_name << std::endl; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/BUILD.bazel b/src/test/cpp/ut/client/BUILD.bazel new file mode 100644 index 000000000..b0757cb35 --- /dev/null +++ b/src/test/cpp/ut/client/BUILD.bazel @@ -0,0 +1,86 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "tls_helper_test", + srcs = [ + "TlsHelperTest.cpp", + ], + deps = [ + "//src/main/cpp/admin:admin_server_library", + "//src/main/cpp/client:client_library", + "@com_google_googletest//:gtest_main", + "@boringssl//:ssl", + ], +) + +cc_test( + name = "rpc_client_test", + srcs = [ + "RpcClientTest.cpp", + ], + deps = [ + "//src/main/cpp/client:client_library", + "//src/main/cpp/client/mocks:client_mocks", + "@com_google_googletest//:gtest_main", + "@boringssl//:ssl", + ], +) + +cc_test( + name = "client_test", + srcs = [ + "ClientTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "client_manager_test", + srcs = [ + "ClientManagerTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "client_manager_factory_test", + srcs = [ + "ClientManagerFactoryTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "topic_assignment_info_test", + srcs = [ + "TopicAssignmentInfoTest.cpp", + ], + deps = [ + "//src/main/cpp/client:client_library", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp new file mode 100644 index 000000000..e48200e41 --- /dev/null +++ b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ClientManagerFactory.h" +#include "ClientConfigMock.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientManagerFactoryTest : public testing::Test { +public: + void SetUp() override { + } + + void TearDown() override { + } + +protected: + testing::NiceMock client_config_; + std::string resource_namespace_{"mq://test"}; +}; + +TEST_F(ClientManagerFactoryTest, testGetClientManager) { + EXPECT_CALL(client_config_, resourceNamespace) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::ReturnRef(resource_namespace_)); + ClientManagerPtr client_manager = ClientManagerFactory::getInstance().getClientManager(client_config_); + EXPECT_TRUE(client_manager); + client_manager->start(); + + client_manager->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientManagerTest.cpp b/src/test/cpp/ut/client/ClientManagerTest.cpp new file mode 100644 index 000000000..3ab764802 --- /dev/null +++ b/src/test/cpp/ut/client/ClientManagerTest.cpp @@ -0,0 +1,357 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ClientManagerImpl.h" +#include "ReceiveMessageCallbackMock.h" +#include "RpcClientMock.h" +#include "apache/rocketmq/v1/definition.pb.h" +#include "gtest/gtest.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientManagerTest : public testing::Test { +public: + void SetUp() override { + client_manager_ = std::make_shared(resource_namespace_); + client_manager_->start(); + rpc_client_ = std::make_shared>(); + ON_CALL(*rpc_client_, ok).WillByDefault(testing::Return(true)); + client_manager_->addRpcClient(target_host_, rpc_client_); + receive_message_callback_ = std::make_shared>(); + metadata_.insert({"foo", "bar"}); + metadata_.insert({"name", "Donald.J.Trump"}); + } + + void TearDown() override { + client_manager_->shutdown(); + } + +protected: + std::string resource_namespace_{"mq://test"}; + std::string topic_{"TestTopic"}; + std::string target_host_{"ipv4:10.0.0.0:10911"}; + std::shared_ptr client_manager_; + std::shared_ptr> rpc_client_; + std::shared_ptr> receive_message_callback_; + absl::Duration io_timeout_{absl::Seconds(3)}; + Metadata metadata_; + std::string message_body_{"Message body"}; + std::string tag_{"TagA"}; + std::string key_{"key-0"}; +}; + +TEST_F(ClientManagerTest, testBasic) { + // Ensure that start/shutdown works well. +} + +TEST_F(ClientManagerTest, testResolveRoute) { + auto rpc_cb = [](const QueryRouteRequest& request, InvocationContext* invocation_context) { + auto partition = new rmq::Partition(); + partition->mutable_topic()->set_resource_namespace(request.topic().resource_namespace()); + partition->mutable_topic()->set_name(request.topic().name()); + partition->mutable_broker()->set_name("broker-0"); + partition->mutable_broker()->set_id(0); + auto address = new rmq::Address(); + address->set_host("10.0.0.1"); + address->set_port(10911); + partition->mutable_broker()->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); + partition->mutable_broker()->mutable_endpoints()->mutable_addresses()->AddAllocated(address); + invocation_context->response.mutable_partitions()->AddAllocated(partition); + + invocation_context->onCompletion(true); + }; + EXPECT_CALL(*rpc_client_, asyncQueryRoute).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(rpc_cb)); + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + QueryRouteRequest request; + request.mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_topic()->set_name(topic_); + auto callback = [&](const std::error_code& ec, const TopicRouteDataPtr&) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + client_manager_->resolveRoute(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + { + absl::MutexLock lk(&mtx); + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + EXPECT_TRUE(completed); +} + +TEST_F(ClientManagerTest, testQueryAssignment) { + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_query_assignment = [&](const QueryAssignmentRequest& request, + InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncQueryAssignment) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_query_assignment)); + QueryAssignmentRequest request; + bool callback_invoked = false; + auto callback = [&](const std::error_code& ec, const QueryAssignmentResponse& response) { callback_invoked = true; }; + + client_manager_->queryAssignment(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +TEST_F(ClientManagerTest, testReceiveMessage) { + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_async_receive = [&](const ReceiveMessageRequest& request, + InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncReceive) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_async_receive)); + ReceiveMessageRequest request; + + EXPECT_CALL(*receive_message_callback_, onCompletion).Times(testing::AtLeast(1)); + + client_manager_->receiveMessage(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), + receive_message_callback_); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); +} + +TEST_F(ClientManagerTest, testReceiveMessage_Failure) { + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_async_receive = [&](const ReceiveMessageRequest& request, + InvocationContext* invocation_context) { + invocation_context->status = grpc::Status::CANCELLED; + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncReceive) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_async_receive)); + ReceiveMessageRequest request; + + EXPECT_CALL(*receive_message_callback_, onCompletion).Times(testing::AtLeast(1)); + + client_manager_->receiveMessage(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), + receive_message_callback_); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); +} + +TEST_F(ClientManagerTest, testAck) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_ack = [&](const AckMessageRequest& request, InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncAck).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_ack)); + AckMessageRequest request; + bool callback_invoked = false; + auto callback = [&](const std::error_code& ec) { callback_invoked = true; }; + + client_manager_->ack(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +TEST_F(ClientManagerTest, testNack) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_nack = [&](const NackMessageRequest& request, InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncNack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_nack)); + NackMessageRequest request; + bool callback_invoked = false; + auto callback = [&](const std::error_code& ec) { callback_invoked = true; }; + + client_manager_->nack(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +TEST_F(ClientManagerTest, testForwardMessageToDeadLetterQueue) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_forward = [&](const ForwardMessageToDeadLetterQueueRequest& request, + InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncForwardMessageToDeadLetterQueue) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_forward)); + ForwardMessageToDeadLetterQueueRequest request; + bool callback_invoked = false; + auto callback = [&](bool ok) { callback_invoked = true; }; + + client_manager_->forwardMessageToDeadLetterQueue(target_host_, metadata_, request, + absl::ToChronoMilliseconds(io_timeout_), callback); + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +TEST_F(ClientManagerTest, testMultiplexingCall) { +} + +TEST_F(ClientManagerTest, testEndTransaction) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_end_transaction = [&](const EndTransactionRequest& request, + InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncEndTransaction) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_end_transaction)); + EndTransactionRequest request; + bool callback_invoked = false; + auto callback = [&](const std::error_code& ec, const EndTransactionResponse& response) { callback_invoked = true; }; + + client_manager_->endTransaction(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +TEST_F(ClientManagerTest, testHealthCheck) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_health_check = [&](const HealthCheckRequest& request, + InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncHealthCheck) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_health_check)); + HealthCheckRequest request; + bool callback_invoked = false; + auto callback = [&](const std::error_code& ec, const InvocationContext* invocation_context) { + callback_invoked = true; + }; + + client_manager_->healthCheck(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientTest.cpp b/src/test/cpp/ut/client/ClientTest.cpp new file mode 100644 index 000000000..3d8148f61 --- /dev/null +++ b/src/test/cpp/ut/client/ClientTest.cpp @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ClientMock.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientTest : public testing::Test { +public: + void SetUp() override { + client_ = std::make_shared>(); + ON_CALL(*client_, active).WillByDefault(testing::Invoke([]() { + std::cout << "active() is invoked" << std::endl; + return true; + })); + } + + void TearDown() override { + } + +protected: + std::shared_ptr> client_; +}; + +TEST_F(ClientTest, testActive) { + EXPECT_TRUE(client_->active()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/RpcClientTest.cpp b/src/test/cpp/ut/client/RpcClientTest.cpp new file mode 100644 index 000000000..f57477978 --- /dev/null +++ b/src/test/cpp/ut/client/RpcClientTest.cpp @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "RpcClientMock.h" + +#include "google/rpc/code.pb.h" +#include "grpcpp/impl/grpc_library.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +namespace ut { + +class RpcClientTest : public testing::Test { + +public: + void SetUp() override { + grpc::internal::GrpcLibraryInitializer initializer; + } + + static void mockQueryRouteInfo(const QueryRouteRequest& request, + InvocationContext* invocation_context) { + invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); + for (int i = 0; i < 3; ++i) { + auto partition = new rmq::Partition; + partition->mutable_topic()->set_name(request.topic().name()); + partition->mutable_broker()->set_name(fmt::format("broker-{}", i)); + partition->mutable_broker()->set_id(0); + auto endpoint = partition->mutable_broker()->mutable_endpoints(); + auto address = new rmq::Address; + address->set_host(fmt::format("10.0.0.{}", i)); + address->set_port(10911); + endpoint->mutable_addresses()->AddAllocated(address); + invocation_context->response.mutable_partitions()->AddAllocated(partition); + } + + invocation_context->onCompletion(true); + } +}; + +TEST_F(RpcClientTest, testMockedGetRouteInfo) { + RpcClientMock rpc_client_mock; + ON_CALL(rpc_client_mock, asyncQueryRoute(testing::_, testing::_)).WillByDefault(testing::Invoke(mockQueryRouteInfo)); + std::string topic = "sample_topic"; + QueryRouteRequest request; + request.mutable_topic()->set_name(topic); + absl::flat_hash_map metadata; + auto invocation_context = new InvocationContext(); + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + auto callback = [&](const InvocationContext* invocation_context) { + EXPECT_TRUE(invocation_context->status.ok()); + EXPECT_EQ(google::rpc::Code::OK, invocation_context->response.common().status().code()); + EXPECT_EQ(3, invocation_context->response.partitions().size()); + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + invocation_context->callback = callback; + rpc_client_mock.asyncQueryRoute(request, invocation_context); + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +} // namespace ut +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/TlsHelperTest.cpp b/src/test/cpp/ut/client/TlsHelperTest.cpp new file mode 100644 index 000000000..89745de50 --- /dev/null +++ b/src/test/cpp/ut/client/TlsHelperTest.cpp @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TlsHelper.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(TlsHelperTest, testSign) { + const char* data = "some random data for test purpose only"; + const char* access_secret = "arbitrary-access-key"; + const std::string& signature = TlsHelper::sign(access_secret, data); + const char* expect = "567868dc8e81f1e8095f88958edff1e07db4290e"; + EXPECT_STRCASEEQ(expect, signature.c_str()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp new file mode 100644 index 000000000..003fab703 --- /dev/null +++ b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TopicAssignmentInfo.h" +#include "rocketmq/ConsumeType.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class QueryAssignmentInfoTest : public testing::Test { +protected: + std::string resource_namespace_{"mq://test"}; + std::string topic_{"TopicTest"}; + std::string broker_name_{"broker-a"}; + int broker_id_ = 0; + int total_ = 16; +}; + +TEST_F(QueryAssignmentInfoTest, testCtor) { + QueryAssignmentResponse response; + for (int i = 0; i < total_; i++) { + auto assignment = new rmq::Assignment; + assignment->mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); + assignment->mutable_partition()->mutable_topic()->set_name(topic_); + assignment->mutable_partition()->set_id(i); + assignment->mutable_partition()->set_permission(rmq::Permission::READ); + auto broker = assignment->mutable_partition()->mutable_broker(); + broker->set_name(broker_name_); + broker->set_id(broker_id_); + broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); + + auto address = new rmq::Address; + address->set_host("10.0.0.1"); + address->set_port(10911); + broker->mutable_endpoints()->mutable_addresses()->AddAllocated(address); + response.mutable_assignments()->AddAllocated(assignment); + } + TopicAssignment assignment(response); + EXPECT_EQ(total_, assignment.assignmentList().size()); + const auto& item = *assignment.assignmentList().begin(); + EXPECT_EQ(item.messageQueue().getBrokerName(), broker_name_); + EXPECT_EQ(item.messageQueue().getTopic(), topic_); + EXPECT_TRUE(item.messageQueue().getQueueId() < 16); +} + +TEST_F(QueryAssignmentInfoTest, testCtor2) { + QueryAssignmentResponse response; + for (int i = 0; i < total_; i++) { + auto assignment = new rmq::Assignment; + assignment->mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); + assignment->mutable_partition()->mutable_topic()->set_name(topic_); + assignment->mutable_partition()->set_id(i); + assignment->mutable_partition()->set_permission(rmq::Permission::READ_WRITE); + auto broker = assignment->mutable_partition()->mutable_broker(); + broker->set_name(broker_name_); + broker->set_id(broker_id_); + broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); + + auto address = new rmq::Address; + address->set_host("10.0.0.1"); + address->set_port(10911); + broker->mutable_endpoints()->mutable_addresses()->AddAllocated(address); + response.mutable_assignments()->AddAllocated(assignment); + } + TopicAssignment assignment(response); + EXPECT_EQ(total_, assignment.assignmentList().size()); + const auto& item = *assignment.assignmentList().begin(); + EXPECT_EQ(item.messageQueue().getBrokerName(), broker_name_); + EXPECT_EQ(item.messageQueue().getTopic(), topic_); + EXPECT_TRUE(item.messageQueue().getQueueId() < 16); +} + +TEST_F(QueryAssignmentInfoTest, testCtor3) { + QueryAssignmentResponse response; + for (int i = 0; i < total_; i++) { + auto assignment = new rmq::Assignment; + assignment->mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); + assignment->mutable_partition()->mutable_topic()->set_name(topic_); + assignment->mutable_partition()->set_id(i); + assignment->mutable_partition()->set_permission(rmq::Permission::NONE); + auto broker = assignment->mutable_partition()->mutable_broker(); + broker->set_name(broker_name_); + broker->set_id(broker_id_); + broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); + + auto address = new rmq::Address; + address->set_host("10.0.0.1"); + address->set_port(10911); + broker->mutable_endpoints()->mutable_addresses()->AddAllocated(address); + response.mutable_assignments()->AddAllocated(assignment); + } + TopicAssignment assignment(response); + EXPECT_TRUE(assignment.assignmentList().empty()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/TracingUtilityTest.cpp b/src/test/cpp/ut/client/TracingUtilityTest.cpp new file mode 100644 index 000000000..42b6cf0e5 --- /dev/null +++ b/src/test/cpp/ut/client/TracingUtilityTest.cpp @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TracingUtility.h" +#include "gtest/gtest.h" +#include + +using namespace opentelemetry::trace; + +ROCKETMQ_NAMESPACE_BEGIN + +template +static std::string hex(const T& id_item) { + char buf[T::kSize * 2]; + id_item.ToLowerBase16(buf); + return std::string(buf, sizeof(buf)); +} + +TEST(TracingUtilityTest, testInject) { + constexpr uint8_t buf_span[] = {1, 2, 3, 4, 5, 6, 7, 8}; + constexpr uint8_t buf_trace[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + SpanContext span_context{TraceId{buf_trace}, SpanId{buf_span}, TraceFlags{true}, true}; + EXPECT_EQ(TracingUtility::injectSpanContextToTraceParent(span_context), + "00-0102030405060708090a0b0c0d0e0f10-0102030405060708-01"); +} + +TEST(TracingUtilityTest, testExtract) { + SpanContext span_context = + TracingUtility::extractContextFromTraceParent("00-0102030405060708090a0b0c0d0e0f10-0102030405060708-01"); + + EXPECT_EQ(hex(span_context.trace_id()), "0102030405060708090a0b0c0d0e0f10"); + EXPECT_EQ(hex(span_context.span_id()), "0102030405060708"); + EXPECT_TRUE(span_context.IsSampled()); + EXPECT_TRUE(span_context.IsRemote()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/concurrent/BUILD.bazel b/src/test/cpp/ut/concurrent/BUILD.bazel new file mode 100644 index 000000000..00e4ad82d --- /dev/null +++ b/src/test/cpp/ut/concurrent/BUILD.bazel @@ -0,0 +1,28 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "countdown_latch_test", + srcs = [ + "CountdownLatchTest.cpp", + ], + deps = [ + "//src/main/cpp/concurrent:countdown_latch_library", + "@com_github_grpc_grpc//:grpc++", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp b/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp new file mode 100644 index 000000000..3204a6055 --- /dev/null +++ b/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "CountdownLatch.h" +#include "rocketmq/RocketMQ.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(CountdownLatchTest, testCountdown) { + CountdownLatch countdown_latch(0); + + countdown_latch.increaseCount(); + + auto start = std::chrono::system_clock::now(); + + std::thread t([&] { + std::this_thread::sleep_for(std::chrono::seconds(3)); + spdlog::info("Counting down now..."); + countdown_latch.countdown(); + }); + + spdlog::info("Start to await"); + countdown_latch.await(); + spdlog::info("Await over"); + auto duration = std::chrono::system_clock::now() - start; + EXPECT_TRUE(std::chrono::duration_cast(duration).count() >= 3); + + if (t.joinable()) { + t.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/logger/BUILD.bazel b/src/test/cpp/ut/logger/BUILD.bazel new file mode 100644 index 000000000..7d51c3c12 --- /dev/null +++ b/src/test/cpp/ut/logger/BUILD.bazel @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "logger_test", + srcs = [ + "LoggerTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/log:log_library", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/logger/LoggerTest.cpp b/src/test/cpp/ut/logger/LoggerTest.cpp new file mode 100644 index 000000000..372ed177f --- /dev/null +++ b/src/test/cpp/ut/logger/LoggerTest.cpp @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/Logger.h" +#include "LoggerImpl.h" +#include "rocketmq/RocketMQ.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" +ROCKETMQ_NAMESPACE_BEGIN + +class LoggerTest : public testing::Test { +protected: + std::size_t log_file_size_{1024 * 4}; +}; + +TEST_F(LoggerTest, testLogger_Trace) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Trace); + logger.setConsoleLevel(Level::Trace); + logger.init(); + SPDLOG_ERROR("=========Trace==========="); + SPDLOG_TRACE("Should show up!"); + SPDLOG_DEBUG("Should show up!"); + SPDLOG_INFO("Should show up!"); + SPDLOG_WARN("Should show up!"); + SPDLOG_ERROR("Should show up!"); +} + +TEST_F(LoggerTest, testLogger_Debug) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Debug); + logger.init(); + SPDLOG_ERROR("=========Debug==========="); + SPDLOG_TRACE("Should NOT get logged"); + SPDLOG_DEBUG("Should show up"); + SPDLOG_INFO("Should show up!!"); + SPDLOG_WARN("Should show up!"); + SPDLOG_ERROR("Should show up!"); +} + +TEST_F(LoggerTest, testLogger_Info) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Info); + logger.init(); + SPDLOG_ERROR("=========Info==========="); + SPDLOG_TRACE("Should NOT get logged"); + SPDLOG_DEBUG("Should NOT get logged"); + SPDLOG_INFO("Should show up!"); + SPDLOG_WARN("Should show up!"); + SPDLOG_ERROR("Should show up!"); +} + +TEST_F(LoggerTest, testLogger_Warn) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Warn); + logger.init(); + SPDLOG_ERROR("=========Warn==========="); + SPDLOG_TRACE("Should NOT get logged"); + SPDLOG_DEBUG("Should NOT get logged"); + SPDLOG_INFO("Should NOT get logged"); + SPDLOG_WARN("Should show up!"); + SPDLOG_ERROR("Should show up!"); +} + +TEST_F(LoggerTest, testLogger_Error) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Error); + logger.init(); + SPDLOG_ERROR("=========Error==========="); + SPDLOG_TRACE("Should NOT get logged"); + SPDLOG_DEBUG("Should NOT get logged"); + SPDLOG_INFO("Should NOT get logged"); + SPDLOG_WARN("Should NOT get logged"); + SPDLOG_ERROR("Should show up!"); +} + +TEST_F(LoggerTest, testFileRolling) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Trace); + logger.init(); + for (size_t i = 0; i < 32; i++) { + SPDLOG_TRACE("Should show up!"); + SPDLOG_DEBUG("Should show up!"); + SPDLOG_INFO("Should show up!"); + SPDLOG_WARN("Should show up!"); + SPDLOG_ERROR("Should show up!"); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/BUILD.bazel b/src/test/cpp/ut/remoting/BUILD.bazel new file mode 100644 index 000000000..68201979e --- /dev/null +++ b/src/test/cpp/ut/remoting/BUILD.bazel @@ -0,0 +1,73 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "query_route_request_header_test", + srcs = [ + "QueryRouteRequestHeaderTest.cpp", + ], + deps = [ + "//src/main/cpp/remoting", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "remoting_command_test", + srcs = [ + "RemotingCommandTest.cpp", + ], + deps = [ + "//src/main/cpp/remoting", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "broker_data_test", + srcs = [ + "BrokerDataTest.cpp", + ], + deps = [ + "//src/main/cpp/remoting", + "@com_google_googletest//:gtest_main", + ] +) + + +cc_test( + name = "queue_data_test", + srcs = [ + "QueueDataTest.cpp", + ], + deps = [ + "//src/main/cpp/remoting", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "topic_route_data_test", + srcs = [ + "TopicRouteDataTest.cpp", + ], + deps = [ + "//src/main/cpp/remoting", + "@com_google_googletest//:gtest_main", + ] +) \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/BrokerDataTest.cpp b/src/test/cpp/ut/remoting/BrokerDataTest.cpp new file mode 100644 index 000000000..96249ebb3 --- /dev/null +++ b/src/test/cpp/ut/remoting/BrokerDataTest.cpp @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "BrokerData.h" + +#include + +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" + +#include "google/protobuf/struct.pb.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(BrokerDataTest, testDecode) { + std::string json = + R"({"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false})"; + google::protobuf::Struct root; + + auto status = google::protobuf::util::JsonStringToMessage(json, &root); + ASSERT_TRUE(status.ok()); + + BrokerData&& broker_data = BrokerData::decode(root); + EXPECT_EQ("b1", broker_data.broker_name_); + EXPECT_EQ("cluster", broker_data.cluster_); + EXPECT_EQ(2, broker_data.broker_addresses_.size()); + EXPECT_EQ("abc", broker_data.broker_addresses_.at(1)); + EXPECT_EQ("def", broker_data.broker_addresses_.at(2)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp b/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp new file mode 100644 index 000000000..d887ba6fa --- /dev/null +++ b/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "QueryRouteRequestHeader.h" + +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(QueryRouteRequestHeaderTest, testEncode) { + google::protobuf::Value root; + QueryRouteRequestHeader header; + header.topic("abc"); + header.encode(root); + + std::string json; + auto status = google::protobuf::util::MessageToJsonString(root, &json); + EXPECT_TRUE(status.ok()); + EXPECT_FALSE(json.empty()); + std::cout << json << std::endl; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/QueueDataTest.cpp b/src/test/cpp/ut/remoting/QueueDataTest.cpp new file mode 100644 index 000000000..81c55ffe3 --- /dev/null +++ b/src/test/cpp/ut/remoting/QueueDataTest.cpp @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "gtest/gtest.h" + +#include "QueueData.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(QueueDataTest, testDecode) { + std::string json = R"({"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8})"; + google::protobuf::Struct root; + auto status = google::protobuf::util::JsonStringToMessage(json, &root); + EXPECT_TRUE(status.ok()); + + QueueData queue_data = QueueData::decode(root); + EXPECT_EQ("broker1", queue_data.broker_name_); + EXPECT_EQ(6, queue_data.perm_); + EXPECT_EQ(4, queue_data.read_queue_number_); + EXPECT_EQ(8, queue_data.write_queue_number_); + EXPECT_EQ(3, queue_data.topic_system_flag_); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/RemotingCommandTest.cpp b/src/test/cpp/ut/remoting/RemotingCommandTest.cpp new file mode 100644 index 000000000..68f887286 --- /dev/null +++ b/src/test/cpp/ut/remoting/RemotingCommandTest.cpp @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "RemotingCommand.h" + +#include +#include +#include + +#include "QueryRouteRequestHeader.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class RemotingCommandTest : public testing::Test { +public: +}; + +TEST_F(RemotingCommandTest, testNextRequestId) { + + std::unordered_set request_ids; + + for (int i = 0; i < 50; i++) { + std::int32_t id = RemotingCommand::nextRequestId(); + EXPECT_TRUE(request_ids.find(id) == request_ids.end()); + request_ids.insert(id); + } +} + +TEST_F(RemotingCommandTest, testCreateRequest) { + auto header = new QueryRouteRequestHeader; + header->topic("abc"); + + auto command = RemotingCommand::createRequest(RequestCode::QueryRoute, header); + google::protobuf::Value root; + command.encodeHeader(root); + + const auto& fields = root.struct_value().fields(); + EXPECT_TRUE(fields.contains("extFields")); + EXPECT_TRUE(fields.contains("code")); + EXPECT_EQ(static_cast(RequestCode::QueryRoute), fields.at("code").number_value()); + + std::string json; + auto status = google::protobuf::util::MessageToJsonString(root, &json); + EXPECT_TRUE(status.ok()); + std::cout << json << std::endl; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp b/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp new file mode 100644 index 000000000..d2718b47b --- /dev/null +++ b/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TopicRouteData.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(TopicRouteDataTest, testDecode) { + std::string json = + R"({"brokerDatas":[{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false}],"filterServerTable":{},"queueDatas":[{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8}]})"; + google::protobuf::Struct root; + auto status = google::protobuf::util::JsonStringToMessage(json, &root); + EXPECT_TRUE(status.ok()); + + auto topic_route_data = TopicRouteData::decode(root); + + EXPECT_FALSE(topic_route_data.broker_data_.empty()); + EXPECT_FALSE(topic_route_data.queue_data_.empty()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp new file mode 100644 index 000000000..e8e437474 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "AwaitPullCallback.h" +#include "MessageAccessor.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/MQClientException.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/PullResult.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(AwaitPullCallbackTest, testOnSuccess) { + std::vector messages; + PullResult pull_result(0, 32, 32, messages); + AwaitPullCallback callback(pull_result); + EXPECT_FALSE(callback.isCompleted()); + + std::string topic{"Test"}; + int total = 32; + int min = 32; + int max = 128; + int next = 64; + auto task = [&]() { + std::vector msgs; + for (int i = 0; i < total; i++) { + MQMessageExt msg; + msg.setTopic(topic); + MessageAccessor::setQueueOffset(msg, i); + msgs.emplace_back(msg); + } + PullResult result(32, 128, 64, msgs); + callback.onSuccess(result); + }; + + std::thread t(task); + + callback.await(); + EXPECT_TRUE(callback.isCompleted()); + EXPECT_EQ(pull_result.messages().size(), total); + EXPECT_EQ(pull_result.min(), min); + EXPECT_EQ(pull_result.max(), max); + EXPECT_EQ(pull_result.next(), next); + + if (t.joinable()) { + t.join(); + } +} + +TEST(AwaitPullCallbackTest, testOnFailure) { + std::vector messages; + PullResult pull_result(0, 32, 32, messages); + AwaitPullCallback callback(pull_result); + std::string topic{"Test"}; + auto task = [&]() { + std::error_code ec = ErrorCode::NotImplemented; + callback.onFailure(ec); + }; + + std::thread t(task); + + callback.await(); + EXPECT_TRUE(callback.isCompleted()); + EXPECT_TRUE(callback.hasFailure()); + if (t.joinable()) { + t.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel new file mode 100644 index 000000000..205cee162 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -0,0 +1,264 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test", "cc_library") + +cc_test( + name = "message_group_queue_selector_test", + srcs = [ + "MessageGroupQueueSelectorTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "executor_test", + srcs = [ + "ExecutorTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "message_test", + srcs = [ + "MessageTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "process_queue_test", + srcs = [ + "ProcessQueueTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "client_interface", + hdrs = [ + "include/MQClientTest.h", + ], + srcs = [], + strip_include_prefix = "//src/test/cpp/ut/rocketmq/include", + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "default_mq_producer_test", + srcs = [ + "DefaultMQProducerTest.cpp", + ], + deps = [ + ":client_interface", + "//src/main/cpp/admin:admin_server_library", + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_test( + name = "ut_credentials_provider", + srcs = [ + "CredentialsProviderTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "ut_send_callbacks", + srcs = [ + "SendCallbacksTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "sts_credentials_provider_test", + srcs = [ + "StsCredentialsProviderImplTest.cpp", + ], + deps = [ + "//src/main/cpp/base/mocks:base_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "filter_expression_test", + srcs = [ + "FilterExpressionTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "consume_message_service_test", + srcs = [ + "ConsumeStandardMessageServiceTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", + "//src/main/cpp/base/mocks:base_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "consumer_test", + srcs = [ + "ConsumerTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "await_pull_callback_test", + srcs = [ + "AwaitPullCallbackTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "push_consumer_impl_test", + srcs = [ + "PushConsumerImplTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "producer_impl_test", + srcs = [ + "ProducerImplTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "pull_consumer_impl_test", + srcs = [ + "PullConsumerImplTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "client_impl_test", + srcs = [ + "ClientImplTest.cpp", + ], + deps = [ + "//src/main/cpp/base/mocks:base_mocks", + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "static_name_server_resolver_test", + srcs = [ + "StaticNameServerResolverTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + + +cc_test( + name = "dynamic_name_server_resolver_test", + srcs = [ + "DynamicNameServerResolverTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "//src/main/cpp/base/mocks:base_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "default_mq_push_consumer_test", + srcs = [ + "DefaultMQPushConsumerTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "naming_scheme_test", + srcs = [ + "NamingSchemeTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp new file mode 100644 index 000000000..f93fbec0c --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include "ClientImpl.h" +#include "ClientManagerFactory.h" +#include "ClientManagerMock.h" +#include "DynamicNameServerResolver.h" +#include "HttpClientMock.h" +#include "NameServerResolverMock.h" +#include "Scheduler.h" +#include "SchedulerImpl.h" +#include "TopAddressing.h" +#include "rocketmq/RocketMQ.h" + +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TestClientImpl : public ClientImpl, public std::enable_shared_from_this { +public: + TestClientImpl(std::string group) : ClientImpl(std::move(group)) { + } + + std::shared_ptr self() override { + return shared_from_this(); + } + + void prepareHeartbeatData(HeartbeatRequest& request) override { + } +}; + +class ClientImplTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + scheduler_ = std::make_shared(); + + http_client_ = absl::make_unique>(); + name_server_resolver_ = std::make_shared(endpoint_, std::chrono::seconds(1)); + client_manager_ = std::make_shared>(); + ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); + + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); + ON_CALL(*client_manager_, start).WillByDefault([&]() { scheduler_->start(); }); + ON_CALL(*client_manager_, shutdown).WillByDefault([&]() { scheduler_->shutdown(); }); + + client_ = std::make_shared(group_); + client_->withNameServerResolver(name_server_resolver_); + } + + void TearDown() override { + grpc_shutdown(); + } + +protected: + std::string endpoint_{"http://jmenv.tbsite.net:8080/rocketmq/nsaddr"}; + std::string resource_namespace_{"mq://test"}; + std::string group_{"Group-0"}; + std::shared_ptr> client_manager_; + SchedulerSharedPtr scheduler_; + std::shared_ptr client_; + std::shared_ptr name_server_resolver_; + std::unique_ptr> http_client_; +}; + +TEST_F(ClientImplTest, testBasic) { + std::string once{"10.0.0.1:9876"}; + std::string then{"10.0.0.1:9876;10.0.0.2:9876"}; + std::multimap header; + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + int http_status = 200; + auto once_cb = + [&](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) { + cb(http_status, header, once); + }; + auto then_cb = + [&](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) { + cb(http_status, header, then); + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + + EXPECT_CALL(*http_client_, get).WillOnce(testing::Invoke(once_cb)).WillRepeatedly(testing::Invoke(then_cb)); + name_server_resolver_->injectHttpClient(std::move(http_client_)); + + client_->resourceNamespace(resource_namespace_); + client_->start(); + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + + ASSERT_TRUE(completed); + + // Now that the derivative class has closed its own resources, state of ClientImpl should be STOPPING. + client_->state(State::STOPPING); + client_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp new file mode 100644 index 000000000..b43267e9c --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ConsumeStandardMessageService.h" +#include "MessageListenerMock.h" +#include "ProcessQueueMock.h" +#include "PushConsumerMock.h" +#include "grpc/grpc.h" +#include "rocketmq/CredentialsProvider.h" +#include "rocketmq/MQMessageExt.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeStandardMessageServiceTest : public testing::Test { +public: + ConsumeStandardMessageServiceTest() + : credentials_provider_(std::make_shared("access_key", "access_secret")) { + } + + void SetUp() override { + grpc_init(); + consumer_ = std::make_shared>(); + ON_CALL(*consumer_, consumeBatchSize).WillByDefault(testing::Return(consume_batch_size_)); + ON_CALL(*consumer_, messageModel).WillByDefault(testing::Return(MessageModel::CLUSTERING)); + std::weak_ptr consumer = std::dynamic_pointer_cast(consumer_); + consume_standard_message_service_ = + std::make_shared(consumer, thread_count_, &message_listener_); + process_queue_ = std::make_shared>(); + ON_CALL(*process_queue_, topic).WillByDefault(testing::Return(topic_)); + auto mock_take = [this](uint32_t batch_size, std::vector& messages) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + message.setBody(body_); + messages.emplace_back(message); + return false; + }; + ON_CALL(*process_queue_, take).WillByDefault(testing::Invoke(mock_take)); + ON_CALL(*process_queue_, getConsumer).WillByDefault(testing::Return(consumer)); + ON_CALL(*consumer_, customExecutor).WillByDefault(testing::ReturnRef(executor_)); + ON_CALL(*consumer_, credentialsProvider).WillByDefault(testing::Return(credentials_provider_)); + ON_CALL(*consumer_, resourceNamespace).WillByDefault(testing::ReturnRef(resource_namespace_)); + ON_CALL(*consumer_, getGroupName).WillByDefault(testing::ReturnRef(group_name_)); + } + + void TearDown() override { + grpc_shutdown(); + } + +protected: + int thread_count_{2}; + std::string topic_{"TestTopic"}; + std::string tag_{"TagA"}; + std::string body_{"Body Content"}; + std::string resource_namespace_{"mq://test"}; + std::string group_name_{"CID_Test"}; + uint32_t consume_batch_size_; + std::shared_ptr> consumer_; + std::shared_ptr consume_standard_message_service_; + testing::NiceMock message_listener_; + std::shared_ptr> process_queue_; + std::shared_ptr credentials_provider_; + Executor executor_; +}; + +TEST_F(ConsumeStandardMessageServiceTest, testStartAndShutdown) { + consume_standard_message_service_->start(); + consume_standard_message_service_->shutdown(); +} + +TEST_F(ConsumeStandardMessageServiceTest, testConsume) { + ASSERT_FALSE(executor_); + consume_standard_message_service_->start(); + + auto callback = [this](const std::function& cb) { cb(process_queue_); }; + + ON_CALL(*consumer_, iterateProcessQueue).WillByDefault(testing::Invoke(callback)); + + bool completed = false; + bool success = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto listener_cb = [&](const std::vector& messages) { + absl::MutexLock lk(&mtx); + completed = true; + success = !messages.empty(); + cv.SignalAll(); + return ConsumeMessageResult::SUCCESS; + }; + + ON_CALL(message_listener_, consumeMessage).WillByDefault(testing::Invoke(listener_cb)); + EXPECT_CALL(*process_queue_, release).Times(testing::AtLeast(1)); + EXPECT_CALL(*consumer_, ack).Times(testing::AtLeast(1)); + + absl::MutexLock lk(&mtx); + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + + consume_standard_message_service_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ConsumerTest.cpp b/src/test/cpp/ut/rocketmq/ConsumerTest.cpp new file mode 100644 index 000000000..0c950c206 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ConsumerTest.cpp @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ConsumerMock.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(ConsumerMockTest, testMock) { + testing::NiceMock mock; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp new file mode 100644 index 000000000..4f8bb9d81 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/CredentialsProvider.h" +#include "MixAll.h" +#include "ghc/filesystem.hpp" +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" +#include "gtest/gtest.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class CredentialsProviderTest : public testing::Test {}; + +TEST_F(CredentialsProviderTest, testStaticCredentialsProvider) { + std::string access_key("abc"); + std::string access_secret("def"); + StaticCredentialsProvider credentials_provider(access_key, access_secret); + Credentials&& credentials = credentials_provider.getCredentials(); + ASSERT_EQ(credentials.accessKey(), access_key); + ASSERT_EQ(credentials.accessSecret(), access_secret); +} + +TEST_F(CredentialsProviderTest, testEnvironmentVariable) { + const char* access_key = "abc"; + const char* access_secret = "def"; + +#ifdef _WIN32 + std::string env_access_key; + env_access_key.append(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY); + env_access_key.push_back('='); + env_access_key.append(access_key); + + _putenv(env_access_key.c_str()); + + std::string env_access_secret; + env_access_secret.append(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET); + env_access_secret.push_back('='); + env_access_secret.append(access_secret); + _putenv(env_access_secret.c_str()); +#else + setenv(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY, access_key, 1); + setenv(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET, access_secret, 1); +#endif + + EnvironmentVariablesCredentialsProvider provider; + const Credentials& credentials = provider.getCredentials(); + EXPECT_STREQ(access_key, credentials.accessKey().c_str()); + EXPECT_STREQ(access_secret, credentials.accessSecret().c_str()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp new file mode 100644 index 000000000..47a17ae06 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -0,0 +1,212 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQProducer.h" + +#include +#include +#include +#include + +#include "MQClientTest.h" +#include "ProducerImpl.h" +#include "rocketmq/CredentialsProvider.h" +#include "rocketmq/MQSelector.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DefaultMQProducerUnitTest : public MQClientTest { +public: + DefaultMQProducerUnitTest() : MQClientTest() { + credentials_provider_ = std::make_shared(access_key_, access_secret_); + } + + void SetUp() override { + MQClientTest::SetUp(); + // More set-up + rpc_client_broker_ = std::make_shared>(); + + ON_CALL(*rpc_client_broker_, needHeartbeat()).WillByDefault(testing::Return(true)); + + ON_CALL(*rpc_client_broker_, ok()).WillByDefault(testing::Invoke([this]() { return client_ok_; })); + + ON_CALL(*rpc_client_broker_, asyncHealthCheck) + .WillByDefault(testing::Invoke( + [this](const HealthCheckRequest& request, InvocationContext* invocation_context) { + invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->onCompletion(true); + })); + + ON_CALL(*rpc_client_broker_, asyncHeartbeat(testing::_, testing::_)) + .WillByDefault(testing::Invoke( + [this](const HeartbeatRequest& request, InvocationContext* invocation_context) { + invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->onCompletion(true); + })); + + ON_CALL(*rpc_client_broker_, asyncSend) + .WillByDefault(testing::Invoke( + [this](const SendMessageRequest& request, InvocationContext* invocation_context) { + invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->response.set_message_id(message_id_); + invocation_context->onCompletion(true); + })); + + const char* address_format = "ipv4:10.0.0.{}:10911"; + for (int i = 0; i < partition_num_ / avg_partition_per_host_; ++i) { + std::string&& address = fmt::format(address_format, i); + client_instance_->addRpcClient(address, rpc_client_broker_); + } + } + + void TearDown() override { + rpc_client_broker_.reset(); + MQClientTest::TearDown(); + } + +protected: + absl::flat_hash_map metadata_; + std::shared_ptr> rpc_client_broker_; + std::string message_id_{"msg_id_0"}; + std::string access_key_{"access_key"}; + std::string access_secret_{"access_secret"}; + std::shared_ptr credentials_provider_; + std::string body_{"Test message body"}; +}; + +TEST_F(DefaultMQProducerUnitTest, testBasicSetUp) { + QueryRouteRequest request; + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + auto invocation_context = new InvocationContext(); + auto callback = [&](const InvocationContext* invocation_context) { + ASSERT_TRUE(invocation_context->status.ok()); + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + invocation_context->callback = callback; + rpc_client_ns_->asyncQueryRoute(request, invocation_context); + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + + completed = false; + SendMessageRequest send_message_request; + auto send_message_invocation_context = new InvocationContext(); + auto send_callback = [&](const InvocationContext* invocation_context) { + ASSERT_TRUE(invocation_context->response.message_id() == message_id_); + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + send_message_invocation_context->callback = send_callback; + + rpc_client_broker_->asyncSend(send_message_request, send_message_invocation_context); + + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +class UnitTestSendCallback : public SendCallback { +public: + UnitTestSendCallback(absl::Mutex& mtx, absl::CondVar& cv, std::string& msg_id, bool& completed) + : mtx_(mtx), cv_(cv), msg_id_(msg_id), completed_(completed) { + } + + void onSuccess(SendResult& send_result) noexcept override { + absl::MutexLock lk(&mtx_); + msg_id_ = send_result.getMsgId(); + completed_ = true; + cv_.SignalAll(); + } + + void onFailure(const std::error_code& e) noexcept override { + absl::MutexLock lk(&mtx_); + completed_ = true; + cv_.SignalAll(); + } + +private: + absl::Mutex& mtx_; + absl::CondVar& cv_; + std::string& msg_id_; + bool& completed_; +}; + +TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { + auto producer = std::make_shared(group_name_); + producer->resourceNamespace(resource_namespace_); + producer->withNameServerResolver(name_server_resolver_); + producer->setCredentialsProvider(credentials_provider_); + producer->start(); + + MQMessage message; + message.setTopic(topic_); + message.setBody(body_); + + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + std::string msg_id; + auto send_callback = new UnitTestSendCallback(mtx, cv, msg_id, completed); + producer->send(message, send_callback); + if (!completed) { + absl::MutexLock lk(&mtx); + cv.WaitWithTimeout(&mtx, absl::Seconds(10)); + } + ASSERT_EQ(msg_id, message_id_); + producer->shutdown(); + delete send_callback; +} + +TEST_F(DefaultMQProducerUnitTest, testSendMessage) { + auto producer = std::make_shared(group_name_); + producer->resourceNamespace(resource_namespace_); + producer->withNameServerResolver(name_server_resolver_); + producer->setCredentialsProvider(credentials_provider_); + producer->start(); + + MQMessage message; + message.setTopic(topic_); + message.setBody(body_); + + std::error_code ec; + SendResult send_result = producer->send(message, ec); + EXPECT_FALSE(ec); + ASSERT_EQ(send_result.getMsgId(), message_id_); + producer->shutdown(); +} + +TEST_F(DefaultMQProducerUnitTest, testEndpointIsolation) { + auto producer = std::make_shared(group_name_); + producer->resourceNamespace(resource_namespace_); + producer->withNameServerResolver(name_server_resolver_); + producer->setCredentialsProvider(credentials_provider_); + producer->start(); + + const char* isolated_endpoint = "ipv4:10.0.0.0:10911"; + producer->isolateEndpoint(isolated_endpoint); + absl::SleepFor(absl::Seconds(10)); + ASSERT_FALSE(producer->isEndpointIsolated(isolated_endpoint)); + producer->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp new file mode 100644 index 000000000..a01cb6511 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQPushConsumer.h" + +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DefaultMQPushConsumerTest : public testing::Test { +public: + DefaultMQPushConsumerTest() : group_name_("group-0"), consumer_(group_name_) { + } + +protected: + std::string group_name_; + DefaultMQPushConsumer consumer_; +}; + +TEST_F(DefaultMQPushConsumerTest, testGroupName) { + EXPECT_EQ(group_name_, consumer_.groupName()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp new file mode 100644 index 000000000..a693fd6aa --- /dev/null +++ b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "DynamicNameServerResolver.h" + +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/str_split.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "HttpClientMock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DynamicNameServerResolverTest : public testing::Test { +public: + DynamicNameServerResolverTest() + : resolver_(std::make_shared(endpoint_, std::chrono::seconds(1))) { + } + + void SetUp() override { + auto http_client = absl::make_unique>(); + + auto callback = + [this](HttpProtocol, const std::string&, std::uint16_t, const std::string&, + const std::function&, const std::string&)>& cb) { + int code = 200; + std::multimap headers; + cb(code, headers, name_server_list_); + }; + + ON_CALL(*http_client, get).WillByDefault(testing::Invoke(callback)); + + resolver_->injectHttpClient(std::move(http_client)); + + resolver_->start(); + } + + void TearDown() override { + resolver_->shutdown(); + } + +protected: + std::string endpoint_{"http://jmenv.tbsite.net:8080/rocketmq/nsaddr"}; + std::string name_server_list_{"10.0.0.0:9876;10.0.0.1:9876"}; + std::shared_ptr resolver_; +}; + +TEST_F(DynamicNameServerResolverTest, testResolve) { + auto name_server_list = resolver_->resolve(); + std::string result = absl::StrReplaceAll(name_server_list_, {std::pair(";", ",")}); + ASSERT_EQ(name_server_list, "ipv4:" + result); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp new file mode 100644 index 000000000..e637b415d --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/RocketMQ.h" +#include "rocketmq/State.h" +#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN +class ExecutorImpl { +public: + explicit ExecutorImpl(unsigned int core) : state_(State::CREATED), core_(core) { + if (core_ <= 0) { + core_ = std::thread::hardware_concurrency(); + } + } + + virtual ~ExecutorImpl() { + switch (state_.load(std::memory_order_relaxed)) { + case CREATED: + case STOPPING: + case STOPPED: + break; + + case STARTING: + case STARTED: + state_.store(State::STOPPED); + for (auto& worker : workers_) { + if (worker.joinable()) { + worker.join(); + } + } + break; + } + } + + void submit(const std::function& task) { + if (State::STOPPED == state_.load(std::memory_order_relaxed)) { + return; + } + + { + std::unique_lock lock(task_mtx_); + tasks_.push_back(task); + } + cv_.notify_one(); + } + + void start() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTING)) { + for (unsigned int i = 0; i < core_; i++) { + workers_.emplace_back(std::bind(&ExecutorImpl::loop, this)); + } + state_.store(State::STARTED); + } + } + + void stop() { + state_.store(State::STOPPED); + for (auto& worker : workers_) { + if (worker.joinable()) { + worker.join(); + } + } + } + +private: + void loop() { + while (state_.load(std::memory_order_relaxed) != State::STOPPED) { + std::function func; + { + std::unique_lock lk(task_mtx_); + if (!tasks_.empty()) { + func = tasks_.back(); + } + } + + if (func) { + func(); + } else { + std::unique_lock lk(task_mtx_); + cv_.wait_for(lk, std::chrono::seconds(3), + [&]() { return state_.load(std::memory_order_relaxed) == State::STOPPED || !tasks_.empty(); }); + } + } + + std::stringstream ss; + ss << "ThreadId=" << std::this_thread::get_id() << " quit."; + printf("%s\n", ss.str().c_str()); + } + + std::atomic state_; + std::vector> tasks_; + std::mutex task_mtx_; + std::condition_variable cv_; + unsigned int core_; + std::vector workers_; +}; + +class ExecutorTest : public ::testing::Test { +public: + void SetUp() override { + executor_.start(); + } + + void TearDown() override { + executor_.stop(); + } + +protected: + ExecutorImpl executor_{0}; +}; + +TEST_F(ExecutorTest, testBasic) { +} + +std::mutex submit_mtx_; +std::condition_variable submit_cv_; +std::atomic_bool submit_status(false); + +void f(int n) { + std::this_thread::sleep_for(std::chrono::milliseconds(n)); + submit_status.store(true, std::memory_order_relaxed); + submit_cv_.notify_all(); +} + +TEST_F(ExecutorTest, testSubmit) { + auto task = std::bind(f, 100); + executor_.submit(task); + { + std::unique_lock lock(submit_mtx_); + submit_cv_.wait(lock, [] { return submit_status.load(std::memory_order_relaxed); }); + } + EXPECT_EQ(true, submit_status.load()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp b/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp new file mode 100644 index 000000000..f7df8ce9c --- /dev/null +++ b/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FilterExpression.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class FilterExpressionTest : public testing::Test { +public: + FilterExpressionTest() : filter_expression_("TagA") { + } + + void SetUp() override { + } + + void TearDown() override { + } + +protected: + FilterExpression filter_expression_; +}; + +TEST_F(FilterExpressionTest, testAccept) { + MQMessageExt message; + message.setTags("TagA"); + EXPECT_TRUE(filter_expression_.accept(message)); + + MQMessageExt message2; + message2.setTags("TagB"); + EXPECT_FALSE(filter_expression_.accept(message2)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp new file mode 100644 index 000000000..1f51c70c9 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "MessageGroupQueueSelector.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(MessageGroupQueueSelectorTest, testSelect) { + std::string message_group("sample"); + std::size_t hash_code = std::hash{}(message_group); + MessageGroupQueueSelector selector(message_group); + std::size_t len = 8; + std::vector mqs; + mqs.resize(len); + for (std::size_t i = 0; i < len; i++) { + MQMessageQueue queue("topic", "broker-a", static_cast(i)); + mqs.emplace_back(queue); + } + + MQMessage message; + MQMessageQueue selected = selector.select(mqs, message, nullptr); + EXPECT_EQ(selected, mqs[hash_code % len]); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/MessageTest.cpp b/src/test/cpp/ut/rocketmq/MessageTest.cpp new file mode 100644 index 000000000..77267d6ad --- /dev/null +++ b/src/test/cpp/ut/rocketmq/MessageTest.cpp @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/MQMessageExt.h" +#include +#include +#include + +TEST(MessageTest, testExpire) { + auto expire_time_point = std::chrono::steady_clock::now() + std::chrono::milliseconds(300); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + EXPECT_TRUE(std::chrono::steady_clock::now() < expire_time_point); +} \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp b/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp new file mode 100644 index 000000000..336f702b2 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NamingScheme.h" +#include "rocketmq/RocketMQ.h" + +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class NamingSchemeTest : public testing::Test { +public: + void SetUp() override { + } + + void TearDown() override { + } + +protected: + NamingScheme naming_scheme_; +}; + +TEST_F(NamingSchemeTest, testBuildAddress) { + std::string address = "www.baidu.com:80"; + std::string result = naming_scheme_.buildAddress({address}); + ASSERT_EQ("dns:www.baidu.com:80", result); + + address = "8.8.8.8:1234"; + result = naming_scheme_.buildAddress({address}); + ASSERT_EQ("ipv4:8.8.8.8:1234", result); + + result = naming_scheme_.buildAddress({address, "4.4.4.4:1234"}); + ASSERT_EQ("ipv4:8.8.8.8:1234,4.4.4.4:1234", result); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp new file mode 100644 index 000000000..bfa7d5177 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp @@ -0,0 +1,365 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#include "Assignment.h" +#include "ClientManagerMock.h" +#include "ConsumeMessageServiceMock.h" +#include "ConsumeMessageType.h" +#include "InvocationContext.h" +#include "MessageAccessor.h" +#include "ProcessQueueImpl.h" +#include "PushConsumerMock.h" +#include "ReceiveMessageCallbackMock.h" +#include "ReceiveMessageResult.h" +#include "RpcClientMock.h" +#include "rocketmq/CredentialsProvider.h" +#include "rocketmq/MQMessageExt.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ProcessQueueTest : public testing::Test { +public: + void SetUp() override { + rpc_client_ = std::make_shared>(); + message_queue_.serviceAddress(service_address_); + message_queue_.setTopic(topic_); + message_queue_.setBrokerName(broker_name_); + message_queue_.setQueueId(queue_id_); + client_manager_ = std::make_shared>(); + credentials_provider_ = std::make_shared(access_key_, access_secret_); + consumer_ = std::make_shared>(); + auto consumer = std::dynamic_pointer_cast(consumer_); + + consume_message_service_ = std::make_shared>(); + ON_CALL(*consume_message_service_, messageListenerType) + .WillByDefault(testing::Return(MessageListenerType::STANDARD)); + + ON_CALL(*consumer_, getConsumeMessageService).WillByDefault(testing::Return(consume_message_service_)); + + process_queue_ = std::make_shared(message_queue_, filter_expression_, consumer, client_manager_); + receive_message_callback_ = std::make_shared>(); + process_queue_->callback(receive_message_callback_); + } + + void TearDown() override { + } + +protected: + std::string tenant_id_{"tenant-0"}; + std::string access_key_{"ak"}; + std::string access_secret_{"secret"}; + std::shared_ptr credentials_provider_; + std::string group_name_{"TestGroup"}; + std::string client_id_{"Client-0"}; + std::string broker_name_{"broker-a"}; + std::string region_{"cn-hangzhou"}; + std::string service_name_{"MQ"}; + int queue_id_{0}; + std::string topic_{"TestTopic"}; + std::string service_address_{"ipv4:10.0.0.1:10911"}; + std::string tag_{"TagA"}; + FilterExpression filter_expression_{tag_}; + MQMessageQueue message_queue_; + std::shared_ptr> rpc_client_; + std::shared_ptr> client_manager_; + std::shared_ptr> consumer_; + std::shared_ptr> consume_message_service_; + std::shared_ptr process_queue_; + std::shared_ptr> receive_message_callback_; + std::string resource_namespace_{"mq://test"}; + std::string message_body_{"Sample body"}; + + uint32_t threshold_quantity_{32}; + uint64_t threshold_memory_{4096}; + uint32_t consume_batch_size_{8}; +}; + +TEST_F(ProcessQueueTest, testBind) { + EXPECT_TRUE(process_queue_->bindFifoConsumeTask()); + EXPECT_FALSE(process_queue_->bindFifoConsumeTask()); + EXPECT_TRUE(process_queue_->unbindFifoConsumeTask()); + EXPECT_FALSE(process_queue_->unbindFifoConsumeTask()); + EXPECT_TRUE(process_queue_->bindFifoConsumeTask()); +} + +TEST_F(ProcessQueueTest, testExpired) { + EXPECT_FALSE(process_queue_->expired()); + process_queue_->idle_since_ -= MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_; + EXPECT_TRUE(process_queue_->expired()); +} + +TEST_F(ProcessQueueTest, testShouldThrottle) { + EXPECT_CALL(*consumer_, maxCachedMessageQuantity) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_quantity_)); + EXPECT_CALL(*consumer_, maxCachedMessageMemory) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_memory_)); + EXPECT_FALSE(process_queue_->shouldThrottle()); +} + +TEST_F(ProcessQueueTest, testShouldThrottle_ByQuantity) { + std::vector messages; + for (uint32_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::to_string(i)); + messages.emplace_back(message); + } + + process_queue_->cacheMessages(messages); + + EXPECT_CALL(*consumer_, maxCachedMessageQuantity) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_quantity_)); + EXPECT_CALL(*consumer_, maxCachedMessageMemory) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_memory_)); + EXPECT_TRUE(process_queue_->shouldThrottle()); +} + +TEST_F(ProcessQueueTest, testShouldThrottle_ByMemory) { + std::vector messages; + size_t body_length = 1024 * 4; + for (uint32_t i = 0; i < threshold_quantity_ / 2; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::string(body_length, 'c')); + messages.emplace_back(message); + } + + process_queue_->cacheMessages(messages); + + EXPECT_CALL(*consumer_, maxCachedMessageQuantity) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_quantity_)); + EXPECT_CALL(*consumer_, maxCachedMessageMemory) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_memory_)); + EXPECT_TRUE(process_queue_->shouldThrottle()); +} + +TEST_F(ProcessQueueTest, testHasPendingMessages) { + EXPECT_FALSE(process_queue_->hasPendingMessages()); +} + +TEST_F(ProcessQueueTest, testHasPendingMessages2) { + std::vector messages; + size_t body_length = 1024; + for (size_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::string(body_length, 'c')); + messages.emplace_back(message); + } + process_queue_->cacheMessages(messages); + EXPECT_TRUE(process_queue_->hasPendingMessages()); +} + +TEST_F(ProcessQueueTest, testTake) { + std::vector messages; + EXPECT_FALSE(process_queue_->take(consume_batch_size_, messages)); + EXPECT_TRUE(messages.empty()); +} + +TEST_F(ProcessQueueTest, testTake2) { + + { + std::vector messages; + size_t body_length = 1024; + for (size_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::string(body_length, 'c')); + messages.emplace_back(message); + } + process_queue_->cacheMessages(messages); + EXPECT_EQ(threshold_quantity_, process_queue_->cachedMessagesSize()); + } + + std::vector msgs; + EXPECT_TRUE(process_queue_->take(consume_batch_size_, msgs)); + EXPECT_FALSE(msgs.empty()); + EXPECT_EQ(tag_, msgs.begin()->getTags()); + EXPECT_EQ(topic_, msgs.begin()->getTopic()); + EXPECT_EQ(threshold_quantity_ - consume_batch_size_, process_queue_->cachedMessagesSize()); +} + +TEST_F(ProcessQueueTest, testRelease) { + EXPECT_CALL(*consumer_, messageModel) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(MessageModel::BROADCASTING)); + + int64_t offset; + EXPECT_FALSE(process_queue_->committedOffset(offset)); + + size_t body_length = 1024; + { + std::vector messages; + for (size_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::string(body_length, 'c')); + messages.emplace_back(message); + } + process_queue_->cacheMessages(messages); + EXPECT_EQ(threshold_quantity_, process_queue_->cachedMessagesSize()); + } + + std::vector msgs; + process_queue_->take(1, msgs); + + EXPECT_TRUE(process_queue_->committedOffset(offset)); + EXPECT_EQ(0, offset); + + process_queue_->release(body_length, 0); + + EXPECT_TRUE(process_queue_->committedOffset(offset)); + EXPECT_EQ(1, offset); +} + +TEST_F(ProcessQueueTest, testOffset) { + EXPECT_CALL(*consumer_, messageModel) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(MessageModel::BROADCASTING)); + + int64_t offset; + EXPECT_FALSE(process_queue_->committedOffset(offset)); + + size_t body_length = 1024; + { + std::vector messages; + for (size_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::string(body_length, 'c')); + messages.emplace_back(message); + } + process_queue_->cacheMessages(messages); + EXPECT_EQ(threshold_quantity_, process_queue_->cachedMessagesSize()); + } + + std::vector msgs; + process_queue_->take(threshold_quantity_, msgs); + + EXPECT_TRUE(process_queue_->committedOffset(offset)); + EXPECT_EQ(0, offset); + + for (size_t i = 0; i < threshold_quantity_; i++) { + process_queue_->release(body_length, i); + } + + EXPECT_TRUE(process_queue_->committedOffset(offset)); + EXPECT_EQ(threshold_quantity_, offset); +} + +TEST_F(ProcessQueueTest, testReceiveMessage_POP) { + EXPECT_CALL(*consumer_, tenantId).WillRepeatedly(testing::ReturnRef(tenant_id_)); + EXPECT_CALL(*consumer_, resourceNamespace).WillRepeatedly(testing::ReturnRef(resource_namespace_)); + EXPECT_CALL(*consumer_, credentialsProvider).WillRepeatedly(testing::Return(credentials_provider_)); + EXPECT_CALL(*consumer_, region).WillRepeatedly(testing::ReturnRef(region_)); + EXPECT_CALL(*consumer_, serviceName).WillRepeatedly(testing::ReturnRef(service_name_)); + EXPECT_CALL(*consumer_, clientId).WillRepeatedly(testing::Return(client_id_)); + EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); + EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); + + auto optional = absl::make_optional(filter_expression_); + + EXPECT_CALL(*consumer_, getFilterExpression).WillRepeatedly(testing::Return(optional)); + EXPECT_CALL(*consumer_, receiveBatchSize).WillRepeatedly(testing::Return(threshold_quantity_)); + EXPECT_CALL(*consumer_, messageModel).WillRepeatedly(testing::Return(MessageModel::CLUSTERING)); + + auto receive_message_mock = [this](const std::string& target, const Metadata& metadata, + const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, + const std::shared_ptr& cb) { + std::error_code ec; + ReceiveMessageResult result; + for (size_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + message.setBody(message_body_); + MessageAccessor::setQueueId(message, queue_id_); + MessageAccessor::setQueueOffset(message, i); + result.messages.emplace_back(message); + } + cb->onCompletion(ec, result); + }; + + EXPECT_CALL(*client_manager_, receiveMessage) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(receive_message_mock)); + process_queue_->receiveMessage(); +} + +TEST_F(ProcessQueueTest, testReceiveMessage_Pull) { + EXPECT_CALL(*consumer_, tenantId).WillRepeatedly(testing::ReturnRef(tenant_id_)); + EXPECT_CALL(*consumer_, resourceNamespace).WillRepeatedly(testing::ReturnRef(resource_namespace_)); + EXPECT_CALL(*consumer_, credentialsProvider).WillRepeatedly(testing::Return(credentials_provider_)); + EXPECT_CALL(*consumer_, region).WillRepeatedly(testing::ReturnRef(region_)); + EXPECT_CALL(*consumer_, serviceName).WillRepeatedly(testing::ReturnRef(service_name_)); + EXPECT_CALL(*consumer_, clientId).WillRepeatedly(testing::Return(client_id_)); + EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); + EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); + + auto optional = absl::make_optional(filter_expression_); + + EXPECT_CALL(*consumer_, getFilterExpression).WillRepeatedly(testing::Return(optional)); + EXPECT_CALL(*consumer_, receiveBatchSize).WillRepeatedly(testing::Return(threshold_quantity_)); + + std::error_code ec; + ReceiveMessageResult result; + + auto pull_message_mock = [&](const std::string& target_host, const Metadata& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(ec, result); + }; + + EXPECT_CALL(*client_manager_, pullMessage) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(pull_message_mock)); + process_queue_->receiveMessage(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp new file mode 100644 index 000000000..5b990524d --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -0,0 +1,273 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "ClientManagerFactory.h" +#include "ClientManagerMock.h" +#include "ProducerImpl.h" +#include "Scheduler.h" +#include "SchedulerImpl.h" +#include "StaticNameServerResolver.h" +#include "TopicRouteData.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQSelector.h" +#include "rocketmq/RocketMQ.h" +#include "rocketmq/SendResult.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ProducerImplTest : public testing::Test { +public: + ProducerImplTest() : credentials_provider_(std::make_shared(access_key_, access_secret_)) { + } + + void SetUp() override { + grpc_init(); + scheduler_ = std::make_shared(); + scheduler_->start(); + name_server_resolver_ = std::make_shared(name_server_list_); + client_manager_ = std::make_shared>(); + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); + ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); + producer_ = std::make_shared(group_); + producer_->resourceNamespace(resource_namespace_); + producer_->withNameServerResolver(name_server_resolver_); + producer_->setCredentialsProvider(credentials_provider_); + + { + std::vector partitions; + Topic topic(resource_namespace_, topic_); + std::vector
broker_addresses{Address(broker_host_, broker_port_)}; + ServiceAddress service_address(AddressScheme::IPv4, broker_addresses); + Broker broker(broker_name_, broker_id_, service_address); + Partition partition(topic, queue_id_, Permission::READ_WRITE, broker); + partitions.emplace_back(partition); + std::string debug_string; + topic_route_data_ = std::make_shared(partitions, debug_string); + } + } + + void TearDown() override { + scheduler_->shutdown(); + grpc_shutdown(); + } + +protected: + SchedulerSharedPtr scheduler_; + std::shared_ptr> client_manager_; + std::shared_ptr producer_; + std::string name_server_list_{"10.0.0.1:9876"}; + std::shared_ptr name_server_resolver_; + std::string resource_namespace_{"mq://test"}; + std::string group_{"CID_test"}; + std::string topic_{"Topic0"}; + int queue_id_{1}; + std::string tag_{"TagA"}; + std::string key_{"key-0"}; + std::string message_group_{"group-0"}; + std::string broker_name_{"broker-a"}; + int broker_id_{0}; + std::string message_body_{"Message Body Content"}; + std::string broker_host_{"10.0.0.1"}; + int broker_port_{10911}; + TopicRouteDataPtr topic_route_data_; + std::string access_key_{"access_key"}; + std::string access_secret_{"access_secret"}; + std::shared_ptr credentials_provider_; +}; + +TEST_F(ProducerImplTest, testStartShutdown) { + producer_->start(); + producer_->shutdown(); +} + +TEST_F(ProducerImplTest, testSend) { + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + bool cb_invoked = false; + SendResult send_result; + auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) { + cb->onSuccess(send_result); + cb_invoked = true; + return true; + }; + + EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); + producer_->start(); + + MQMessage message(topic_, tag_, message_body_); + std::error_code ec; + producer_->send(message, ec); + EXPECT_FALSE(ec); + EXPECT_TRUE(cb_invoked); + producer_->shutdown(); +} + +TEST_F(ProducerImplTest, testSend_WithMessageGroup) { + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + bool cb_invoked = false; + SendResult send_result; + auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) { + cb->onSuccess(send_result); + cb_invoked = true; + return true; + }; + + EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); + producer_->start(); + + MQMessage message(topic_, tag_, message_body_); + message.bindMessageGroup(message_group_); + std::error_code ec; + producer_->send(message, ec); + EXPECT_FALSE(ec); + EXPECT_TRUE(cb_invoked); + producer_->shutdown(); +} + +TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + bool cb_invoked = false; + SendResult send_result; + auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) { + cb->onSuccess(send_result); + cb_invoked = true; + return true; + }; + + EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); + producer_->start(); + + MQMessage message(topic_, tag_, message_body_); + + std::error_code ec; + auto&& list = producer_->listMessageQueue(topic_, ec); + + EXPECT_FALSE(list.empty()); + + message.bindMessageQueue(list[0]); + + producer_->send(message, ec); + + EXPECT_TRUE(cb_invoked); + producer_->shutdown(); +} + +class TestSendCallback : public SendCallback { +public: + TestSendCallback(bool& completed, absl::Mutex& mtx, absl::CondVar& cv) : completed_(completed), mtx_(mtx), cv_(cv) { + } + void onSuccess(SendResult& send_result) noexcept override { + absl::MutexLock lk(&mtx_); + completed_ = true; + cv_.SignalAll(); + } + + void onFailure(const std::error_code& ec) noexcept override { + absl::MutexLock lk(&mtx_); + completed_ = true; + cv_.SignalAll(); + } + +protected: + bool& completed_; + absl::Mutex& mtx_; + absl::CondVar& cv_; +}; + +TEST_F(ProducerImplTest, testAsyncSend) { + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + bool cb_invoked = false; + SendResult send_result; + auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) { + cb->onSuccess(send_result); + cb_invoked = true; + return true; + }; + + EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto send_callback = absl::make_unique(completed, mtx, cv); + + producer_->start(); + + MQMessage message(topic_, tag_, message_body_); + producer_->send(message, send_callback.get()); + + if (!completed) { + absl::MutexLock lk(&mtx); + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + + EXPECT_TRUE(cb_invoked); + producer_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp new file mode 100644 index 000000000..729aac0ee --- /dev/null +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -0,0 +1,423 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "ClientManagerFactory.h" +#include "ClientManagerMock.h" +#include "InvocationContext.h" +#include "PullConsumerImpl.h" +#include "Scheduler.h" +#include "StaticNameServerResolver.h" +#include "apache/rocketmq/v1/definition.pb.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/ConsumeType.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/RocketMQ.h" + +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PullConsumerImplTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + scheduler_ = std::make_shared(); + name_server_resolver_ = std::make_shared(name_server_list_); + + scheduler_->start(); + client_manager_ = std::make_shared>(); + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); + ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); + + pull_consumer_ = std::make_shared(group_); + pull_consumer_->withNameServerResolver(name_server_resolver_); + pull_consumer_->resourceNamespace(resource_namespace_); + + { + std::vector partitions; + Topic topic(resource_namespace_, topic_); + std::vector
broker_addresses{Address(broker_host_, broker_port_)}; + ServiceAddress service_address(AddressScheme::IPv4, broker_addresses); + Broker broker(broker_name_, broker_id_, service_address); + Partition partition(topic, queue_id_, Permission::READ_WRITE, broker); + partitions.emplace_back(partition); + std::string debug_string; + topic_route_data_ = std::make_shared(partitions, debug_string); + } + } + + void TearDown() override { + grpc_shutdown(); + scheduler_->shutdown(); + } + +protected: + std::string resource_namespace_{"mq://test"}; + std::string name_server_list_{"10.0.0.1:9876"}; + std::shared_ptr name_server_resolver_; + std::string group_{"Group-0"}; + std::string topic_{"Test"}; + std::string tag_{"TagB"}; + std::shared_ptr> client_manager_; + std::shared_ptr pull_consumer_; + SchedulerSharedPtr scheduler_; + std::string broker_name_{"broker-a"}; + int broker_id_{0}; + std::string message_body_{"Message Body Content"}; + std::string broker_host_{"10.0.0.1"}; + int broker_port_{10911}; + int queue_id_{1}; + TopicRouteDataPtr topic_route_data_; + int batch_size_{32}; +}; + +TEST_F(PullConsumerImplTest, testStartShutdown) { + pull_consumer_->start(); + pull_consumer_->shutdown(); +} + +TEST_F(PullConsumerImplTest, testQueuesFor) { + pull_consumer_->start(); + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + pull_consumer_->queuesFor(topic_); + + pull_consumer_->shutdown(); +} + +class TestPullCallback : public PullCallback { +public: + TestPullCallback(bool& success, bool& failure) : success_(success), failure_(failure) { + } + void onSuccess(const PullResult& pull_result) noexcept override { + success_ = true; + failure_ = false; + } + + void onFailure(const std::error_code& e) noexcept override { + failure_ = true; + success_ = false; + } + +private: + bool& success_; + bool& failure_; +}; + +TEST_F(PullConsumerImplTest, testPull) { + pull_consumer_->start(); + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::error_code ec; + ReceiveMessageResult result; + + for (int i = 0; i < batch_size_; i++) { + MQMessageExt message; + message.setBody(message_body_); + message.setTopic(topic_); + message.setTags(tag_); + result.messages.emplace_back(message); + } + + auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(ec, result); + }; + + EXPECT_CALL(*client_manager_, pullMessage) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_pull_message)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + PullMessageQuery query; + query.message_queue = *queues.begin(); + query.offset = 0; + query.await_time = std::chrono::seconds(3); + + bool success = false; + bool failure = false; + auto pull_callback = new TestPullCallback(success, failure); + pull_consumer_->pull(query, pull_callback); + + EXPECT_TRUE(success); + EXPECT_FALSE(failure); + + pull_consumer_->shutdown(); + delete pull_callback; +} + +TEST_F(PullConsumerImplTest, testPull_gRPC_error) { + pull_consumer_->start(); + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::error_code ec; + ReceiveMessageResult result; + auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + ec = ErrorCode::BadRequest; + cb(ec, result); + }; + + EXPECT_CALL(*client_manager_, pullMessage) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_pull_message)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + PullMessageQuery query; + query.message_queue = *queues.begin(); + query.offset = 0; + query.await_time = std::chrono::seconds(3); + + bool success = false; + bool failure = false; + auto pull_callback = new TestPullCallback(success, failure); + pull_consumer_->pull(query, pull_callback); + + EXPECT_TRUE(failure); + EXPECT_FALSE(success); + + pull_consumer_->shutdown(); + delete pull_callback; +} + +TEST_F(PullConsumerImplTest, testPull_biz_error) { + pull_consumer_->start(); + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::error_code ec; + ReceiveMessageResult result; + + auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + ec = ErrorCode::BadRequest; + cb(ec, result); + }; + + EXPECT_CALL(*client_manager_, pullMessage) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_pull_message)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + PullMessageQuery query; + query.message_queue = *queues.begin(); + query.offset = 0; + query.await_time = std::chrono::seconds(3); + + bool success = false; + bool failure = false; + auto pull_callback = new TestPullCallback(success, failure); + pull_consumer_->pull(query, pull_callback); + + EXPECT_FALSE(success); + EXPECT_TRUE(failure); + + pull_consumer_->shutdown(); + delete pull_callback; +} + +TEST_F(PullConsumerImplTest, testQueryOffset) { + pull_consumer_->start(); + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + QueryOffsetResponse response; + int64_t offset = 1; + response.set_offset(offset); + + auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, + const QueryOffsetRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, response); + }; + + EXPECT_CALL(*client_manager_, queryOffset) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_query_offset)); + + OffsetQuery query; + query.policy = QueryOffsetPolicy::BEGINNING; + query.message_queue = *queues.begin(); + + std::future offset_future = pull_consumer_->queryOffset(query); + EXPECT_EQ(offset, offset_future.get()); + + pull_consumer_->shutdown(); +} + +TEST_F(PullConsumerImplTest, testQueryOffset_End) { + pull_consumer_->start(); + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + QueryOffsetResponse response; + int64_t offset = 1; + response.set_offset(offset); + + auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, + const QueryOffsetRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, response); + }; + + EXPECT_CALL(*client_manager_, queryOffset) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_query_offset)); + + OffsetQuery query; + query.policy = QueryOffsetPolicy::END; + query.message_queue = *queues.begin(); + + std::future offset_future = pull_consumer_->queryOffset(query); + EXPECT_EQ(offset, offset_future.get()); + + pull_consumer_->shutdown(); +} + +TEST_F(PullConsumerImplTest, testQueryOffset_Timepoint) { + pull_consumer_->start(); + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + QueryOffsetResponse response; + int64_t offset = 1; + response.set_offset(offset); + + auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, + const QueryOffsetRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, response); + }; + + EXPECT_CALL(*client_manager_, queryOffset) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_query_offset)); + + OffsetQuery query; + query.policy = QueryOffsetPolicy::TIME_POINT; + query.time_point = std::chrono::system_clock::now(); + query.message_queue = *queues.begin(); + + std::future offset_future = pull_consumer_->queryOffset(query); + EXPECT_EQ(offset, offset_future.get()); + + pull_consumer_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp new file mode 100644 index 000000000..b84dbea60 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include "gtest/gtest.h" +#include + +#include "ClientManagerFactory.h" +#include "ClientManagerMock.h" +#include "InvocationContext.h" +#include "MessageAccessor.h" +#include "PushConsumerImpl.h" +#include "Scheduler.h" +#include "StaticNameServerResolver.h" +#include "grpc/grpc.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/MessageListener.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TestStandardMessageListener : public StandardMessageListener { +public: + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { + return ConsumeMessageResult::SUCCESS; + } +}; + +class PushConsumerImplTest : public testing::Test { +public: + PushConsumerImplTest() : message_listener_(absl::make_unique()) { + } + + void SetUp() override { + grpc_init(); + scheduler_ = std::make_shared(); + name_server_resolver_ = std::make_shared(name_server_list_); + client_manager_ = std::make_shared>(); + ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); + push_consumer_ = std::make_shared(group_); + push_consumer_->resourceNamespace(resource_namespace_); + push_consumer_->withNameServerResolver(name_server_resolver_); + push_consumer_->registerMessageListener(message_listener_.get()); + scheduler_->start(); + + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); + } + + void TearDown() override { + scheduler_->shutdown(); + grpc_shutdown(); + } + +protected: + SchedulerSharedPtr scheduler_; + std::string name_server_list_{"10.0.0.1:9876"}; + std::shared_ptr name_server_resolver_; + std::string resource_namespace_{"mq://test"}; + std::string group_{"CID_test"}; + std::string topic_{"Topic0"}; + std::string tag_{"TagA"}; + std::string key_{"key-0"}; + std::string message_body_{"Message Body Content"}; + int delay_level_{1}; + std::shared_ptr> client_manager_; + std::unique_ptr message_listener_; + std::shared_ptr push_consumer_; + const std::string target_endpoint_{"localhost:10911"}; +}; + +TEST_F(PushConsumerImplTest, testAck) { + auto ack_cb = [](const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& cb) { + std::error_code ec; + cb(ec); + }; + + EXPECT_CALL(*client_manager_, ack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(ack_cb)); + + push_consumer_->start(); + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + auto callback = [&](const std::error_code& ec) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + + MQMessageExt message; + message.setTopic(topic_); + message.setBody(message_body_); + message.setTags(tag_); + message.setKey(key_); + message.setDelayTimeLevel(delay_level_); + MessageAccessor::setTargetEndpoint(message, target_endpoint_); + + push_consumer_->ack(message, callback); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + push_consumer_->shutdown(); +} + +TEST_F(PushConsumerImplTest, testNack) { + auto nack_cb = [](const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& cb) { + std::error_code ec; + cb(ec); + }; + + EXPECT_CALL(*client_manager_, nack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(nack_cb)); + + push_consumer_->start(); + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + auto callback = [&](const std::error_code& ec) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + + MQMessageExt message; + message.setTopic(topic_); + message.setBody(message_body_); + message.setTags(tag_); + message.setKey(key_); + message.setDelayTimeLevel(delay_level_); + MessageAccessor::setTargetEndpoint(message, target_endpoint_); + push_consumer_->nack(message, callback); + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + push_consumer_->shutdown(); +} + +TEST_F(PushConsumerImplTest, testForward) { + InvocationContext invocation_context; + + auto forward_cb = + [&](const std::string& target_host, const Metadata& metadata, + const ForwardMessageToDeadLetterQueueRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) { + cb(&invocation_context); + }; + + EXPECT_CALL(*client_manager_, forwardMessageToDeadLetterQueue) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(forward_cb)); + + push_consumer_->start(); + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + auto callback = [&](bool ok) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + + MQMessageExt message; + message.setTopic(topic_); + message.setBody(message_body_); + message.setTags(tag_); + message.setKey(key_); + message.setDelayTimeLevel(delay_level_); + + push_consumer_->forwardToDeadLetterQueue(message, callback); + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + push_consumer_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp new file mode 100644 index 000000000..da037d99a --- /dev/null +++ b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "SendCallbacks.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class SendCallbacksTest : public testing::Test {}; + +TEST_F(SendCallbacksTest, testAwait) { + std::string msg_id("Msg-0"); + AwaitSendCallback callback; + std::thread waker([&]() { + std::this_thread::sleep_for(std::chrono::seconds(3)); + SendResult send_result; + send_result.setMsgId(msg_id); + callback.onSuccess(send_result); + }); + + callback.await(); + EXPECT_TRUE(callback); + EXPECT_EQ(callback.sendResult().getMsgId(), msg_id); + if (waker.joinable()) { + waker.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp new file mode 100644 index 000000000..0bca7e429 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "StaticNameServerResolver.h" + +#include "absl/strings/str_replace.h" +#include "absl/strings/str_split.h" + +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class StaticNameServerResolverTest : public testing::Test { +public: + StaticNameServerResolverTest() : resolver_(name_server_list_) { + } + + void SetUp() override { + resolver_.start(); + } + + void TearDown() override { + resolver_.shutdown(); + } + +protected: + std::string name_server_list_{"10.0.0.1:9876;10.0.0.2:9876"}; + StaticNameServerResolver resolver_; +}; + +TEST_F(StaticNameServerResolverTest, testResolve) { + std::string result = + "ipv4:" + absl::StrReplaceAll(name_server_list_, {std::pair(";", ",")}); + ASSERT_EQ(result, resolver_.resolve()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp new file mode 100644 index 000000000..9cdec60fd --- /dev/null +++ b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "StsCredentialsProviderImpl.h" +#include "HttpClientMock.h" +#include "absl/memory/memory.h" +#include "grpc/grpc.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class StsCredentialsProviderImplTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + sts_credentials_provider = std::make_shared("test"); + auto http_client_ = absl::make_unique>(); + auto http_get_action = + [](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) { + std::multimap header; + std::string body = R"( + { + "AccessKeyId": "key", + "AccessKeySecret": "secret", + "SecurityToken": "token", + "Expiration" : "2017-11-01T05:20:01Z", + "LastUpdated" : "2017-10-31T23:20:01Z", + "Code" : "Success" + } + )"; + cb(200, header, body); + }; + + EXPECT_CALL(*http_client_, get).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(http_get_action)); + sts_credentials_provider->withHttpClient(std::move(http_client_)); + } + + void TearDown() override { + grpc_shutdown(); + } + +protected: + std::shared_ptr sts_credentials_provider; +}; + +TEST_F(StsCredentialsProviderImplTest, testGetCredentials) { + auto credentials = sts_credentials_provider->getCredentials(); + EXPECT_EQ(credentials.accessKey(), "key"); + EXPECT_EQ(credentials.accessSecret(), "secret"); + absl::Time time; + std::string input = "2017-11-01 05:20:01"; + std::string format = "%Y-%m-%d %H:%H:%S"; + std::string error; + EXPECT_TRUE(absl::ParseTime(format, input, &time, &error)); + EXPECT_EQ(credentials.expirationInstant(), absl::ToChronoTime(time)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h new file mode 100644 index 000000000..3e84ef2b0 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "google/rpc/code.pb.h" +#include "grpc/grpc.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "ClientManagerFactory.h" +#include "ClientManagerImpl.h" +#include "RpcClientMock.h" +#include "StaticNameServerResolver.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MQClientTest : public testing::Test { +public: + MQClientTest() = default; + + void SetUp() override { + grpc_init(); + name_server_list_.emplace_back(name_server_address_); + client_instance_ = std::make_shared(resource_namespace_); + rpc_client_ns_ = std::make_shared>(); + ON_CALL(*rpc_client_ns_, needHeartbeat()).WillByDefault(testing::Return(false)); + ON_CALL(*rpc_client_ns_, ok()).WillByDefault(testing::Invoke([this]() { return client_ok_; })); + ON_CALL(*rpc_client_ns_, asyncQueryRoute(testing::_, testing::_)) + .WillByDefault(testing::Invoke( + std::bind(&MQClientTest::mockQueryRoute, this, std::placeholders::_1, std::placeholders::_2))); + client_instance_->addRpcClient(name_server_address_, rpc_client_ns_); + ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_instance_); + + name_server_resolver_ = std::make_shared(name_server_address_); + } + + void TearDown() override { + rpc_client_ns_.reset(); + client_instance_->cleanRpcClients(); + client_instance_.reset(); + grpc_shutdown(); + } + +protected: + std::shared_ptr client_instance_; + std::shared_ptr> rpc_client_ns_; + const int16_t port_{10911}; + const int32_t partition_num_{24}; + const int32_t avg_partition_per_host_{8}; + std::string name_server_address_{"ipv4:127.0.0.1:9876"}; + std::shared_ptr name_server_resolver_; + std::string group_name_{"CID_Test"}; + std::string topic_{"Topic_Test"}; + std::string resource_namespace_{"mq://test"}; + google::rpc::Code ok_{google::rpc::Code::OK}; + bool client_ok_{true}; + std::vector name_server_list_; + +private: + void mockQueryRoute(const QueryRouteRequest& request, + InvocationContext* invocation_context) const { + invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); + auto partitions = invocation_context->response.mutable_partitions(); + for (int i = 0; i < partition_num_; i++) { + auto partition = new rmq::Partition(); + partition->set_id(i % avg_partition_per_host_); + partition->set_permission(rmq::Permission::READ_WRITE); + partition->mutable_topic()->set_name(request.topic().name()); + partition->mutable_topic()->set_resource_namespace(request.topic().resource_namespace()); + std::string broker_name{"broker-"}; + broker_name.push_back('a' + i / avg_partition_per_host_); + partition->mutable_broker()->set_name(broker_name); + + auto endpoint = partition->mutable_broker()->mutable_endpoints(); + endpoint->set_scheme(rmq::AddressScheme::IPv4); + auto addresses = endpoint->mutable_addresses(); + auto address = new rmq::Address; + address->set_host(fmt::format("10.0.0.{}", i / avg_partition_per_host_)); + address->set_port(port_); + addresses->AddAllocated(address); + partitions->AddAllocated(partition); + } + // Mock invoke callback. + invocation_context->onCompletion(true); + } +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/scheduler/BUILD.bazel b/src/test/cpp/ut/scheduler/BUILD.bazel new file mode 100644 index 000000000..23615c556 --- /dev/null +++ b/src/test/cpp/ut/scheduler/BUILD.bazel @@ -0,0 +1,30 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "scheduler_test", + srcs = [ + "SchedulerTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/base:base_library", + "//src/main/cpp/concurrent:countdown_latch_library", + "//src/main/cpp/scheduler:scheduler_library", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp new file mode 100644 index 000000000..19a3dae72 --- /dev/null +++ b/src/test/cpp/ut/scheduler/SchedulerTest.cpp @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "SchedulerImpl.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class SchedulerTest : public testing::Test { +public: + SchedulerTest() : scheduler(std::make_shared()) { + } + + void SetUp() override { + scheduler->start(); + } + + void TearDown() override { + scheduler->shutdown(); + } + +protected: + SchedulerSharedPtr scheduler; +}; + +TEST_F(SchedulerTest, testSingleShot) { + absl::Mutex mtx; + absl::CondVar cv; + int callback_fire_count{0}; + auto callback = [&]() { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + }; + + scheduler->schedule(callback, "single-shot", std::chrono::milliseconds(10), std::chrono::milliseconds(0)); + + // Wait till callback is executed. + { + absl::MutexLock lock(&mtx); + if (!callback_fire_count) { + cv.Wait(&mtx); + } + } +} + +TEST_F(SchedulerTest, testCancel) { + absl::Mutex mtx; + absl::CondVar cv; + int callback_fire_count{0}; + auto callback = [&]() { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + }; + + std::uint32_t task_id = + scheduler->schedule(callback, "test-cancel", std::chrono::milliseconds(100), std::chrono::milliseconds(100)); + scheduler->cancel(task_id); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ASSERT_EQ(0, callback_fire_count); +} + +TEST_F(SchedulerTest, testPeriodicShot) { + absl::Mutex mtx; + absl::CondVar cv; + int callback_fire_count{0}; + auto callback = [&]() { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + }; + + std::uintptr_t task_id = + scheduler->schedule(callback, "periodic-task", std::chrono::milliseconds(10), std::chrono::milliseconds(100)); + // Wait till callback is executed. + std::this_thread::sleep_for(std::chrono::milliseconds(600)); + ASSERT_TRUE(callback_fire_count >= 4); + scheduler->cancel(task_id); +} + +TEST_F(SchedulerTest, testSingleShotWithZeroDelay) { + absl::Mutex mtx; + absl::CondVar cv; + int callback_fire_count{0}; + auto callback = [&]() { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + }; + + scheduler->schedule(callback, "single-shot-with-0-delay", std::chrono::milliseconds(0), std::chrono::milliseconds(0)); + + // Wait till callback is executed. + { + absl::MutexLock lock(&mtx); + if (!callback_fire_count) { + cv.Wait(&mtx); + } + } +} + +TEST_F(SchedulerTest, testException) { + absl::Mutex mtx; + absl::CondVar cv; + int callback_fire_count{0}; + auto callback = [&]() { + { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + } + + std::exception e; + throw e; + }; + + scheduler->schedule(callback, "test-exception", std::chrono::milliseconds(100), std::chrono::milliseconds(100)); + + // Wait till callback is executed. + { + absl::MutexLock lock(&mtx); + if (callback_fire_count <= 5) { + cv.Wait(&mtx); + } + } +} +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/tracing/exporters/BUILD.bazel b/src/test/cpp/ut/tracing/exporters/BUILD.bazel new file mode 100644 index 000000000..1fc34774a --- /dev/null +++ b/src/test/cpp/ut/tracing/exporters/BUILD.bazel @@ -0,0 +1,40 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "otlp_exporter_test", + srcs = [ + "OtlpExportersTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/tracing/exporters:otlp_exporter", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "span_context_test", + srcs = [ + "SpanContextTest.cpp", + ], + deps = [ + "@io_opencensus_cpp//opencensus/trace", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp new file mode 100644 index 000000000..1d859dc85 --- /dev/null +++ b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ClientConfigMock.h" +#include "ClientManagerMock.h" +#include "OtlpExporter.h" +#include "opencensus/trace/sampler.h" +#include "opencensus/trace/span.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class OtlpExporterTest : public testing::Test { +public: + void SetUp() override { + client_manager_ = std::make_shared>(); + } + + void TearDown() override { + } + +protected: + std::shared_ptr> client_manager_; + ClientConfigMock client_config_; +}; + +TEST_F(OtlpExporterTest, testExport) { + auto exporter = std::make_shared(client_manager_, &client_config_); + exporter->traceMode(TraceMode::Develop); + exporter->start(); + + auto& sampler = Samplers::always(); + + auto span_generator = [&] { + int total = 20; + while (total) { + auto span = opencensus::trace::Span::StartSpan("TestSpan", nullptr, {&sampler}); + span.AddAnnotation("annotation-1", {{"key-1", "value-1"}, {"key-2", 2}, {"key-3", true}}); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + span.End(); + if (0 == --total % 10) { + std::cout << "Total: " << total << std::endl; + } + } + }; + + std::thread t(span_generator); + if (t.joinable()) { + t.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp new file mode 100644 index 000000000..beb39fa13 --- /dev/null +++ b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "opencensus/trace/propagation/trace_context.h" +#include "gtest/gtest.h" + +TEST(SpanContextTest, testSpanContext) { + std::string empty_context; + opencensus::trace::SpanContext span_context; + EXPECT_NO_THROW(span_context = opencensus::trace::propagation::FromTraceParentHeader(empty_context)); + EXPECT_FALSE(span_context.IsValid()); +} diff --git a/src/thread/disruptor/batch_descriptor.h b/src/thread/disruptor/batch_descriptor.h deleted file mode 100755 index ba1a035f2..000000000 --- a/src/thread/disruptor/batch_descriptor.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_BATCH_DESCRIPTOR_H_ // NOLINT -#define DISRUPTOR_BATCH_DESCRIPTOR_H_ // NOLINT - -#include "sequence.h" - -namespace rocketmq { - -// Used to record the batch of sequences claimed via {@link Sequencer}. -class BatchDescriptor { - public: - // Create a holder for tracking a batch of claimed sequences in a - // {@link Sequencer} - // - // @param size of the batch to claim. - BatchDescriptor(int size) : - size_(size), - end_(kInitialCursorValue) {} - - // Get the size of the batch - int size() const { return size_; } - - // Get the end sequence of a batch. - // - // @return the end sequence in the batch. - int64_t end() const { return end_; } - - // Set the end sequence of a batch. - // - // @param end sequence in the batch. - void set_end(int64_t end) { end_ = end; } - - - // Get the starting sequence of the batch. - // - // @return starting sequence in the batch. - int64_t Start() const { return end_ - size_ + 1L; } - - private: - int size_; - int64_t end_; -}; - -}; // namespace rocketmq - -#endif // DISRUPTOR_SEQUENCE_BATCH_H_ NOLINT diff --git a/src/thread/disruptor/claim_strategy.h b/src/thread/disruptor/claim_strategy.h deleted file mode 100755 index 0f3263a39..000000000 --- a/src/thread/disruptor/claim_strategy.h +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_CLAIM_STRATEGY_H_ // NOLINT -#define DISRUPTOR_CLAIM_STRATEGY_H_ // NOLINT - -#include -#include -#include - -#include "interface.h" - -namespace rocketmq { - -enum ClaimStrategyOption { - kSingleThreadedStrategy, - kMultiThreadedStrategy -}; - -// Optimised strategy can be used when there is a single publisher thread -// claiming {@link AbstractEvent}s. -class SingleThreadedStrategy :public noncopyable, public ClaimStrategyInterface { - public: - SingleThreadedStrategy(const int& buffer_size) : - buffer_size_(buffer_size), - sequence_(kInitialCursorValue), - min_gating_sequence_(kInitialCursorValue) {} - - virtual int64_t IncrementAndGet( - const std::vector& dependent_sequences) { - int64_t next_sequence = sequence_.IncrementAndGet(1L); - WaitForFreeSlotAt(next_sequence, dependent_sequences); - return next_sequence; - } - - virtual int64_t IncrementAndGet(const int& delta, - const std::vector& dependent_sequences) { - int64_t next_sequence = sequence_.IncrementAndGet(delta); - WaitForFreeSlotAt(next_sequence, dependent_sequences); - return next_sequence; - } - - virtual bool HasAvalaibleCapacity( - const std::vector& dependent_sequences) { - int64_t wrap_point = sequence_.sequence() + 1L - buffer_size_; - if (wrap_point > min_gating_sequence_.sequence()) { - int64_t min_sequence = GetMinimumSequence(dependent_sequences); - min_gating_sequence_.set_sequence(min_sequence); - if (wrap_point > min_sequence) - return false; - } - return true; - } - - virtual void SetSequence(const int64_t& sequence, - const std::vector& dependent_sequences) { - sequence_.set_sequence(sequence); - WaitForFreeSlotAt(sequence, dependent_sequences); - } - - virtual void SerialisePublishing(const int64_t& sequence, - const Sequence& cursor, - const int64_t& batch_size) {} - - private: - SingleThreadedStrategy(); - - void WaitForFreeSlotAt(const int64_t& sequence, - const std::vector& dependent_sequences) { - int64_t wrap_point = sequence - buffer_size_; - if (wrap_point > min_gating_sequence_.sequence()) { - int64_t min_sequence; - while (wrap_point > (min_sequence = GetMinimumSequence(dependent_sequences))) { - boost::this_thread::yield(); - } - } - } - - const int buffer_size_; - PaddedLong sequence_; - PaddedLong min_gating_sequence_; - -}; - -// Strategy to be used when there are multiple publisher threads claiming -// {@link AbstractEvent}s. -/* -class MultiThreadedStrategy : public ClaimStrategyInterface { - public: - MultiThreadedStrategy(const int& buffer_size) : - buffer_size_(buffer_size), - sequence_(kInitialCursorValue), - min_processor_sequence_(kInitialCursorValue) {} - - virtual int64_t IncrementAndGet( - const std::vector& dependent_sequences) { - WaitForCapacity(dependent_sequences, min_gating_sequence_local_); - int64_t next_sequence = sequence_.IncrementAndGet(); - WaitForFreeSlotAt(next_sequence, - dependent_sequences, - min_gating_sequence_local_); - return next_sequence; - } - - virtual int64_t IncrementAndGet(const int& delta, - const std::vector& dependent_sequences) { - int64_t next_sequence = sequence_.IncrementAndGet(delta); - WaitForFreeSlotAt(next_sequence, - dependent_sequences, - min_gating_sequence_local_); - return next_sequence; - } - virtual void SetSequence(const int64_t& sequence, - const std::vector& dependent_sequences) { - sequence_.set_sequence(sequence); - WaitForFreeSlotAt(sequence, - dependent_sequences, - min_gating_sequence_local_); - } - - virtual bool HasAvalaibleCapacity( - const std::vector& dependent_sequences) { - const int64_t wrap_point = sequence_.sequence() + 1L - buffer_size_; - if (wrap_point > min_gating_sequence_local_.sequence()) { - int64_t min_sequence = GetMinimumSequence(dependent_sequences); - min_gating_sequence_local_.set_sequence(min_sequence); - if (wrap_point > min_sequence) - return false; - } - return true; - } - - virtual void SerialisePublishing(const Sequence& cursor, - const int64_t& sequence, - const int64_t& batch_size) { - int64_t expected_sequence = sequence - batch_size; - int counter = retries; - - while (expected_sequence != cursor.sequence()) { - if (0 == --counter) { - counter = retries; - std::this_thread::yield(); - } - } - } - - private: - // Methods - void WaitForCapacity(const std::vector& dependent_sequences, - const MutableLong& min_gating_sequence) { - const int64_t wrap_point = sequence_.sequence() + 1L - buffer_size_; - if (wrap_point > min_gating_sequence.sequence()) { - int counter = retries; - int64_t min_sequence; - while (wrap_point > (min_sequence = GetMinimumSequence(dependent_sequences))) { - counter = ApplyBackPressure(counter); - } - min_gating_sequence.set_sequence(min_sequence); - } - } - - void WaitForFreeSlotAt(const int64_t& sequence, - const std::vector& dependent_sequences, - const MutableLong& min_gating_sequence) { - const int64_t wrap_point = sequence - buffer_size_; - if (wrap_point > min_gating_sequence.sequence()) { - int64_t min_sequence; - while (wrap_point > (min_sequence = GetMinimumSequence(dependent_sequences))) { - std::this_thread::yield(); - } - min_gating_sequence.set_sequence(min_sequence); - } - } - - int ApplyBackPressure(int counter) { - if (0 != counter) { - --counter; - std::this_thread::yield(); - } else { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - - return counter; - } - - const int buffer_size_; - PaddedSequence sequence_; - thread_local PaddedLong min_gating_sequence_local_; - - const int retries = 100; - -}; -*/ - -ClaimStrategyInterface* CreateClaimStrategy(ClaimStrategyOption option, - const int& buffer_size) { - switch (option) { - case kSingleThreadedStrategy: - return new SingleThreadedStrategy(buffer_size); - // case kMultiThreadedStrategy: - // return new MultiThreadedStrategy(buffer_size); - default: - return NULL; - } -}; - -}; // namespace rocketmq - -#endif // DISRUPTOR_CLAIM_STRATEGY_H_ NOLINT diff --git a/src/thread/disruptor/event_processor.h b/src/thread/disruptor/event_processor.h deleted file mode 100755 index 3ad080ddd..000000000 --- a/src/thread/disruptor/event_processor.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_EVENT_PROCESSOR_H_ // NOLINT -#define DISRUPTOR_EVENT_PROCESSOR_H_ // NOLINT - -#include -#include "ring_buffer.h" - -namespace rocketmq { - -template -class NoOpEventProcessor : public EventProcessorInterface { - public: - NoOpEventProcessor(RingBuffer* ring_buffer) : - ring_buffer_(ring_buffer) { } - - virtual Sequence* GetSequence() { - return ring_buffer_->GetSequencePtr(); - } - - virtual void Halt() {} - - virtual void Run() {} - - private: - RingBuffer* ring_buffer_; -}; - -template -class BatchEventProcessor : public boost::noncopyable, public EventProcessorInterface { - public: - BatchEventProcessor(RingBuffer* ring_buffer, - SequenceBarrierInterface* sequence_barrier, - EventHandlerInterface* event_handler, - ExceptionHandlerInterface* exception_handler) : - running_(false), - ring_buffer_(ring_buffer), - sequence_barrier_(sequence_barrier), - event_handler_(event_handler), - exception_handler_(exception_handler) {} - - - virtual Sequence* GetSequence() { return &sequence_; } - - virtual void Halt() { - running_.store(false); - sequence_barrier_->Alert(); - } - - virtual void Run() { - if (running_.load()) - { - printf("Thread is already running\r\n"); - } - running_.store(true); - sequence_barrier_->ClearAlert(); - event_handler_->OnStart(); - - T* event = NULL; - int64_t next_sequence = sequence_.sequence() + 1L; - - while (true) { - try { - int64_t avalaible_sequence = \ - sequence_barrier_->WaitFor(next_sequence, 300*1000);//wait 300 milliseconds to avoid taskThread blocking on BlockingStrategy::WaitFor when shutdown - //rocketmq::LOG_INFO("avalaible_sequence:%d, next_sequence:%d", avalaible_sequence,next_sequence); - while (next_sequence <= avalaible_sequence) { - event = ring_buffer_->Get(next_sequence); - event_handler_->OnEvent(next_sequence, - next_sequence == avalaible_sequence, event); - next_sequence++; - } - - sequence_.set_sequence(next_sequence - 1L); - } catch(const AlertException& e) { - //rocketmq::LOG_INFO("catch alertException"); - if (!running_.load()) - break; - } catch(const std::exception& e) { - //rocketmq::LOG_ERROR("catch stdException"); - exception_handler_->Handle(e, next_sequence, event); - sequence_.set_sequence(next_sequence); - next_sequence++; - } - } - //rocketmq::LOG_INFO("BatchEventProcessor shutdown"); - event_handler_->OnShutdown(); - running_.store(false); - } - - void operator()() { Run(); } - - private: - boost::atomic running_; - Sequence sequence_; - - RingBuffer* ring_buffer_; - SequenceBarrierInterface* sequence_barrier_; - EventHandlerInterface* event_handler_; - ExceptionHandlerInterface* exception_handler_; - -}; - - -}; // namespace rocketmq - -#endif // DISRUPTOR_EVENT_PROCESSOR_H_ NOLINT diff --git a/src/thread/disruptor/event_publisher.h b/src/thread/disruptor/event_publisher.h deleted file mode 100755 index ae0efd9ec..000000000 --- a/src/thread/disruptor/event_publisher.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_EVENT_PUBLISHER_H_ // NOLINT -#define DISRUPTOR_EVENT_PUBLISHER_H_ // NOLINT - -#include "ring_buffer.h" - -namespace rocketmq { - -template -class EventPublisher { - public: - EventPublisher(RingBuffer* ring_buffer) : ring_buffer_(ring_buffer) {} - - void PublishEvent(EventTranslatorInterface* translator) { - int64_t sequence = ring_buffer_->Next(); - translator->TranslateTo(sequence, ring_buffer_->Get(sequence)); - ring_buffer_->Publish(sequence); - } - - private: - RingBuffer* ring_buffer_; -}; - -}; // namespace rocketmq - -#endif diff --git a/src/thread/disruptor/exception_handler.h b/src/thread/disruptor/exception_handler.h deleted file mode 100755 index e7979a04f..000000000 --- a/src/thread/disruptor/exception_handler.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_EXCEPTION_HANDLER_H_ // NOLINT -#define DISRUPTOR_EXCEPTION_HANDLER_H_ // NOLINT - -#include - -#include "interface.h" - -namespace rocketmq { - -template -class IgnoreExceptionHandler: public ExceptionHandlerInterface { - public: - virtual void Handle(const std::exception& exception, - const int64_t& sequence, - T* event) { - // do nothing with the exception. - ; - } -}; - -template -class FatalExceptionHandler: public ExceptionHandlerInterface { - public: - virtual void Handle(const std::exception& exception, - const int64_t& sequence, - T* event) { - // rethrow the exception - throw exception; - } -}; - -}; // namespace rocketmq - -#endif // DISRUPTOR_EXCEPTION_HANDLER_H_ NOLINT diff --git a/src/thread/disruptor/exceptions.h b/src/thread/disruptor/exceptions.h deleted file mode 100755 index f968043a2..000000000 --- a/src/thread/disruptor/exceptions.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_EXCEPTIONS_H_ // NOLINT -#define DISRUPTOR_EXCEPTIONS_H_ // NOLINT - -#include - -namespace rocketmq { - -class AlertException : public std::exception { -}; - -}; // namespace rocketmq - -#endif // DISRUPTOR_EXCEPTIONS_H_ NOLINT diff --git a/src/thread/disruptor/interface.h b/src/thread/disruptor/interface.h deleted file mode 100755 index 0c07774c3..000000000 --- a/src/thread/disruptor/interface.h +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_INTERFACE_H_ // NOLINT -#define DISRUPTOR_INTERFACE_H_ // NOLINT - -#include -#include - -#include "sequence.h" -#include "batch_descriptor.h" - -namespace rocketmq { - -// Strategies employed for claiming the sequence of events in the -// {@link Seqencer} by publishers. -class ClaimStrategyInterface { - public: - // Is there available capacity in the buffer for the requested sequence. - // - // @param dependent_sequences to be checked for range. - // @return true if the buffer has capacity for the requested sequence. - virtual ~ClaimStrategyInterface() {} - virtual bool HasAvalaibleCapacity( - const std::vector& dependent_sequences) = 0; - - // Claim the next sequence in the {@link Sequencer}. - // - // @param dependent_sequences to be checked for range. - // @return the index to be used for the publishing. - virtual int64_t IncrementAndGet( - const std::vector& dependent_sequences) = 0; - - // Claim the next sequence in the {@link Sequencer}. - // - // @param delta to increment by. - // @param dependent_sequences to be checked for range. - // @return the index to be used for the publishing. - virtual int64_t IncrementAndGet(const int& delta, - const std::vector& dependent_sequences) = 0; - - // Set the current sequence value for claiming an event in the - // {@link Sequencer}. - // - // @param sequence to be set as the current value. - // @param dependent_sequences to be checked for range. - virtual void SetSequence(const int64_t& sequence, - const std::vector& dependent_sequences) = 0; - - // Serialise publishing in sequence. - // - // @param sequence to be applied. - // @param cursor to be serialise against. - // @param batch_size of the sequence. - virtual void SerialisePublishing(const int64_t& sequence, - const Sequence& cursor, - const int64_t& batch_size) = 0; -}; - -// Coordination barrier for tracking the cursor for publishers and sequence of -// dependent {@link EventProcessor}s for processing a data structure. -class SequenceBarrierInterface { - public: - // Wait for the given sequence to be available for consumption. - // - // @param sequence to wait for. - // @return the sequence up to which is available. - // - // @throws AlertException if a status change has occurred for the - // Disruptor. - virtual ~SequenceBarrierInterface(){} - virtual int64_t WaitFor(const int64_t& sequence) = 0; - - // Wait for the given sequence to be available for consumption with a - // time out. - // - // @param sequence to wait for. - // @param timeout in microseconds. - // @return the sequence up to which is available. - // - // @throws AlertException if a status change has occurred for the - // Disruptor. - virtual int64_t WaitFor(const int64_t& sequence, - const int64_t& timeout_micro) = 0; - - // Delegate a call to the {@link Sequencer#getCursor()} - // - // @return value of the cursor for entries that have been published. - virtual int64_t GetCursor() const = 0; - - // The current alert status for the barrier. - // - // @return true if in alert otherwise false. - virtual bool IsAlerted() const = 0; - - // Alert the {@link EventProcessor}s of a status change and stay in this - // status until cleared. - virtual void Alert() = 0; - - // Clear the current alert status. - virtual void ClearAlert() = 0; - - // Check if barrier is alerted, if so throws an AlertException - // - // @throws AlertException if barrier is alerted - virtual void CheckAlert() const = 0; -}; - -// Called by the {@link RingBuffer} to pre-populate all the events to fill the -// RingBuffer. -// -// @param event implementation storing the data for sharing during exchange -// or parallel coordination of an event. -template -class EventFactoryInterface { - public: - virtual ~EventFactoryInterface(){} - virtual T* NewInstance(const int& size) const = 0; -}; - -// Callback interface to be implemented for processing events as they become -// available in the {@link RingBuffer}. -// -// @param event implementation storing the data for sharing during exchange -// or parallel coordination of an event. -template -class EventHandlerInterface { - public: - // Called when a publisher has published an event to the {@link RingBuffer} - // - // @param event published to the {@link RingBuffer} - // @param sequence of the event being processed - // @param end_of_batch flag to indicate if this is the last event in a batch - // from the {@link RingBuffer} - // - // @throws Exception if the EventHandler would like the exception handled - // further up the chain. - virtual ~EventHandlerInterface(){} - virtual void OnEvent(const int64_t& sequence, - const bool& end_of_batch, - T* event) = 0; - - // Called once on thread start before processing the first event. - virtual void OnStart() = 0; - - // Called once on thread stop just before shutdown. - virtual void OnShutdown() = 0; -}; - -// Implementations translate another data representations into events claimed -// for the {@link RingBuffer}. -// -// @param event implementation storing the data for sharing during exchange -// or parallel coordination of an event. -template -class EventTranslatorInterface { - public: - // Translate a data representation into fields set in given event - // - // @param event into which the data should be translated. - // @param sequence that is assigned to events. - // @return the resulting event after it has been translated. - virtual ~EventTranslatorInterface(){} - virtual T* TranslateTo(const int64_t& sequence, T* event) { return NULL;} -}; - -// EventProcessors wait for events to become available for consumption from -// the {@link RingBuffer}. An event processor should be associated with a -// thread. -// -// @param event implementation storing the data for sharing during exchange -// or parallel coordination of an event. -template -class EventProcessorInterface { - public: - // Get a pointer to the {@link Sequence} being used by this - // {@link EventProcessor}. - // - // @return pointer to the {@link Sequence} for this - // {@link EventProcessor} - virtual ~EventProcessorInterface(){} - virtual Sequence* GetSequence() = 0; - - // Signal that this EventProcessor should stop when it has finished - // consuming at the next clean break. - // It will call {@link DependencyBarrier#alert()} to notify the thread to - // check status. - virtual void Halt() = 0; -}; - -// Callback handler for uncaught exception in the event processing cycle -// of the {@link BatchEventProcessor}. -// -// @param event type stored in the {@link RingBuffer}. -template -class ExceptionHandlerInterface { - public: - // Strategy for handling uncaught exceptions when processing an event. - // If the strategy wishes to suspend further processing by the - // {@link BatchEventProcessor} then it should throw a std::runtime_error. - // - // @param exception that propagated from the {@link EventHandler}. - // @param sequence of the event which caused the exception. - // @param event being processed when the exception occured. - virtual ~ExceptionHandlerInterface(){} - virtual void Handle(const std::exception& exception, - const int64_t& sequence, - T* event) = 0; -}; - -// Strategy employed for making {@link EventProcessor}s wait on a cursor -// {@link Sequence}. -class WaitStrategyInterface: public boost::noncopyable { - public: - // Wait for the given sequence to be available for consumption. - // - // @param dependents further back the chain that must advance first. - // @param cursor on which to wait. - // @param barrier the consumer is waiting on. - // @param sequence to be waited on. - // @return the sequence that is available which may be greater than the - // requested sequence. - // - // @throws AlertException if the status of the Disruptor has changed. - virtual ~WaitStrategyInterface(){} - virtual int64_t WaitFor(const std::vector& dependents, - const Sequence& cursor, - const SequenceBarrierInterface& barrier, - const int64_t& sequence) = 0; - - // Wait for the given sequence to be available for consumption in a - // {@link RingBuffer} with a timeout specified. - // - // @param dependents further back the chain that must advance first - // @param cursor on which to wait. - // @param barrier the consumer is waiting on. - // @param sequence to be waited on. - // @param timeout value in micro seconds to abort after. - // @return the sequence that is available which may be greater than the - // requested sequence. - // - // @throws AlertException if the status of the Disruptor has changed. - // @throws InterruptedException if the thread is interrupted. - virtual int64_t WaitFor(const std::vector& dependents, - const Sequence& cursor, - const SequenceBarrierInterface& barrier, - const int64_t & sequence, - const int64_t & timeout_micros) = 0; - - // Signal those waiting that the cursor has advanced. - virtual void SignalAllWhenBlocking() = 0; -}; - -}; // namespace rocketmq - -#endif // DISRUPTOR_INTERFACE_H_ NOLINT diff --git a/src/thread/disruptor/ring_buffer.h b/src/thread/disruptor/ring_buffer.h deleted file mode 100755 index c7150f152..000000000 --- a/src/thread/disruptor/ring_buffer.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_RING_BUFFER_H_ // NOLINT -#define DISRUPTOR_RING_BUFFER_H_ // NOLINT - -#include -#include - -#include "interface.h" -#include "claim_strategy.h" -#include "wait_strategy.h" -#include "sequencer.h" -#include "sequence_barrier.h" - -namespace rocketmq { - -// Ring based store of reusable entries containing the data representing an -// event beign exchanged between publisher and {@link EventProcessor}s. -// -// @param implementation storing the data for sharing during exchange -// or parallel coordination of an event. -template -class RingBuffer : public Sequencer { - public: - // Construct a RingBuffer with the full option set. - // - // @param event_factory to instance new entries for filling the RingBuffer. - // @param buffer_size of the RingBuffer, must be a power of 2. - // @param claim_strategy_option threading strategy for publishers claiming - // entries in the ring. - // @param wait_strategy_option waiting strategy employed by - // processors_to_track waiting in entries becoming available. - RingBuffer(EventFactoryInterface* event_factory, - int buffer_size, - ClaimStrategyOption claim_strategy_option, - WaitStrategyOption wait_strategy_option) : - Sequencer(buffer_size, - claim_strategy_option, - wait_strategy_option), - buffer_size_(buffer_size), - mask_(buffer_size - 1), - events_(event_factory->NewInstance(buffer_size)) { - } - - ~RingBuffer() { - delete[] events_; - } - - // Get the event for a given sequence in the RingBuffer. - // - // @param sequence for the event - // @return event pointer at the specified sequence position. - T* Get(const int64_t& sequence) { - return &events_[sequence & mask_]; - } - - private: - // Members - int buffer_size_; - int mask_; - T* events_; - -}; - -}; // namespace rocketmq - -#endif // DISRUPTOR_RING_BUFFER_H_ NOLINT diff --git a/src/thread/disruptor/sequence.h b/src/thread/disruptor/sequence.h deleted file mode 100755 index 4a5c75cec..000000000 --- a/src/thread/disruptor/sequence.h +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef CACHE_LINE_SIZE_IN_BYTES // NOLINT -#define CACHE_LINE_SIZE_IN_BYTES 64 // NOLINT -#endif // NOLINT -#define ATOMIC_SEQUENCE_PADDING_LENGTH \ - (CACHE_LINE_SIZE_IN_BYTES - sizeof(boost::atomic))/8 -#define SEQUENCE_PADDING_LENGTH \ - (CACHE_LINE_SIZE_IN_BYTES - sizeof(int64_t))/8 - -#ifndef DISRUPTOR_SEQUENCE_H_ // NOLINT -#define DISRUPTOR_SEQUENCE_H_ // NOLINT - -#include -#include -#include -#include -#include -using namespace boost; -namespace rocketmq { - -const int64_t kInitialCursorValue = -1L; - -// Sequence counter. -class Sequence:public noncopyable { - public: - // Construct a sequence counter that can be tracked across threads. - // - // @param initial_value for the counter. - Sequence(int64_t initial_value = kInitialCursorValue) : - value_(initial_value) {} - - // Get the current value of the {@link Sequence}. - // - // @return the current value. - int64_t sequence() const { return value_.load(boost::memory_order_acquire); } - - // Set the current value of the {@link Sequence}. - // - // @param the value to which the {@link Sequence} will be set. - void set_sequence(int64_t value) { value_.store(value, boost::memory_order_release); } - - // Increment and return the value of the {@link Sequence}. - // - // @param increment the {@link Sequence}. - // @return the new value incremented. - int64_t IncrementAndGet(const int64_t& increment) { - return value_.fetch_add(increment, boost::memory_order_release) + increment; - } - - private: - // members - boost::atomic value_; - -}; - -// Cache line padded sequence counter. -// -// Can be used across threads without worrying about false sharing if a -// located adjacent to another counter in memory. -class PaddedSequence : public Sequence { - public: - PaddedSequence(int64_t initial_value = kInitialCursorValue) : - Sequence(initial_value) {} - - //private: - // padding - //int64_t padding_[ATOMIC_SEQUENCE_PADDING_LENGTH]; - -}; - -// Non-atomic sequence counter. -// -// This counter is not thread safe. -class MutableLong { - public: - MutableLong(int64_t initial_value = kInitialCursorValue) : - sequence_(initial_value) {} - - int64_t sequence() const { return sequence_; } - - void set_sequence(const int64_t& sequence) { sequence_ = sequence; }; - - int64_t IncrementAndGet(const int64_t& delta) { sequence_ += delta; return sequence_; } - - private: - volatile int64_t sequence_; -}; - -// Cache line padded non-atomic sequence counter. -// -// This counter is not thread safe. -class PaddedLong : public MutableLong { - public: - PaddedLong(int64_t initial_value = kInitialCursorValue) : - MutableLong(initial_value) {} - //private: - //int64_t padding_[SEQUENCE_PADDING_LENGTH]; -}; - -int64_t GetMinimumSequence( - const std::vector& sequences) { - int64_t minimum = std::numeric_limits::max(); - - std::vector::const_iterator it= sequences.begin(); - for (;it!=sequences.end();it++) { - int64_t sequence = (*it)->sequence(); - minimum = minimum < sequence ? minimum : sequence; - } - - return minimum; -}; - -}; // namespace rocketmq - -#endif // DISRUPTOR_SEQUENCE_H_ NOLINT diff --git a/src/thread/disruptor/sequence_barrier.h b/src/thread/disruptor/sequence_barrier.h deleted file mode 100755 index 5882e39db..000000000 --- a/src/thread/disruptor/sequence_barrier.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_SEQUENCE_BARRIER_H_ // NOLINT -#define DISRUPTOR_SEQUENCE_BARRIER_H_ // NOLINT - -#include -#include - -#include "exceptions.h" -#include "interface.h" -namespace rocketmq { - -class ProcessingSequenceBarrier : SequenceBarrierInterface { - public: - ProcessingSequenceBarrier(WaitStrategyInterface* wait_strategy, - Sequence* sequence, - const std::vector& sequences) : - wait_strategy_(wait_strategy), - cursor_(sequence), - dependent_sequences_(sequences), - alerted_(false) { - } - - virtual int64_t WaitFor(const int64_t& sequence) { - return wait_strategy_->WaitFor(dependent_sequences_, *cursor_, *this, - sequence); - } - - virtual int64_t WaitFor(const int64_t& sequence, - const int64_t& timeout_micros) { - return wait_strategy_->WaitFor(dependent_sequences_, *cursor_, *this, - sequence, timeout_micros); - } - - virtual int64_t GetCursor() const { - return cursor_->sequence(); - } - - virtual bool IsAlerted() const { - return alerted_.load(boost::memory_order_acquire); - } - - virtual void Alert() { - //rocketmq::LOG_INFO("set alert to true"); - alerted_.store(true, boost::memory_order_release); - } - - virtual void ClearAlert() { - alerted_.store(false, boost::memory_order_release); - } - - virtual void CheckAlert() const { - if (IsAlerted()) - { - //rocketmq::LOG_INFO("throw alert exception\r\n"); - throw AlertException(); - } - } - - private: - WaitStrategyInterface* wait_strategy_; - Sequence* cursor_; - std::vector dependent_sequences_; - boost::atomic alerted_; -}; - -}; // namespace rocketmq - -#endif // DISRUPTOR_DEPENDENCY_BARRIER_H_ NOLINT diff --git a/src/thread/disruptor/sequencer.h b/src/thread/disruptor/sequencer.h deleted file mode 100755 index 98d617f5e..000000000 --- a/src/thread/disruptor/sequencer.h +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_SEQUENCER_H_ // NOLINT -#define DISRUPTOR_SEQUENCER_H_ // NOLINT - -#include - -#include "batch_descriptor.h" -#include "claim_strategy.h" -#include "interface.h" -#include "sequence_barrier.h" -#include "wait_strategy.h" - -namespace rocketmq { - -// Coordinator for claiming sequences for access to a data structures while -// tracking dependent {@link Sequence}s -class Sequencer: public boost::noncopyable { - public: - // Construct a Sequencer with the selected strategies. - // - // @param buffer_size over which sequences are valid. - // @param claim_strategy_option for those claiming sequences. - // @param wait_strategy_option for those waiting on sequences. - Sequencer(int buffer_size, - ClaimStrategyOption claim_strategy_option, - WaitStrategyOption wait_strategy_option) : - buffer_size_(buffer_size), - claim_strategy_(CreateClaimStrategy(claim_strategy_option, - buffer_size_)), - wait_strategy_(CreateWaitStrategy(wait_strategy_option)) { } - - ~Sequencer() { - delete claim_strategy_; - delete wait_strategy_; - } - - // Set the sequences that will gate publishers to prevent the buffer - // wrapping. - // - // @param sequences to be gated on. - void set_gating_sequences( - const std::vector& sequences) { - gating_sequences_ = sequences; - } - - // Create a {@link SequenceBarrier} that gates on the cursor and a list of - // {@link Sequence}s. - // - // @param sequences_to_track this barrier will track. - // @return the barrier gated as required. - ProcessingSequenceBarrier* NewBarrier( - const std::vector& sequences_to_track) { - return new ProcessingSequenceBarrier(wait_strategy_, &cursor_, - sequences_to_track); - } - - // Create a new {@link BatchDescriptor} that is the minimum of the - // requested size and the buffer_size. - // - // @param size for the new batch. - // @return the new {@link BatchDescriptor}. - BatchDescriptor* NewBatchDescriptor(const int& size) { - return new BatchDescriptor(sizeHasAvalaibleCapacity(gating_sequences_); - } - - // Claim the next event in sequence for publishing to the {@link RingBuffer}. - // - // @return the claimed sequence. - int64_t Next() { - return claim_strategy_->IncrementAndGet(gating_sequences_); - } - - // Claim the next batch of sequence numbers for publishing. - // - // @param batch_descriptor to be updated for the batch range. - // @return the updated batch_descriptor. - BatchDescriptor* Next(BatchDescriptor* batch_descriptor) { - int64_t sequence = claim_strategy_->IncrementAndGet(batch_descriptor->size(), gating_sequences_); - batch_descriptor->set_end(sequence); - return batch_descriptor; - } - - // Claim a specific sequence when only one publisher is involved. - // - // @param sequence to be claimed. - // @return sequence just claime. - int64_t Claim(const int64_t& sequence) { - claim_strategy_->SetSequence(sequence, gating_sequences_); - return sequence; - } - - // Publish an event and make it visible to {@link EventProcessor}s. - // - // @param sequence to be published. - void Publish(const int64_t& sequence) { - Publish(sequence, 1); - } - - // Publish the batch of events in sequence. - // - // @param sequence to be published. - void Publish(const BatchDescriptor& batch_descriptor) { - Publish(batch_descriptor.end(), batch_descriptor.size()); - } - - // Force the publication of a cursor sequence. - // - // Only use this method when forcing a sequence and you are sure only one - // publisher exists. This will cause the cursor to advance to this - // sequence. - // - // @param sequence to which is to be forced for publication. - void ForcePublish(const int64_t& sequence) { - cursor_.set_sequence(sequence); - wait_strategy_->SignalAllWhenBlocking(); - } - - // TODO(fsaintjacques): This was added to overcome - // NoOpEventProcessor::GetSequence(), this is not a clean solution. - Sequence* GetSequencePtr() { - return &cursor_; - } - - private: - // Helpers - void Publish(const int64_t& sequence, const int64_t& batch_size) { - //LOG_DEBUG("publish sequence:%d", sequence); - claim_strategy_->SerialisePublishing(sequence, cursor_, batch_size); - cursor_.set_sequence(sequence); - wait_strategy_->SignalAllWhenBlocking(); - } - - // Members - const int buffer_size_; - - PaddedSequence cursor_; - std::vector gating_sequences_; - - ClaimStrategyInterface* claim_strategy_; - WaitStrategyInterface* wait_strategy_; - -}; - -}; // namespace rocketmq - -#endif // DISRUPTOR_RING_BUFFER_H_ NOLINT diff --git a/src/thread/disruptor/utils.h b/src/thread/disruptor/utils.h deleted file mode 100755 index 0730093e2..000000000 --- a/src/thread/disruptor/utils.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_UTILS_H_ // NOLINT -#define DISRUPTOR_UTILS_H_ // NOLINT - -// From Google C++ Standard, modified to use C++11 deleted functions. -// A macro to disallow the copy constructor and operator= functions. -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) delete \ - void operator=(const TypeName&) delete; - -#endif // DISRUPTOR_UTILS_H_ NOLINT diff --git a/src/thread/disruptor/wait_strategy.h b/src/thread/disruptor/wait_strategy.h deleted file mode 100755 index 92044a8b0..000000000 --- a/src/thread/disruptor/wait_strategy.h +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright (c) 2011, François Saint-Jacques -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the disruptor-- nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL FRANÇOIS SAINT-JACQUES BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DISRUPTOR_WAITSTRATEGY_H_ // NOLINT -#define DISRUPTOR_WAITSTRATEGY_H_ // NOLINT - -#include -#include -#include -#include - -#include "exceptions.h" -#include "interface.h" -#include "sequence.h" - -namespace rocketmq { - -// Strategy options which are available to those waiting on a -// {@link RingBuffer} -enum WaitStrategyOption { - // This strategy uses a condition variable inside a lock to block the - // event procesor which saves CPU resource at the expense of lock - // contention. - kBlockingStrategy, - // This strategy uses a progressive back off strategy by first spinning, - // then yielding, then sleeping for 1ms period. This is a good strategy - // for burst traffic then quiet periods when latency is not critical. - kSleepingStrategy, - // This strategy calls Thread.yield() in a loop as a waiting strategy - // which reduces contention at the expense of CPU resource. - kYieldingStrategy, - // This strategy call spins in a loop as a waiting strategy which is - // lowest and most consistent latency but ties up a CPU. - kBusySpinStrategy -}; - -// Blocking strategy that uses a lock and condition variable for -// {@link Consumer}s waiting on a barrier. -// This strategy should be used when performance and low-latency are not as -// important as CPU resource. -class BlockingStrategy : public WaitStrategyInterface { - public: - BlockingStrategy() {} - - virtual int64_t WaitFor(const std::vector& dependents, - const Sequence& cursor, - const SequenceBarrierInterface& barrier, - const int64_t& sequence) { - int64_t available_sequence = 0; - // We need to wait. - if ((available_sequence = cursor.sequence()) < sequence) { - // acquire lock - boost::unique_lock ulock(mutex_); - while ((available_sequence = cursor.sequence()) < sequence) { - barrier.CheckAlert(); - consumer_notify_condition_.wait(ulock); - } - } // unlock happens here, on ulock destruction. - - if (0 != dependents.size()) { - while ((available_sequence = GetMinimumSequence(dependents)) < sequence) { - barrier.CheckAlert(); - } - } - - return available_sequence; - } - - virtual int64_t WaitFor(const std::vector& dependents, - const Sequence& cursor, - const SequenceBarrierInterface& barrier, - const int64_t& sequence, - const int64_t& timeout_micros) { - int64_t available_sequence = 0; - // We have to wait - if ((available_sequence = cursor.sequence()) < sequence) { - boost::unique_lock ulock(mutex_); - while ((available_sequence = cursor.sequence()) < sequence) { - barrier.CheckAlert(); - if (boost::cv_status::timeout == - consumer_notify_condition_.wait_for( - ulock, boost::chrono::microseconds(timeout_micros))) - break; - } - } // unlock happens here, on ulock destruction - - if (0 != dependents.size()) { - while ((available_sequence = GetMinimumSequence(dependents)) < sequence) { - barrier.CheckAlert(); - } - } - - return available_sequence; - } - - virtual void SignalAllWhenBlocking() { - boost::unique_lock ulock(mutex_); - consumer_notify_condition_.notify_all(); - } - - private: - boost::recursive_mutex mutex_; - boost::condition_variable_any consumer_notify_condition_; -}; - -// Sleeping strategy -class SleepingStrategy : public WaitStrategyInterface { - public: - SleepingStrategy() {} - - virtual int64_t WaitFor(const std::vector& dependents, - const Sequence& cursor, - const SequenceBarrierInterface& barrier, - const int64_t& sequence) { - int64_t available_sequence = 0; - int counter = kRetries; - - if (0 == dependents.size()) { - while ((available_sequence = cursor.sequence()) < sequence) { - counter = ApplyWaitMethod(barrier, counter); - } - } else { - while ((available_sequence = GetMinimumSequence(dependents)) < sequence) { - counter = ApplyWaitMethod(barrier, counter); - } - } - - return available_sequence; - } - - virtual int64_t WaitFor(const std::vector& dependents, - const Sequence& cursor, - const SequenceBarrierInterface& barrier, - const int64_t& sequence, - const int64_t& timeout_micros) { - // timing - boost::posix_time::ptime current_date_microseconds = - boost::posix_time::microsec_clock::local_time(); - int64_t start_micro = - current_date_microseconds.time_of_day().total_milliseconds(); - - int64_t available_sequence = 0; - int counter = kRetries; - - if (0 == dependents.size()) { - while ((available_sequence = cursor.sequence()) < sequence) { - counter = ApplyWaitMethod(barrier, counter); - boost::posix_time::ptime current_date_microseconds = - boost::posix_time::microsec_clock::local_time(); - int64_t end_micro = - current_date_microseconds.time_of_day().total_milliseconds(); - if (timeout_micros < (end_micro - start_micro)) break; - } - } else { - while ((available_sequence = GetMinimumSequence(dependents)) < sequence) { - counter = ApplyWaitMethod(barrier, counter); - boost::posix_time::ptime current_date_microseconds = - boost::posix_time::microsec_clock::local_time(); - int64_t end_micro = - current_date_microseconds.time_of_day().total_milliseconds(); - if (timeout_micros < (end_micro - start_micro)) break; - } - } - - return available_sequence; - } - - virtual void SignalAllWhenBlocking() {} - - static const int kRetries = 200; - - private: - int ApplyWaitMethod(const SequenceBarrierInterface& barrier, int counter) { - barrier.CheckAlert(); - if (counter > 100) { - counter--; - } else if (counter > 0) { - counter--; - boost::this_thread::yield(); - } else { - boost::this_thread::sleep_for(boost::chrono::milliseconds(1)); - } - - return counter; - } -}; - -// Yielding strategy that uses a sleep(0) for {@link EventProcessor}s waiting -// on a barrier. This strategy is a good compromise between performance and -// CPU resource. -class YieldingStrategy : public WaitStrategyInterface { - public: - YieldingStrategy() {} - - virtual int64_t WaitFor(const std::vector& dependents, - const Sequence& cursor, - const SequenceBarrierInterface& barrier, - const int64_t& sequence) { - int64_t available_sequence = 0; - int counter = kSpinTries; - - if (0 == dependents.size()) { - while ((available_sequence = cursor.sequence()) < sequence) { - counter = ApplyWaitMethod(barrier, counter); - } - } else { - while ((available_sequence = GetMinimumSequence(dependents)) < sequence) { - counter = ApplyWaitMethod(barrier, counter); - } - } - - return available_sequence; - } - - virtual int64_t WaitFor(const std::vector& dependents, - const Sequence& cursor, - const SequenceBarrierInterface& barrier, - const int64_t& sequence, - const int64_t& timeout_micros) { - boost::posix_time::ptime current_date_microseconds = - boost::posix_time::microsec_clock::local_time(); - int64_t start_micro = - current_date_microseconds.time_of_day().total_milliseconds(); - - int64_t available_sequence = 0; - int counter = kSpinTries; - - if (0 == dependents.size()) { - while ((available_sequence = cursor.sequence()) < sequence) { - counter = ApplyWaitMethod(barrier, counter); - boost::posix_time::ptime current_date_microseconds = - boost::posix_time::microsec_clock::local_time(); - int64_t end_micro = - current_date_microseconds.time_of_day().total_milliseconds(); - if (timeout_micros < (end_micro - start_micro)) break; - } - } else { - while ((available_sequence = GetMinimumSequence(dependents)) < sequence) { - counter = ApplyWaitMethod(barrier, counter); - boost::posix_time::ptime current_date_microseconds = - boost::posix_time::microsec_clock::local_time(); - int64_t end_micro = - current_date_microseconds.time_of_day().total_milliseconds(); - if (timeout_micros < (end_micro - start_micro)) break; - } - } - - return available_sequence; - } - - virtual void SignalAllWhenBlocking() {} - - static const int kSpinTries = 100; - - private: - int ApplyWaitMethod(const SequenceBarrierInterface& barrier, int counter) { - barrier.CheckAlert(); - if (counter == 0) { - boost::this_thread::yield(); - } else { - counter--; - } - - return counter; - } -}; - -// Busy Spin strategy that uses a busy spin loop for {@link EventProcessor}s -// waiting on a barrier. -// This strategy will use CPU resource to avoid syscalls which can introduce -// latency jitter. It is best used when threads can be bound to specific -// CPU cores. -class BusySpinStrategy : public WaitStrategyInterface { - public: - BusySpinStrategy() {} - - virtual int64_t WaitFor(const std::vector& dependents, - const Sequence& cursor, - const SequenceBarrierInterface& barrier, - const int64_t& sequence) { - int64_t available_sequence = 0; - if (0 == dependents.size()) { - while ((available_sequence = cursor.sequence()) < sequence) { - barrier.CheckAlert(); - } - } else { - while ((available_sequence = GetMinimumSequence(dependents)) < sequence) { - barrier.CheckAlert(); - } - } - - return available_sequence; - } - - virtual int64_t WaitFor(const std::vector& dependents, - const Sequence& cursor, - const SequenceBarrierInterface& barrier, - const int64_t& sequence, - const int64_t& timeout_micros) { - boost::posix_time::ptime current_date_microseconds = - boost::posix_time::microsec_clock::local_time(); - int64_t start_micro = - current_date_microseconds.time_of_day().total_milliseconds(); - int64_t available_sequence = 0; - - if (0 == dependents.size()) { - while ((available_sequence = cursor.sequence()) < sequence) { - barrier.CheckAlert(); - boost::posix_time::ptime current_date_microseconds = - boost::posix_time::microsec_clock::local_time(); - int64_t end_micro = - current_date_microseconds.time_of_day().total_milliseconds(); - if (timeout_micros < (end_micro - start_micro)) break; - } - } else { - while ((available_sequence = GetMinimumSequence(dependents)) < sequence) { - barrier.CheckAlert(); - boost::posix_time::ptime current_date_microseconds = - boost::posix_time::microsec_clock::local_time(); - int64_t end_micro = - current_date_microseconds.time_of_day().total_milliseconds(); - if (timeout_micros < (end_micro - start_micro)) break; - } - } - - return available_sequence; - } - - virtual void SignalAllWhenBlocking() {} -}; - -WaitStrategyInterface* CreateWaitStrategy(WaitStrategyOption wait_option) { - switch (wait_option) { - case kBlockingStrategy: - return new BlockingStrategy(); - case kSleepingStrategy: - return new SleepingStrategy(); - case kYieldingStrategy: - return new YieldingStrategy(); - case kBusySpinStrategy: - return new BusySpinStrategy(); - default: - return NULL; - } -} - -}; // namespace rocketmq - -#endif // DISRUPTOR_WAITSTRATEGY_H_ NOLINT diff --git a/src/thread/disruptorLFQ.h b/src/thread/disruptorLFQ.h deleted file mode 100644 index 30e5a1858..000000000 --- a/src/thread/disruptorLFQ.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -#ifndef _DISRUPTORLFQ_ -#define _DISRUPTORLFQ_ - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace rocketmq { -class Task; -class taskEventFactory : public EventFactoryInterface { - public: - virtual Task* NewInstance(const int& size) const; -}; - -class taskBatchHandler : public EventHandlerInterface { - public: - taskBatchHandler(int pullMsgThreadPoolNum); - virtual ~taskBatchHandler() {} - - virtual void OnEvent(const int64_t& sequence, const bool& end_of_batch, Task* event); - virtual void OnStart() {} - virtual void OnShutdown() {} - void runTaskEvent(Task event, int64_t sequence); - void stopIOService(); - - private: - boost::asio::io_service m_ioService; - boost::thread_group m_threadpool; - boost::asio::io_service::work m_ioServiceWork; -}; - -class taskEventTranslator : public EventTranslatorInterface { - public: - taskEventTranslator(Task* event); - virtual ~taskEventTranslator() {} - virtual Task* TranslateTo(const int64_t& sequence, Task* event); - - private: - Task* m_taskEvent; -}; - -class taskExceptionHandler : public ExceptionHandlerInterface { - public: - virtual void Handle(const std::exception& exception, const int64_t& sequence, Task* event) {} -}; - -class disruptorLFQ { - public: - disruptorLFQ(int threadCount) { - m_task_factory.reset(new taskEventFactory()); - m_ring_buffer.reset(new RingBuffer(m_task_factory.get(), - 1024, // default size is 1024, must be n power of 2 - kSingleThreadedStrategy, - kBlockingStrategy)); // load normal, lowest CPU occupy, but - // largest consume latency - - m_sequence_to_track.reset(new std::vector(0)); - m_sequenceBarrier.reset(m_ring_buffer->NewBarrier(*(m_sequence_to_track.get()))); - - m_task_handler.reset(new taskBatchHandler(threadCount)); - m_task_exception_handler.reset(new taskExceptionHandler()); - m_processor.reset(new BatchEventProcessor(m_ring_buffer.get(), - (SequenceBarrierInterface*)m_sequenceBarrier.get(), - m_task_handler.get(), m_task_exception_handler.get())); - - /* - Publisher will try to publish BUFFER_SIZE + 1 events. - The last event should wait for at least one consume before publishing, thus - preventing an overwrite. - After the single consume, the publisher should resume and publish the last - event. - */ - m_gating_sequences.push_back(m_processor.get()->GetSequence()); - m_ring_buffer->set_gating_sequences(m_gating_sequences); // prevent overlap, publishEvent will be blocked - // on ring_buffer_->Next(); - - m_publisher.reset(new EventPublisher(m_ring_buffer.get())); - } - virtual ~disruptorLFQ() {} - - public: - boost::scoped_ptr m_task_factory; - boost::scoped_ptr m_task_handler; - boost::scoped_ptr m_task_exception_handler; - boost::scoped_ptr> m_sequence_to_track; - boost::scoped_ptr> m_ring_buffer; - boost::scoped_ptr m_sequenceBarrier; - boost::scoped_ptr> m_processor; - boost::scoped_ptr> m_publisher; - std::vector m_gating_sequences; -}; -} -// -#endif -#include "UtilAll.h" -#include "disruptorLFQ.h" - -namespace rocketmq { -//m_task_handler->stopIOService(); - m_disruptorLFQ->m_processor->Halt(); -} - -bool TaskQueue::bTaskQueueStatusOK() { - return m_flag.load(boost::memory_order_acquire) == true; -} - -void TaskQueue::produce(const Task& task) { - boost::mutex::scoped_lock lock(m_publishLock); - taskEventTranslator pTranslator(const_cast(&task)); - m_disruptorLFQ->m_publisher->PublishEvent(&pTranslator); -} - -int TaskQueue::run() { - while (true) { - m_disruptorLFQ->m_processor->Run(); - if (m_flag.load(boost::memory_order_acquire) == false) { - break; - } - } - return 0; -} - -// -#include -#include -#include -using namespace std; - -namespace rocketmq { - -//fork()) {} - Task() { m_pTaskImpl = new Task_impl(&Task::dumy, 0); } - virtual ~Task() { delete m_pTaskImpl; } - Task& operator=(const Task& src_) { - delete m_pTaskImpl; - m_pTaskImpl = src_.m_pTaskImpl->fork(); - return *this; - } - void run() { - if (m_pTaskImpl) - m_pTaskImpl->run(); - } - - private: - ITask_impl* m_pTaskImpl; -}; - -// TaskList; - - public: - virtual ~ITaskQueue() {} - virtual void close() = 0; - virtual void produce(const Task& task) = 0; - // virtual void multi_produce(const TaskList& tasks) = 0; - // virtual int consume(Task& task) = 0; - // virtual int consume_all(TaskList& tasks) = 0; - virtual int run() = 0; - // virtual int batch_run() = 0; - virtual bool bTaskQueueStatusOK() = 0; -}; - -// - static Task gen(RET (*func)(void)) { - struct lambda { - static void taskfunc(void* p_) { (*(RET(*)(void))p_)(); }; - }; - return Task(lambda::taskfunc, (void*)func); - } - - template - static Task gen(FUNCT func, ARG1 arg1) { - struct lambda : public ITask_impl { - FUNCT dest_func; - ARG1 arg1; - lambda(FUNCT func, const ARG1& arg1) : dest_func(func), arg1(arg1) {} - virtual void run() { (*dest_func)(arg1); } - virtual ITask_impl* fork() { return new lambda(dest_func, arg1); } - }; - return Task(new lambda(func, arg1)); - } - - template - static Task gen(FUNCT func, ARG1 arg1, ARG2 arg2) { - struct lambda : public ITask_impl { - FUNCT dest_func; - ARG1 arg1; - ARG2 arg2; - lambda(FUNCT func, const ARG1& arg1, const ARG2& arg2) : dest_func(func), arg1(arg1), arg2(arg2) {} - virtual void run() { (*dest_func)(arg1, arg2); } - virtual ITask_impl* fork() { return new lambda(dest_func, arg1, arg2); } - }; - return Task(new lambda(func, arg1, arg2)); - } - - template - static Task gen(FUNCT func, ARG1 arg1, ARG2 arg2, ARG3 arg3) { - struct lambda : public ITask_impl { - FUNCT dest_func; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - lambda(FUNCT func, const ARG1& arg1, const ARG2& arg2, const ARG3& arg3) - : dest_func(func), arg1(arg1), arg2(arg2), arg3(arg3) {} - virtual void run() { (*dest_func)(arg1, arg2, arg3); } - virtual ITask_impl* fork() { return new lambda(dest_func, arg1, arg2, arg3); } - }; - return Task(new lambda(func, arg1, arg2, arg3)); - } - - template - static Task gen(FUNCT func, ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4) { - struct lambda : public ITask_impl { - FUNCT dest_func; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - lambda(FUNCT func, const ARG1& arg1, const ARG2& arg2, const ARG3& arg3, const ARG4& arg4) - : dest_func(func), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) {} - virtual void run() { (*dest_func)(arg1, arg2, arg3, arg4); } - virtual ITask_impl* fork() { return new lambda(dest_func, arg1, arg2, arg3, arg4); } - }; - return Task(new lambda(func, arg1, arg2, arg3, arg4)); - } - - template - static Task gen(FUNCT func, ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4, ARG5 arg5) { - struct lambda : public ITask_impl { - FUNCT dest_func; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - ARG5 arg5; - lambda(FUNCT func, const ARG1& arg1, const ARG2& arg2, const ARG3& arg3, const ARG4& arg4, const ARG5& arg5) - : dest_func(func), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) {} - virtual void run() { (*dest_func)(arg1, arg2, arg3, arg4, arg5); } - virtual ITask_impl* fork() { return new lambda(dest_func, arg1, arg2, arg3, arg4, arg5); } - }; - return Task(new lambda(func, arg1, arg2, arg3, arg4, arg5)); - } - - template - static Task gen(FUNCT func, ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4, ARG5 arg5, ARG6 arg6) { - struct lambda : public ITask_impl { - FUNCT dest_func; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - ARG5 arg5; - ARG6 arg6; - lambda(FUNCT func, - const ARG1& arg1, - const ARG2& arg2, - const ARG3& arg3, - const ARG4& arg4, - const ARG5& arg5, - const ARG6& arg6) - : dest_func(func), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5), arg6(arg6) {} - virtual void run() { (*dest_func)(arg1, arg2, arg3, arg4, arg5, arg6); } - virtual ITask_impl* fork() { return new lambda(dest_func, arg1, arg2, arg3, arg4, arg5, arg6); } - }; - return Task(new lambda(func, arg1, arg2, arg3, arg4, arg5, arg6)); - } - - template - static Task gen(FUNCT func, ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4, ARG5 arg5, ARG6 arg6, ARG7 arg7) { - struct lambda : public ITask_impl { - FUNCT dest_func; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - ARG5 arg5; - ARG6 arg6; - ARG7 arg7; - lambda(FUNCT func, - const ARG1& arg1, - const ARG2& arg2, - const ARG3& arg3, - const ARG4& arg4, - const ARG5& arg5, - const ARG6& arg6, - const ARG7& arg7) - : dest_func(func), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5), arg6(arg6), arg7(arg7) {} - virtual void run() { (*dest_func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); } - virtual ITask_impl* fork() { return new lambda(dest_func, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } - }; - return Task(new lambda(func, arg1, arg2, arg3, arg4, arg5, arg6, arg7)); - } - - template - static Task gen(FUNCT func, ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4, ARG5 arg5, ARG6 arg6, ARG7 arg7, ARG8 arg8) { - struct lambda : public ITask_impl { - FUNCT dest_func; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - ARG5 arg5; - ARG6 arg6; - ARG7 arg7; - ARG8 arg8; - lambda(FUNCT func, - const ARG1& arg1, - const ARG2& arg2, - const ARG3& arg3, - const ARG4& arg4, - const ARG5& arg5, - const ARG6& arg6, - const ARG7& arg7, - const ARG8& arg8) - : dest_func(func), - arg1(arg1), - arg2(arg2), - arg3(arg3), - arg4(arg4), - arg5(arg5), - arg6(arg6), - arg7(arg7), - arg8(arg8) {} - virtual void run() { (*dest_func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } - virtual ITask_impl* fork() { return new lambda(dest_func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } - }; - return Task(new lambda(func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); - } - - template - static Task - gen(FUNCT func, ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4, ARG5 arg5, ARG6 arg6, ARG7 arg7, ARG8 arg8, ARG9 arg9) { - struct lambda : public ITask_impl { - FUNCT dest_func; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - ARG5 arg5; - ARG6 arg6; - ARG7 arg7; - ARG8 arg8; - ARG9 arg9; - lambda(FUNCT func, - const ARG1& arg1, - const ARG2& arg2, - const ARG3& arg3, - const ARG4& arg4, - const ARG5& arg5, - const ARG6& arg6, - const ARG7& arg7, - const ARG8& arg8, - const ARG9& arg9) - : dest_func(func), - arg1(arg1), - arg2(arg2), - arg3(arg3), - arg4(arg4), - arg5(arg5), - arg6(arg6), - arg7(arg7), - arg8(arg8), - arg9(arg9) {} - virtual void run() { (*dest_func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); } - virtual ITask_impl* fork() { return new lambda(dest_func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); } - }; - return Task(new lambda(func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)); - } - - // - static Task gen(RET (T::*func)(void), T* obj) { - struct lambda : public ITask_impl { - RET (T::*dest_func)(void); - T* obj; - lambda(RET (T::*func)(void), T* obj) : dest_func(func), obj(obj) {} - virtual void run() { (obj->*dest_func)(); } - virtual ITask_impl* fork() { return new lambda(dest_func, obj); } - }; - return Task(new lambda(func, obj)); - } - - template - static Task gen(RET (T::*func)(FARG1), T* obj, ARG1 arg1) { - struct lambda : public ITask_impl { - RET (T::*dest_func)(FARG1); - T* obj; - ARG1 arg1; - lambda(RET (T::*pfunc)(FARG1), T* obj, const ARG1& arg1) : dest_func(pfunc), obj(obj), arg1(arg1) {} - virtual void run() { (obj->*dest_func)(arg1); } - virtual ITask_impl* fork() { return new lambda(dest_func, obj, arg1); } - }; - return Task(new lambda(func, obj, arg1)); - } - - template - static Task gen(RET (T::*func)(FARG1, FARG2), T* obj, ARG1 arg1, ARG2 arg2) { - struct lambda : public ITask_impl { - RET (T::*dest_func)(FARG1, FARG2); - T* obj; - ARG1 arg1; - ARG2 arg2; - lambda(RET (T::*func)(FARG1, FARG2), T* obj, const ARG1& arg1, const ARG2& arg2) - : dest_func(func), obj(obj), arg1(arg1), arg2(arg2) {} - virtual void run() { (obj->*dest_func)(arg1, arg2); } - virtual ITask_impl* fork() { return new lambda(dest_func, obj, arg1, arg2); } - }; - return Task(new lambda(func, obj, arg1, arg2)); - } - - template - static Task gen(RET (T::*func)(FARG1, FARG2, FARG3), T* obj, ARG1 arg1, ARG2 arg2, ARG3 arg3) { - struct lambda : public ITask_impl { - RET (T::*dest_func)(FARG1, FARG2, FARG3); - T* obj; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - lambda(RET (T::*func)(FARG1, FARG2, FARG3), T* obj, const ARG1& arg1, const ARG2& arg2, const ARG3& arg3) - : dest_func(func), obj(obj), arg1(arg1), arg2(arg2), arg3(arg3) {} - virtual void run() { (obj->*dest_func)(arg1, arg2, arg3); } - virtual ITask_impl* fork() { return new lambda(dest_func, obj, arg1, arg2, arg3); } - }; - return Task(new lambda(func, obj, arg1, arg2, arg3)); - } - - template - static Task gen(RET (T::*func)(FARG1, FARG2, FARG3, FARG4), T* obj, ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4) { - struct lambda : public ITask_impl { - RET (T::*dest_func)(FARG1, FARG2, FARG3, FARG4); - T* obj; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - lambda(RET (T::*func)(FARG1, FARG2, FARG3, FARG4), - T* obj, - const ARG1& arg1, - const ARG2& arg2, - const ARG3& arg3, - const ARG4& arg4) - : dest_func(func), obj(obj), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) {} - virtual void run() { (obj->*dest_func)(arg1, arg2, arg3, arg4); } - virtual ITask_impl* fork() { return new lambda(dest_func, obj, arg1, arg2, arg3, arg4); } - }; - return Task(new lambda(func, obj, arg1, arg2, arg3, arg4)); - } - - template - static Task gen(RET (T::*func)(FARG1, FARG2, FARG3, FARG4, FARG5), - T* obj, - ARG1 arg1, - ARG2 arg2, - ARG3 arg3, - ARG4 arg4, - ARG5 arg5) { - struct lambda : public ITask_impl { - RET (T::*dest_func)(FARG1, FARG2, FARG3, FARG4, FARG5); - T* obj; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - ARG5 arg5; - lambda(RET (T::*func)(FARG1, FARG2, FARG3, FARG4, FARG5), - T* obj, - const ARG1& arg1, - const ARG2& arg2, - const ARG3& arg3, - const ARG4& arg4, - const ARG5& arg5) - : dest_func(func), obj(obj), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) {} - virtual void run() { (obj->*dest_func)(arg1, arg2, arg3, arg4, arg5); } - virtual ITask_impl* fork() { return new lambda(dest_func, obj, arg1, arg2, arg3, arg4, arg5); } - }; - return Task(new lambda(func, obj, arg1, arg2, arg3, arg4, arg5)); - } - - template - static Task gen(RET (T::*func)(FARG1, FARG2, FARG3, FARG4, FARG5, FARG6), - T* obj, - ARG1 arg1, - ARG2 arg2, - ARG3 arg3, - ARG4 arg4, - ARG5 arg5, - ARG6 arg6) { - struct lambda : public ITask_impl { - RET (T::*dest_func)(FARG1, FARG2, FARG3, FARG4, FARG5, FARG6); - T* obj; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - ARG5 arg5; - ARG6 arg6; - lambda(RET (T::*func)(FARG1, FARG2, FARG3, FARG4, FARG5, FARG6), - T* obj, - const ARG1& arg1, - const ARG2& arg2, - const ARG3& arg3, - const ARG4& arg4, - const ARG5& arg5, - const ARG6& arg6) - : dest_func(func), obj(obj), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5), arg6(arg6) {} - virtual void run() { (obj->*dest_func)(arg1, arg2, arg3, arg4, arg5, arg6); } - virtual ITask_impl* fork() { return new lambda(dest_func, obj, arg1, arg2, arg3, arg4, arg5, arg6); } - }; - return Task(new lambda(func, obj, arg1, arg2, arg3, arg4, arg5, arg6)); - } - - template - static Task gen(RET (T::*func)(FARG1, FARG2, FARG3, FARG4, FARG5, FARG6, FARG7), - T* obj, - ARG1 arg1, - ARG2 arg2, - ARG3 arg3, - ARG4 arg4, - ARG5 arg5, - ARG6 arg6, - ARG7 arg7) { - struct lambda : public ITask_impl { - RET (T::*dest_func)(FARG1, FARG2, FARG3, FARG4, FARG5, FARG6, FARG7); - T* obj; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - ARG5 arg5; - ARG6 arg6; - ARG7 arg7; - lambda(RET (T::*func)(FARG1, FARG2, FARG3, FARG4, FARG5, FARG6, FARG7), - T* obj, - const ARG1& arg1, - const ARG2& arg2, - const ARG3& arg3, - const ARG4& arg4, - const ARG5& arg5, - const ARG6& arg6, - const ARG7& arg7) - : dest_func(func), - obj(obj), - arg1(arg1), - arg2(arg2), - arg3(arg3), - arg4(arg4), - arg5(arg5), - arg6(arg6), - arg7(arg7) {} - virtual void run() { (obj->*dest_func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); } - virtual ITask_impl* fork() { return new lambda(dest_func, obj, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } - }; - return Task(new lambda(func, obj, arg1, arg2, arg3, arg4, arg5, arg6, arg7)); - } - - template - static Task gen(RET (T::*func)(FARG1, FARG2, FARG3, FARG4, FARG5, FARG6, FARG7, FARG8), - T* obj, - ARG1 arg1, - ARG2 arg2, - ARG3 arg3, - ARG4 arg4, - ARG5 arg5, - ARG6 arg6, - ARG7 arg7, - ARG8 arg8) { - struct lambda : public ITask_impl { - RET(T::*dest_func) - (FARG1, FARG2, FARG3, FARG4, FARG5, FARG6, FARG7, FARG8); - T* obj; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - ARG5 arg5; - ARG6 arg6; - ARG7 arg7; - ARG8 arg8; - lambda(RET (T::*func)(FARG1, FARG2, FARG3, FARG4, FARG5, FARG6, FARG7, FARG8), - T* obj, - const ARG1& arg1, - const ARG2& arg2, - const ARG3& arg3, - const ARG4& arg4, - const ARG5& arg5, - const ARG6& arg6, - const ARG7& arg7, - const ARG8& arg8) - : dest_func(func), - obj(obj), - arg1(arg1), - arg2(arg2), - arg3(arg3), - arg4(arg4), - arg5(arg5), - arg6(arg6), - arg7(arg7), - arg8(arg8) {} - virtual void run() { (obj->*dest_func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } - virtual ITask_impl* fork() { return new lambda(dest_func, obj, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } - }; - return Task(new lambda(func, obj, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); - } - - template - static Task gen(RET (T::*func)(FARG1, FARG2, FARG3, FARG4, FARG5, FARG6, FARG7, FARG8, FARG9), - T* obj, - ARG1 arg1, - ARG2 arg2, - ARG3 arg3, - ARG4 arg4, - ARG5 arg5, - ARG6 arg6, - ARG7 arg7, - ARG8 arg8, - ARG9 arg9) { - struct lambda : public ITask_impl { - RET(T::*dest_func) - (FARG1, FARG2, FARG3, FARG4, FARG5, FARG6, FARG7, FARG8, FARG9); - T* obj; - ARG1 arg1; - ARG2 arg2; - ARG3 arg3; - ARG4 arg4; - ARG5 arg5; - ARG6 arg6; - ARG7 arg7; - ARG8 arg8; - ARG9 arg9; - lambda(RET (T::*func)(FARG1, FARG2, FARG3, FARG4, FARG5, FARG6, FARG7, FARG8, FARG9), - T* obj, - const ARG1& arg1, - const ARG2& arg2, - const ARG3& arg3, - const ARG4& arg4, - const ARG5& arg5, - const ARG6& arg6, - const ARG7& arg7, - const ARG8& arg8, - const ARG9& arg9) - : dest_func(func), - obj(obj), - arg1(arg1), - arg2(arg2), - arg3(arg3), - arg4(arg4), - arg5(arg5), - arg6(arg6), - arg7(arg7), - arg8(arg8), - arg9(arg9) {} - virtual void run() { (obj->*dest_func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); } - virtual ITask_impl* fork() { - return new lambda(dest_func, obj, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - } - }; - return Task(new lambda(func, obj, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)); - } -}; - -// m_flag; - disruptorLFQ* m_disruptorLFQ; - boost::mutex m_publishLock; -}; - -// -#include -#include "DefaultMQPushConsumerImpl.h" - -namespace rocketmq { -ConsumeMessageContext::ConsumeMessageContext() { - m_defaultMQPushConsumer = NULL; - // m_traceContext = NULL; -} -ConsumeMessageContext::~ConsumeMessageContext() { - m_traceContext.reset(); -} -std::string ConsumeMessageContext::getConsumerGroup() { - return m_consumerGroup; -} - -void ConsumeMessageContext::setConsumerGroup(const std::string& mConsumerGroup) { - m_consumerGroup = mConsumerGroup; -} - -bool ConsumeMessageContext::getSuccess() { - return m_success; -} - -void ConsumeMessageContext::setSuccess(bool mSuccess) { - m_success = mSuccess; -} - -std::vector ConsumeMessageContext::getMsgList() { - return m_msgList; -} - -void ConsumeMessageContext::setMsgList(std::vector mMsgList) { - m_msgList = mMsgList; -} - -std::string ConsumeMessageContext::getStatus() { - return m_status; -} - -void ConsumeMessageContext::setStatus(const std::string& mStatus) { - m_status = mStatus; -} - -int ConsumeMessageContext::getMsgIndex() { - return m_msgIndex; -} - -void ConsumeMessageContext::setMsgIndex(int mMsgIndex) { - m_msgIndex = mMsgIndex; -} - -MQMessageQueue ConsumeMessageContext::getMessageQueue() { - return m_messageQueue; -} - -void ConsumeMessageContext::setMessageQueue(const MQMessageQueue& mMessageQueue) { - m_messageQueue = mMessageQueue; -} - -DefaultMQPushConsumerImpl* ConsumeMessageContext::getDefaultMQPushConsumer() { - return m_defaultMQPushConsumer; -} - -void ConsumeMessageContext::setDefaultMQPushConsumer(DefaultMQPushConsumerImpl* mDefaultMqPushConsumer) { - m_defaultMQPushConsumer = mDefaultMqPushConsumer; -} - -std::shared_ptr ConsumeMessageContext::getTraceContext() { - return m_traceContext; -} - -void ConsumeMessageContext::setTraceContext(TraceContext* mTraceContext) { - m_traceContext.reset(mTraceContext); -} - -std::string ConsumeMessageContext::getNameSpace() { - return m_nameSpace; -} - -void ConsumeMessageContext::setNameSpace(const std::string& mNameSpace) { - m_nameSpace = mNameSpace; -} -} // namespace rocketmq diff --git a/src/trace/ConsumeMessageContext.h b/src/trace/ConsumeMessageContext.h deleted file mode 100644 index 109b08d7c..000000000 --- a/src/trace/ConsumeMessageContext.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __ROCKETMQ_CONSUME_MESSAGE_CONTEXT_H__ -#define __ROCKETMQ_CONSUME_MESSAGE_CONTEXT_H__ - -#include -#include -#include -#include "MQMessageExt.h" -#include "MQMessageQueue.h" -#include "TraceBean.h" -#include "TraceConstant.h" -#include "TraceContext.h" - -namespace rocketmq { -class DefaultMQPushConsumerImpl; -class ConsumeMessageContext { - public: - ConsumeMessageContext(); - - virtual ~ConsumeMessageContext(); - - std::string getConsumerGroup(); - - void setConsumerGroup(const std::string& mConsumerGroup); - - bool getSuccess(); - - void setSuccess(bool mSuccess); - - std::vector getMsgList(); - - void setMsgList(std::vector mMsgList); - - std::string getStatus(); - - void setStatus(const std::string& mStatus); - - int getMsgIndex(); - - void setMsgIndex(int mMsgIndex); - - MQMessageQueue getMessageQueue(); - - void setMessageQueue(const MQMessageQueue& mMessageQueue); - - DefaultMQPushConsumerImpl* getDefaultMQPushConsumer(); - - void setDefaultMQPushConsumer(DefaultMQPushConsumerImpl* mDefaultMqPushConsumer); - - std::shared_ptr getTraceContext(); - - void setTraceContext(TraceContext* mTraceContext); - - std::string getNameSpace(); - - void setNameSpace(const std::string& mNameSpace); - - private: - std::string m_consumerGroup; - bool m_success; - std::vector m_msgList; - std::string m_status; - int m_msgIndex; - MQMessageQueue m_messageQueue; - DefaultMQPushConsumerImpl* m_defaultMQPushConsumer; - // TraceContext* m_traceContext; - std::shared_ptr m_traceContext; - std::string m_nameSpace; -}; -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/src/trace/ConsumeMessageHook.h b/src/trace/ConsumeMessageHook.h deleted file mode 100644 index d6317fb11..000000000 --- a/src/trace/ConsumeMessageHook.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __ROCKETMQ_COUNSUME_MESSAGE_RPC_HOOK_H__ -#define __ROCKETMQ_COUNSUME_MESSAGE_RPC_HOOK_H__ - -#include -#include "ConsumeMessageContext.h" -namespace rocketmq { -class ConsumeMessageHook { - public: - virtual ~ConsumeMessageHook() {} - virtual std::string getHookName() = 0; - virtual void executeHookBefore(ConsumeMessageContext* context) = 0; - virtual void executeHookAfter(ConsumeMessageContext* context) = 0; -}; -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/src/trace/SendMessageContext.cpp b/src/trace/SendMessageContext.cpp deleted file mode 100644 index 9cf076627..000000000 --- a/src/trace/SendMessageContext.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SendMessageContext.h" -#include - -namespace rocketmq { -SendMessageContext::SendMessageContext() {} - -SendMessageContext::~SendMessageContext() { - m_defaultMQProducer = NULL; -} - -std::string SendMessageContext::getProducerGroup() { - return m_producerGroup; -} - -void SendMessageContext::setProducerGroup(const std::string& mProducerGroup) { - m_producerGroup = mProducerGroup; -} - -MQMessage* SendMessageContext::getMessage() { - return &m_message; -} - -void SendMessageContext::setMessage(const MQMessage& mMessage) { - m_message = mMessage; -} - -TraceMessageType SendMessageContext::getMsgType() { - return m_msgType; -} - -void SendMessageContext::setMsgType(TraceMessageType mMsgType) { - m_msgType = mMsgType; -} - -MQMessageQueue* SendMessageContext::getMessageQueue() { - return &m_messageQueue; -} - -void SendMessageContext::setMessageQueue(const MQMessageQueue& mMq) { - m_messageQueue = mMq; -} - -std::string SendMessageContext::getBrokerAddr() { - return m_brokerAddr; -} - -void SendMessageContext::setBrokerAddr(const std::string& mBrokerAddr) { - m_brokerAddr = mBrokerAddr; -} - -std::string SendMessageContext::getBornHost() { - return m_bornHost; -} - -void SendMessageContext::setBornHost(const std::string& mBornHost) { - m_bornHost = mBornHost; -} - -CommunicationMode SendMessageContext::getCommunicationMode() { - return m_communicationMode; -} - -void SendMessageContext::setCommunicationMode(CommunicationMode mCommunicationMode) { - m_communicationMode = mCommunicationMode; -} - -DefaultMQProducerImpl* SendMessageContext::getDefaultMqProducer() { - return m_defaultMQProducer; -} - -void SendMessageContext::setDefaultMqProducer(DefaultMQProducerImpl* mDefaultMqProducer) { - m_defaultMQProducer = mDefaultMqProducer; -} - -SendResult* SendMessageContext::getSendResult() { - return &m_sendResult; -} - -void SendMessageContext::setSendResult(const SendResult& mSendResult) { - m_sendResult = mSendResult; -} - -TraceContext* SendMessageContext::getTraceContext() { - return m_traceContext; -} - -void SendMessageContext::setTraceContext(TraceContext* mTraceContext) { - m_traceContext = mTraceContext; -} - -std::string SendMessageContext::getNameSpace() { - return m_nameSpace; -} - -void SendMessageContext::setNameSpace(const std::string& mNameSpace) { - m_nameSpace = mNameSpace; -} -} // namespace rocketmq diff --git a/src/trace/SendMessageContext.h b/src/trace/SendMessageContext.h deleted file mode 100644 index ca7a52256..000000000 --- a/src/trace/SendMessageContext.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __ROCKETMQ_SEND_MESSAGE_CONTEXT_H__ -#define __ROCKETMQ_SEND_MESSAGE_CONTEXT_H__ - -#include -#include "CommunicationMode.h" -#include "MQMessage.h" -#include "MQMessageQueue.h" -#include "SendResult.h" -#include "TraceBean.h" -#include "TraceConstant.h" -#include "TraceContext.h" - -namespace rocketmq { -class DefaultMQProducerImpl; -class SendMessageContext { - public: - SendMessageContext(); - - virtual ~SendMessageContext(); - - std::string getProducerGroup(); - - void setProducerGroup(const std::string& mProducerGroup); - - MQMessage* getMessage(); - - void setMessage(const MQMessage& mMessage); - - TraceMessageType getMsgType(); - - void setMsgType(TraceMessageType mMsgType); - - MQMessageQueue* getMessageQueue(); - - void setMessageQueue(const MQMessageQueue& mMq); - - std::string getBrokerAddr(); - - void setBrokerAddr(const std::string& mBrokerAddr); - - std::string getBornHost(); - - void setBornHost(const std::string& mBornHost); - - CommunicationMode getCommunicationMode(); - - void setCommunicationMode(CommunicationMode mCommunicationMode); - - DefaultMQProducerImpl* getDefaultMqProducer(); - - void setDefaultMqProducer(DefaultMQProducerImpl* mDefaultMqProducer); - - SendResult* getSendResult(); - - void setSendResult(const SendResult& mSendResult); - - TraceContext* getTraceContext(); - - void setTraceContext(TraceContext* mTraceContext); - - std::string getNameSpace(); - - void setNameSpace(const std::string& mNameSpace); - - private: - std::string m_producerGroup; - MQMessage m_message; - TraceMessageType m_msgType; - MQMessageQueue m_messageQueue; - std::string m_brokerAddr; - std::string m_bornHost; - CommunicationMode m_communicationMode; - DefaultMQProducerImpl* m_defaultMQProducer; - SendResult m_sendResult; - TraceContext* m_traceContext; - std::string m_nameSpace; -}; - -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/src/trace/SendMessageHook.h b/src/trace/SendMessageHook.h deleted file mode 100644 index 70c39277a..000000000 --- a/src/trace/SendMessageHook.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __ROCKETMQ_SEND_MESSAGE_RPC_HOOK_H__ -#define __ROCKETMQ_SEND_MESSAGE_RPC_HOOK_H__ - -#include -#include "SendMessageContext.h" -namespace rocketmq { -class SendMessageHook { - public: - virtual ~SendMessageHook() {} - virtual std::string getHookName() = 0; - virtual void executeHookBefore(SendMessageContext* context) = 0; - virtual void executeHookAfter(SendMessageContext* context) = 0; -}; -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/src/trace/TraceBean.cpp b/src/trace/TraceBean.cpp deleted file mode 100644 index cf7275b86..000000000 --- a/src/trace/TraceBean.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "TraceBean.h" -#include -#include - -namespace rocketmq { -TraceBean::TraceBean() - : m_topic("null"), - m_msgId("null"), - m_offsetMsgId("null"), - m_tags("null"), - m_keys("null"), - m_storeHost("null"), - m_clientHost("null") {} - -TraceBean::~TraceBean() {} - -const std::string& TraceBean::getTopic() const { - return m_topic; -} - -void TraceBean::setTopic(const std::string& topic) { - m_topic = topic; -} - -const std::string& TraceBean::getMsgId() const { - return m_msgId; -} - -void TraceBean::setMsgId(const std::string& msgId) { - m_msgId = msgId; -} - -const std::string& TraceBean::getOffsetMsgId() const { - return m_offsetMsgId; -} - -void TraceBean::setOffsetMsgId(const std::string& offsetMsgId) { - m_offsetMsgId = offsetMsgId; -} - -const std::string& TraceBean::getTags() const { - return m_tags; -} - -void TraceBean::setTags(const std::string& tags) { - m_tags = tags; -} - -const std::string& TraceBean::getKeys() const { - return m_keys; -} - -void TraceBean::setKeys(const std::string& keys) { - m_keys = keys; -} - -const std::string& TraceBean::getStoreHost() const { - return m_storeHost; -} - -void TraceBean::setStoreHost(const std::string& storeHost) { - m_storeHost = storeHost; -} - -const std::string& TraceBean::getClientHost() const { - return m_clientHost; -} - -void TraceBean::setClientHost(const std::string& clientHost) { - m_clientHost = clientHost; -} - -TraceMessageType TraceBean::getMsgType() const { - return m_msgType; -} - -void TraceBean::setMsgType(TraceMessageType msgType) { - m_msgType = msgType; -} - -long long int TraceBean::getStoreTime() const { - return m_storeTime; -} - -void TraceBean::setStoreTime(long long int storeTime) { - m_storeTime = storeTime; -} - -int TraceBean::getRetryTimes() const { - return m_retryTimes; -} - -void TraceBean::setRetryTimes(int retryTimes) { - m_retryTimes = retryTimes; -} - -int TraceBean::getBodyLength() const { - return m_bodyLength; -} - -void TraceBean::setBodyLength(int bodyLength) { - m_bodyLength = bodyLength; -} -} // namespace rocketmq \ No newline at end of file diff --git a/src/trace/TraceBean.h b/src/trace/TraceBean.h deleted file mode 100644 index a5fedc9f1..000000000 --- a/src/trace/TraceBean.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __ROCKETMQ_TRACE_BEAN_H__ -#define __ROCKETMQ_TRACE_BEAN_H__ - -#include -#include -#include "TraceConstant.h" - -namespace rocketmq { -class TraceBean { - public: - TraceBean(); - virtual ~TraceBean(); - - const std::string& getTopic() const; - - void setTopic(const std::string& topic); - - const std::string& getMsgId() const; - - void setMsgId(const std::string& msgId); - - const std::string& getOffsetMsgId() const; - - void setOffsetMsgId(const std::string& offsetMsgId); - - const std::string& getTags() const; - - void setTags(const std::string& tags); - - const std::string& getKeys() const; - - void setKeys(const std::string& keys); - - const std::string& getStoreHost() const; - - void setStoreHost(const std::string& storeHost); - - const std::string& getClientHost() const; - - void setClientHost(const std::string& clientHost); - - TraceMessageType getMsgType() const; - - void setMsgType(TraceMessageType msgType); - - long long int getStoreTime() const; - - void setStoreTime(long long int storeTime); - - int getRetryTimes() const; - - void setRetryTimes(int retryTimes); - - int getBodyLength() const; - - void setBodyLength(int bodyLength); - - private: - std::string m_topic; - std::string m_msgId; - std::string m_offsetMsgId; - std::string m_tags; - std::string m_keys; - std::string m_storeHost; - std::string m_clientHost; - TraceMessageType m_msgType; - long long m_storeTime; - int m_retryTimes; - int m_bodyLength; -}; -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/src/trace/TraceConstant.cpp b/src/trace/TraceConstant.cpp deleted file mode 100644 index 5e2c5ec75..000000000 --- a/src/trace/TraceConstant.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "TraceConstant.h" -#include - -namespace rocketmq { -std::string TraceConstant::GROUP_NAME = "_INNER_TRACE_PRODUCER"; -std::string TraceConstant::TRACE_TOPIC = "rmq_sys_TRACE_DATA_"; -std::string TraceConstant::DEFAULT_REDION = "DEFAULT_REGION"; -char TraceConstant::CONTENT_SPLITOR = 1; -char TraceConstant::FIELD_SPLITOR = 2; -std::string TraceConstant::TRACE_TYPE_PUB = "Pub"; -std::string TraceConstant::TRACE_TYPE_BEFORE = "SubBefore"; -std::string TraceConstant::TRACE_TYPE_AFTER = "SubAfter"; -} // namespace rocketmq diff --git a/src/trace/TraceConstant.h b/src/trace/TraceConstant.h deleted file mode 100644 index bf41e69ff..000000000 --- a/src/trace/TraceConstant.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __ROCKETMQ_TRACE_CONTANT_H_ -#define __ROCKETMQ_TRACE_CONTANT_H_ - -#include - -namespace rocketmq { -class TraceConstant { - public: - static std::string GROUP_NAME; - static std::string TRACE_TOPIC; - static std::string DEFAULT_REDION; - static char CONTENT_SPLITOR; - static char FIELD_SPLITOR; - static std::string TRACE_TYPE_PUB; - static std::string TRACE_TYPE_BEFORE; - static std::string TRACE_TYPE_AFTER; -}; -enum TraceMessageType { - TRACE_NORMAL_MSG = 0, - TRACE_TRANS_HALF_MSG, - TRACE_TRANS_COMMIT_MSG, - TRACE_DELAY_MSG, -}; -enum TraceType { - Pub, // for send message - SubBefore, // for consume message before - SubAfter, // for consum message after -}; -} // namespace rocketmq -#endif // diff --git a/src/trace/TraceContext.cpp b/src/trace/TraceContext.cpp deleted file mode 100644 index 6696f069b..000000000 --- a/src/trace/TraceContext.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "TraceContext.h" -#include -#include - -#include "StringIdMaker.h" -#include "UtilAll.h" - -namespace rocketmq { -TraceContext::TraceContext() : m_timeStamp(UtilAll::currentTimeMillis()) { - m_requestId = StringIdMaker::getInstance().createUniqID(); -} - -TraceContext::TraceContext(const std::string& mGroupName) : m_groupName(mGroupName) {} - -TraceContext::~TraceContext() {} - -TraceMessageType TraceContext::getMsgType() const { - return m_msgType; -} - -void TraceContext::setMsgType(TraceMessageType msgType) { - m_msgType = msgType; -} - -TraceType TraceContext::getTraceType() const { - return m_traceType; -} - -void TraceContext::setTraceType(TraceType traceType) { - m_traceType = traceType; -} - -long long int TraceContext::getTimeStamp() const { - return m_timeStamp; -} - -void TraceContext::setTimeStamp(long long int timeStamp) { - m_timeStamp = timeStamp; -} - -const string& TraceContext::getRegionId() const { - return m_regionId; -} - -void TraceContext::setRegionId(const string& regionId) { - m_regionId = regionId; -} - -const string& TraceContext::getGroupName() const { - return m_groupName; -} - -void TraceContext::setGroupName(const string& groupName) { - m_groupName = groupName; -} - -int TraceContext::getCostTime() const { - return m_costTime; -} - -void TraceContext::setCostTime(int costTime) { - m_costTime = costTime; -} - -bool TraceContext::getStatus() const { - return m_status; -} - -void TraceContext::setStatus(bool isSuccess) { - m_status = isSuccess; -} - -const string& TraceContext::getRequestId() const { - return m_requestId; -} - -void TraceContext::setRequestId(const string& requestId) { - m_requestId = requestId; -} - -int TraceContext::getTraceBeanIndex() const { - return m_traceBeanIndex; -} - -void TraceContext::setTraceBeanIndex(int traceBeanIndex) { - m_traceBeanIndex = traceBeanIndex; -} - -const vector& TraceContext::getTraceBeans() const { - return m_traceBeans; -} - -void TraceContext::setTraceBean(const TraceBean& traceBean) { - m_traceBeans.push_back(traceBean); -} -} // namespace rocketmq \ No newline at end of file diff --git a/src/trace/TraceContext.h b/src/trace/TraceContext.h deleted file mode 100644 index 93237d406..000000000 --- a/src/trace/TraceContext.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __ROCKETMQ_TRACE_CONTEXT_H__ -#define __ROCKETMQ_TRACE_CONTEXT_H__ - -#include -#include -#include "TraceBean.h" -#include "TraceConstant.h" - -namespace rocketmq { -class TraceContext { - public: - TraceContext(); - - TraceContext(const std::string& mGroupName); - - virtual ~TraceContext(); - - TraceMessageType getMsgType() const; - - void setMsgType(TraceMessageType msgType); - - TraceType getTraceType() const; - - void setTraceType(TraceType traceType); - - long long int getTimeStamp() const; - - void setTimeStamp(long long int timeStamp); - - const std::string& getRegionId() const; - - void setRegionId(const std::string& regionId); - - const std::string& getGroupName() const; - - void setGroupName(const std::string& groupName); - - int getCostTime() const; - - void setCostTime(int costTime); - - bool getStatus() const; - - void setStatus(bool isSuccess); - - const std::string& getRequestId() const; - - void setRequestId(const std::string& requestId); - - int getTraceBeanIndex() const; - - void setTraceBeanIndex(int traceBeanIndex); - - const std::vector& getTraceBeans() const; - - void setTraceBean(const TraceBean& traceBean); - - private: - TraceMessageType m_msgType; - TraceType m_traceType; - long long m_timeStamp; - std::string m_regionId; - std::string m_groupName; - int m_costTime; - bool m_status; - std::string m_requestId; - int m_traceBeanIndex; - std::vector m_traceBeans; -}; -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/src/trace/TraceTransferBean.cpp b/src/trace/TraceTransferBean.cpp deleted file mode 100644 index bd04c333e..000000000 --- a/src/trace/TraceTransferBean.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "TraceTransferBean.h" -#include -#include - -namespace rocketmq { -std::string TraceTransferBean::getTransData() { - return m_transData; -} - -void TraceTransferBean::setTransData(const std::string& transData) { - m_transData = transData; -} - -std::vector TraceTransferBean::getTransKey() { - return m_transKey; -} - -void TraceTransferBean::setTransKey(const std::string& transkey) { - m_transKey.push_back(transkey); -} -} // namespace rocketmq \ No newline at end of file diff --git a/src/trace/TraceTransferBean.h b/src/trace/TraceTransferBean.h deleted file mode 100644 index 5ca054a11..000000000 --- a/src/trace/TraceTransferBean.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __ROCKETMQ_TRACE_TRANSFER_BEAN_H__ -#define __ROCKETMQ_TRACE_TRANSFER_BEAN_H__ - -#include -#include - -namespace rocketmq { -class TraceTransferBean { - public: - std::string getTransData(); - - void setTransData(const std::string& transData); - - std::vector getTransKey(); - - void setTransKey(const std::string& transkey); - - private: - std::string m_transData; - std::vector m_transKey; -}; -} // namespace rocketmq -#endif \ No newline at end of file diff --git a/src/trace/TraceUtil.cpp b/src/trace/TraceUtil.cpp deleted file mode 100644 index 980b8c3ab..000000000 --- a/src/trace/TraceUtil.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "TraceUtil.h" -#include -#include -#include "TraceConstant.h" - -namespace rocketmq { -std::string TraceUtil::CovertTraceTypeToString(TraceType type) { - switch (type) { - case Pub: - return TraceConstant::TRACE_TYPE_PUB; - case SubBefore: - return TraceConstant::TRACE_TYPE_BEFORE; - case SubAfter: - return TraceConstant::TRACE_TYPE_AFTER; - default: - return TraceConstant::TRACE_TYPE_PUB; - } -} - -TraceTransferBean TraceUtil::CovertTraceContextToTransferBean(TraceContext* ctx) { - std::ostringstream ss; - std::vector beans = ctx->getTraceBeans(); - switch (ctx->getTraceType()) { - case Pub: { - std::vector::iterator it = beans.begin(); - ss << TraceUtil::CovertTraceTypeToString(ctx->getTraceType()) << TraceConstant::CONTENT_SPLITOR; - ss << ctx->getTimeStamp() << TraceConstant::CONTENT_SPLITOR; - ss << ctx->getRegionId() << TraceConstant::CONTENT_SPLITOR; - ss << ctx->getGroupName() << TraceConstant::CONTENT_SPLITOR; - ss << it->getTopic() << TraceConstant::CONTENT_SPLITOR; - ss << it->getMsgId() << TraceConstant::CONTENT_SPLITOR; - ss << it->getTags() << TraceConstant::CONTENT_SPLITOR; - ss << it->getKeys() << TraceConstant::CONTENT_SPLITOR; - ss << it->getStoreHost() << TraceConstant::CONTENT_SPLITOR; - ss << it->getBodyLength() << TraceConstant::CONTENT_SPLITOR; - ss << ctx->getCostTime() << TraceConstant::CONTENT_SPLITOR; - ss << it->getMsgType() << TraceConstant::CONTENT_SPLITOR; - ss << it->getOffsetMsgId() << TraceConstant::CONTENT_SPLITOR; - ss << (ctx->getStatus() ? "true" : "false") << TraceConstant::FIELD_SPLITOR; - } break; - - case SubBefore: { - std::vector::iterator it = beans.begin(); - for (; it != beans.end(); ++it) { - ss << TraceUtil::CovertTraceTypeToString(ctx->getTraceType()) << TraceConstant::CONTENT_SPLITOR; - ss << ctx->getTimeStamp() << TraceConstant::CONTENT_SPLITOR; - ss << ctx->getRegionId() << TraceConstant::CONTENT_SPLITOR; - ss << ctx->getGroupName() << TraceConstant::CONTENT_SPLITOR; - ss << ctx->getRequestId() << TraceConstant::CONTENT_SPLITOR; - ss << it->getMsgId() << TraceConstant::CONTENT_SPLITOR; - ss << it->getRetryTimes() << TraceConstant::CONTENT_SPLITOR; - // this is a bug caused by broker. - std::string defaultKey = "dKey"; - if (!it->getKeys().empty()) { - defaultKey = it->getKeys(); - } - ss << defaultKey << TraceConstant::FIELD_SPLITOR; - } - } break; - - case SubAfter: { - std::vector::iterator it = beans.begin(); - ss << TraceUtil::CovertTraceTypeToString(ctx->getTraceType()) << TraceConstant::CONTENT_SPLITOR; - ss << ctx->getRequestId() << TraceConstant::CONTENT_SPLITOR; - ss << it->getMsgId() << TraceConstant::CONTENT_SPLITOR; - ss << ctx->getCostTime() << TraceConstant::CONTENT_SPLITOR; - ss << (ctx->getStatus() ? "true" : "false") << TraceConstant::CONTENT_SPLITOR; - // this is a bug caused by broker. - std::string defaultKey = "dKey"; - if (!it->getKeys().empty()) { - defaultKey = it->getKeys(); - } - ss << defaultKey << TraceConstant::FIELD_SPLITOR; - } break; - - default: - break; - } - - TraceTransferBean transferBean; - transferBean.setTransData(ss.str()); - - switch (ctx->getTraceType()) { - case Pub: - case SubAfter: { - std::vector::iterator it = beans.begin(); - transferBean.setTransKey(it->getMsgId()); - if (it->getKeys() != "") { - transferBean.setTransKey(it->getKeys()); - } - } break; - case SubBefore: { - std::vector::iterator it = beans.begin(); - for (; it != beans.end(); ++it) { - transferBean.setTransKey((*it).getMsgId()); - if ((*it).getKeys() != "") { - transferBean.setTransKey((*it).getKeys()); - } - } - } break; - default: - break; - } - - return transferBean; -} -} // namespace rocketmq diff --git a/src/trace/TraceUtil.h b/src/trace/TraceUtil.h deleted file mode 100644 index 5a7c502e2..000000000 --- a/src/trace/TraceUtil.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __ROCKETMQ_TRACE_UTIL_H_ -#define __ROCKETMQ_TRACE_UTIL_H_ - -#include -#include "TraceConstant.h" -#include "TraceContext.h" -#include "TraceTransferBean.h" - -namespace rocketmq { -class TraceUtil { - public: - static std::string CovertTraceTypeToString(TraceType type); - static TraceTransferBean CovertTraceContextToTransferBean(TraceContext* ctx); -}; -} // namespace rocketmq -#endif // diff --git a/src/transport/ClientRemotingProcessor.cpp b/src/transport/ClientRemotingProcessor.cpp deleted file mode 100644 index a98d9abd0..000000000 --- a/src/transport/ClientRemotingProcessor.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ClientRemotingProcessor.h" -#include "ClientRPCHook.h" -#include "ConsumerRunningInfo.h" -#include "MQClientFactory.h" -#include "UtilAll.h" - -namespace rocketmq { - -ClientRemotingProcessor::ClientRemotingProcessor(MQClientFactory* mqClientFactory) - : m_mqClientFactory(mqClientFactory) {} - -ClientRemotingProcessor::~ClientRemotingProcessor() {} - -RemotingCommand* ClientRemotingProcessor::processRequest(const string& addr, RemotingCommand* request) { - LOG_INFO("request Command received:processRequest, addr:%s, code:%d", addr.data(), request->getCode()); - switch (request->getCode()) { - case CHECK_TRANSACTION_STATE: - return checkTransactionState(addr, request); - break; - case NOTIFY_CONSUMER_IDS_CHANGED: - return notifyConsumerIdsChanged(request); - break; - case RESET_CONSUMER_CLIENT_OFFSET: // oneWayRPC - return resetOffset(request); - case GET_CONSUMER_STATUS_FROM_CLIENT: - // return getConsumeStatus( request); - break; - case GET_CONSUMER_RUNNING_INFO: - return getConsumerRunningInfo(addr, request); - break; - case CONSUME_MESSAGE_DIRECTLY: - // return consumeMessageDirectly( request); - break; - default: - break; - } - return NULL; -} - -RemotingCommand* ClientRemotingProcessor::resetOffset(RemotingCommand* request) { - request->SetExtHeader(request->getCode()); - const MemoryBlock* pbody = request->GetBody(); - if (pbody->getSize()) { - ResetOffsetBody* offsetBody = ResetOffsetBody::Decode(pbody); - ResetOffsetRequestHeader* offsetHeader = (ResetOffsetRequestHeader*)request->getCommandHeader(); - if (offsetBody) { - m_mqClientFactory->resetOffset(offsetHeader->getGroup(), offsetHeader->getTopic(), offsetBody->getOffsetTable()); - delete offsetBody; - offsetBody = nullptr; - } else { - LOG_ERROR("resetOffset failed as received data could not be unserialized"); - } - } - return NULL; // as resetOffset is oneWayRPC, do not need return any response -} - -std::map ResetOffsetBody::getOffsetTable() { - return m_offsetTable; -} - -void ResetOffsetBody::setOffsetTable(const MQMessageQueue& mq, int64 offset) { - m_offsetTable[mq] = offset; -} - -ResetOffsetBody* ResetOffsetBody::Decode(const MemoryBlock* mem) { - const char* const pData = static_cast(mem->getData()); - Json::Reader reader; - Json::Value root; - const char* begin = pData; - const char* end = pData + mem->getSize(); - - if (!reader.parse(begin, end, root, true)) { - LOG_ERROR("ResetOffsetBody::Decode fail"); - return NULL; - } - - ResetOffsetBody* rfb = new ResetOffsetBody(); - Json::Value qds = root["offsetTable"]; - for (unsigned int i = 0; i < qds.size(); i++) { - MQMessageQueue mq; - Json::Value qd = qds[i]; - mq.setBrokerName(qd["brokerName"].asString()); - mq.setQueueId(qd["queueId"].asInt()); - mq.setTopic(qd["topic"].asString()); - int64 offset = qd["offset"].asInt64(); - LOG_INFO("ResetOffsetBody brokerName:%s, queueID:%d, topic:%s, offset:%lld", mq.getBrokerName().c_str(), - mq.getQueueId(), mq.getTopic().c_str(), offset); - rfb->setOffsetTable(mq, offset); - } - return rfb; -} - -RemotingCommand* ClientRemotingProcessor::getConsumerRunningInfo(const string& addr, RemotingCommand* request) { - request->SetExtHeader(request->getCode()); - GetConsumerRunningInfoRequestHeader* requestHeader = - (GetConsumerRunningInfoRequestHeader*)request->getCommandHeader(); - LOG_INFO("getConsumerRunningInfo:%s", requestHeader->getConsumerGroup().c_str()); - - RemotingCommand* pResponse = - new RemotingCommand(request->getCode(), "CPP", request->getVersion(), request->getOpaque(), request->getFlag(), - request->getRemark(), NULL); - - unique_ptr runningInfo( - m_mqClientFactory->consumerRunningInfo(requestHeader->getConsumerGroup())); - if (runningInfo) { - if (requestHeader->isJstackEnable()) { - /*string jstack = UtilAll::jstack(); - consumerRunningInfo->setJstack(jstack);*/ - } - pResponse->setCode(SUCCESS_VALUE); - string body = runningInfo->encode(); - pResponse->SetBody(body.c_str(), body.length()); - pResponse->setMsgBody(body); - } else { - pResponse->setCode(SYSTEM_ERROR); - pResponse->setRemark("The Consumer Group not exist in this consumer"); - } - - SessionCredentials sessionCredentials; - m_mqClientFactory->getSessionCredentialFromConsumer(requestHeader->getConsumerGroup(), sessionCredentials); - ClientRPCHook rpcHook(sessionCredentials); - rpcHook.doBeforeRequest(addr, *pResponse); - pResponse->Encode(); - return pResponse; -} - -RemotingCommand* ClientRemotingProcessor::notifyConsumerIdsChanged(RemotingCommand* request) { - request->SetExtHeader(request->getCode()); - NotifyConsumerIdsChangedRequestHeader* requestHeader = - (NotifyConsumerIdsChangedRequestHeader*)request->getCommandHeader(); - if (requestHeader == nullptr) { - LOG_ERROR("notifyConsumerIdsChanged requestHeader null"); - return NULL; - } - string group = requestHeader->getGroup(); - LOG_INFO("notifyConsumerIdsChanged:%s", group.c_str()); - m_mqClientFactory->doRebalanceByConsumerGroup(requestHeader->getGroup()); - return NULL; -} - -RemotingCommand* ClientRemotingProcessor::checkTransactionState(const std::string& addr, RemotingCommand* request) { - if (!request) { - LOG_ERROR("checkTransactionState request null"); - return nullptr; - } - - LOG_INFO("checkTransactionState addr:%s, request: %s", addr.data(), request->ToString().data()); - - request->SetExtHeader(request->getCode()); - CheckTransactionStateRequestHeader* requestHeader = (CheckTransactionStateRequestHeader*)request->getCommandHeader(); - if (!requestHeader) { - LOG_ERROR("checkTransactionState CheckTransactionStateRequestHeader requestHeader null"); - return nullptr; - } - LOG_INFO("checkTransactionState request: %s", requestHeader->toString().data()); - - const MemoryBlock* block = request->GetBody(); - if (block && block->getSize() > 0) { - std::vector mqvec; - MQDecoder::decodes(block, mqvec); - if (mqvec.size() == 0) { - LOG_ERROR("checkTransactionState decodes MQMessageExt fail, request:%s", requestHeader->toString().data()); - return nullptr; - } - - MQMessageExt& messageExt = mqvec[0]; - string transactionId = messageExt.getProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX); - if (transactionId != "") { - messageExt.setTransactionId(transactionId); - } - - m_mqClientFactory->checkTransactionState(addr, messageExt, *requestHeader); - } else { - LOG_ERROR("checkTransactionState getbody null or size 0, request Header:%s", requestHeader->toString().data()); - } - return nullptr; -} - -} // namespace rocketmq diff --git a/src/transport/ClientRemotingProcessor.h b/src/transport/ClientRemotingProcessor.h deleted file mode 100644 index 5d79be428..000000000 --- a/src/transport/ClientRemotingProcessor.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __CLIENTREMOTINGPROCESSOR_H__ -#define __CLIENTREMOTINGPROCESSOR_H__ - -#include "MQMessageQueue.h" -#include "MQProtos.h" -#include "RemotingCommand.h" - -namespace rocketmq { - -class MQClientFactory; -class ClientRemotingProcessor { - public: - ClientRemotingProcessor(MQClientFactory* mqClientFactory); - virtual ~ClientRemotingProcessor(); - - RemotingCommand* processRequest(const string& addr, RemotingCommand* request); - RemotingCommand* resetOffset(RemotingCommand* request); - RemotingCommand* getConsumerRunningInfo(const string& addr, RemotingCommand* request); - RemotingCommand* notifyConsumerIdsChanged(RemotingCommand* request); - RemotingCommand* checkTransactionState(const string& addr, RemotingCommand* request); - - private: - MQClientFactory* m_mqClientFactory; -}; - -class ResetOffsetBody { - public: - ResetOffsetBody() {} - virtual ~ResetOffsetBody() { m_offsetTable.clear(); } - void setOffsetTable(const MQMessageQueue& mq, int64 offset); - std::map getOffsetTable(); - static ResetOffsetBody* Decode(const MemoryBlock* mem); - - private: - std::map m_offsetTable; -}; - -class CheckTransactionStateBody { - public: - CheckTransactionStateBody() {} - virtual ~CheckTransactionStateBody() { m_offsetTable.clear(); } - void setOffsetTable(MQMessageQueue mq, int64 offset); - std::map getOffsetTable(); - static ResetOffsetBody* Decode(const MemoryBlock* mem); - - private: - std::map m_offsetTable; -}; -} // namespace rocketmq - -#endif diff --git a/src/transport/EventLoop.cpp b/src/transport/EventLoop.cpp deleted file mode 100644 index 7c6eee4fe..000000000 --- a/src/transport/EventLoop.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "EventLoop.h" - -#if !defined(WIN32) && !defined(__APPLE__) -#include -#endif - -#include - -#include - -#include "Logging.h" - -namespace rocketmq { - -EventLoop* EventLoop::GetDefaultEventLoop() { - static EventLoop defaultEventLoop; - return &defaultEventLoop; -} - -EventLoop::EventLoop(const struct event_config* config, bool run_immediately) { -#ifdef WIN32 - evthread_use_windows_threads(); -#else - evthread_use_pthreads(); -#endif - - if (config == nullptr) { - m_eventBase = event_base_new(); - } else { - m_eventBase = event_base_new_with_config(config); - } - - if (m_eventBase == nullptr) { - // failure... - LOG_ERROR("Failed to create event base!"); - return; - } - - evthread_make_base_notifiable(m_eventBase); - - if (run_immediately) { - start(); - } -} - -EventLoop::~EventLoop() { - stop(); - - if (m_eventBase != nullptr) { - event_base_free(m_eventBase); - m_eventBase = nullptr; - } -} - -void EventLoop::start() { - if (m_loopThread == nullptr) { - // start event loop -#if !defined(WIN32) && !defined(__APPLE__) - string taskName = UtilAll::getProcessName(); - prctl(PR_SET_NAME, "EventLoop", 0, 0, 0); -#endif - m_loopThread = new std::thread(&EventLoop::runLoop, this); -#if !defined(WIN32) && !defined(__APPLE__) - prctl(PR_SET_NAME, taskName.c_str(), 0, 0, 0); -#endif - } -} - -void EventLoop::stop() { - if (m_loopThread != nullptr /*&& m_loopThread.joinable()*/) { - m_isRuning = false; - m_loopThread->join(); - - delete m_loopThread; - m_loopThread = nullptr; - } -} - -void EventLoop::runLoop() { - m_isRuning = true; - - while (m_isRuning) { - int ret; - - ret = event_base_dispatch(m_eventBase); - // ret = event_base_loop(m_eventBase, EVLOOP_NONBLOCK); - - if (ret == 1) { - // no event - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - } -} - -bool EventLoop::CreateSslContext(const std::string& ssl_property_file) { - ERR_load_crypto_strings(); - SSL_load_error_strings(); - SSL_library_init(); - OpenSSL_add_all_algorithms(); - - m_sslCtx.reset(SSL_CTX_new(SSLv23_client_method())); - if (!m_sslCtx) { - LOG_ERROR("Failed to create ssl context!"); - return false; - } - - std::string client_key_file = DEFAULT_CLIENT_KEY_FILE; - std::string client_key_passwd = DEFAULT_CLIENT_KEY_PASSWD; - std::string client_cert_file = DEFAULT_CLIENT_CERT_FILE; - std::string ca_cert_file = DEFAULT_CA_CERT_FILE; - std::string verify = "true"; - auto properties = UtilAll::ReadProperties(ssl_property_file); - if (!properties.empty()) { - if (properties.find("tls.client.keyPath") != properties.end()) { - client_key_file = properties["tls.client.keyPath"]; - } - if (properties.find("tls.client.keyPassword") != properties.end()) { - client_key_passwd = properties["tls.client.keyPassword"]; - } - if (properties.find("tls.client.certPath") != properties.end()) { - client_cert_file = properties["tls.client.certPath"]; - } - if (properties.find("tls.client.trustCertPath") != properties.end()) { - ca_cert_file = properties["tls.client.trustCertPath"]; - } - if (properties.find("tls.client.authServer") != properties.end()) { - verify = properties["tls.client.authServer"]; - boost::algorithm::trim(verify); - boost::algorithm::to_lower(verify); - } - } else { - LOG_WARN( - "The tls properties file is not specified or empty. " - "Set it by modifying the api of setTlsPropertyFile and fill the configuration content."); - } - - if (verify == "false") { - SSL_CTX_set_verify(m_sslCtx.get(), SSL_VERIFY_NONE, NULL); - } else { - SSL_CTX_set_verify(m_sslCtx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); - } - SSL_CTX_set_mode(m_sslCtx.get(), SSL_MODE_AUTO_RETRY); - - if (client_key_passwd.empty()) { - LOG_WARN( - "The pass phrase is not specified. " - "Set it by adding the 'tls.client.keyPassword' property in configuration file."); - } else { - SSL_CTX_set_default_passwd_cb_userdata(m_sslCtx.get(), (void*)client_key_passwd.c_str()); - } - - bool check_flag{true}; - if (!boost::filesystem::exists(ca_cert_file.c_str())) { - check_flag = false; - LOG_WARN( - "'%s' does not exist. Please make sure the 'tls.client.trustCertPath' property " - "in the configuration file is configured correctly.", - ca_cert_file.c_str()); - } else if (SSL_CTX_load_verify_locations(m_sslCtx.get(), ca_cert_file.c_str(), NULL) <= 0) { - LOG_ERROR("SSL_CTX_load_verify_locations error!"); - ERR_print_errors_fp(stderr); - return false; - } - - if (!boost::filesystem::exists(client_cert_file.c_str())) { - check_flag = false; - LOG_WARN( - "'%s' does not exist. Please make sure the 'tls.client.certPath' property " - "in the configuration file is configured correctly.", - client_cert_file.c_str()); - } else if (SSL_CTX_use_certificate_file(m_sslCtx.get(), client_cert_file.c_str(), SSL_FILETYPE_PEM) <= 0) { - LOG_ERROR("SSL_CTX_use_certificate_file error!"); - ERR_print_errors_fp(stderr); - return false; - } - - if (!boost::filesystem::exists(client_key_file.c_str())) { - check_flag = false; - LOG_WARN( - "'%s' does not exist. Please make sure the 'tls.client.keyPath' property " - "in the configuration file is configured correctly.", - client_key_file.c_str()); - } else if (SSL_CTX_use_PrivateKey_file(m_sslCtx.get(), client_key_file.c_str(), SSL_FILETYPE_PEM) <= 0) { - LOG_ERROR("SSL_CTX_use_PrivateKey_file error!"); - ERR_print_errors_fp(stderr); - return false; - } - - if (check_flag && SSL_CTX_check_private_key(m_sslCtx.get()) <= 0) { - LOG_ERROR("SSL_CTX_check_private_key error!"); - ERR_print_errors_fp(stderr); - return false; - } - - return true; -} - -#define OPT_UNLOCK_CALLBACKS (BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS) - -BufferEvent* EventLoop::createBufferEvent(socket_t fd, - int options, - bool enable_ssl, - const std::string& ssl_property_file) { - struct bufferevent* event{nullptr}; - - if (enable_ssl) { - if (!m_sslCtx && !CreateSslContext(ssl_property_file)) { - LOG_ERROR("Failed to create ssl context!"); - return nullptr; - } - - SSL* ssl = SSL_new(m_sslCtx.get()); - if (ssl == nullptr) { - LOG_ERROR("Failed to create ssl handle!"); - return nullptr; - } - - // create ssl bufferevent - event = bufferevent_openssl_socket_new(m_eventBase, fd, ssl, BUFFEREVENT_SSL_CONNECTING, options); - - /* create filter ssl bufferevent - struct bufferevent *bev = bufferevent_socket_new(m_eventBase, fd, options); - event = bufferevent_openssl_filter_new(m_eventBase, bev, ssl, - BUFFEREVENT_SSL_CONNECTING, options); - */ - } else { - event = bufferevent_socket_new(m_eventBase, fd, options); - } - - if (event == nullptr) { - LOG_ERROR("Failed to create bufferevent!"); - return nullptr; - } - - bool unlock = (options & OPT_UNLOCK_CALLBACKS) == OPT_UNLOCK_CALLBACKS; - - return new BufferEvent(event, unlock); -} - -BufferEvent::BufferEvent(struct bufferevent* event, bool unlockCallbacks) - : m_bufferEvent(event), - m_unlockCallbacks(unlockCallbacks), - m_readCallback(nullptr), - m_writeCallback(nullptr), - m_eventCallback(nullptr), - m_callbackTransport() { -#ifdef ROCKETMQ_BUFFEREVENT_PROXY_ALL_CALLBACK - if (m_bufferEvent != nullptr) { - bufferevent_setcb(m_bufferEvent, read_callback, write_callback, event_callback, this); - } -#endif // ROCKETMQ_BUFFEREVENT_PROXY_ALL_CALLBACK -} - -BufferEvent::~BufferEvent() { - if (m_bufferEvent != nullptr) { - // free function will set all callbacks to NULL first. - bufferevent_free(m_bufferEvent); - m_bufferEvent = nullptr; - } -} - -void BufferEvent::setCallback(BufferEventDataCallback readCallback, - BufferEventDataCallback writeCallback, - BufferEventEventCallback eventCallback, - std::shared_ptr transport) { - // use lock in bufferevent - bufferevent_lock(m_bufferEvent); - - // wrap callback - m_readCallback = readCallback; - m_writeCallback = writeCallback; - m_eventCallback = eventCallback; - m_callbackTransport = transport; - -#ifndef ROCKETMQ_BUFFEREVENT_PROXY_ALL_CALLBACK - bufferevent_data_cb readcb = readCallback != nullptr ? read_callback : nullptr; - bufferevent_data_cb writecb = writeCallback != nullptr ? write_callback : nullptr; - bufferevent_event_cb eventcb = eventCallback != nullptr ? event_callback : nullptr; - - bufferevent_setcb(m_bufferEvent, readcb, writecb, eventcb, this); -#endif // ROCKETMQ_BUFFEREVENT_PROXY_ALL_CALLBACK - - bufferevent_unlock(m_bufferEvent); -} - -void BufferEvent::read_callback(struct bufferevent* bev, void* ctx) { - auto event = static_cast(ctx); - - if (event->m_unlockCallbacks) - bufferevent_lock(event->m_bufferEvent); - - BufferEventDataCallback callback = event->m_readCallback; - std::shared_ptr transport = event->m_callbackTransport.lock(); - - if (event->m_unlockCallbacks) - bufferevent_unlock(event->m_bufferEvent); - - if (callback) { - callback(event, transport.get()); - } -} - -void BufferEvent::write_callback(struct bufferevent* bev, void* ctx) { - auto event = static_cast(ctx); - - if (event->m_unlockCallbacks) - bufferevent_lock(event->m_bufferEvent); - - BufferEventDataCallback callback = event->m_writeCallback; - std::shared_ptr transport = event->m_callbackTransport.lock(); - - if (event->m_unlockCallbacks) - bufferevent_unlock(event->m_bufferEvent); - - if (callback) { - callback(event, transport.get()); - } -} - -static std::string buildPeerAddrPort(socket_t fd) { - sockaddr_in addr; - socklen_t len = sizeof(addr); - - getpeername(fd, (struct sockaddr*)&addr, &len); - - LOG_DEBUG("socket: %d, addr: %s, port: %d", fd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); - std::string addrPort(inet_ntoa(addr.sin_addr)); - addrPort.append(":"); - addrPort.append(UtilAll::to_string(ntohs(addr.sin_port))); - - return addrPort; -} - -void BufferEvent::event_callback(struct bufferevent* bev, short what, void* ctx) { - auto event = static_cast(ctx); - - if (what & BEV_EVENT_CONNECTED) { - socket_t fd = event->getfd(); - event->m_peerAddrPort = buildPeerAddrPort(fd); - } - - if (event->m_unlockCallbacks) - bufferevent_lock(event->m_bufferEvent); - - BufferEventEventCallback callback = event->m_eventCallback; - std::shared_ptr transport = event->m_callbackTransport.lock(); - - if (event->m_unlockCallbacks) - bufferevent_unlock(event->m_bufferEvent); - - if (callback) { - callback(event, what, transport.get()); - } -} - -} // namespace rocketmq diff --git a/src/transport/EventLoop.h b/src/transport/EventLoop.h deleted file mode 100644 index 24eb9bafb..000000000 --- a/src/transport/EventLoop.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __EVENTLOOP_H__ -#define __EVENTLOOP_H__ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "UtilAll.h" -#include "noncopyable.h" - -using socket_t = evutil_socket_t; - -namespace rocketmq { - -class BufferEvent; - -class EventLoop : public noncopyable { - public: - static EventLoop* GetDefaultEventLoop(); - - public: - explicit EventLoop(const struct event_config* config = nullptr, bool run_immediately = true); - virtual ~EventLoop(); - - void start(); - void stop(); - - BufferEvent* createBufferEvent(socket_t fd, int options, bool enable_ssl, const std::string& ssl_property_file); - - private: - void runLoop(); - bool CreateSslContext(const std::string& ssl_property_file); - - private: - struct event_base* m_eventBase{nullptr}; - std::thread* m_loopThread{nullptr}; - using SSL_CTX_ptr = std::unique_ptr; - SSL_CTX_ptr m_sslCtx{nullptr, ::SSL_CTX_free}; - bool m_isRuning{false}; // aotmic is unnecessary -}; - -class TcpTransport; - -using BufferEventDataCallback = void (*)(BufferEvent* event, TcpTransport* transport); -using BufferEventEventCallback = void (*)(BufferEvent* event, short what, TcpTransport* transport); - -class BufferEvent : public noncopyable { - public: - virtual ~BufferEvent(); - - void setCallback(BufferEventDataCallback readCallback, - BufferEventDataCallback writeCallback, - BufferEventEventCallback eventCallback, - std::shared_ptr transport); - - void setWatermark(short events, size_t lowmark, size_t highmark) { - bufferevent_setwatermark(m_bufferEvent, events, lowmark, highmark); - } - - int enable(short event) { return bufferevent_enable(m_bufferEvent, event); } - - int connect(const struct sockaddr* addr, int socklen) { - return bufferevent_socket_connect(m_bufferEvent, (struct sockaddr*)addr, socklen); - } - - int write(const void* data, size_t size) { return bufferevent_write(m_bufferEvent, data, size); } - - size_t read(void* data, size_t size) { return bufferevent_read(m_bufferEvent, data, size); } - - struct evbuffer* getInput() { - return bufferevent_get_input(m_bufferEvent); - } - - socket_t getfd() const { return bufferevent_getfd(m_bufferEvent); } - - std::string getPeerAddrPort() const { return m_peerAddrPort; } - - private: - BufferEvent(struct bufferevent* event, bool unlockCallbacks); - friend EventLoop; - - static void read_callback(struct bufferevent* bev, void* ctx); - static void write_callback(struct bufferevent* bev, void* ctx); - static void event_callback(struct bufferevent* bev, short what, void* ctx); - - private: - struct bufferevent* m_bufferEvent; - const bool m_unlockCallbacks; - - BufferEventDataCallback m_readCallback; - BufferEventDataCallback m_writeCallback; - BufferEventEventCallback m_eventCallback; - std::weak_ptr m_callbackTransport; // avoid reference cycle - - // cache properties - std::string m_peerAddrPort; -}; - -} // namespace rocketmq - -#endif //__EVENTLOOP_H__ diff --git a/src/transport/ResponseFuture.cpp b/src/transport/ResponseFuture.cpp deleted file mode 100644 index 8ac926bb2..000000000 --- a/src/transport/ResponseFuture.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ResponseFuture.h" - -#include - -#include "Logging.h" -#include "TcpRemotingClient.h" - -namespace rocketmq { - -// pCallback) - : m_requestCode(requestCode), - m_opaque(opaque), - m_timeout(timeout), - m_bAsync(bAsync), - m_pCallbackWrap(pCallback), - m_asyncCallbackStatus(ASYNC_CALLBACK_STATUS_INIT), - m_haveResponse(false), - m_sendRequestOK(false), - m_pResponseCommand(nullptr), - m_maxRetrySendTimes(1), - m_retrySendTimes(1) { - m_brokerAddr = ""; - m_beginTimestamp = UtilAll::currentTimeMillis(); -} - -ResponseFuture::~ResponseFuture() {} - -void ResponseFuture::releaseThreadCondition() { - m_defaultEvent.notify_all(); -} - -RemotingCommand* ResponseFuture::waitResponse(int timeoutMillis) { - std::unique_lock eventLock(m_defaultEventLock); - if (!m_haveResponse) { - if (timeoutMillis <= 0) { - timeoutMillis = m_timeout; - } - if (m_defaultEvent.wait_for(eventLock, std::chrono::milliseconds(timeoutMillis)) == std::cv_status::timeout) { - LOG_WARN("waitResponse of code:%d with opaque:%d timeout", m_requestCode, m_opaque); - m_haveResponse = true; - } - } - return m_pResponseCommand; -} - -bool ResponseFuture::setResponse(RemotingCommand* pResponseCommand) { - std::unique_lock eventLock(m_defaultEventLock); - - if (m_haveResponse) { - return false; - } - - m_pResponseCommand = pResponseCommand; - m_haveResponse = true; - - if (!getAsyncFlag()) { - m_defaultEvent.notify_all(); - } - - return true; -} - -const bool ResponseFuture::getAsyncFlag() { - return m_bAsync; -} - -bool ResponseFuture::isSendRequestOK() const { - return m_sendRequestOK; -} - -void ResponseFuture::setSendRequestOK(bool sendRequestOK) { - m_sendRequestOK = sendRequestOK; -} - -int ResponseFuture::getOpaque() const { - return m_opaque; -} - -int ResponseFuture::getRequestCode() const { - return m_requestCode; -} - -void ResponseFuture::invokeCompleteCallback() { - if (m_pCallbackWrap == nullptr) { - deleteAndZero(m_pResponseCommand); - return; - } else { - m_pCallbackWrap->operationComplete(this, true); - } -} - -void ResponseFuture::invokeExceptionCallback() { - if (m_pCallbackWrap == nullptr) { - LOG_ERROR("m_pCallbackWrap is NULL, critical error"); - return; - } else { - // here no need retrySendTimes process because of it have timeout - LOG_ERROR("send msg, callback timeout, opaque:%d, sendTimes:%d, maxRetryTimes:%d", getOpaque(), getRetrySendTimes(), - getMaxRetrySendTimes()); - - m_pCallbackWrap->onException(); - } -} - -bool ResponseFuture::isTimeOut() const { - int64 diff = UtilAll::currentTimeMillis() - m_beginTimestamp; - // m_timeout; -} - -int ResponseFuture::getMaxRetrySendTimes() const { - return m_maxRetrySendTimes; -} -int ResponseFuture::getRetrySendTimes() const { - return m_retrySendTimes; -} - -void ResponseFuture::setMaxRetrySendTimes(int maxRetryTimes) { - m_maxRetrySendTimes = maxRetryTimes; -} -void ResponseFuture::setRetrySendTimes(int retryTimes) { - m_retrySendTimes = retryTimes; -} - -void ResponseFuture::setBrokerAddr(const std::string& brokerAddr) { - m_brokerAddr = brokerAddr; -} - -std::string ResponseFuture::getBrokerAddr() const { - return m_brokerAddr; -} - -void ResponseFuture::setRequestCommand(const RemotingCommand& requestCommand) { - m_requestCommand = requestCommand; -} -const RemotingCommand& ResponseFuture::getRequestCommand() { - return m_requestCommand; -} - -int64 ResponseFuture::leftTime() const { - int64 diff = UtilAll::currentTimeMillis() - m_beginTimestamp; - return m_timeout - diff; -} - -RemotingCommand* ResponseFuture::getCommand() const { - return m_pResponseCommand; -} - -std::shared_ptr ResponseFuture::getAsyncCallbackWrap() { - return m_pCallbackWrap; -} - -// -#include - -#include "AsyncCallbackWrap.h" -#include "RemotingCommand.h" -#include "UtilAll.h" - -namespace rocketmq { - -typedef enum AsyncCallbackStatus { - ASYNC_CALLBACK_STATUS_INIT = 0, - ASYNC_CALLBACK_STATUS_RESPONSE = 1, - ASYNC_CALLBACK_STATUS_TIMEOUT = 2 -} AsyncCallbAackStatus; - -class TcpRemotingClient; -// pCallback = std::shared_ptr()); - virtual ~ResponseFuture(); - - void releaseThreadCondition(); - RemotingCommand* waitResponse(int timeoutMillis = 0); - RemotingCommand* getCommand() const; - - bool setResponse(RemotingCommand* pResponseCommand); - - bool isSendRequestOK() const; - void setSendRequestOK(bool sendRequestOK); - int getRequestCode() const; - int getOpaque() const; - - // getAsyncCallbackWrap(); - - void setMaxRetrySendTimes(int maxRetryTimes); - void setRetrySendTimes(int retryTimes); - void setBrokerAddr(const std::string& brokerAddr); - void setRequestCommand(const RemotingCommand& requestCommand); - const RemotingCommand& getRequestCommand(); - std::string getBrokerAddr() const; - - private: - int m_requestCode; - int m_opaque; - int64 m_timeout; // ms - - const bool m_bAsync; - std::shared_ptr m_pCallbackWrap; - - AsyncCallbackStatus m_asyncCallbackStatus; - std::mutex m_asyncCallbackLock; - - bool m_haveResponse; - std::mutex m_defaultEventLock; - std::condition_variable m_defaultEvent; - - int64 m_beginTimestamp; - bool m_sendRequestOK; - RemotingCommand* m_pResponseCommand; //h_aliases; - if (*alias != 0) { - return *alias; - } else { - return inet_ntoa(in.sin_addr); - } -} - -uint64 swapll(uint64 v) { -#ifdef ENDIANMODE_BIG - return v; -#else - uint64 ret = ((v << 56) | ((v & 0xff00) << 40) | ((v & 0xff0000) << 24) | ((v & 0xff000000) << 8) | - ((v >> 8) & 0xff000000) | ((v >> 24) & 0xff0000) | ((v >> 40) & 0xff00) | (v >> 56)); - - return ret; -#endif -} - -uint64 h2nll(uint64 v) { - return swapll(v); -} - -uint64 n2hll(uint64 v) { - return swapll(v); -} -} // namespace rocketmq diff --git a/src/transport/SocketUtil.h b/src/transport/SocketUtil.h deleted file mode 100644 index 55cb5a2d1..000000000 --- a/src/transport/SocketUtil.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __SOCKETUTIL_H__ -#define __SOCKETUTIL_H__ - -#ifdef WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#include -#pragma comment(lib, "ws2_32.lib") -#else -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#include "UtilAll.h" - -namespace rocketmq { -// -#if !defined(WIN32) && !defined(__APPLE__) -#include -#endif - -#include "Logging.h" -#include "MemoryOutputStream.h" -#include "TopAddressing.h" -#include "UtilAll.h" -#include -#include - -namespace rocketmq { - -//interrupt(); - m_timerServiceThread->join(); - removeAllTimerCallback(); - - { - std::lock_guard lock(m_tcpTableLock); - for (const auto& trans : m_tcpTable) { - trans.second->disconnect(trans.first); - } - m_tcpTable.clear(); - } - - m_handleService.stop(); - m_handleThreadPool.join_all(); - - m_dispatchService.stop(); - m_dispatchThreadPool.join_all(); - - { - std::lock_guard lock(m_futureTableLock); - for (const auto& future : m_futureTable) { - if (future.second) { - if (!future.second->getAsyncFlag()) { - future.second->releaseThreadCondition(); - } - } - } - } - - LOG_ERROR("TcpRemotingClient::stopAllTcpTransportThread End, m_tcpTable:%lu", m_tcpTable.size()); -} - -void TcpRemotingClient::updateNameServerAddressList(const string& addrs) { - LOG_INFO("updateNameServerAddressList: [%s]", addrs.c_str()); - - if (addrs.empty()) { - return; - } - - std::unique_lock lock(m_namesrvLock, std::try_to_lock); - if (!lock.owns_lock()) { - if (!lock.try_lock_for(std::chrono::seconds(10))) { - LOG_ERROR("updateNameServerAddressList get timed_mutex timeout"); - return; - } - } - - // clear first; - m_namesrvAddrList.clear(); - - vector out; - UtilAll::Split(out, addrs, ";"); - - std::random_device rd; - std::mt19937 g(rd()); - std::shuffle(out.begin(), out.end(), g); - - for (auto addr : out) { - UtilAll::Trim(addr); - - string hostName; - short portNumber; - if (UtilAll::SplitURL(addr, hostName, portNumber)) { - LOG_INFO("update Namesrv:%s", addr.c_str()); - m_namesrvAddrList.push_back(addr); - } else { - LOG_INFO("This may be invalid namer server: [%s]", addr.c_str()); - } - } - out.clear(); -} - -bool TcpRemotingClient::invokeHeartBeat(const string& addr, RemotingCommand& request, int timeoutMillis) { - std::shared_ptr pTcp = GetTransport(addr, true); - if (pTcp != nullptr) { - int code = request.getCode(); - int opaque = request.getOpaque(); - std::shared_ptr cbw; - std::shared_ptr responseFuture(new ResponseFuture(code, opaque, this, timeoutMillis, false, cbw)); - addResponseFuture(opaque, responseFuture); - - if (SendCommand(pTcp, request)) { - responseFuture->setSendRequestOK(true); - unique_ptr pRsp(responseFuture->waitResponse()); - if (pRsp == nullptr) { - LOG_ERROR("wait response timeout of heartbeat, so closeTransport of addr:%s", addr.c_str()); - // avoid responseFuture leak; - findAndDeleteResponseFuture(opaque); - CloseTransport(addr, pTcp); - return false; - } else if (pRsp->getCode() == SUCCESS_VALUE) { - return true; - } else { - LOG_WARN("get error response:%d of heartbeat to addr:%s", pRsp->getCode(), addr.c_str()); - return false; - } - } else { - // avoid responseFuture leak; - findAndDeleteResponseFuture(opaque); - CloseTransport(addr, pTcp); - } - } - return false; -} - -RemotingCommand* TcpRemotingClient::invokeSync(const string& addr, RemotingCommand& request, int timeoutMillis) { - LOG_DEBUG("InvokeSync:", addr.c_str()); - std::shared_ptr pTcp = GetTransport(addr, true); - if (pTcp != nullptr) { - int code = request.getCode(); - int opaque = request.getOpaque(); - std::shared_ptr cbw; - std::shared_ptr responseFuture(new ResponseFuture(code, opaque, this, timeoutMillis, false, cbw)); - addResponseFuture(opaque, responseFuture); - - if (SendCommand(pTcp, request)) { - responseFuture->setSendRequestOK(true); - RemotingCommand* pRsp = responseFuture->waitResponse(); - if (pRsp == nullptr) { - if (code != GET_CONSUMER_LIST_BY_GROUP) { - LOG_WARN("wait response timeout or get NULL response of code:%d, so closeTransport of addr:%s", code, - addr.c_str()); - CloseTransport(addr, pTcp); - } - // avoid responseFuture leak; - findAndDeleteResponseFuture(opaque); - return nullptr; - } else { - return pRsp; - } - } else { - // avoid responseFuture leak; - findAndDeleteResponseFuture(opaque); - CloseTransport(addr, pTcp); - } - } - LOG_DEBUG("InvokeSync [%s] Failed: Cannot Get Transport.", addr.c_str()); - return nullptr; -} - -bool TcpRemotingClient::invokeAsync(const string& addr, - RemotingCommand& request, - std::shared_ptr callback, - int64 timeoutMillis, - int maxRetrySendTimes, - int retrySendTimes) { - std::shared_ptr pTcp = GetTransport(addr, true); - if (pTcp != nullptr) { - int code = request.getCode(); - int opaque = request.getOpaque(); - - // delete in callback - std::shared_ptr responseFuture( - new ResponseFuture(code, opaque, this, timeoutMillis, true, callback)); - responseFuture->setMaxRetrySendTimes(maxRetrySendTimes); - responseFuture->setRetrySendTimes(retrySendTimes); - responseFuture->setBrokerAddr(addr); - responseFuture->setRequestCommand(request); - addResponseFuture(opaque, responseFuture); - - // timeout monitor - boost::asio::deadline_timer* t = - new boost::asio::deadline_timer(m_timerService, boost::posix_time::milliseconds(timeoutMillis)); - addTimerCallback(t, opaque); - t->async_wait( - boost::bind(&TcpRemotingClient::handleAsyncRequestTimeout, this, boost::asio::placeholders::error, opaque)); - - // even if send failed, asyncTimerThread will trigger next pull request or report send msg failed - if (SendCommand(pTcp, request)) { - LOG_DEBUG("invokeAsync success, addr:%s, code:%d, opaque:%d", addr.c_str(), code, opaque); - responseFuture->setSendRequestOK(true); - } - return true; - } - - LOG_ERROR("invokeAsync failed of addr:%s", addr.c_str()); - return false; -} - -void TcpRemotingClient::invokeOneway(const string& addr, RemotingCommand& request) { - // pTcp = GetTransport(addr, true); - if (pTcp != nullptr) { - request.markOnewayRPC(); - if (SendCommand(pTcp, request)) { - LOG_DEBUG("invokeOneway success. addr:%s, code:%d", addr.c_str(), request.getCode()); - } else { - LOG_WARN("invokeOneway failed. addr:%s, code:%d", addr.c_str(), request.getCode()); - } - } else { - LOG_WARN("invokeOneway failed: NULL transport. addr:%s, code:%d", addr.c_str(), request.getCode()); - } -} - -std::shared_ptr TcpRemotingClient::GetTransport(const string& addr, bool needResponse) { - if (addr.empty()) { - LOG_DEBUG("GetTransport of NameServer"); - return CreateNameServerTransport(needResponse); - } - return CreateTransport(addr, needResponse); -} - -std::shared_ptr TcpRemotingClient::CreateTransport(const string& addr, bool needResponse) { - std::shared_ptr tts; - - { - // try get m_tcpLock util m_tcpTransportTryLockTimeout to avoid blocking - // long time, if could not get m_tcpLock, return NULL - std::unique_lock lock(m_tcpTableLock, std::try_to_lock); - if (!lock.owns_lock()) { - if (!lock.try_lock_for(std::chrono::seconds(m_tcpTransportTryLockTimeout))) { - LOG_ERROR("GetTransport of:%s get timed_mutex timeout", addr.c_str()); - std::shared_ptr pTcp; - return pTcp; - } - } - - // check for reuse - if (m_tcpTable.find(addr) != m_tcpTable.end()) { - std::shared_ptr tcp = m_tcpTable[addr]; - - if (tcp) { - TcpConnectStatus connectStatus = tcp->getTcpConnectStatus(); - if (connectStatus == TCP_CONNECT_STATUS_SUCCESS) { - return tcp; - } else if (connectStatus == TCP_CONNECT_STATUS_WAIT) { - tts = tcp; - } else if (connectStatus == TCP_CONNECT_STATUS_FAILED) { - LOG_ERROR("tcpTransport with server disconnected, erase server:%s", addr.c_str()); - tcp->disconnect(addr); // avoid coredump when connection with broker was broken - m_tcpTable.erase(addr); - } else { - LOG_ERROR("go to fault state, erase:%s from tcpMap, and reconnect it", addr.c_str()); - m_tcpTable.erase(addr); - } - } - } - - //connect(addr, 0); // use non-block - if (connectStatus != TCP_CONNECT_STATUS_WAIT) { - LOG_WARN("can not connect to:%s", addr.c_str()); - tts->disconnect(addr); - std::shared_ptr pTcp; - return pTcp; - } else { - // even if connecting failed finally, this server transport will be erased by next CreateTransport - m_tcpTable[addr] = tts; - } - } - } - - TcpConnectStatus connectStatus = tts->waitTcpConnectEvent(static_cast(m_tcpConnectTimeout)); - if (connectStatus != TCP_CONNECT_STATUS_SUCCESS) { - LOG_WARN("can not connect to server:%s", addr.c_str()); - tts->disconnect(addr); - std::shared_ptr pTcp; - return pTcp; - } else { - LOG_INFO("connect server with addr:%s success", addr.c_str()); - return tts; - } -} - -std::shared_ptr TcpRemotingClient::CreateNameServerTransport(bool needResponse) { - // m_namesrvLock was added to avoid operation of nameServer was blocked by - // m_tcpLock, it was used by single Thread mostly, so no performance impact - // try get m_tcpLock until m_tcpTransportTryLockTimeout to avoid blocking long - // time, if could not get m_namesrvlock, return NULL - LOG_DEBUG("--CreateNameserverTransport--"); - std::unique_lock lock(m_namesrvLock, std::try_to_lock); - if (!lock.owns_lock()) { - if (!lock.try_lock_for(std::chrono::seconds(m_tcpTransportTryLockTimeout))) { - LOG_ERROR("CreateNameserverTransport get timed_mutex timeout"); - std::shared_ptr pTcp; - return pTcp; - } - } - - if (!m_namesrvAddrChoosed.empty()) { - std::shared_ptr pTcp = CreateTransport(m_namesrvAddrChoosed, true); - if (pTcp) - return pTcp; - else - m_namesrvAddrChoosed.clear(); - } - - for (unsigned i = 0; i < m_namesrvAddrList.size(); i++) { - unsigned int index = m_namesrvIndex++ % m_namesrvAddrList.size(); - LOG_INFO("namesrvIndex is:%d, index:%d, namesrvaddrlist size:" SIZET_FMT "", m_namesrvIndex, index, - m_namesrvAddrList.size()); - std::shared_ptr pTcp = CreateTransport(m_namesrvAddrList[index], true); - if (pTcp) { - m_namesrvAddrChoosed = m_namesrvAddrList[index]; - return pTcp; - } - } - - std::shared_ptr pTcp; - return pTcp; -} - -bool TcpRemotingClient::CloseTransport(const string& addr, std::shared_ptr pTcp) { - if (addr.empty()) { - return CloseNameServerTransport(pTcp); - } - - std::unique_lock lock(m_tcpTableLock, std::try_to_lock); - if (!lock.owns_lock()) { - if (!lock.try_lock_for(std::chrono::seconds(m_tcpTransportTryLockTimeout))) { - LOG_ERROR("CloseTransport of:%s get timed_mutex timeout", addr.c_str()); - return false; - } - } - - LOG_ERROR("CloseTransport of:%s", addr.c_str()); - - bool removeItemFromTable = true; - if (m_tcpTable.find(addr) != m_tcpTable.end()) { - if (m_tcpTable[addr]->getStartTime() != pTcp->getStartTime()) { - LOG_INFO("tcpTransport with addr:%s has been closed before, and has been created again, nothing to do", - addr.c_str()); - removeItemFromTable = false; - } - } else { - LOG_INFO("tcpTransport with addr:%s had been removed from tcpTable before", addr.c_str()); - removeItemFromTable = false; - } - - if (removeItemFromTable) { - LOG_WARN("closeTransport: disconnect:%s with state:%d", addr.c_str(), m_tcpTable[addr]->getTcpConnectStatus()); - if (m_tcpTable[addr]->getTcpConnectStatus() == TCP_CONNECT_STATUS_SUCCESS) - m_tcpTable[addr]->disconnect(addr); // avoid coredump when connection with server was broken - LOG_WARN("closeTransport: erase broker: %s", addr.c_str()); - m_tcpTable.erase(addr); - } - - LOG_ERROR("CloseTransport of:%s end", addr.c_str()); - - return removeItemFromTable; -} - -bool TcpRemotingClient::CloseNameServerTransport(std::shared_ptr pTcp) { - std::unique_lock lock(m_namesrvLock, std::try_to_lock); - if (!lock.owns_lock()) { - if (!lock.try_lock_for(std::chrono::seconds(m_tcpTransportTryLockTimeout))) { - LOG_ERROR("CreateNameServerTransport get timed_mutex timeout"); - return false; - } - } - - string addr = m_namesrvAddrChoosed; - - bool removeItemFromTable = CloseTransport(addr, pTcp); - if (removeItemFromTable) { - m_namesrvAddrChoosed.clear(); - } - - return removeItemFromTable; -} - -bool TcpRemotingClient::SendCommand(std::shared_ptr pTts, RemotingCommand& msg) { - const MemoryBlock* pHead = msg.GetHead(); - const MemoryBlock* pBody = msg.GetBody(); - - unique_ptr buffer(new MemoryOutputStream(1024)); - if (pHead->getSize() > 0) { - buffer->write(pHead->getData(), static_cast(pHead->getSize())); - } - if (pBody->getSize() > 0) { - buffer->write(pBody->getData(), static_cast(pBody->getSize())); - } - - const char* pData = static_cast(buffer->getData()); - size_t len = buffer->getDataSize(); - return pTts->sendMessage(pData, len); -} - -void TcpRemotingClient::static_messageReceived(void* context, const MemoryBlock& mem, const string& addr) { - auto* pTcpRemotingClient = reinterpret_cast(context); - if (pTcpRemotingClient) - pTcpRemotingClient->messageReceived(mem, addr); -} - -void TcpRemotingClient::messageReceived(const MemoryBlock& mem, const string& addr) { - m_dispatchService.post(boost::bind(&TcpRemotingClient::ProcessData, this, mem, addr)); -} - -void TcpRemotingClient::ProcessData(const MemoryBlock& mem, const string& addr) { - RemotingCommand* pRespondCmd = nullptr; - try { - pRespondCmd = RemotingCommand::Decode(mem); - } catch (...) { - LOG_ERROR("processData error"); - return; - } - - int opaque = pRespondCmd->getOpaque(); - - //isResponseType()) { - std::shared_ptr pFuture = findAndDeleteResponseFuture(opaque); - if (!pFuture) { - LOG_DEBUG("responseFuture was deleted by timeout of opaque:%d", opaque); - deleteAndZero(pRespondCmd); - return; - } - - LOG_DEBUG("find_response opaque:%d", opaque); - processResponseCommand(pRespondCmd, pFuture); - } else { - m_handleService.post(boost::bind(&TcpRemotingClient::processRequestCommand, this, pRespondCmd, addr)); - } -} - -void TcpRemotingClient::processResponseCommand(RemotingCommand* pCmd, std::shared_ptr pFuture) { - int code = pFuture->getRequestCode(); - pCmd->SetExtHeader(code); // set head, for response use - - int opaque = pCmd->getOpaque(); - LOG_DEBUG("processResponseCommand, code:%d, opaque:%d, maxRetryTimes:%d, retrySendTimes:%d", code, opaque, - pFuture->getMaxRetrySendTimes(), pFuture->getRetrySendTimes()); - - if (!pFuture->setResponse(pCmd)) { - // this branch is unreachable normally. - LOG_WARN("response already timeout of opaque:%d", opaque); - deleteAndZero(pCmd); - return; - } - - if (pFuture->getAsyncFlag()) { - cancelTimerCallback(opaque); - - m_handleService.post(boost::bind(&ResponseFuture::invokeCompleteCallback, pFuture)); - } -} - -void TcpRemotingClient::handleAsyncRequestTimeout(const boost::system::error_code& e, int opaque) { - if (e == boost::asio::error::operation_aborted) { - LOG_DEBUG("handleAsyncRequestTimeout aborted opaque:%d, e_code:%d, msg:%s", opaque, e.value(), e.message().data()); - return; - } - - LOG_DEBUG("handleAsyncRequestTimeout opaque:%d, e_code:%d, msg:%s", opaque, e.value(), e.message().data()); - - std::shared_ptr pFuture(findAndDeleteResponseFuture(opaque)); - if (pFuture) { - LOG_ERROR("no response got for opaque:%d", opaque); - eraseTimerCallback(opaque); - if (pFuture->getAsyncCallbackWrap()) { - m_handleService.post(boost::bind(&ResponseFuture::invokeExceptionCallback, pFuture)); - } - } -} - -void TcpRemotingClient::processRequestCommand(RemotingCommand* pCmd, const string& addr) { - unique_ptr pRequestCommand(pCmd); - int requestCode = pRequestCommand->getCode(); - if (m_requestTable.find(requestCode) == m_requestTable.end()) { - LOG_ERROR("can_not_find request:%d processor", requestCode); - } else { - unique_ptr pResponse(m_requestTable[requestCode]->processRequest(addr, pRequestCommand.get())); - if (!pRequestCommand->isOnewayRPC()) { - if (pResponse) { - pResponse->setOpaque(pRequestCommand->getOpaque()); - pResponse->markResponseType(); - pResponse->Encode(); - - invokeOneway(addr, *pResponse); - } - } - } -} - -void TcpRemotingClient::addResponseFuture(int opaque, std::shared_ptr pFuture) { - std::lock_guard lock(m_futureTableLock); - m_futureTable[opaque] = pFuture; -} - -// Note: after call this function, shared_ptr of m_syncFutureTable[opaque] will -// be erased, so caller must ensure the life cycle of returned shared_ptr; -std::shared_ptr TcpRemotingClient::findAndDeleteResponseFuture(int opaque) { - std::lock_guard lock(m_futureTableLock); - std::shared_ptr pResponseFuture; - if (m_futureTable.find(opaque) != m_futureTable.end()) { - pResponseFuture = m_futureTable[opaque]; - m_futureTable.erase(opaque); - } - return pResponseFuture; -} - -void TcpRemotingClient::registerProcessor(MQRequestCode requestCode, ClientRemotingProcessor* clientRemotingProcessor) { - if (m_requestTable.find(requestCode) != m_requestTable.end()) - m_requestTable.erase(requestCode); - m_requestTable[requestCode] = clientRemotingProcessor; -} - -void TcpRemotingClient::addTimerCallback(boost::asio::deadline_timer* t, int opaque) { - std::lock_guard lock(m_asyncTimerTableLock); - if (m_asyncTimerTable.find(opaque) != m_asyncTimerTable.end()) { - LOG_DEBUG("addTimerCallback:erase timerCallback opaque:%lld", opaque); - boost::asio::deadline_timer* old_t = m_asyncTimerTable[opaque]; - m_asyncTimerTable.erase(opaque); - try { - old_t->cancel(); - } catch (const std::exception& ec) { - LOG_WARN("encounter exception when cancel old timer: %s", ec.what()); - } - delete old_t; - } - m_asyncTimerTable[opaque] = t; -} - -void TcpRemotingClient::eraseTimerCallback(int opaque) { - std::lock_guard lock(m_asyncTimerTableLock); - if (m_asyncTimerTable.find(opaque) != m_asyncTimerTable.end()) { - LOG_DEBUG("eraseTimerCallback: opaque:%lld", opaque); - boost::asio::deadline_timer* t = m_asyncTimerTable[opaque]; - m_asyncTimerTable.erase(opaque); - delete t; - } -} - -void TcpRemotingClient::cancelTimerCallback(int opaque) { - std::lock_guard lock(m_asyncTimerTableLock); - if (m_asyncTimerTable.find(opaque) != m_asyncTimerTable.end()) { - LOG_DEBUG("cancelTimerCallback: opaque:%lld", opaque); - boost::asio::deadline_timer* t = m_asyncTimerTable[opaque]; - m_asyncTimerTable.erase(opaque); - try { - t->cancel(); - } catch (const std::exception& ec) { - LOG_WARN("encounter exception when cancel timer: %s", ec.what()); - } - delete t; - } -} - -void TcpRemotingClient::removeAllTimerCallback() { - std::lock_guard lock(m_asyncTimerTableLock); - for (const auto& timer : m_asyncTimerTable) { - boost::asio::deadline_timer* t = timer.second; - try { - t->cancel(); - } catch (const std::exception& ec) { - LOG_WARN("encounter exception when cancel timer: %s", ec.what()); - } - delete t; - } - m_asyncTimerTable.clear(); -} - -// -#include - -#include -#include -#include -#include - -#include "ClientRemotingProcessor.h" -#include "RemotingCommand.h" -#include "ResponseFuture.h" -#include "SocketUtil.h" -#include "TcpTransport.h" - -namespace rocketmq { -// cbw, - int64 timeoutMilliseconds, - int maxRetrySendTimes = 1, - int retrySendTimes = 1); - - virtual void invokeOneway(const string& addr, RemotingCommand& request); - - virtual void registerProcessor(MQRequestCode requestCode, ClientRemotingProcessor* clientRemotingProcessor); - - private: - static void static_messageReceived(void* context, const MemoryBlock& mem, const string& addr); - - void messageReceived(const MemoryBlock& mem, const string& addr); - void ProcessData(const MemoryBlock& mem, const string& addr); - void processRequestCommand(RemotingCommand* pCmd, const string& addr); - void processResponseCommand(RemotingCommand* pCmd, std::shared_ptr pFuture); - void handleAsyncRequestTimeout(const boost::system::error_code& e, int opaque); - - std::shared_ptr GetTransport(const string& addr, bool needResponse); - std::shared_ptr CreateTransport(const string& addr, bool needResponse); - std::shared_ptr CreateNameServerTransport(bool needResponse); - - bool CloseTransport(const string& addr, std::shared_ptr pTcp); - bool CloseNameServerTransport(std::shared_ptr pTcp); - - bool SendCommand(std::shared_ptr pTts, RemotingCommand& msg); - - void addResponseFuture(int opaque, std::shared_ptr pFuture); - std::shared_ptr findAndDeleteResponseFuture(int opaque); - - void addTimerCallback(boost::asio::deadline_timer* t, int opaque); - void eraseTimerCallback(int opaque); - void cancelTimerCallback(int opaque); - void removeAllTimerCallback(); - - void boost_asio_work(); - - private: - using RequestMap = map; - using TcpMap = map>; - using ResMap = map>; - using AsyncTimerMap = map; - - RequestMap m_requestTable; - - TcpMap m_tcpTable; //tcp; - std::timed_mutex m_tcpTableLock; - - ResMap m_futureTable; //future; - std::mutex m_futureTableLock; - - AsyncTimerMap m_asyncTimerTable; - std::mutex m_asyncTimerTableLock; - - int m_dispatchThreadNum; - int m_pullThreadNum; - uint64_t m_tcpConnectTimeout; // ms - uint64_t m_tcpTransportTryLockTimeout; // s - - bool m_enableSsl; - std::string m_sslPropertyFile; - - // m_namesrvAddrList; - string m_namesrvAddrChoosed; - unsigned int m_namesrvIndex; - - boost::asio::io_service m_dispatchService; - boost::asio::io_service::work m_dispatchServiceWork; - boost::thread_group m_dispatchThreadPool; - - boost::asio::io_service m_handleService; - boost::asio::io_service::work m_handleServiceWork; - boost::thread_group m_handleThreadPool; - - boost::asio::io_service m_timerService; - unique_ptr m_timerServiceThread; -}; - -// - -#ifndef WIN32 -#include // for sockaddr_in and inet_ntoa... -#include -#include // for socket(), bind(), and connect()... -#endif - -#include "Logging.h" -#include "TcpRemotingClient.h" -#include "UtilAll.h" - -namespace rocketmq { - -//setCallback(nullptr, nullptr, nullptr, nullptr); - } - - // then, release BufferEvent - m_event.reset(); -} - -void TcpTransport::setTcpConnectStatus(TcpConnectStatus connectStatus) { - m_tcpConnectStatus = connectStatus; -} - -TcpConnectStatus TcpTransport::getTcpConnectStatus() { - return m_tcpConnectStatus; -} - -TcpConnectStatus TcpTransport::waitTcpConnectEvent(int timeoutMillis) { - if (m_tcpConnectStatus == TCP_CONNECT_STATUS_WAIT) { - std::unique_lock eventLock(m_connectEventLock); - if (!m_connectEvent.wait_for(eventLock, std::chrono::milliseconds(timeoutMillis), - [&] { return m_tcpConnectStatus != TCP_CONNECT_STATUS_WAIT; })) { - LOG_INFO("connect timeout"); - } - } - return m_tcpConnectStatus; -} - -// internal method -void TcpTransport::setTcpConnectEvent(TcpConnectStatus connectStatus) { - TcpConnectStatus baseStatus = m_tcpConnectStatus.exchange(connectStatus, std::memory_order_relaxed); - if (baseStatus == TCP_CONNECT_STATUS_WAIT) { - // awake waiting thread - m_connectEvent.notify_all(); - } -} - -u_long TcpTransport::getInetAddr(string& hostname) { - u_long addr = inet_addr(hostname.c_str()); - - if (INADDR_NONE == addr) { - constexpr size_t length = 128; - struct evutil_addrinfo hints; - struct evutil_addrinfo* answer = NULL; - /* Build the hints to tell getaddrinfo how to act. */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; /* v4 or v6 is fine. */ - // Look up the hostname. - int err = evutil_getaddrinfo(hostname.c_str(), NULL, &hints, &answer); - if (err != 0) { - string info = "Failed to resolve host name(" + hostname + "): " + evutil_gai_strerror(err); - THROW_MQEXCEPTION(MQClientException, info, -1); - } - - struct evutil_addrinfo* addressInfo; - for (addressInfo = answer; addressInfo; addressInfo = addressInfo->ai_next) { - char buf[length]; - const char* address = NULL; - if (addressInfo->ai_family == AF_INET) { - struct sockaddr_in* sin = (struct sockaddr_in*)addressInfo->ai_addr; - address = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, length); - } else if (addressInfo->ai_family == AF_INET6) { - struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addressInfo->ai_addr; - address = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, length); - } - if (address) { - addr = inet_addr(address); - if (addr != INADDR_NONE) { - break; - } - } - } - evutil_freeaddrinfo(answer); - } - - return addr; -} - -void TcpTransport::disconnect(const string& addr) { - // disconnect is idempotent. - std::lock_guard lock(m_eventLock); - if (getTcpConnectStatus() != TCP_CONNECT_STATUS_INIT) { - LOG_INFO("disconnect:%s start. event:%p", addr.c_str(), m_event.get()); - freeBufferEvent(); - setTcpConnectEvent(TCP_CONNECT_STATUS_INIT); - LOG_INFO("disconnect:%s completely", addr.c_str()); - } -} - -TcpConnectStatus TcpTransport::connect(const string& strServerURL, int timeoutMillis) { - string hostname; - short port; - LOG_DEBUG("connect to [%s].", strServerURL.c_str()); - if (!UtilAll::SplitURL(strServerURL, hostname, port)) { - LOG_INFO("connect to [%s] failed, Invalid url.", strServerURL.c_str()); - return TCP_CONNECT_STATUS_FAILED; - } - - { - std::lock_guard lock(m_eventLock); - - struct sockaddr_in sin; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - try { - sin.sin_addr.s_addr = getInetAddr(hostname); - } catch (const MQClientException& e) { - LOG_INFO("connect to %s failed, %s", strServerURL.c_str(), e.what()); - setTcpConnectStatus(TCP_CONNECT_STATUS_FAILED); - return TCP_CONNECT_STATUS_FAILED; - } - sin.sin_port = htons(port); - - m_event.reset(EventLoop::GetDefaultEventLoop()->createBufferEvent(-1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE, - m_enableSsl, m_sslPropertyFile)); - m_event->setCallback(readNextMessageIntCallback, nullptr, eventCallback, shared_from_this()); - m_event->setWatermark(EV_READ, 4, 0); - m_event->enable(EV_READ | EV_WRITE); - - setTcpConnectStatus(TCP_CONNECT_STATUS_WAIT); - if (m_event->connect((struct sockaddr*)&sin, sizeof(sin)) < 0) { - LOG_INFO("connect to fd:%d failed", m_event->getfd()); - freeBufferEvent(); - setTcpConnectStatus(TCP_CONNECT_STATUS_FAILED); - return TCP_CONNECT_STATUS_FAILED; - } - } - - if (timeoutMillis <= 0) { - LOG_INFO("try to connect to fd:%d, addr:%s", m_event->getfd(), hostname.c_str()); - return TCP_CONNECT_STATUS_WAIT; - } - - TcpConnectStatus connectStatus = waitTcpConnectEvent(timeoutMillis); - if (connectStatus != TCP_CONNECT_STATUS_SUCCESS) { - LOG_WARN("can not connect to server:%s", strServerURL.c_str()); - - std::lock_guard lock(m_eventLock); - freeBufferEvent(); - setTcpConnectStatus(TCP_CONNECT_STATUS_FAILED); - return TCP_CONNECT_STATUS_FAILED; - } - - return TCP_CONNECT_STATUS_SUCCESS; -} - -void TcpTransport::eventCallback(BufferEvent* event, short what, TcpTransport* transport) { - socket_t fd = event->getfd(); - LOG_INFO("eventcb: received event:%x on fd:%d", what, fd); - if (what & BEV_EVENT_CONNECTED) { - LOG_INFO("eventcb: connect to fd:%d successfully", fd); - - // disable Nagle - int val = 1; -#ifdef WIN32 - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&val), sizeof(val)); -#else - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&val), sizeof(val)); -#endif - transport->setTcpConnectEvent(TCP_CONNECT_STATUS_SUCCESS); - } else if (what & (BEV_EVENT_ERROR | BEV_EVENT_EOF | BEV_EVENT_READING | BEV_EVENT_WRITING)) { - LOG_INFO("eventcb: received error event cb:%x on fd:%d", what, fd); - // if error, stop callback. - event->setCallback(nullptr, nullptr, nullptr, nullptr); - transport->setTcpConnectEvent(TCP_CONNECT_STATUS_FAILED); - } else { - LOG_ERROR("eventcb: received error event:%d on fd:%d", what, fd); - } -} - -void TcpTransport::readNextMessageIntCallback(BufferEvent* event, TcpTransport* transport) { - /* This callback is invoked when there is data to read on bev. */ - - // protocol:
- // 1 2 3 4 - // rocketmq protocol contains 4 parts as following: - // 1, big endian 4 bytes int, its length is sum of 2,3 and 4 - // 2, big endian 4 bytes int, its length is 3 - // 3, use json to serialization data - // 4, application could self-defined binary data - - struct evbuffer* input = event->getInput(); - while (1) { - struct evbuffer_iovec v[4]; - int n = evbuffer_peek(input, 4, NULL, v, sizeof(v) / sizeof(v[0])); - - char hdr[4]; - char* p = hdr; - size_t needed = 4; - - for (int idx = 0; idx < n; idx++) { - if (needed > 0) { - size_t tmp = needed < v[idx].iov_len ? needed : v[idx].iov_len; - memcpy(p, v[idx].iov_base, tmp); - p += tmp; - needed -= tmp; - } else { - break; - } - } - - if (needed > 0) { - LOG_DEBUG("too little data received with sum = %d", 4 - needed); - /** - * reset read water mark to 4 - */ - event->setWatermark(EV_READ, 4, 0); - return; - } - - uint32 totalLenOfOneMsg = *(uint32*)hdr; // first 4 bytes, which indicates 1st part of protocol - uint32 msgLen = ntohl(totalLenOfOneMsg); - size_t recvLen = evbuffer_get_length(input); - if (recvLen >= msgLen + 4) { - LOG_DEBUG("had received all data. msgLen:%d, from:%d, recvLen:%d", msgLen, event->getfd(), recvLen); - } else { - LOG_DEBUG("didn't received whole. msgLen:%d, from:%d, recvLen:%d", msgLen, event->getfd(), recvLen); - /** - * set read water mark to msgLen + 4,wait for receiving whole data - */ - event->setWatermark(EV_READ, msgLen + 4, 0); - return; // consider large data which was not received completely by now - } - - if (msgLen > 0) { - MemoryBlock msg(msgLen, true); - - event->read(hdr, 4); // skip length field - event->read(msg.getData(), msgLen); - - transport->messageReceived(msg, event->getPeerAddrPort()); - } - } -} - -void TcpTransport::messageReceived(const MemoryBlock& mem, const std::string& addr) { - if (m_readCallback != nullptr) { - m_readCallback(m_tcpRemotingClient, mem, addr); - } -} - -bool TcpTransport::sendMessage(const char* pData, size_t len) { - std::lock_guard lock(m_eventLock); - if (getTcpConnectStatus() != TCP_CONNECT_STATUS_SUCCESS) { - return false; - } - - /* NOTE: - do not need to consider large data which could not send by once, as - bufferevent could handle this case; - */ - return m_event != nullptr && m_event->write(pData, len) == 0; -} - -const string TcpTransport::getPeerAddrAndPort() { - std::lock_guard lock(m_eventLock); - return m_event ? m_event->getPeerAddrPort() : ""; -} - -const uint64_t TcpTransport::getStartTime() const { - return m_startTime; -} - -} // namespace rocketmq diff --git a/src/transport/TcpTransport.h b/src/transport/TcpTransport.h deleted file mode 100644 index 79b14d799..000000000 --- a/src/transport/TcpTransport.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __TCPTRANSPORT_H__ -#define __TCPTRANSPORT_H__ - -#include -#include -#include - -#include "EventLoop.h" -#include "dataBlock.h" - -namespace rocketmq { - -// { - public: - static std::shared_ptr CreateTransport(TcpRemotingClient* pTcpRemotingClient, - bool enableSsl, - const std::string& sslPropertyFile, - TcpTransportReadCallback handle = nullptr) { - // transport must be managed by smart pointer - std::shared_ptr transport(new TcpTransport(pTcpRemotingClient, enableSsl, sslPropertyFile, handle)); - return transport; - } - - virtual ~TcpTransport(); - - void disconnect(const std::string& addr); - TcpConnectStatus connect(const std::string& strServerURL, int timeoutMillis = 3000); - TcpConnectStatus waitTcpConnectEvent(int timeoutMillis = 3000); - TcpConnectStatus getTcpConnectStatus(); - - bool sendMessage(const char* pData, size_t len); - const std::string getPeerAddrAndPort(); - const uint64_t getStartTime() const; - - private: - TcpTransport(TcpRemotingClient* pTcpRemotingClient, - bool enableSsl, - const std::string& sslPropertyFile, - TcpTransportReadCallback handle = nullptr); - - static void readNextMessageIntCallback(BufferEvent* event, TcpTransport* transport); - static void eventCallback(BufferEvent* event, short what, TcpTransport* transport); - - void messageReceived(const MemoryBlock& mem, const std::string& addr); - void freeBufferEvent(); // not thread-safe - - void setTcpConnectEvent(TcpConnectStatus connectStatus); - void setTcpConnectStatus(TcpConnectStatus connectStatus); - - u_long getInetAddr(std::string& hostname); - - private: - uint64_t m_startTime; - - std::shared_ptr m_event; // NOTE: use m_event in callback is unsafe. - std::mutex m_eventLock; - std::atomic m_tcpConnectStatus; - - std::mutex m_connectEventLock; - std::condition_variable m_connectEvent; - - //, int64, int, int)); -}; -class MockMQClientAPIImpl : public MQClientAPIImpl { - public: - MockMQClientAPIImpl(const string& mqClientId, - ClientRemotingProcessor* clientRemotingProcessor, - int pullThreadNum, - uint64_t tcpConnectTimeout, - uint64_t tcpTransportTryLockTimeout, - string unitName) - : MQClientAPIImpl(mqClientId, true, DEFAULT_SSL_PROPERTY_FILE) {} - void reInitRemoteClient(TcpRemotingClient* client) { m_pRemotingClient.reset(client); } -}; - -TEST(MQClientAPIImplTest, getMaxOffset) { - SessionCredentials sc; - MockMQClientAPIImpl* impl = new MockMQClientAPIImpl("testMockAPIImpl", nullptr, 1, 2, 3, "testUnit"); - Mock::AllowLeak(impl); - MockTcpRemotingClient* pClient = new MockTcpRemotingClient(); - Mock::AllowLeak(pClient); - impl->reInitRemoteClient(pClient); - GetMaxOffsetResponseHeader* pHead = new GetMaxOffsetResponseHeader(); - pHead->offset = 4096; - RemotingCommand* pCommandFailed = new RemotingCommand(SYSTEM_ERROR, nullptr); - RemotingCommand* pCommandSuccuss = new RemotingCommand(SUCCESS_VALUE, pHead); - EXPECT_CALL(*pClient, invokeSync(_, _, _)) - .Times(3) - .WillOnce(Return(nullptr)) - .WillOnce(Return(pCommandFailed)) - .WillOnce(Return(pCommandSuccuss)); - EXPECT_ANY_THROW(impl->getMaxOffset("127.0.0.0:10911", "testTopic", 0, 1000, sc)); - EXPECT_ANY_THROW(impl->getMaxOffset("127.0.0.0:10911", "testTopic", 0, 1000, sc)); - int64 offset = impl->getMaxOffset("127.0.0.0:10911", "testTopic", 0, 1000, sc); - EXPECT_EQ(4096, offset); -} - -TEST(MQClientAPIImplTest, getMinOffset) { - SessionCredentials sc; - MockMQClientAPIImpl* impl = new MockMQClientAPIImpl("testMockAPIImpl", nullptr, 1, 2, 3, "testUnit"); - Mock::AllowLeak(impl); - MockTcpRemotingClient* pClient = new MockTcpRemotingClient(); - Mock::AllowLeak(pClient); - impl->reInitRemoteClient(pClient); - GetMinOffsetResponseHeader* pHead = new GetMinOffsetResponseHeader(); - pHead->offset = 2048; - RemotingCommand* pCommandFailed = new RemotingCommand(SYSTEM_ERROR, nullptr); - RemotingCommand* pCommandSuccuss = new RemotingCommand(SUCCESS_VALUE, pHead); - EXPECT_CALL(*pClient, invokeSync(_, _, _)) - .Times(3) - .WillOnce(Return(nullptr)) - .WillOnce(Return(pCommandFailed)) - .WillOnce(Return(pCommandSuccuss)); - EXPECT_ANY_THROW(impl->getMinOffset("127.0.0.0:10911", "testTopic", 0, 1000, sc)); - EXPECT_ANY_THROW(impl->getMinOffset("127.0.0.0:10911", "testTopic", 0, 1000, sc)); - int64 offset = impl->getMinOffset("127.0.0.0:10911", "testTopic", 0, 1000, sc); - EXPECT_EQ(2048, offset); -} -class MyMockAutoDeleteSendCallback : public AutoDeleteSendCallBack { - public: - virtual ~MyMockAutoDeleteSendCallback() {} - virtual void onSuccess(SendResult& sendResult) { - std::cout << "send Success" << std::endl; - return; - } - virtual void onException(MQException& e) { - std::cout << "send Exception" << e << std::endl; - return; - } -}; - -TEST(MQClientAPIImplTest, sendMessage) { - string cid = "testClientId"; - SessionCredentials sc; - MockMQClientAPIImpl* impl = new MockMQClientAPIImpl("testMockAPIImpl", nullptr, 1, 2, 3, "testUnit"); - Mock::AllowLeak(impl); - MockTcpRemotingClient* pClient = new MockTcpRemotingClient(); - Mock::AllowLeak(pClient); - impl->reInitRemoteClient(pClient); - SendMessageResponseHeader* pHead = new SendMessageResponseHeader(); - pHead->msgId = "MessageID"; - pHead->queueId = 1; - pHead->queueOffset = 409600; - RemotingCommand* pCommandSync = new RemotingCommand(SUCCESS_VALUE, pHead); - EXPECT_CALL(*pClient, invokeSync(_, _, _)).Times(1).WillOnce(Return(pCommandSync)); - MQMessage message("testTopic", "Hello, RocketMQ"); - string unique_msgId = "UniqMessageID"; - message.setProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, unique_msgId); - SendMessageRequestHeader* requestHeader = new SendMessageRequestHeader(); - requestHeader->producerGroup = cid; - requestHeader->topic = (message.getTopic()); - requestHeader->defaultTopic = DEFAULT_TOPIC; - requestHeader->defaultTopicQueueNums = 4; - requestHeader->bornTimestamp = UtilAll::currentTimeMillis(); - SendResult result = - impl->sendMessage("127.0.0.0:10911", "testBroker", message, *requestHeader, 100, 1, ComMode_SYNC, nullptr, sc); - EXPECT_EQ(result.getSendStatus(), SEND_OK); - EXPECT_EQ(result.getMsgId(), unique_msgId); - EXPECT_EQ(result.getQueueOffset(), 409600); - EXPECT_EQ(result.getOffsetMsgId(), "MessageID"); - EXPECT_EQ(result.getMessageQueue().getBrokerName(), "testBroker"); - EXPECT_EQ(result.getMessageQueue().getTopic(), "testTopic"); - - // Try to test Async send - - EXPECT_CALL(*pClient, invokeAsync(_, _, _, _, _, _)) - .Times(7) - .WillOnce(Return(false)) - .WillOnce(Return(true)) - .WillOnce(Return(false)) - .WillOnce(Return(true)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)); - - SendMessageRequestHeader* requestHeader2 = new SendMessageRequestHeader(); - requestHeader2->producerGroup = cid; - requestHeader2->topic = (message.getTopic()); - requestHeader2->defaultTopic = DEFAULT_TOPIC; - requestHeader2->defaultTopicQueueNums = 4; - requestHeader2->bornTimestamp = UtilAll::currentTimeMillis(); - EXPECT_ANY_THROW( - impl->sendMessage("127.0.0.0:10911", "testBroker", message, *requestHeader2, 100, 1, ComMode_ASYNC, nullptr, sc)); - - SendMessageRequestHeader* requestHeader3 = new SendMessageRequestHeader(); - requestHeader3->producerGroup = cid; - requestHeader3->topic = (message.getTopic()); - requestHeader3->defaultTopic = DEFAULT_TOPIC; - requestHeader3->defaultTopicQueueNums = 4; - requestHeader3->bornTimestamp = UtilAll::currentTimeMillis(); - SendCallback* pSendCallback = new MyMockAutoDeleteSendCallback(); - EXPECT_NO_THROW(impl->sendMessage("127.0.0.0:10911", "testBroker", message, *requestHeader3, 100, 1, ComMode_ASYNC, - pSendCallback, sc)); - - SendMessageRequestHeader* requestHeader4 = new SendMessageRequestHeader(); - requestHeader4->producerGroup = cid; - requestHeader4->topic = (message.getTopic()); - requestHeader4->defaultTopic = DEFAULT_TOPIC; - requestHeader4->defaultTopicQueueNums = 4; - requestHeader4->bornTimestamp = UtilAll::currentTimeMillis(); - SendCallback* pSendCallback2 = new MyMockAutoDeleteSendCallback(); - EXPECT_NO_THROW(impl->sendMessage("127.0.0.0:10911", "testBroker", message, *requestHeader4, 1000, 2, ComMode_ASYNC, - pSendCallback2, sc)); - - SendMessageRequestHeader* requestHeader5 = new SendMessageRequestHeader(); - requestHeader5->producerGroup = cid; - requestHeader5->topic = (message.getTopic()); - requestHeader5->defaultTopic = DEFAULT_TOPIC; - requestHeader5->defaultTopicQueueNums = 4; - requestHeader5->bornTimestamp = UtilAll::currentTimeMillis(); - SendCallback* pSendCallback3 = new MyMockAutoDeleteSendCallback(); - EXPECT_NO_THROW(impl->sendMessage("127.0.0.0:10911", "testBroker", message, *requestHeader5, 1000, 3, ComMode_ASYNC, - pSendCallback3, sc)); -} - -TEST(MQClientAPIImplTest, consumerSendMessageBack) { - SessionCredentials sc; - MQMessageExt msg; - MockMQClientAPIImpl* impl = new MockMQClientAPIImpl("testMockAPIImpl", nullptr, 1, 2, 3, "testUnit"); - Mock::AllowLeak(impl); - MockTcpRemotingClient* pClient = new MockTcpRemotingClient(); - Mock::AllowLeak(pClient); - impl->reInitRemoteClient(pClient); - RemotingCommand* pCommandFailed = new RemotingCommand(SYSTEM_ERROR, nullptr); - RemotingCommand* pCommandSuccuss = new RemotingCommand(SUCCESS_VALUE, nullptr); - EXPECT_CALL(*pClient, invokeSync(_, _, _)) - .Times(3) - .WillOnce(Return(nullptr)) - .WillOnce(Return(pCommandFailed)) - .WillOnce(Return(pCommandSuccuss)); - EXPECT_ANY_THROW(impl->consumerSendMessageBack("127.0.0.0:10911", msg, "testGroup", 0, 1000, 16, sc)); - EXPECT_ANY_THROW(impl->consumerSendMessageBack("127.0.0.0:10911", msg, "testGroup", 0, 1000, 16, sc)); - EXPECT_NO_THROW(impl->consumerSendMessageBack("127.0.0.0:10911", msg, "testGroup", 0, 1000, 16, sc)); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(filter) = "MQClientAPIImplTest.*"; - return RUN_ALL_TESTS(); -} diff --git a/test/src/MQClientFactoryTest.cpp b/test/src/MQClientFactoryTest.cpp deleted file mode 100644 index cab4f32e7..000000000 --- a/test/src/MQClientFactoryTest.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "ConsumerRunningInfo.h" -#include "DefaultMQPushConsumerImpl.h" -#include "MQClientFactory.h" - -using namespace std; -using namespace rocketmq; -using rocketmq::ConsumerRunningInfo; -using rocketmq::DefaultMQPushConsumerImpl; -using rocketmq::MQClientFactory; -using rocketmq::TopicRouteData; -using testing::_; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Mock; -using testing::Return; - -class MockPushConsumerImpl : public DefaultMQPushConsumerImpl { - public: - MockPushConsumerImpl(const std::string& groupname) : DefaultMQPushConsumerImpl() {} - MOCK_METHOD0(getConsumerRunningInfo, ConsumerRunningInfo*()); -}; - -class MockMQClientAPIImpl : public MQClientAPIImpl { - public: - MockMQClientAPIImpl(const string& mqClientId, - ClientRemotingProcessor* clientRemotingProcessor, - int pullThreadNum, - uint64_t tcpConnectTimeout, - uint64_t tcpTransportTryLockTimeout, - string unitName) - : MQClientAPIImpl(mqClientId, true, DEFAULT_SSL_PROPERTY_FILE) {} - - MOCK_METHOD5(getMinOffset, int64(const string&, const string&, int, int, const SessionCredentials&)); - MOCK_METHOD3(getTopicRouteInfoFromNameServer, TopicRouteData*(const string&, int, const SessionCredentials&)); -}; -class MockMQClientFactory : public MQClientFactory { - public: - MockMQClientFactory(const string& mqClientId, - int pullThreadNum, - uint64_t tcpConnectTimeout, - uint64_t tcpTransportTryLockTimeout, - string unitName) - : MQClientFactory(mqClientId, true, DEFAULT_SSL_PROPERTY_FILE) {} - void reInitClientImpl(MQClientAPIImpl* pImpl) { m_pClientAPIImpl.reset(pImpl); } - void addTestConsumer(const string& consumerName, MQConsumer* pMQConsumer) { - addConsumerToTable(consumerName, pMQConsumer); - } -}; - -TEST(MQClientFactoryTest, minOffset) { - string clientId = "testClientId"; - int pullThreadNum = 1; - uint64_t tcpConnectTimeout = 3000; - uint64_t tcpTransportTryLockTimeout = 3000; - string unitName = "central"; - MockMQClientFactory* factory = - new MockMQClientFactory(clientId, pullThreadNum, tcpConnectTimeout, tcpTransportTryLockTimeout, unitName); - MockMQClientAPIImpl* pImpl = new MockMQClientAPIImpl(clientId, nullptr, pullThreadNum, tcpConnectTimeout, - tcpTransportTryLockTimeout, unitName); - factory->reInitClientImpl(pImpl); - MQMessageQueue mq; - mq.setTopic("testTopic"); - mq.setBrokerName("testBroker"); - mq.setQueueId(1); - SessionCredentials session_credentials; - - TopicRouteData* pData = new TopicRouteData(); - pData->setOrderTopicConf("OrderTopicConf"); - QueueData qd; - qd.brokerName = "testBroker"; - qd.readQueueNums = 8; - qd.writeQueueNums = 8; - qd.perm = 1; - pData->getQueueDatas().push_back(qd); - BrokerData bd; - bd.brokerName = "testBroker"; - bd.brokerAddrs[0] = "127.0.0.1:10091"; - bd.brokerAddrs[1] = "127.0.0.2:10092"; - pData->getBrokerDatas().push_back(bd); - - EXPECT_CALL(*pImpl, getMinOffset(_, _, _, _, _)).Times(1).WillOnce(Return(1024)); - EXPECT_CALL(*pImpl, getTopicRouteInfoFromNameServer(_, _, _)).Times(1).WillOnce(Return(pData)); - int64 offset = factory->minOffset(mq, session_credentials); - EXPECT_EQ(1024, offset); - delete factory; -} - -TEST(MQClientFactoryTest, consumerRunningInfo) { - string clientId = "testClientId"; - int pullThreadNum = 1; - uint64_t tcpConnectTimeout = 3000; - uint64_t tcpTransportTryLockTimeout = 3000; - string unitName = "central"; - MockMQClientFactory* factory = - new MockMQClientFactory(clientId, pullThreadNum, tcpConnectTimeout, tcpTransportTryLockTimeout, unitName); - MockPushConsumerImpl* mockPushConsumer = new MockPushConsumerImpl(clientId); - Mock::AllowLeak(mockPushConsumer); - factory->addTestConsumer(clientId, mockPushConsumer); - ConsumerRunningInfo* info = new ConsumerRunningInfo(); - info->setJstack("Hello,JStack"); - EXPECT_CALL(*mockPushConsumer, getConsumerRunningInfo()).Times(1).WillOnce(Return(info)); - ConsumerRunningInfo* info2 = factory->consumerRunningInfo(clientId); - EXPECT_EQ(info2->getJstack(), "Hello,JStack"); - delete factory; -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/test/src/MQClientManagerTest.cpp b/test/src/MQClientManagerTest.cpp deleted file mode 100644 index 208770e69..000000000 --- a/test/src/MQClientManagerTest.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "MQClientManager.h" - -using namespace std; -using namespace rocketmq; -using rocketmq::MQClientFactory; -using rocketmq::MQClientManager; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -TEST(MQClientManagerTest, getClientFactory) { - string clientId = "testClientId"; - string unitName = "central"; - MQClientFactory* factory = MQClientManager::getInstance()->getMQClientFactory(clientId, 1, 1000, 3000, unitName, true, - DEFAULT_SSL_PROPERTY_FILE); - MQClientFactory* factory2 = MQClientManager::getInstance()->getMQClientFactory(clientId, 1, 1000, 3000, unitName, - true, DEFAULT_SSL_PROPERTY_FILE); - EXPECT_EQ(factory, factory2); - factory->shutdown(); - - MQClientManager::getInstance()->removeClientFactory(clientId); -} -TEST(MQClientManagerTest, removeClientFactory) { - string clientId = "testClientId"; - MQClientManager::getInstance()->removeClientFactory(clientId); -} -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/test/src/common/ClientRPCHookTest.cpp b/test/src/common/ClientRPCHookTest.cpp deleted file mode 100644 index 6fd48b631..000000000 --- a/test/src/common/ClientRPCHookTest.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "ClientRPCHook.h" -#include "CommandHeader.h" -#include "RemotingCommand.h" -#include "SessionCredentials.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::RemotingCommand; -using rocketmq::SendMessageRequestHeader; -using rocketmq::SessionCredentials; - -using rocketmq::ClientRPCHook; - -TEST(clientRPCHook, doBeforeRequest) { - SessionCredentials sessionCredentials; - sessionCredentials.setAccessKey("accessKey"); - sessionCredentials.setSecretKey("secretKey"); - sessionCredentials.setAuthChannel("onsChannel"); - - ClientRPCHook clientRPCHook(sessionCredentials); - - RemotingCommand remotingCommand; - clientRPCHook.doBeforeRequest("127.0.0.1:9876", remotingCommand); - - SendMessageRequestHeader* sendMessageRequestHeader = new SendMessageRequestHeader(); - RemotingCommand headeRremotingCommand(17, sendMessageRequestHeader); - clientRPCHook.doBeforeRequest("127.0.0.1:9876", headeRremotingCommand); - - headeRremotingCommand.setMsgBody("1231231"); - clientRPCHook.doBeforeRequest("127.0.0.1:9876", headeRremotingCommand); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "clientRPCHook.doBeforeRequest"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/MQVersionTest.cpp b/test/src/common/MQVersionTest.cpp deleted file mode 100644 index 587339bef..000000000 --- a/test/src/common/MQVersionTest.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "MQVersion.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MQVersion; -using rocketmq::RocketMQCPPClientVersion; - -TEST(MQVersionTest, Version2String) { - for (int v = MQVersion::V3_0_0_SNAPSHOT; v <= MQVersion::HIGHER_VERSION; v++) { - EXPECT_STREQ(MQVersion::GetVersionDesc(v), RocketMQCPPClientVersion[v]); - } - EXPECT_STREQ(MQVersion::GetVersionDesc(-100), MQVersion::GetVersionDesc(MQVersion::V3_0_0_SNAPSHOT)); - EXPECT_STREQ(MQVersion::GetVersionDesc(MQVersion::V4_6_0), "V4_6_0"); - EXPECT_STREQ(MQVersion::GetVersionDesc(MQVersion::HIGHER_VERSION + 100), - MQVersion::GetVersionDesc(MQVersion::HIGHER_VERSION)); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "MQVersionTest.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/MemoryBlockTest.cpp b/test/src/common/MemoryBlockTest.cpp deleted file mode 100644 index 4b72d6cd2..000000000 --- a/test/src/common/MemoryBlockTest.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "dataBlock.h" - -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MemoryBlock; - -TEST(memoryBlock, init) { - MemoryBlock memoryBlock; - EXPECT_EQ(memoryBlock.getSize(), 0); - EXPECT_TRUE(memoryBlock.getData() == nullptr); - - MemoryBlock twoMemoryBlock(-1, true); - EXPECT_EQ(twoMemoryBlock.getSize(), 0); - EXPECT_TRUE(twoMemoryBlock.getData() == nullptr); - - MemoryBlock threeMemoryBlock(10, true); - EXPECT_EQ(threeMemoryBlock.getSize(), 10); - EXPECT_TRUE(threeMemoryBlock.getData() != nullptr); - - MemoryBlock frouMemoryBlock(12, false); - EXPECT_EQ(frouMemoryBlock.getSize(), 12); - EXPECT_TRUE(frouMemoryBlock.getData() != nullptr); - - char* buf = (char*)malloc(sizeof(char) * 9); - strcpy(buf, "RocketMQ"); - MemoryBlock fiveMemoryBlock(buf, 0); - EXPECT_EQ(fiveMemoryBlock.getSize(), 0); - EXPECT_TRUE(fiveMemoryBlock.getData() == nullptr); - - char* bufNull = NULL; - MemoryBlock sixMemoryBlock(bufNull, 16); - EXPECT_EQ(sixMemoryBlock.getSize(), 16); - EXPECT_TRUE(sixMemoryBlock.getData() != nullptr); - - buf = (char*)realloc(buf, 20); - MemoryBlock sevenMemoryBlock(buf, 20); - EXPECT_EQ(sevenMemoryBlock.getSize(), 20); - sevenMemoryBlock.getData(); - EXPECT_EQ(string(sevenMemoryBlock.getData()), string(buf)); - - MemoryBlock nineMemoryBlock(sevenMemoryBlock); - EXPECT_EQ(nineMemoryBlock.getSize(), sevenMemoryBlock.getSize()); - EXPECT_EQ(string(nineMemoryBlock.getData()), string(sevenMemoryBlock.getData())); - - MemoryBlock eightMemoryBlock(fiveMemoryBlock); - EXPECT_EQ(eightMemoryBlock.getSize(), fiveMemoryBlock.getSize()); - EXPECT_TRUE(eightMemoryBlock.getData() == nullptr); - - free(buf); -} - -TEST(memoryBlock, operators) { - MemoryBlock memoryBlock(12, false); - - MemoryBlock operaterMemoryBlock = memoryBlock; - - EXPECT_TRUE(operaterMemoryBlock == memoryBlock); - - char* buf = (char*)malloc(sizeof(char) * 16); - memset(buf, 0, 16); - strcpy(buf, "RocketMQ"); - MemoryBlock twoMemoryBlock(buf, 12); - EXPECT_FALSE(memoryBlock == twoMemoryBlock); - - MemoryBlock threeMemoryBlock(buf, 16); - EXPECT_FALSE(memoryBlock == threeMemoryBlock); - EXPECT_TRUE(twoMemoryBlock != threeMemoryBlock); - - threeMemoryBlock.fillWith(49); - EXPECT_EQ(string(threeMemoryBlock.getData(), 16), "1111111111111111"); - - threeMemoryBlock.reset(); - EXPECT_EQ(threeMemoryBlock.getSize(), 0); - EXPECT_TRUE(threeMemoryBlock.getData() == nullptr); - - threeMemoryBlock.setSize(16, 0); - EXPECT_EQ(threeMemoryBlock.getSize(), 16); - // EXPECT_EQ(threeMemoryBlock.getData() , buf); - - threeMemoryBlock.setSize(0, 0); - EXPECT_EQ(threeMemoryBlock.getSize(), 0); - EXPECT_TRUE(threeMemoryBlock.getData() == nullptr); - - MemoryBlock appendMemoryBlock; - EXPECT_EQ(appendMemoryBlock.getSize(), 0); - EXPECT_TRUE(appendMemoryBlock.getData() == nullptr); - - appendMemoryBlock.append(buf, -1); - EXPECT_EQ(appendMemoryBlock.getSize(), 0); - EXPECT_TRUE(appendMemoryBlock.getData() == nullptr); - - appendMemoryBlock.append(buf, 8); - EXPECT_EQ(appendMemoryBlock.getSize(), 8); - - MemoryBlock replaceWithMemoryBlock; - replaceWithMemoryBlock.append(buf, 8); - - char* aliyunBuf = (char*)malloc(sizeof(char) * 8); - memset(aliyunBuf, 0, 8); - strcpy(aliyunBuf, "aliyun"); - replaceWithMemoryBlock.replaceWith(aliyunBuf, 0); - EXPECT_EQ(replaceWithMemoryBlock.getSize(), 8); - EXPECT_EQ(string(replaceWithMemoryBlock.getData(), 8), "RocketMQ"); - - replaceWithMemoryBlock.replaceWith(aliyunBuf, 6); - EXPECT_EQ(replaceWithMemoryBlock.getSize(), 6); - EXPECT_EQ(string(replaceWithMemoryBlock.getData(), strlen(aliyunBuf)), "aliyun"); - - MemoryBlock insertMemoryBlock; - insertMemoryBlock.append(buf, 8); - insertMemoryBlock.insert(aliyunBuf, -1, -1); - EXPECT_EQ(string(insertMemoryBlock.getData(), 8), "RocketMQ"); - - /* MemoryBlock fourInsertMemoryBlock; - fourInsertMemoryBlock.append(buf , 8); - // 6+ (-1) - fourInsertMemoryBlock.insert(aliyunBuf , 8 , -1); - string fourStr( fourInsertMemoryBlock.getData()); - EXPECT_TRUE( fourStr == "liyun");*/ - - MemoryBlock twoInsertMemoryBlock; - twoInsertMemoryBlock.append(buf, 8); - twoInsertMemoryBlock.insert(aliyunBuf, strlen(aliyunBuf), 0); - EXPECT_EQ(string(twoInsertMemoryBlock.getData(), 8 + strlen(aliyunBuf)), "aliyunRocketMQ"); - - MemoryBlock threeInsertMemoryBlock; - threeInsertMemoryBlock.append(buf, 8); - threeInsertMemoryBlock.insert(aliyunBuf, 6, 100); - EXPECT_EQ(string(threeInsertMemoryBlock.getData(), 8 + strlen(aliyunBuf)), "RocketMQaliyun"); - - MemoryBlock removeSectionMemoryBlock(buf, 8); - removeSectionMemoryBlock.removeSection(8, -1); - EXPECT_EQ(string(removeSectionMemoryBlock.getData(), 8), "RocketMQ"); - - MemoryBlock twoRemoveSectionMemoryBlock(buf, 8); - twoRemoveSectionMemoryBlock.removeSection(1, 4); - string str(twoRemoveSectionMemoryBlock.getData(), 4); - EXPECT_TRUE(str == "RtMQ"); - - free(buf); - free(aliyunBuf); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "memoryBlock.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/MemoryOutputStreamTest.cpp b/test/src/common/MemoryOutputStreamTest.cpp deleted file mode 100644 index 05af181e0..000000000 --- a/test/src/common/MemoryOutputStreamTest.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "string.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "MemoryInputStream.h" -#include "MemoryOutputStream.h" -#include "dataBlock.h" - -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MemoryBlock; -using rocketmq::MemoryInputStream; -using rocketmq::MemoryOutputStream; - -TEST(memoryOutputStream, init) { - MemoryOutputStream memoryOutput; - EXPECT_EQ(memoryOutput.getMemoryBlock().getSize(), 0); - EXPECT_TRUE(memoryOutput.getData() != nullptr); - - EXPECT_EQ(memoryOutput.getPosition(), 0); - EXPECT_EQ(memoryOutput.getDataSize(), 0); - - MemoryOutputStream twoMemoryOutput(512); - EXPECT_EQ(memoryOutput.getMemoryBlock().getSize(), 0); - - MemoryBlock memoryBlock(12, false); - MemoryOutputStream threeMemoryOutput(memoryBlock, false); - EXPECT_EQ(threeMemoryOutput.getPosition(), 0); - EXPECT_EQ(threeMemoryOutput.getDataSize(), 0); - - MemoryOutputStream frouMemoryOutput(memoryBlock, true); - EXPECT_EQ(frouMemoryOutput.getPosition(), memoryBlock.getSize()); - EXPECT_EQ(frouMemoryOutput.getDataSize(), memoryBlock.getSize()); - - char* buf = (char*)malloc(sizeof(char) * 9); - strcpy(buf, "RocketMQ"); - MemoryOutputStream fiveMemoryOutputStream(buf, 8); - EXPECT_EQ(fiveMemoryOutputStream.getData(), buf); - - fiveMemoryOutputStream.reset(); - EXPECT_EQ(memoryOutput.getPosition(), 0); - EXPECT_EQ(memoryOutput.getDataSize(), 0); - free(buf); -} - -TEST(memoryOutputStream, flush) { - char* buf = (char*)malloc(sizeof(char) * 9); - strcpy(buf, "RocketMQ"); - MemoryOutputStream memoryOutput; - memoryOutput.write(buf, 9); - memoryOutput.flush(); - EXPECT_FALSE(memoryOutput.getData() == buf); - free(buf); -} - -TEST(memoryOutputStream, preallocate) { - MemoryOutputStream memoryOutput; - memoryOutput.preallocate(250); - - memoryOutput.preallocate(256); -} - -TEST(memoryOutputStream, getMemoryBlock) { - char* buf = (char*)malloc(sizeof(char) * 9); - strcpy(buf, "RocketMQ"); - MemoryOutputStream memoryOutput; - memoryOutput.write(buf, 9); - MemoryBlock memoryBlock = memoryOutput.getMemoryBlock(); - EXPECT_EQ(memoryBlock.getSize(), memoryOutput.getDataSize()); - free(buf); -} - -TEST(memoryOutputStream, prepareToWriteAndGetData) { - char* buf = (char*)malloc(sizeof(char) * 9); - strcpy(buf, "RocketMQ"); - MemoryOutputStream memoryOutput(buf, 9); - EXPECT_EQ(memoryOutput.getData(), buf); - - // prepareToWrite - // EXPECT_TRUE(memoryOutput.writeIntBigEndian(123)); - EXPECT_TRUE(memoryOutput.writeByte('r')); - const char* data = static_cast(memoryOutput.getData()); - EXPECT_EQ(string(data), "rocketMQ"); - - MemoryOutputStream blockMmoryOutput(8); - char* memoryData = (char*)blockMmoryOutput.getData(); - - EXPECT_EQ(memoryData[blockMmoryOutput.getDataSize()], 0); - - blockMmoryOutput.write(buf, 8); - blockMmoryOutput.write(buf, 8); - data = static_cast(blockMmoryOutput.getData()); - EXPECT_EQ(string(data), "rocketMQrocketMQ"); - free(buf); -} - -TEST(memoryOutputStream, position) { - char* buf = (char*)malloc(sizeof(char) * 9); - strcpy(buf, "RocketMQ"); - - MemoryOutputStream memoryOutput; - EXPECT_EQ(memoryOutput.getPosition(), 0); - - memoryOutput.write(buf, 8); - EXPECT_EQ(memoryOutput.getPosition(), 8); - - EXPECT_FALSE(memoryOutput.setPosition(9)); - - EXPECT_TRUE(memoryOutput.setPosition(-1)); - EXPECT_EQ(memoryOutput.getPosition(), 0); - - EXPECT_TRUE(memoryOutput.setPosition(8)); - EXPECT_EQ(memoryOutput.getPosition(), 8); - - EXPECT_TRUE(memoryOutput.setPosition(7)); - EXPECT_EQ(memoryOutput.getPosition(), 7); - free(buf); -} - -TEST(memoryOutputStream, write) { - MemoryOutputStream memoryOutput; - MemoryInputStream memoryInput(memoryOutput.getData(), 256, false); - - EXPECT_TRUE(memoryOutput.writeBool(true)); - EXPECT_TRUE(memoryInput.readBool()); - - EXPECT_TRUE(memoryOutput.writeBool(false)); - EXPECT_FALSE(memoryInput.readBool()); - - EXPECT_TRUE(memoryOutput.writeByte('a')); - EXPECT_EQ(memoryInput.readByte(), 'a'); - - EXPECT_TRUE(memoryOutput.writeShortBigEndian(128)); - EXPECT_EQ(memoryInput.readShortBigEndian(), 128); - - EXPECT_TRUE(memoryOutput.writeIntBigEndian(123)); - EXPECT_EQ(memoryInput.readIntBigEndian(), 123); - - EXPECT_TRUE(memoryOutput.writeInt64BigEndian(123123)); - EXPECT_EQ(memoryInput.readInt64BigEndian(), 123123); - - EXPECT_TRUE(memoryOutput.writeDoubleBigEndian(12.71)); - EXPECT_EQ(memoryInput.readDoubleBigEndian(), 12.71); - - EXPECT_TRUE(memoryOutput.writeFloatBigEndian(12.1)); - float f = 12.1; - EXPECT_EQ(memoryInput.readFloatBigEndian(), f); - - // EXPECT_TRUE(memoryOutput.writeRepeatedByte(8 , 8)); -} - -TEST(memoryInputStream, info) { - char* buf = (char*)malloc(sizeof(char) * 9); - strcpy(buf, "RocketMQ"); - - MemoryInputStream memoryInput(buf, 8, false); - - char* memoryData = (char*)memoryInput.getData(); - EXPECT_EQ(memoryData, buf); - - EXPECT_EQ(memoryInput.getTotalLength(), 8); - - MemoryInputStream twoMemoryInput(buf, 8, true); - EXPECT_NE(twoMemoryInput.getData(), buf); - - memoryData = (char*)twoMemoryInput.getData(); - EXPECT_NE(&memoryData, &buf); - - MemoryBlock memoryBlock(buf, 8); - MemoryInputStream threeMemoryInput(memoryBlock, false); - memoryData = (char*)threeMemoryInput.getData(); - EXPECT_EQ(memoryData, threeMemoryInput.getData()); - EXPECT_EQ(threeMemoryInput.getTotalLength(), 8); - - MemoryInputStream frouMemoryInput(memoryBlock, true); - EXPECT_NE(frouMemoryInput.getData(), memoryBlock.getData()); - free(buf); -} - -TEST(memoryInputStream, position) { - char* buf = (char*)malloc(sizeof(char) * 9); - strcpy(buf, "RocketMQ"); - - MemoryInputStream memoryInput(buf, 8, false); - EXPECT_EQ(memoryInput.getPosition(), 0); - EXPECT_FALSE(memoryInput.isExhausted()); - - memoryInput.setPosition(9); - EXPECT_EQ(memoryInput.getPosition(), 8); - EXPECT_TRUE(memoryInput.isExhausted()); - - memoryInput.setPosition(-1); - EXPECT_EQ(memoryInput.getPosition(), 0); - free(buf); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "*.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/NameSpaceUtilTest.cpp b/test/src/common/NameSpaceUtilTest.cpp deleted file mode 100644 index 08873b5f7..000000000 --- a/test/src/common/NameSpaceUtilTest.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "NameSpaceUtil.h" - -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::NameSpaceUtil; - -TEST(NameSpaceUtil, isEndPointURL) { - const string url = "http://rocketmq.nameserver.com"; - EXPECT_TRUE(NameSpaceUtil::isEndPointURL(url)); - EXPECT_FALSE(NameSpaceUtil::isEndPointURL("rocketmq.nameserver.com")); - EXPECT_FALSE(NameSpaceUtil::isEndPointURL("127.0.0.1")); -} -TEST(NameSpaceUtil, formatNameServerURL) { - string url = "http://rocketmq.nameserver.com"; - string urlFormatted = "rocketmq.nameserver.com"; - EXPECT_EQ(NameSpaceUtil::formatNameServerURL(url), urlFormatted); - EXPECT_EQ(NameSpaceUtil::formatNameServerURL(urlFormatted), urlFormatted); -} -TEST(NameSpaceUtil, getNameSpaceFromNsURL) { - string url = "http://MQ_INST_UNITTEST.rocketmq.nameserver.com"; - string url2 = "MQ_INST_UNITTEST.rocketmq.nameserver.com"; - string noInstUrl = "http://rocketmq.nameserver.com"; - string inst = "MQ_INST_UNITTEST"; - EXPECT_EQ(NameSpaceUtil::getNameSpaceFromNsURL(url), inst); - EXPECT_EQ(NameSpaceUtil::getNameSpaceFromNsURL(url2), inst); - EXPECT_EQ(NameSpaceUtil::getNameSpaceFromNsURL(noInstUrl), ""); -} -TEST(NameSpaceUtil, checkNameSpaceExistInNsURL) { - string url = "http://MQ_INST_UNITTEST.rocketmq.nameserver.com"; - string url2 = "MQ_INST_UNITTEST.rocketmq.nameserver.com"; - string noInstUrl = "http://rocketmq.nameserver.com"; - EXPECT_TRUE(NameSpaceUtil::checkNameSpaceExistInNsURL(url)); - EXPECT_FALSE(NameSpaceUtil::checkNameSpaceExistInNsURL(url2)); - EXPECT_FALSE(NameSpaceUtil::checkNameSpaceExistInNsURL(noInstUrl)); -} -TEST(NameSpaceUtil, checkNameSpaceExistInNameServer) { - string url = "http://MQ_INST_UNITTEST.rocketmq.nameserver.com"; - string url2 = "MQ_INST_UNITTEST.rocketmq.nameserver.com"; - string noInstUrl = "rocketmq.nameserver.com"; - string nsIP = "127.0.0.1"; - EXPECT_TRUE(NameSpaceUtil::checkNameSpaceExistInNameServer(url)); - EXPECT_TRUE(NameSpaceUtil::checkNameSpaceExistInNameServer(url2)); - EXPECT_FALSE(NameSpaceUtil::checkNameSpaceExistInNameServer(noInstUrl)); - EXPECT_FALSE(NameSpaceUtil::checkNameSpaceExistInNameServer(nsIP)); -} -TEST(NameSpaceUtil, withNameSpace) { - string source = "testTopic"; - string ns = "MQ_INST_UNITTEST"; - string nsSource = "MQ_INST_UNITTEST%testTopic"; - EXPECT_EQ(NameSpaceUtil::withNameSpace(source, ns), nsSource); - EXPECT_EQ(NameSpaceUtil::withNameSpace(source, ""), source); -} -TEST(NameSpaceUtil, hasNameSpace) { - string source = "testTopic"; - string ns = "MQ_INST_UNITTEST"; - string nsSource = "MQ_INST_UNITTEST%testTopic"; - string nsTraceSource = "rmq_sys_TRACE_DATA_Region"; - EXPECT_TRUE(NameSpaceUtil::hasNameSpace(nsSource, ns)); - EXPECT_FALSE(NameSpaceUtil::hasNameSpace(source, ns)); - EXPECT_FALSE(NameSpaceUtil::hasNameSpace(source, "")); - EXPECT_TRUE(NameSpaceUtil::hasNameSpace(nsTraceSource, ns)); -} -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "NameSpaceUtil.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/NamesrvConfigTest.cpp b/test/src/common/NamesrvConfigTest.cpp deleted file mode 100644 index e4e1206ae..000000000 --- a/test/src/common/NamesrvConfigTest.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "NamesrvConfig.h" - -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::NamesrvConfig; - -TEST(namesrvConfig, init) { - NamesrvConfig namesrvConfig; - - const string home = "/home/rocketmq"; - namesrvConfig.setRocketmqHome(home); - EXPECT_EQ(namesrvConfig.getRocketmqHome(), "/home/rocketmq"); - - namesrvConfig.setKvConfigPath("/home/rocketmq"); - EXPECT_EQ(namesrvConfig.getKvConfigPath(), "/home/rocketmq"); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "namesrvConfig.init"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/PermNametTest.cpp b/test/src/common/PermNametTest.cpp deleted file mode 100644 index 3296b84d7..000000000 --- a/test/src/common/PermNametTest.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "PermName.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::PermName; - -TEST(permName, perm2String) { - EXPECT_EQ(PermName::perm2String(0), "---"); - EXPECT_EQ(PermName::perm2String(1), "--X"); - EXPECT_EQ(PermName::perm2String(2), "-W"); - EXPECT_EQ(PermName::perm2String(3), "-WX"); - EXPECT_EQ(PermName::perm2String(4), "R--"); - EXPECT_EQ(PermName::perm2String(5), "R-X"); - EXPECT_EQ(PermName::perm2String(6), "RW"); - EXPECT_EQ(PermName::perm2String(7), "RWX"); - EXPECT_EQ(PermName::perm2String(8), "---"); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "permName.perm2String"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/PullSysFlagTest.cpp b/test/src/common/PullSysFlagTest.cpp deleted file mode 100644 index 7b7c9db95..000000000 --- a/test/src/common/PullSysFlagTest.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "PullSysFlag.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::PullSysFlag; - -TEST(pullSysFlag, flag) { - EXPECT_EQ(PullSysFlag::buildSysFlag(false, false, false, false), 0); - - EXPECT_EQ(PullSysFlag::buildSysFlag(true, false, false, false), 1); - EXPECT_EQ(PullSysFlag::buildSysFlag(true, true, false, false), 3); - EXPECT_EQ(PullSysFlag::buildSysFlag(true, true, true, false), 7); - EXPECT_EQ(PullSysFlag::buildSysFlag(true, true, true, true), 15); - - EXPECT_EQ(PullSysFlag::buildSysFlag(false, true, false, false), 2); - EXPECT_EQ(PullSysFlag::buildSysFlag(false, true, true, false), 6); - EXPECT_EQ(PullSysFlag::buildSysFlag(false, true, true, true), 14); - - EXPECT_EQ(PullSysFlag::buildSysFlag(false, false, true, false), 4); - EXPECT_EQ(PullSysFlag::buildSysFlag(false, false, true, true), 12); - - EXPECT_EQ(PullSysFlag::buildSysFlag(false, false, false, true), 8); - - int FLAG_COMMIT_OFFSET = 0x1 << 0; - int FLAG_SUSPEND = 0x1 << 1; - int FLAG_SUBSCRIPTION = 0x1 << 2; - int FLAG_CLASS_FILTER = 0x1 << 3; - - for (int i = 0; i < 16; i++) { - if ((i & FLAG_COMMIT_OFFSET) == FLAG_COMMIT_OFFSET) { - EXPECT_TRUE(PullSysFlag::hasCommitOffsetFlag(i)); - } else { - EXPECT_FALSE(PullSysFlag::hasCommitOffsetFlag(i)); - } - - if ((i & FLAG_SUSPEND) == FLAG_SUSPEND) { - EXPECT_TRUE(PullSysFlag::hasSuspendFlag(i)); - } else { - EXPECT_FALSE(PullSysFlag::hasSuspendFlag(i)); - } - - if ((i & FLAG_SUBSCRIPTION) == FLAG_SUBSCRIPTION) { - EXPECT_TRUE(PullSysFlag::hasSubscriptionFlag(i)); - } else { - EXPECT_FALSE(PullSysFlag::hasSubscriptionFlag(i)); - } - - if ((i & FLAG_CLASS_FILTER) == FLAG_CLASS_FILTER) { - EXPECT_TRUE(PullSysFlag::hasClassFilterFlag(i)); - } else { - EXPECT_FALSE(PullSysFlag::hasClassFilterFlag(i)); - } - - if ((i & FLAG_COMMIT_OFFSET) == FLAG_COMMIT_OFFSET) { - EXPECT_TRUE(PullSysFlag::hasCommitOffsetFlag(i)); - } else { - EXPECT_FALSE(PullSysFlag::hasCommitOffsetFlag(i)); - } - - if (i == 0 || i == 1) { - EXPECT_EQ(PullSysFlag::clearCommitOffsetFlag(i), 0); - } else { - EXPECT_TRUE(PullSysFlag::clearCommitOffsetFlag(i) > 0); - } - } -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "pullSysFlag.flag"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/TopicConfigTest.cpp b/test/src/common/TopicConfigTest.cpp deleted file mode 100644 index 8c163942c..000000000 --- a/test/src/common/TopicConfigTest.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "string.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "PermName.h" -#include "TopicConfig.h" -#include "TopicFilterType.h" - -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::PermName; -using rocketmq::TopicConfig; -using rocketmq::TopicFilterType; - -TEST(topicConfig, encodeAndDecode) { - TopicConfig topicConfig("testTopic", 4, 4, PermName::PERM_READ); - string str = topicConfig.encode(); - - TopicConfig topicDecodeConfig; - topicDecodeConfig.decode(str); - EXPECT_EQ(str, topicDecodeConfig.encode()); -} - -TEST(topicConfig, info) { - TopicConfig topicConfig; - - topicConfig.setTopicName("testTopic"); - EXPECT_EQ(topicConfig.getTopicName(), "testTopic"); - - topicConfig.setReadQueueNums(4); - EXPECT_EQ(topicConfig.getReadQueueNums(), 4); - - topicConfig.setWriteQueueNums(4); - EXPECT_EQ(topicConfig.getWriteQueueNums(), 4); - - topicConfig.setPerm(PermName::PERM_READ); - EXPECT_EQ(topicConfig.getPerm(), PermName::PERM_READ); - - topicConfig.setTopicFilterType(TopicFilterType::MULTI_TAG); - EXPECT_EQ(topicConfig.getTopicFilterType(), TopicFilterType::MULTI_TAG); -} - -TEST(topicConfig, init) { - TopicConfig topicConfig; - EXPECT_TRUE(topicConfig.getTopicName() == ""); - EXPECT_EQ(topicConfig.getReadQueueNums(), TopicConfig::DefaultReadQueueNums); - EXPECT_EQ(topicConfig.getWriteQueueNums(), TopicConfig::DefaultWriteQueueNums); - EXPECT_EQ(topicConfig.getPerm(), PermName::PERM_READ | PermName::PERM_WRITE); - EXPECT_EQ(topicConfig.getTopicFilterType(), TopicFilterType::SINGLE_TAG); - - TopicConfig twoTopicConfig("testTopic"); - EXPECT_EQ(twoTopicConfig.getTopicName(), "testTopic"); - EXPECT_EQ(twoTopicConfig.getReadQueueNums(), TopicConfig::DefaultReadQueueNums); - EXPECT_EQ(twoTopicConfig.getWriteQueueNums(), TopicConfig::DefaultWriteQueueNums); - EXPECT_EQ(twoTopicConfig.getPerm(), PermName::PERM_READ | PermName::PERM_WRITE); - EXPECT_EQ(twoTopicConfig.getTopicFilterType(), TopicFilterType::SINGLE_TAG); - - TopicConfig threeTopicConfig("testTopic", 4, 4, PermName::PERM_READ); - EXPECT_EQ(threeTopicConfig.getTopicName(), "testTopic"); - EXPECT_EQ(threeTopicConfig.getReadQueueNums(), 4); - EXPECT_EQ(threeTopicConfig.getWriteQueueNums(), 4); - EXPECT_EQ(threeTopicConfig.getPerm(), PermName::PERM_READ); - EXPECT_EQ(threeTopicConfig.getTopicFilterType(), TopicFilterType::SINGLE_TAG); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "topicConfig.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/UrlTest.cpp b/test/src/common/UrlTest.cpp deleted file mode 100644 index 636c2dee2..000000000 --- a/test/src/common/UrlTest.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include "CCommon.h" -#include "CMQException.h" -#include "CMessage.h" -#include "CProducer.h" -#include "CSendResult.h" -#include "TopicConfig.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "url.h" - -#include - -#include -#include "CCommon.h" -#include "CMQException.h" -#include "CMessage.h" -#include "CProducer.h" -#include "CSendResult.h" - -using namespace std; -using rocketmq::TopicConfig; -using rocketmq::Url; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -class MockTopicConfig : public TopicConfig { - public: - MOCK_METHOD0(getReadQueueNums, int()); -}; - -TEST(Url, Url) { - Url url_s("172.17.0.2:9876"); - EXPECT_EQ(url_s.protocol_, "172.17.0.2:9876"); - - Url url_z("https://www.aliyun.com/RocketMQ?5.0"); - EXPECT_EQ(url_z.protocol_, "https"); - EXPECT_EQ(url_z.host_, "www.aliyun.com"); - EXPECT_EQ(url_z.port_, "80"); - EXPECT_EQ(url_z.path_, "/RocketMQ"); - EXPECT_EQ(url_z.query_, "5.0"); - - Url url_path("https://www.aliyun.com:9876/RocketMQ?5.0"); - EXPECT_EQ(url_path.port_, "9876"); - MockTopicConfig topicConfig; - EXPECT_CALL(topicConfig, getReadQueueNums()).WillRepeatedly(Return(-1)); - int nums = topicConfig.getReadQueueNums(); - cout << nums << endl; -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(filter) = "Url.Url"; - int itestts = RUN_ALL_TESTS(); - ; - return itestts; -} diff --git a/test/src/common/UtilAllTest.cpp b/test/src/common/UtilAllTest.cpp deleted file mode 100644 index 86e1130fb..000000000 --- a/test/src/common/UtilAllTest.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "UtilAll.h" - -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::UtilAll; - -TEST(UtilAll, startsWith_retry) { - string source = "testTopic"; - string retrySource = "%RETRY%testTopic"; - string noRetrySource = "%DLQ%testTopic"; - EXPECT_TRUE(UtilAll::startsWith_retry(retrySource)); - EXPECT_FALSE(UtilAll::startsWith_retry(source)); - EXPECT_FALSE(UtilAll::startsWith_retry(noRetrySource)); -} -TEST(UtilAll, getRetryTopic) { - string source = "testTopic"; - string retrySource = "%RETRY%testTopic"; - EXPECT_EQ(UtilAll::getRetryTopic(source), retrySource); -} -TEST(UtilAll, Trim) { - string source = "testTopic"; - string preSource = " testTopic"; - string surSource = "testTopic "; - string allSource = " testTopic "; - UtilAll::Trim(preSource); - UtilAll::Trim(surSource); - UtilAll::Trim(allSource); - EXPECT_EQ(preSource, source); - EXPECT_EQ(surSource, source); - EXPECT_EQ(allSource, source); -} -TEST(UtilAll, hexstr2ull) { - const char* a = "1"; - const char* b = "FF"; - const char* c = "1a"; - const char* d = "101"; - EXPECT_EQ(UtilAll::hexstr2ull(a), 1); - EXPECT_EQ(UtilAll::hexstr2ull(b), 255); - EXPECT_EQ(UtilAll::hexstr2ull(c), 26); - EXPECT_EQ(UtilAll::hexstr2ull(d), 257); -} -TEST(UtilAll, SplitURL) { - string source = "127.0.0.1"; - string source1 = "127.0.0.1:0"; - string source2 = "127.0.0.1:9876"; - string addr; - string addr1; - string addr2; - short port; - EXPECT_FALSE(UtilAll::SplitURL(source, addr, port)); - EXPECT_FALSE(UtilAll::SplitURL(source1, addr1, port)); - EXPECT_TRUE(UtilAll::SplitURL(source2, addr2, port)); - EXPECT_EQ(addr2, "127.0.0.1"); - EXPECT_EQ(port, 9876); -} -TEST(UtilAll, SplitOne) { - string source = "127.0.0.1:9876"; - vector ret; - EXPECT_EQ(UtilAll::Split(ret, source, '.'), 4); - EXPECT_EQ(ret[0], "127"); -} -TEST(UtilAll, SplitStr) { - string source = "11AA222AA3333AA44444AA5"; - vector ret; - EXPECT_EQ(UtilAll::Split(ret, source, "AA"), 5); - EXPECT_EQ(ret[0], "11"); -} -TEST(UtilAll, StringToInt32) { - string source = "123"; - int value; - EXPECT_TRUE(UtilAll::StringToInt32(source, value)); - EXPECT_EQ(123, value); - EXPECT_FALSE(UtilAll::StringToInt32("123456789X123456789", value)); - EXPECT_FALSE(UtilAll::StringToInt32("-1234567890123456789", value)); - EXPECT_FALSE(UtilAll::StringToInt32("1234567890123456789", value)); -} -TEST(UtilAll, StringToInt64) { - string source = "123"; - int64_t value; - EXPECT_TRUE(UtilAll::StringToInt64(source, value)); - EXPECT_EQ(123, value); - EXPECT_FALSE(UtilAll::StringToInt64("XXXXXXXXXXX", value)); - EXPECT_FALSE(UtilAll::StringToInt64("123456789X123456789", value)); - EXPECT_EQ(123456789, value); - EXPECT_FALSE(UtilAll::StringToInt64("-123456789012345678901234567890123456789012345678901234567890", value)); - EXPECT_FALSE(UtilAll::StringToInt64("123456789012345678901234567890123456789012345678901234567890", value)); -} -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "UtilAll.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/ValidatorsTest.cpp b/test/src/common/ValidatorsTest.cpp deleted file mode 100644 index 23594cdd3..000000000 --- a/test/src/common/ValidatorsTest.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "string.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "MQClientException.h" -#include "MQMessage.h" -#include "Validators.h" - -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MQClientException; -using rocketmq::MQMessage; -using rocketmq::Validators; - -TEST(validators, regularExpressionMatcher) { - EXPECT_FALSE(Validators::regularExpressionMatcher(string(), string())); - - EXPECT_TRUE(Validators::regularExpressionMatcher(string("123456"), string())); - - EXPECT_TRUE(Validators::regularExpressionMatcher(string("123456"), string("123"))); -} - -TEST(validators, getGroupWithRegularExpression) { - EXPECT_EQ(Validators::getGroupWithRegularExpression(string(), string()), ""); -} - -TEST(validators, checkTopic) { - EXPECT_THROW(Validators::checkTopic(string()), MQClientException); - string exceptionTopic = "1234567890"; - for (int i = 0; i < 25; i++) { - exceptionTopic.append("1234567890"); - } - EXPECT_THROW(Validators::checkTopic(exceptionTopic), MQClientException); - - EXPECT_THROW(Validators::checkTopic("TBW102"), MQClientException); -} - -TEST(validators, checkGroup) { - EXPECT_THROW(Validators::checkGroup(string()), MQClientException); - string exceptionTopic = "1234567890"; - for (int i = 0; i < 25; i++) { - exceptionTopic.append("1234567890"); - } - EXPECT_THROW(Validators::checkGroup(exceptionTopic), MQClientException); -} - -TEST(validators, checkMessage) { - MQMessage message("testTopic", string()); - - EXPECT_THROW(Validators::checkMessage(MQMessage("testTopic", string()), 1), MQClientException); - - EXPECT_THROW(Validators::checkMessage(MQMessage("testTopic", string("123")), 2), MQClientException); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "validators.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/common/VirtualEnvUtilTest.cpp b/test/src/common/VirtualEnvUtilTest.cpp deleted file mode 100644 index 1300c90d5..000000000 --- a/test/src/common/VirtualEnvUtilTest.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "string.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "VirtualEnvUtil.h" - -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::VirtualEnvUtil; - -TEST(virtualEnvUtil, buildWithProjectGroup) { - string origin = "origin"; - string originWithGroupA = "origin%PROJECT_testGroupA%"; - string originWithGroupB = "origin%PROJECT_testGroupB%"; - string originWithGroupAB = "origin%PROJECT_testGroupA%%PROJECT_testGroupB%"; - string projectGroupA = "testGroupA"; - string projectGroupB = "testGroupB"; - EXPECT_EQ(VirtualEnvUtil::buildWithProjectGroup(origin, string()), origin); - EXPECT_EQ(VirtualEnvUtil::buildWithProjectGroup(origin, projectGroupA), originWithGroupA); - EXPECT_EQ(VirtualEnvUtil::buildWithProjectGroup(originWithGroupA, projectGroupA), originWithGroupA); - EXPECT_EQ(VirtualEnvUtil::buildWithProjectGroup(originWithGroupA, projectGroupB), originWithGroupAB); -} - -TEST(virtualEnvUtil, clearProjectGroup) { - string origin = "origin"; - string originWithGroup = "origin%PROJECT_testGroup%"; - string projectGroup = "testGroup"; - string projectGroupB = "testGroupB"; - EXPECT_EQ(VirtualEnvUtil::clearProjectGroup(origin, string()), origin); - EXPECT_EQ(VirtualEnvUtil::clearProjectGroup(originWithGroup, string()), originWithGroup); - EXPECT_EQ(VirtualEnvUtil::clearProjectGroup(originWithGroup, projectGroupB), originWithGroup); - EXPECT_EQ(VirtualEnvUtil::clearProjectGroup(origin, projectGroup), origin); - EXPECT_EQ(VirtualEnvUtil::clearProjectGroup(originWithGroup, projectGroup), origin); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "virtualEnvUtil.*"; - int iTest = RUN_ALL_TESTS(); - return iTest; -} diff --git a/test/src/common/big_endianTest.cpp b/test/src/common/big_endianTest.cpp deleted file mode 100644 index 06432d2b0..000000000 --- a/test/src/common/big_endianTest.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "big_endian.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::BigEndianReader; -using rocketmq::BigEndianWriter; - -TEST(big_endian, bigEndianObject) { - char* buf = (char*)malloc(sizeof(char) * 32); - - BigEndianWriter writer(buf, 32); - BigEndianReader reader(buf, 32); - - uint8_t* unit8 = (uint8_t*)malloc(sizeof(uint8_t)); - EXPECT_TRUE(writer.WriteU8((uint8_t)12)); - EXPECT_TRUE(reader.ReadU8(unit8)); - EXPECT_EQ(*unit8, 12); - free(unit8); - - uint16_t* unit16 = (uint16_t*)malloc(sizeof(uint16_t)); - EXPECT_TRUE(writer.WriteU16((uint16_t)1200)); - EXPECT_TRUE(reader.ReadU16(unit16)); - EXPECT_EQ(*unit16, 1200); - free(unit16); - - uint32_t* unit32 = (uint32_t*)malloc(sizeof(uint32_t)); - - EXPECT_TRUE(writer.WriteU32((uint32_t)120000)); - EXPECT_TRUE(reader.ReadU32(unit32)); - EXPECT_EQ(*unit32, 120000); - free(unit32); - - uint64_t* unit64 = (uint64_t*)malloc(sizeof(uint64_t)); - - EXPECT_TRUE(writer.WriteU64((uint64_t)120000)); - EXPECT_TRUE(reader.ReadU64(unit64)); - EXPECT_EQ(*unit64, 120000); - free(unit64); - - char* newBuf = (char*)malloc(sizeof(char) * 8); - char* writeBuf = (char*)malloc(sizeof(char) * 8); - strncpy(writeBuf, "RocketMQ", 8); - EXPECT_TRUE(writer.WriteBytes(writeBuf, (size_t)8)); - EXPECT_TRUE(reader.ReadBytes(newBuf, (size_t)8)); - EXPECT_EQ(*writeBuf, *newBuf); - - free(newBuf); - free(writeBuf); -} - -TEST(big_endian, bigEndian) { - char writeBuf[8]; - - /*TODO - char *newBuf = (char *) malloc(sizeof(char) * 8); - strncpy(newBuf, "RocketMQ", 8); - - char readBuf[8]; - rocketmq::WriteBigEndian(writeBuf, newBuf); - rocketmq::ReadBigEndian(writeBuf, readBuf); - EXPECT_EQ(writeBuf, readBuf); - */ - - rocketmq::WriteBigEndian(writeBuf, (uint8_t)12); - uint8_t* out = (uint8_t*)malloc(sizeof(uint8_t)); - rocketmq::ReadBigEndian(writeBuf, out); - EXPECT_EQ(*out, 12); - free(out); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "big_endian.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/consumer/DefaultMQPushConsumerImplTest.cpp b/test/src/consumer/DefaultMQPushConsumerImplTest.cpp deleted file mode 100644 index cfdc95fc6..000000000 --- a/test/src/consumer/DefaultMQPushConsumerImplTest.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "ConsumeMessageContext.h" -#include "ConsumeMessageHookImpl.h" -#include "DefaultMQProducerImpl.h" -#include "DefaultMQPushConsumerImpl.h" -#include "MQMessageExt.h" -#include "MQMessageQueue.h" - -using namespace std; -using namespace rocketmq; -using rocketmq::DefaultMQProducerImpl; -using rocketmq::DefaultMQPushConsumerImpl; -using testing::_; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -class MockDefaultMQProducerImpl : public DefaultMQProducerImpl { - public: - MockDefaultMQProducerImpl(const string& groupId) : DefaultMQProducerImpl(groupId) {} - MOCK_METHOD3(send, void(MQMessage&, SendCallback*, bool)); -}; -TEST(DefaultMQPushConsumerImplTest, init) { - DefaultMQPushConsumerImpl* impl = new DefaultMQPushConsumerImpl("testMQConsumerGroup"); - EXPECT_EQ(impl->getGroupName(), "testMQConsumerGroup"); - impl->setUnitName("testUnit"); - EXPECT_EQ(impl->getUnitName(), "testUnit"); - impl->setTcpTransportPullThreadNum(64); - EXPECT_EQ(impl->getTcpTransportPullThreadNum(), 64); - impl->setTcpTransportConnectTimeout(2000); - EXPECT_EQ(impl->getTcpTransportConnectTimeout(), 2000); - impl->setTcpTransportTryLockTimeout(3000); - EXPECT_EQ(impl->getTcpTransportTryLockTimeout(), 3); - impl->setNamesrvAddr("http://rocketmq.nameserver.com"); - EXPECT_EQ(impl->getNamesrvAddr(), "rocketmq.nameserver.com"); - impl->setNameSpace("MQ_INST_NAMESPACE_TEST"); - EXPECT_EQ(impl->getNameSpace(), "MQ_INST_NAMESPACE_TEST"); - impl->setMessageTrace(true); - EXPECT_TRUE(impl->getMessageTrace()); - impl->setAsyncPull(true); - - impl->setConsumeMessageBatchMaxSize(3000); - EXPECT_EQ(impl->getConsumeMessageBatchMaxSize(), 3000); - - impl->setConsumeThreadCount(3); - EXPECT_EQ(impl->getConsumeThreadCount(), 3); - - impl->setMaxReconsumeTimes(30); - EXPECT_EQ(impl->getMaxReconsumeTimes(), 30); - - impl->setMaxCacheMsgSizePerQueue(3000); - EXPECT_EQ(impl->getMaxCacheMsgSizePerQueue(), 3000); - - impl->setPullMsgThreadPoolCount(10); - EXPECT_EQ(impl->getPullMsgThreadPoolCount(), 10); -} - -TEST(DefaultMQPushConsumerImpl, Trace) { - DefaultMQPushConsumerImpl* impl = new DefaultMQPushConsumerImpl(); - MockDefaultMQProducerImpl* implProducer = new MockDefaultMQProducerImpl("testMockProducerTraceGroup"); - std::shared_ptr hook(new ConsumeMessageHookImpl()); - impl->setMessageTrace(true); - impl->setDefaultMqProducerImpl(implProducer); - impl->registerConsumeMessageHook(hook); - EXPECT_CALL(*implProducer, send(_, _, _)).WillRepeatedly(Return()); - - ConsumeMessageContext consumeMessageContext; - MQMessageQueue messageQueue("TestTopic", "BrokerA", 0); - MQMessageExt messageExt; - messageExt.setMsgId("MessageID"); - messageExt.setKeys("MessageKey"); - vector msgs; - consumeMessageContext.setDefaultMQPushConsumer(impl); - consumeMessageContext.setConsumerGroup("testMockProducerTraceGroup"); - consumeMessageContext.setMessageQueue(messageQueue); - consumeMessageContext.setMsgList(msgs); - consumeMessageContext.setSuccess(false); - consumeMessageContext.setNameSpace("NameSpace"); - impl->executeConsumeMessageHookBefore(&consumeMessageContext); - impl->executeConsumeMessageHookAfter(&consumeMessageContext); - - msgs.push_back(messageExt); - consumeMessageContext.setMsgList(msgs); - - impl->executeConsumeMessageHookBefore(&consumeMessageContext); - - consumeMessageContext.setMsgIndex(0); - consumeMessageContext.setStatus("CONSUME_SUCCESS"); - consumeMessageContext.setSuccess(true); - impl->executeConsumeMessageHookAfter(&consumeMessageContext); - EXPECT_TRUE(impl->hasConsumeMessageHook()); - delete implProducer; -} -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/test/src/extern/CMessageExtTest.cpp b/test/src/extern/CMessageExtTest.cpp deleted file mode 100644 index 386054a54..000000000 --- a/test/src/extern/CMessageExtTest.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "CCommon.h" -#include "CMessageExt.h" -#include "MQMessageExt.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MQMessageExt; - -TEST(cmessageExt, info) { - MQMessageExt* mqMessageExt = new MQMessageExt(); - CMessageExt* messageExt = (CMessageExt*)mqMessageExt; - - mqMessageExt->setTopic("testTopic"); - EXPECT_EQ(GetMessageTopic(messageExt), mqMessageExt->getTopic()); - - mqMessageExt->setTags("testTags"); - EXPECT_EQ(GetMessageTags(messageExt), mqMessageExt->getTags()); - - mqMessageExt->setKeys("testKeys"); - EXPECT_EQ(GetMessageKeys(messageExt), mqMessageExt->getKeys()); - - std::string body("testBody"); - body.append(3, '\0'); - mqMessageExt->setBody(body.c_str(), body.length()); - std::string retrieved_body(GetMessageBody(messageExt), GetMessageBodyLength(messageExt)); - EXPECT_TRUE(body == retrieved_body); - - mqMessageExt->setProperty("testKey", "testValues"); - EXPECT_EQ(GetMessageProperty(messageExt, "testKey"), mqMessageExt->getProperty("testKey")); - - mqMessageExt->setMsgId("msgId123456"); - EXPECT_EQ(GetMessageId(messageExt), mqMessageExt->getMsgId()); - - mqMessageExt->setDelayTimeLevel(1); - EXPECT_EQ(GetMessageDelayTimeLevel(messageExt), mqMessageExt->getDelayTimeLevel()); - - mqMessageExt->setQueueId(4); - EXPECT_EQ(GetMessageQueueId(messageExt), mqMessageExt->getQueueId()); - - mqMessageExt->setReconsumeTimes(1234567); - EXPECT_EQ(GetMessageReconsumeTimes(messageExt), mqMessageExt->getReconsumeTimes()); - - mqMessageExt->setStoreSize(127); - EXPECT_EQ(GetMessageStoreSize(messageExt), mqMessageExt->getStoreSize()); - - mqMessageExt->setBornTimestamp(9876543); - EXPECT_EQ(GetMessageBornTimestamp(messageExt), mqMessageExt->getBornTimestamp()); - - mqMessageExt->setStoreTimestamp(123123); - EXPECT_EQ(GetMessageStoreTimestamp(messageExt), mqMessageExt->getStoreTimestamp()); - - mqMessageExt->setQueueOffset(1024); - EXPECT_EQ(GetMessageQueueOffset(messageExt), mqMessageExt->getQueueOffset()); - - mqMessageExt->setCommitLogOffset(2048); - EXPECT_EQ(GetMessageCommitLogOffset(messageExt), mqMessageExt->getCommitLogOffset()); - - mqMessageExt->setPreparedTransactionOffset(4096); - EXPECT_EQ(GetMessagePreparedTransactionOffset(messageExt), mqMessageExt->getPreparedTransactionOffset()); - - delete mqMessageExt; -} - -TEST(cmessageExt, null) { - EXPECT_TRUE(GetMessageTopic(NULL) == NULL); - EXPECT_TRUE(GetMessageTags(NULL) == NULL); - EXPECT_TRUE(GetMessageKeys(NULL) == NULL); - EXPECT_TRUE(GetMessageBody(NULL) == NULL); - EXPECT_TRUE(GetMessageProperty(NULL, NULL) == NULL); - EXPECT_TRUE(GetMessageId(NULL) == NULL); - EXPECT_EQ(GetMessageDelayTimeLevel(NULL), NULL_POINTER); - EXPECT_EQ(GetMessageQueueId(NULL), NULL_POINTER); - EXPECT_EQ(GetMessageReconsumeTimes(NULL), NULL_POINTER); - EXPECT_EQ(GetMessageStoreSize(NULL), NULL_POINTER); - EXPECT_EQ(GetMessageBornTimestamp(NULL), NULL_POINTER); - EXPECT_EQ(GetMessageStoreTimestamp(NULL), NULL_POINTER); - EXPECT_EQ(GetMessageQueueOffset(NULL), NULL_POINTER); - EXPECT_EQ(GetMessageCommitLogOffset(NULL), NULL_POINTER); - EXPECT_EQ(GetMessagePreparedTransactionOffset(NULL), NULL_POINTER); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(filter) = "cmessageExt.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/extern/CMessageTest.cpp b/test/src/extern/CMessageTest.cpp deleted file mode 100644 index 4ac24f1bc..000000000 --- a/test/src/extern/CMessageTest.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "CCommon.h" -#include "CMessage.h" -#include "MQMessage.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MQMessage; - -TEST(cmessages, originMessage) { - CMessage* message = CreateMessage(NULL); - EXPECT_STREQ(GetOriginMessageTopic(message), ""); - - SetMessageTopic(message, "testTopic"); - EXPECT_STREQ(GetOriginMessageTopic(message), "testTopic"); - - SetMessageTags(message, "testTags"); - EXPECT_STREQ(GetOriginMessageTags(message), "testTags"); - - SetMessageKeys(message, "testKeys"); - EXPECT_STREQ(GetOriginMessageKeys(message), "testKeys"); - - std::string body("test_body"); - body.append(3, '\0'); - SetByteMessageBody(message, body.c_str(), body.length()); - std::string retrieved_body(GetOriginMessageBody(message), GetOriginMessageBodyLength(message)); - EXPECT_TRUE(body == retrieved_body); - - SetMessageProperty(message, "testKey", "testValue"); - EXPECT_STREQ(GetOriginMessageProperty(message, "testKey"), "testValue"); - - SetDelayTimeLevel(message, 1); - EXPECT_EQ(GetOriginDelayTimeLevel(message), 1); - - EXPECT_EQ(DestroyMessage(message), OK); - - CMessage* message2 = CreateMessage("testTwoTopic"); - EXPECT_STREQ(GetOriginMessageTopic(message2), "testTwoTopic"); - - EXPECT_EQ(DestroyMessage(message2), OK); -} - -TEST(cmessages, info) { - CMessage* message = CreateMessage(NULL); - MQMessage* mqMessage = (MQMessage*)message; - EXPECT_EQ(mqMessage->getTopic(), ""); - - SetMessageTopic(message, "testTopic"); - EXPECT_EQ(mqMessage->getTopic(), "testTopic"); - - SetMessageTags(message, "testTags"); - EXPECT_EQ(mqMessage->getTags(), "testTags"); - - SetMessageKeys(message, "testKeys"); - EXPECT_EQ(mqMessage->getKeys(), "testKeys"); - - SetMessageBody(message, "testBody"); - EXPECT_EQ(mqMessage->getBody(), "testBody"); - - SetByteMessageBody(message, "testBody", 5); - EXPECT_EQ(mqMessage->getBody(), "testB"); - - SetMessageProperty(message, "testKey", "testValue"); - EXPECT_EQ(mqMessage->getProperty("testKey"), "testValue"); - - SetDelayTimeLevel(message, 1); - EXPECT_EQ(mqMessage->getDelayTimeLevel(), 1); - - EXPECT_EQ(DestroyMessage(message), OK); - - CMessage* twomessage = CreateMessage("testTwoTopic"); - MQMessage* twoMqMessage = (MQMessage*)twomessage; - EXPECT_EQ(twoMqMessage->getTopic(), "testTwoTopic"); - - EXPECT_EQ(DestroyMessage(twomessage), OK); -} - -TEST(cmessages, null) { - EXPECT_EQ(SetMessageTopic(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetMessageTags(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetMessageKeys(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetMessageBody(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetByteMessageBody(NULL, NULL, 0), NULL_POINTER); - EXPECT_EQ(SetMessageProperty(NULL, NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetDelayTimeLevel(NULL, 0), NULL_POINTER); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - - // testing::GTEST_FLAG(filter) = "cmessages.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/extern/CProducerTest.cpp b/test/src/extern/CProducerTest.cpp deleted file mode 100644 index 9a5156fea..000000000 --- a/test/src/extern/CProducerTest.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "string.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "CMessage.h" -#include "CProducer.h" -#include "CSendResult.h" - -#include "AsyncCallback.h" -#include "DefaultMQProducer.h" -#include "MQMessage.h" -#include "MQMessageQueue.h" -#include "MQSelector.h" -#include "SendResult.h" -#include "SessionCredentials.h" - -using std::string; - -using ::testing::_; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Mock; -using testing::Return; - -using rocketmq::DefaultMQProducer; -using rocketmq::elogLevel; -using rocketmq::MessageQueueSelector; -using rocketmq::MQMessage; -using rocketmq::MQMessageQueue; -using rocketmq::SendCallback; -using rocketmq::SendResult; -using rocketmq::SendStatus; -using rocketmq::SessionCredentials; - -class MockDefaultMQProducer : public DefaultMQProducer { - public: - MockDefaultMQProducer(const string& groupname) : DefaultMQProducer(groupname) {} - MOCK_METHOD0(start, void()); - MOCK_METHOD0(shutdown, void()); - MOCK_METHOD2(setLogFileSizeAndNum, void(int, long)); - MOCK_METHOD1(SetProducerLogLevel, void(elogLevel)); - MOCK_METHOD2(send, SendResult(MQMessage&, bool)); - MOCK_METHOD3(send, void(MQMessage&, SendCallback*, bool)); - MOCK_METHOD2(sendOneway, void(MQMessage&, bool)); - MOCK_METHOD5(send, SendResult(MQMessage&, MessageQueueSelector*, void*, int, bool)); -}; - -void CSendSuccessCallbackFunc(CSendResult result) {} -void cSendExceptionCallbackFunc(CMQException e) {} - -TEST(cProducer, SendMessageAsync) { - MockDefaultMQProducer* mockProducer = new MockDefaultMQProducer("testGroup"); - CProducer* cProducer = CreateProducer("testGroup"); - // cProducer= mockProducer; - DefaultMQProducer** aProducer = (DefaultMQProducer**)cProducer; - aProducer[0] = mockProducer; - CMessage* msg = (CMessage*)new MQMessage(); - - EXPECT_EQ(SendMessageAsync(NULL, NULL, NULL, NULL), NULL_POINTER); - EXPECT_EQ(SendMessageAsync(cProducer, NULL, NULL, NULL), NULL_POINTER); - EXPECT_EQ(SendMessageAsync(cProducer, msg, CSendSuccessCallbackFunc, NULL), NULL_POINTER); - - // EXPECT_CALL(*mockProducer, send(_, _)).Times(1); - EXPECT_EQ(SendMessageAsync(cProducer, msg, CSendSuccessCallbackFunc, cSendExceptionCallbackFunc), OK); - Mock::AllowLeak(mockProducer); - DestroyMessage(msg); -} - -int QueueSelectorCallbackFunc(int size, CMessage* msg, void* arg) { - return 0; -} - -TEST(cProducer, sendMessageOrderly) { - MockDefaultMQProducer* mockProducer = new MockDefaultMQProducer("testGroup"); - // CProducer* cProducer = (CProducer*)mockProducer; - CProducer* cProducer = CreateOrderlyProducer("testGroup"); - // cProducer= mockProducer; - DefaultMQProducer** aProducer = (DefaultMQProducer**)cProducer; - aProducer[0] = mockProducer; - CMessage* msg = (CMessage*)new MQMessage(); - MQMessageQueue messageQueue; - - EXPECT_EQ(SendMessageOrderly(NULL, NULL, NULL, msg, 1, NULL), NULL_POINTER); - EXPECT_EQ(SendMessageOrderly(cProducer, NULL, NULL, msg, 1, NULL), NULL_POINTER); - EXPECT_EQ(SendMessageOrderly(cProducer, msg, NULL, msg, 1, NULL), NULL_POINTER); - EXPECT_EQ(SendMessageOrderly(cProducer, msg, QueueSelectorCallbackFunc, NULL, 1, NULL), NULL_POINTER); - EXPECT_EQ(SendMessageOrderly(cProducer, msg, QueueSelectorCallbackFunc, msg, 1, NULL), NULL_POINTER); - - EXPECT_CALL(*mockProducer, send(_, _, _, _, _)) - .WillOnce(Return(SendResult(SendStatus::SEND_OK, "3", "offset1", messageQueue, 14))); - // EXPECT_EQ(SendMessageOrderly(cProducer, msg, callback, msg, 1, result), OK); - Mock::AllowLeak(mockProducer); - DestroyMessage(msg); - // free(result); -} - -TEST(cProducer, sendOneway) { - MockDefaultMQProducer* mockProducer = new MockDefaultMQProducer("testGroup"); - // CProducer* cProducer = (CProducer*)mockProducer; - CProducer* cProducer = CreateProducer("testGroup"); - // cProducer= mockProducer; - DefaultMQProducer** aProducer = (DefaultMQProducer**)cProducer; - aProducer[0] = mockProducer; - CMessage* msg = (CMessage*)new MQMessage(); - - EXPECT_EQ(SendMessageOneway(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SendMessageOneway(cProducer, NULL), NULL_POINTER); - - EXPECT_CALL(*mockProducer, sendOneway(_, _)).Times(1); - EXPECT_EQ(SendMessageOneway(cProducer, msg), OK); - Mock::AllowLeak(mockProducer); - DestroyMessage(msg); -} - -TEST(cProducer, sendMessageSync) { - MockDefaultMQProducer* mockProducer = new MockDefaultMQProducer("testGroup"); - // CProducer* cProducer = (CProducer*)mockProducer; - CProducer* cProducer = CreateProducer("testGroup"); - // cProducer= mockProducer; - DefaultMQProducer** aProducer = (DefaultMQProducer**)cProducer; - aProducer[0] = mockProducer; - MQMessage* mqMessage = new MQMessage(); - CMessage* msg = (CMessage*)mqMessage; - CSendResult* result; - MQMessageQueue messageQueue; - EXPECT_EQ(SendMessageSync(NULL, NULL, NULL), NULL_POINTER); - EXPECT_EQ(SendMessageSync(cProducer, NULL, NULL), NULL_POINTER); - - EXPECT_EQ(SendMessageSync(cProducer, msg, NULL), NULL_POINTER); - - result = (CSendResult*)malloc(sizeof(CSendResult)); - - EXPECT_CALL(*mockProducer, send(_, _)) - .Times(5) - .WillOnce(Return(SendResult(SendStatus::SEND_FLUSH_DISK_TIMEOUT, "1", "offset1", messageQueue, 14))) - .WillOnce(Return(SendResult(SendStatus::SEND_FLUSH_SLAVE_TIMEOUT, "2", "offset1", messageQueue, 14))) - .WillOnce(Return(SendResult(SendStatus::SEND_SLAVE_NOT_AVAILABLE, "3", "offset1", messageQueue, 14))) - .WillOnce(Return(SendResult(SendStatus::SEND_OK, "3", "offset1", messageQueue, 14))) - .WillOnce(Return(SendResult((SendStatus)-1, "4", "offset1", messageQueue, 14))); - - EXPECT_EQ(SendMessageSync(cProducer, msg, result), OK); - EXPECT_EQ(result->sendStatus, E_SEND_FLUSH_DISK_TIMEOUT); - - EXPECT_EQ(SendMessageSync(cProducer, msg, result), OK); - EXPECT_EQ(result->sendStatus, E_SEND_FLUSH_SLAVE_TIMEOUT); - - EXPECT_EQ(SendMessageSync(cProducer, msg, result), OK); - EXPECT_EQ(result->sendStatus, E_SEND_SLAVE_NOT_AVAILABLE); - - EXPECT_EQ(SendMessageSync(cProducer, msg, result), OK); - EXPECT_EQ(result->sendStatus, E_SEND_OK); - - EXPECT_EQ(SendMessageSync(cProducer, msg, result), OK); - EXPECT_EQ(result->sendStatus, E_SEND_OK); - Mock::AllowLeak(mockProducer); - DestroyMessage(msg); - free(result); -} - -TEST(cProducer, infoMock) { - MockDefaultMQProducer* mockProducer = new MockDefaultMQProducer("testGroup"); - // CProducer* cProducer = (CProducer*)mockProducer; - CProducer* cProducer = CreateProducer("testGroup"); - // cProducer= mockProducer; - DefaultMQProducer** aProducer = (DefaultMQProducer**)cProducer; - aProducer[0] = mockProducer; - EXPECT_CALL(*mockProducer, start()).Times(1); - EXPECT_EQ(StartProducer(cProducer), OK); - - EXPECT_CALL(*mockProducer, shutdown()).Times(1); - EXPECT_EQ(ShutdownProducer(cProducer), OK); - - EXPECT_CALL(*mockProducer, setLogFileSizeAndNum(_, _)).Times(1); - EXPECT_EQ(SetProducerLogFileNumAndSize(cProducer, 1, 1), OK); - - EXPECT_CALL(*mockProducer, SetProducerLogLevel(_)).Times(1); - EXPECT_EQ(SetProducerLogLevel(cProducer, E_LOG_LEVEL_FATAL), OK); - Mock::AllowLeak(mockProducer); -} - -TEST(cProducer, info) { - CProducer* cProducer = CreateProducer("groupTest"); - // DefaultMQProducer* defaultMQProducer = (DefaultMQProducer*)cProducer; - DefaultMQProducer** aProducer = (DefaultMQProducer**)cProducer; - DefaultMQProducer* defaultMQProducer = aProducer[0]; - EXPECT_TRUE(cProducer != NULL); - EXPECT_EQ(defaultMQProducer->getGroupName(), "groupTest"); - - EXPECT_EQ(SetProducerNameServerAddress(cProducer, "127.0.0.1:9876"), OK); - EXPECT_EQ(defaultMQProducer->getNamesrvAddr(), "127.0.0.1:9876"); - - EXPECT_EQ(SetProducerNameServerDomain(cProducer, "domain"), OK); - EXPECT_EQ(defaultMQProducer->getNamesrvDomain(), "domain"); - - EXPECT_EQ(SetProducerGroupName(cProducer, "testGroup"), OK); - EXPECT_EQ(defaultMQProducer->getGroupName(), "testGroup"); - - EXPECT_EQ(SetProducerInstanceName(cProducer, "instance"), OK); - EXPECT_EQ(defaultMQProducer->getInstanceName(), "instance"); - - EXPECT_EQ(SetProducerSendMsgTimeout(cProducer, 1), OK); - EXPECT_EQ(defaultMQProducer->getSendMsgTimeout(), 1); - - EXPECT_EQ(SetProducerMaxMessageSize(cProducer, 2), OK); - EXPECT_EQ(defaultMQProducer->getMaxMessageSize(), 2); - - EXPECT_EQ(SetProducerCompressLevel(cProducer, 1), OK); - EXPECT_EQ(defaultMQProducer->getCompressLevel(), 1); - - EXPECT_EQ(SetProducerSessionCredentials(NULL, NULL, NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetProducerSessionCredentials(cProducer, "accessKey", "secretKey", "channel"), OK); - SessionCredentials sessionCredentials = defaultMQProducer->getSessionCredentials(); - EXPECT_EQ(sessionCredentials.getAccessKey(), "accessKey"); - - EXPECT_EQ(SetProducerMessageTrace(cProducer, OPEN), OK); - EXPECT_EQ(defaultMQProducer->getMessageTrace(), true); - - Mock::AllowLeak(defaultMQProducer); -} - -TEST(cProducer, null) { - EXPECT_TRUE(CreateProducer(NULL) == NULL); - EXPECT_EQ(StartProducer(NULL), NULL_POINTER); - EXPECT_EQ(ShutdownProducer(NULL), NULL_POINTER); - EXPECT_EQ(SetProducerNameServerAddress(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetProducerNameServerDomain(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetProducerGroupName(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetProducerInstanceName(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetProducerSessionCredentials(NULL, NULL, NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetProducerLogLevel(NULL, E_LOG_LEVEL_FATAL), NULL_POINTER); - EXPECT_EQ(SetProducerSendMsgTimeout(NULL, 1), NULL_POINTER); - EXPECT_EQ(SetProducerCompressLevel(NULL, 1), NULL_POINTER); - EXPECT_EQ(SetProducerMaxMessageSize(NULL, 2), NULL_POINTER); - EXPECT_EQ(SetProducerLogLevel(NULL, E_LOG_LEVEL_FATAL), NULL_POINTER); - EXPECT_EQ(DestroyProducer(NULL), NULL_POINTER); -} -TEST(cProducer, version) { - CProducer* cProducer = CreateProducer("groupTestVersion"); - EXPECT_TRUE(cProducer != NULL); - string version(ShowProducerVersion(cProducer)); - EXPECT_GT(version.length(), 0); - CProducer* cProducer2 = CreateOrderlyProducer("orderGroupTestVersion"); - EXPECT_TRUE(cProducer2 != NULL); - string version2(ShowProducerVersion(cProducer2)); - EXPECT_GT(version2.length(), 0); - - CProducer* cProducer3 = CreateTransactionProducer("tranGroupTestVersion", NULL, NULL); - EXPECT_TRUE(cProducer3 != NULL); - string version3(ShowProducerVersion(cProducer3)); - EXPECT_GT(version3.length(), 0); -} -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "cProducer.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/extern/CPullConsumerTest.cpp b/test/src/extern/CPullConsumerTest.cpp deleted file mode 100644 index a5459d9c3..000000000 --- a/test/src/extern/CPullConsumerTest.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "CCommon.h" -#include "CPullConsumer.h" - -#include "DefaultMQPullConsumer.h" -#include "MQClient.h" -#include "MQMessageExt.h" -#include "MQMessageQueue.h" -#include "PullResult.h" -#include "SessionCredentials.h" - -using std::string; -using std::vector; - -using ::testing::_; -using testing::Expectation; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; -using testing::SetArgReferee; - -using rocketmq::DefaultMQPullConsumer; -using rocketmq::elogLevel; -using rocketmq::MessageModel; -using rocketmq::MQMessageExt; -using rocketmq::MQMessageQueue; -using rocketmq::PullResult; -using rocketmq::PullStatus; -using rocketmq::SessionCredentials; - -class MockDefaultMQPullConsumer : public DefaultMQPullConsumer { - public: - MockDefaultMQPullConsumer(const string& groupname) : DefaultMQPullConsumer(groupname) {} - MOCK_METHOD0(start, void()); - MOCK_METHOD0(shutdown, void()); - MOCK_METHOD2(setLogFileSizeAndNum, void(int, long)); - MOCK_METHOD1(setLogLevel, void(elogLevel)); - MOCK_METHOD2(fetchSubscribeMessageQueues, void(const string&, vector&)); - MOCK_METHOD4(pull, PullResult(const MQMessageQueue&, const string&, int64, int)); -}; - -TEST(cpullConsumer, pull) { - MockDefaultMQPullConsumer* mqPullConsumer = new MockDefaultMQPullConsumer("groudId"); - CPullConsumer* pullConsumer = (CPullConsumer*)mqPullConsumer; - - CMessageQueue cMessageQueue; - strncpy(cMessageQueue.topic, "testTopic", 8); - strncpy(cMessageQueue.brokerName, "testBroker", 9); - cMessageQueue.queueId = 1; - - PullResult timeOutPullResult(PullStatus::BROKER_TIMEOUT, 1, 2, 3); - - PullResult noNewMsgPullResult(PullStatus::NO_NEW_MSG, 1, 2, 3); - - PullResult noMatchedMsgPullResult(PullStatus::NO_MATCHED_MSG, 1, 2, 3); - - PullResult offsetIllegalPullResult(PullStatus::OFFSET_ILLEGAL, 1, 2, 3); - - PullResult defaultPullResult((PullStatus)-1, 1, 2, 3); - - vector src; - for (int i = 0; i < 5; i++) { - MQMessageExt ext; - src.push_back(ext); - } - - PullResult foundPullResult(PullStatus::FOUND, 1, 2, 3, src); - EXPECT_CALL(*mqPullConsumer, pull(_, _, _, _)) - .WillOnce(Return(timeOutPullResult)) - .WillOnce(Return(noNewMsgPullResult)) - .WillOnce(Return(noMatchedMsgPullResult)) - .WillOnce(Return(offsetIllegalPullResult)) - .WillOnce(Return(defaultPullResult)) - //.WillOnce(Return(timeOutPullResult)) //will not called - .WillOnce(Return(foundPullResult)); - CPullResult timeOutcPullResult = Pull(pullConsumer, &cMessageQueue, "123123", 0, 0); - EXPECT_EQ(timeOutcPullResult.pullStatus, E_BROKER_TIMEOUT); - CPullResult noNewMsgcPullResult = Pull(pullConsumer, &cMessageQueue, "123123", 0, 0); - EXPECT_EQ(noNewMsgcPullResult.pullStatus, E_NO_NEW_MSG); - - CPullResult noMatchedMsgcPullResult = Pull(pullConsumer, &cMessageQueue, "123123", 0, 0); - EXPECT_EQ(noMatchedMsgcPullResult.pullStatus, E_NO_MATCHED_MSG); - - CPullResult offsetIllegalcPullResult = Pull(pullConsumer, &cMessageQueue, "123123", 0, 0); - EXPECT_EQ(offsetIllegalcPullResult.pullStatus, E_OFFSET_ILLEGAL); - - CPullResult defaultcPullResult = Pull(pullConsumer, &cMessageQueue, "123123", 0, 0); - EXPECT_EQ(defaultcPullResult.pullStatus, E_NO_NEW_MSG); - CPullResult exceptionPullResult = Pull(pullConsumer, &cMessageQueue, NULL, 0, 0); - EXPECT_EQ(exceptionPullResult.pullStatus, E_BROKER_TIMEOUT); - CPullResult foundcPullResult = Pull(pullConsumer, &cMessageQueue, "123123", 0, 0); - EXPECT_EQ(foundcPullResult.pullStatus, E_FOUND); - - delete mqPullConsumer; -} - -TEST(cpullConsumer, infoMock) { - MockDefaultMQPullConsumer* mqPullConsumer = new MockDefaultMQPullConsumer("groudId"); - CPullConsumer* pullConsumer = (CPullConsumer*)mqPullConsumer; - - Expectation exp = EXPECT_CALL(*mqPullConsumer, start()).Times(1); - EXPECT_EQ(StartPullConsumer(pullConsumer), OK); - - EXPECT_CALL(*mqPullConsumer, shutdown()).Times(1); - EXPECT_EQ(ShutdownPullConsumer(pullConsumer), OK); - - // EXPECT_CALL(*mqPullConsumer,setLogFileSizeAndNum(_,_)).Times(1); - EXPECT_EQ(SetPullConsumerLogFileNumAndSize(pullConsumer, 1, 2), OK); - - // EXPECT_CALL(*mqPullConsumer,setLogLevel(_)).Times(1); - EXPECT_EQ(SetPullConsumerLogLevel(pullConsumer, E_LOG_LEVEL_INFO), OK); - - std::vector fullMQ; - for (int i = 0; i < 5; i++) { - MQMessageQueue queue("testTopic", "testsBroker", i); - fullMQ.push_back(queue); - } - - EXPECT_CALL(*mqPullConsumer, fetchSubscribeMessageQueues(_, _)).Times(1).WillOnce(SetArgReferee<1>(fullMQ)); - CMessageQueue* mqs = NULL; - int size = 0; - FetchSubscriptionMessageQueues(pullConsumer, "testTopic", &mqs, &size); - EXPECT_EQ(size, 5); - - delete mqPullConsumer; -} - -TEST(cpullConsumer, init) { - CPullConsumer* pullConsumer = CreatePullConsumer("testGroupId"); - DefaultMQPullConsumer* defaultMQPullConsumer = (DefaultMQPullConsumer*)pullConsumer; - EXPECT_FALSE(pullConsumer == NULL); - - EXPECT_EQ(SetPullConsumerGroupID(pullConsumer, "groupId"), OK); - EXPECT_EQ(GetPullConsumerGroupID(pullConsumer), defaultMQPullConsumer->getGroupName().c_str()); - - EXPECT_EQ(SetPullConsumerNameServerAddress(pullConsumer, "127.0.0.1:10091"), OK); - EXPECT_EQ(defaultMQPullConsumer->getNamesrvAddr(), "127.0.0.1:10091"); - - EXPECT_EQ(SetPullConsumerNameServerDomain(pullConsumer, "domain"), OK); - EXPECT_EQ(defaultMQPullConsumer->getNamesrvDomain(), "domain"); - - EXPECT_EQ(SetPullConsumerSessionCredentials(pullConsumer, "accessKey", "secretKey", "channel"), OK); - SessionCredentials sessionCredentials = defaultMQPullConsumer->getSessionCredentials(); - EXPECT_EQ(sessionCredentials.getAccessKey(), "accessKey"); - - EXPECT_EQ(SetPullConsumerLogPath(pullConsumer, ""), OK); - - // EXPECT_EQ(SetPullConsumerLogFileNumAndSize(pullConsumer,NULL,NULL),NULL_POINTER); - EXPECT_EQ(SetPullConsumerLogLevel(pullConsumer, E_LOG_LEVEL_DEBUG), OK); -} - -TEST(cpullConsumer, null) { - CPullConsumer* pullConsumer = CreatePullConsumer("testGroupId"); - DefaultMQPullConsumer* defaultMQPullConsumer = (DefaultMQPullConsumer*)pullConsumer; - EXPECT_FALSE(pullConsumer == NULL); - - EXPECT_EQ(SetPullConsumerGroupID(pullConsumer, "groupId"), OK); - EXPECT_EQ(GetPullConsumerGroupID(pullConsumer), defaultMQPullConsumer->getGroupName().c_str()); - - EXPECT_EQ(SetPullConsumerNameServerAddress(pullConsumer, "127.0.0.1:10091"), OK); - EXPECT_EQ(defaultMQPullConsumer->getNamesrvAddr(), "127.0.0.1:10091"); - - EXPECT_EQ(SetPullConsumerNameServerDomain(pullConsumer, "domain"), OK); - EXPECT_EQ(defaultMQPullConsumer->getNamesrvDomain(), "domain"); - - EXPECT_EQ(SetPullConsumerSessionCredentials(pullConsumer, "accessKey", "secretKey", "channel"), OK); - SessionCredentials sessionCredentials = defaultMQPullConsumer->getSessionCredentials(); - EXPECT_EQ(sessionCredentials.getAccessKey(), "accessKey"); - - EXPECT_EQ(SetPullConsumerLogPath(pullConsumer, ""), OK); - // EXPECT_EQ(SetPullConsumerLogFileNumAndSize(pullConsumer,NULL,NULL),NULL_POINTER); - EXPECT_EQ(SetPullConsumerLogLevel(pullConsumer, E_LOG_LEVEL_DEBUG), OK); - EXPECT_EQ(DestroyPullConsumer(pullConsumer), OK); - EXPECT_EQ(StartPullConsumer(NULL), NULL_POINTER); - EXPECT_EQ(ShutdownPullConsumer(NULL), NULL_POINTER); -} -TEST(cpullConsumer, version) { - CPullConsumer* pullConsumer = CreatePullConsumer("groupTestVersion"); - EXPECT_TRUE(pullConsumer != NULL); - string version(ShowPullConsumerVersion(pullConsumer)); - EXPECT_GT(version.length(), 0); -} -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "cpullConsumer.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/extern/CPushConsumerTest.cpp b/test/src/extern/CPushConsumerTest.cpp deleted file mode 100644 index 6b516ab84..000000000 --- a/test/src/extern/CPushConsumerTest.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "string.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "CPushConsumer.h" - -#include "ConsumeType.h" -#include "DefaultMQPushConsumer.h" -#include "MQMessageListener.h" -#include "SessionCredentials.h" - -using std::string; - -using ::testing::_; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Mock; -using testing::Return; - -using rocketmq::DefaultMQPushConsumer; -using rocketmq::elogLevel; -using rocketmq::MessageListenerType; -using rocketmq::MessageModel; -using rocketmq::SessionCredentials; - -class MockDefaultMQPushConsumer : public DefaultMQPushConsumer { - public: - MockDefaultMQPushConsumer(const string& groupname) : DefaultMQPushConsumer(groupname) {} - - MOCK_METHOD0(start, void()); - MOCK_METHOD0(shutdown, void()); - MOCK_METHOD2(setLogFileSizeAndNum, void(int, long)); - MOCK_METHOD1(setLogLevel, void(elogLevel)); -}; - -TEST(cPushComsumer, infomock) { - MockDefaultMQPushConsumer* pushComsumer = new MockDefaultMQPushConsumer("testGroup"); - CPushConsumer* consumer = (CPushConsumer*)pushComsumer; - - EXPECT_CALL(*pushComsumer, start()).Times(1); - EXPECT_EQ(StartPushConsumer(consumer), OK); - - EXPECT_CALL(*pushComsumer, shutdown()).Times(1); - EXPECT_EQ(ShutdownPushConsumer(consumer), OK); - - EXPECT_CALL(*pushComsumer, setLogFileSizeAndNum(1, 1)).Times(1); - pushComsumer->setLogFileSizeAndNum(1, 1); - EXPECT_EQ(SetPushConsumerLogFileNumAndSize(consumer, 1, 1), OK); - - // EXPECT_CALL(*pushComsumer,setLogLevel(_)).Times(1); - EXPECT_EQ(SetPushConsumerLogLevel(consumer, E_LOG_LEVEL_FATAL), OK); - - Mock::AllowLeak(pushComsumer); -} - -int MessageCallBackFunc(CPushConsumer* consumer, CMessageExt* msg) { - return 0; -} - -TEST(cPushComsumer, info) { - CPushConsumer* cpushConsumer = CreatePushConsumer("testGroup"); - DefaultMQPushConsumer* mqPushConsumer = (DefaultMQPushConsumer*)cpushConsumer; - - EXPECT_TRUE(cpushConsumer != NULL); - EXPECT_EQ(string(GetPushConsumerGroupID(cpushConsumer)), "testGroup"); - - EXPECT_EQ(SetPushConsumerGroupID(cpushConsumer, "testGroupTwo"), OK); - EXPECT_EQ(string(GetPushConsumerGroupID(cpushConsumer)), "testGroupTwo"); - - EXPECT_EQ(SetPushConsumerNameServerAddress(cpushConsumer, "127.0.0.1:9876"), OK); - EXPECT_EQ(mqPushConsumer->getNamesrvAddr(), "127.0.0.1:9876"); - - EXPECT_EQ(SetPushConsumerNameServerDomain(cpushConsumer, "domain"), OK); - EXPECT_EQ(mqPushConsumer->getNamesrvDomain(), "domain"); - - EXPECT_EQ(Subscribe(cpushConsumer, "testTopic", "testSub"), OK); - - EXPECT_EQ(RegisterMessageCallbackOrderly(cpushConsumer, MessageCallBackFunc), OK); - EXPECT_EQ(mqPushConsumer->getMessageListenerType(), MessageListenerType::messageListenerOrderly); - - EXPECT_EQ(RegisterMessageCallback(cpushConsumer, MessageCallBackFunc), OK); - EXPECT_EQ(mqPushConsumer->getMessageListenerType(), MessageListenerType::messageListenerConcurrently); - - EXPECT_EQ(UnregisterMessageCallbackOrderly(cpushConsumer), OK); - EXPECT_EQ(UnregisterMessageCallback(cpushConsumer), OK); - - EXPECT_EQ(SetPushConsumerThreadCount(cpushConsumer, 10), OK); - EXPECT_EQ(mqPushConsumer->getConsumeThreadCount(), 10); - - EXPECT_EQ(SetPushConsumerMessageBatchMaxSize(cpushConsumer, 1024), OK); - EXPECT_EQ(mqPushConsumer->getConsumeMessageBatchMaxSize(), 1024); - - EXPECT_EQ(SetPushConsumerInstanceName(cpushConsumer, "instance"), OK); - EXPECT_EQ(mqPushConsumer->getInstanceName(), "instance"); - - EXPECT_EQ(SetPushConsumerSessionCredentials(cpushConsumer, "accessKey", "secretKey", "channel"), OK); - SessionCredentials sessionCredentials = mqPushConsumer->getSessionCredentials(); - EXPECT_EQ(sessionCredentials.getAccessKey(), "accessKey"); - - EXPECT_EQ(SetPushConsumerMessageModel(cpushConsumer, BROADCASTING), OK); - EXPECT_EQ(mqPushConsumer->getMessageModel(), MessageModel::BROADCASTING); - - EXPECT_EQ(SetPushConsumerMessageTrace(cpushConsumer, CLOSE), OK); - EXPECT_EQ(mqPushConsumer->getMessageTrace(), false); - Mock::AllowLeak(mqPushConsumer); -} - -TEST(cPushComsumer, null) { - CPushConsumer* cpushConsumer = CreatePushConsumer("testGroup"); - - EXPECT_TRUE(CreatePushConsumer(NULL) == NULL); - EXPECT_EQ(DestroyPushConsumer(NULL), NULL_POINTER); - EXPECT_EQ(StartPushConsumer(NULL), NULL_POINTER); - EXPECT_EQ(ShutdownPushConsumer(NULL), NULL_POINTER); - EXPECT_EQ(SetPushConsumerGroupID(NULL, "testGroup"), NULL_POINTER); - EXPECT_TRUE(GetPushConsumerGroupID(NULL) == NULL); - EXPECT_EQ(SetPushConsumerNameServerAddress(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetPushConsumerNameServerDomain(NULL, NULL), NULL_POINTER); - EXPECT_EQ(Subscribe(NULL, NULL, NULL), NULL_POINTER); - EXPECT_EQ(RegisterMessageCallbackOrderly(NULL, NULL), NULL_POINTER); - EXPECT_EQ(RegisterMessageCallbackOrderly(cpushConsumer, NULL), NULL_POINTER); - EXPECT_EQ(RegisterMessageCallback(NULL, NULL), NULL_POINTER); - EXPECT_EQ(UnregisterMessageCallbackOrderly(NULL), NULL_POINTER); - EXPECT_EQ(UnregisterMessageCallback(NULL), NULL_POINTER); - EXPECT_EQ(SetPushConsumerThreadCount(NULL, 0), NULL_POINTER); - EXPECT_EQ(SetPushConsumerMessageBatchMaxSize(NULL, 0), NULL_POINTER); - EXPECT_EQ(SetPushConsumerInstanceName(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetPushConsumerSessionCredentials(NULL, NULL, NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetPushConsumerLogPath(NULL, NULL), NULL_POINTER); - EXPECT_EQ(SetPushConsumerLogFileNumAndSize(NULL, 1, 1), NULL_POINTER); - EXPECT_EQ(SetPushConsumerLogLevel(NULL, E_LOG_LEVEL_LEVEL_NUM), NULL_POINTER); - EXPECT_EQ(SetPushConsumerMessageModel(NULL, BROADCASTING), NULL_POINTER); -} -TEST(cPushComsumer, version) { - CPushConsumer* pushConsumer = CreatePushConsumer("groupTestVersion"); - EXPECT_TRUE(pushConsumer != NULL); - string version(ShowPushConsumerVersion(pushConsumer)); - EXPECT_GT(version.length(), 0); -} -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(filter) = "cPushComsumer.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/message/BatchMessageTest.cpp b/test/src/message/BatchMessageTest.cpp deleted file mode 100644 index 789e09bfe..000000000 --- a/test/src/message/BatchMessageTest.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include -#include -#include -#include "BatchMessage.h" -#include "MQMessage.h" -#include - -using namespace std; -using namespace rocketmq; -using ::testing::InitGoogleTest; -using ::testing::InitGoogleMock; -using testing::Return; - -TEST(BatchMessageEncodeTest, encodeMQMessage) { - MQMessage msg1("topic", "*", "test"); - // const map& properties = msg1.getProperties(); - // for (auto& pair : properties) { - // std::cout << pair.first << " : " << pair.second << std::endl; - //} - - EXPECT_EQ(msg1.getProperties().size(), 2); - EXPECT_EQ(msg1.getBody().size(), 4); - // 20 + bodyLen + 2 + propertiesLength; - string encodeMessage = BatchMessage::encode(msg1); - EXPECT_EQ(encodeMessage.size(), 43); - - msg1.setProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, "1"); - encodeMessage = BatchMessage::encode(msg1); - EXPECT_EQ(encodeMessage.size(), 54); -} - -TEST(BatchMessageEncodeTest, encodeMQMessages) { - std::vector msgs; - MQMessage msg1("topic", "*", "test1"); - // const map& properties = msg1.getProperties(); - // for (auto& pair : properties) { - // std::cout << pair.first << " : " << pair.second << std::endl; - //} - msgs.push_back(msg1); - // 20 + bodyLen + 2 + propertiesLength; - string encodeMessage = BatchMessage::encode(msgs); - EXPECT_EQ(encodeMessage.size(), 86); - MQMessage msg2("topic", "*", "test2"); - MQMessage msg3("topic", "*", "test3"); - msgs.push_back(msg2); - msgs.push_back(msg3); - encodeMessage = BatchMessage::encode(msgs); - EXPECT_EQ(encodeMessage.size(), 258); // 86*3 -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/test/src/message/MQDecoderTest.cpp b/test/src/message/MQDecoderTest.cpp deleted file mode 100644 index a92ba10fb..000000000 --- a/test/src/message/MQDecoderTest.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "MemoryInputStream.h" -#include "MemoryOutputStream.h" - -#include "CommandHeader.h" -#include "MQDecoder.h" -#include "MQMessage.h" -#include "MQMessageExt.h" -#include "MQMessageId.h" -#include "MessageSysFlag.h" -#include "RemotingCommand.h" -#include "UtilAll.h" - -using namespace std; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MemoryBlock; -using rocketmq::MemoryInputStream; -using rocketmq::MemoryOutputStream; -using rocketmq::MessageSysFlag; -using rocketmq::MQDecoder; -using rocketmq::MQMessage; -using rocketmq::MQMessageExt; -using rocketmq::MQMessageId; -using rocketmq::RemotingCommand; -using rocketmq::SendMessageRequestHeader; -using rocketmq::UtilAll; - -// TODO -TEST(decoder, messageId) { - int host; - int port; - int64 offset = 1234567890; - string msgIdStr = - MQDecoder::createMessageId(rocketmq::IPPort2socketAddress(ntohl(inet_addr("127.0.0.1")), 10091), offset); - MQMessageId msgId = MQDecoder::decodeMessageId(msgIdStr); - - EXPECT_EQ(msgId.getOffset(), offset); - - rocketmq::socketAddress2IPPort(msgId.getAddress(), host, port); - EXPECT_EQ(host, ntohl(inet_addr("127.0.0.1"))); - EXPECT_EQ(port, 10091); -} - -TEST(decoder, decoder) { - MQMessageExt mext; - MemoryOutputStream* memoryOut = new MemoryOutputStream(1024); - - // 1 TOTALSIZE 4 - memoryOut->writeIntBigEndian(107); - mext.setStoreSize(107); - - // 2 MAGICCODE sizeof(int) 8=4+4 - memoryOut->writeIntBigEndian(14); - - // 3 BODYCRC 12=8+4 - memoryOut->writeIntBigEndian(24); - mext.setBodyCRC(24); - // 4 QUEUEID 16=12+4 - memoryOut->writeIntBigEndian(4); - mext.setQueueId(4); - // 5 FLAG 20=16+4 - memoryOut->writeIntBigEndian(4); - mext.setFlag(4); - // 6 QUEUEOFFSET 28 = 20+8 - memoryOut->writeInt64BigEndian((int64)1024); - mext.setQueueOffset(1024); - // 7 PHYSICALOFFSET 36=28+8 - memoryOut->writeInt64BigEndian((int64)2048); - mext.setCommitLogOffset(2048); - // 8 SYSFLAG 40=36+4 - memoryOut->writeIntBigEndian(0); - mext.setSysFlag(0); - // 9 BORNTIMESTAMP 48 = 40+8 - memoryOut->writeInt64BigEndian((int64)4096); - mext.setBornTimestamp(4096); - // 10 BORNHOST 56= 48+8 - memoryOut->writeIntBigEndian(ntohl(inet_addr("127.0.0.1"))); - memoryOut->writeIntBigEndian(10091); - mext.setBornHost(rocketmq::IPPort2socketAddress(ntohl(inet_addr("127.0.0.1")), 10091)); - // 11 STORETIMESTAMP 64 =56+8 - memoryOut->writeInt64BigEndian((int64)4096); - mext.setStoreTimestamp(4096); - // 12 STOREHOST 72 = 64+8 - memoryOut->writeIntBigEndian(ntohl(inet_addr("127.0.0.2"))); - memoryOut->writeIntBigEndian(10092); - mext.setStoreHost(rocketmq::IPPort2socketAddress(ntohl(inet_addr("127.0.0.2")), 10092)); - // 13 RECONSUMETIMES 76 = 72+4 - mext.setReconsumeTimes(111111); - memoryOut->writeIntBigEndian(mext.getReconsumeTimes()); - // 14 Prepared Transaction Offset 84 = 76+8 - memoryOut->writeInt64BigEndian((int64)12); - mext.setPreparedTransactionOffset(12); - // 15 BODY 88 = 84+4 10 - string* body = new string("1234567890"); - mext.setBody(body->c_str()); - memoryOut->writeIntBigEndian(10); - memoryOut->write(body->c_str(), body->size()); - - // 16 TOPIC - memoryOut->writeByte(10); - memoryOut->write(body->c_str(), body->size()); - mext.setTopic(body->c_str()); - - // 17 PROPERTIES - memoryOut->writeShortBigEndian(0); - - mext.setMsgId(MQDecoder::createMessageId(mext.getStoreHost(), (int64)mext.getCommitLogOffset())); - - vector mqvec; - MemoryBlock block = memoryOut->getMemoryBlock(); - MQDecoder::decodes(&block, mqvec); - EXPECT_EQ(mqvec.size(), 1); - std::cout << mext.toString() << "\n"; - std::cout << mqvec[0].toString() << "\n"; - EXPECT_EQ(mqvec[0].toString(), mext.toString()); - - mqvec.clear(); - MQDecoder::decodes(&block, mqvec, false); - EXPECT_FALSE(mqvec[0].getBody().size()); - - //=============================================================== - // 8 SYSFLAG 40=36+4 - mext.setSysFlag(0 | MessageSysFlag::CompressedFlag); - memoryOut->setPosition(36); - memoryOut->writeIntBigEndian(mext.getSysFlag()); - - // 15 Body 84 - string outBody; - string boody("123123123"); - UtilAll::deflate(boody, outBody, 5); - mext.setBody(outBody); - - memoryOut->setPosition(84); - memoryOut->writeIntBigEndian(outBody.size()); - memoryOut->write(outBody.c_str(), outBody.size()); - - // 16 TOPIC - memoryOut->writeByte(10); - memoryOut->write(body->c_str(), body->size()); - mext.setTopic(body->c_str()); - - // 17 PROPERTIES - map properties; - properties["RocketMQ"] = "cpp-client"; - properties[MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX] = "123456"; - mext.setProperties(properties); - mext.setMsgId("123456"); - - string proString = MQDecoder::messageProperties2String(properties); - - memoryOut->writeShortBigEndian(proString.size()); - memoryOut->write(proString.c_str(), proString.size()); - - mext.setStoreSize(memoryOut->getDataSize()); - memoryOut->setPosition(0); - memoryOut->writeIntBigEndian(mext.getStoreSize()); - - block = memoryOut->getMemoryBlock(); - MQDecoder::decodes(&block, mqvec); - EXPECT_EQ(mqvec[0].toString(), mext.toString()); -} - -TEST(decoder, messagePropertiesAndToString) { - map properties; - properties["RocketMQ"] = "cpp-client"; - string proString = MQDecoder::messageProperties2String(properties); - - map newProperties; - MQDecoder::string2messageProperties(proString, newProperties); - EXPECT_EQ(properties, newProperties); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - - testing::GTEST_FLAG(filter) = "decoder.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/message/MQMessageExtTest.cpp b/test/src/message/MQMessageExtTest.cpp deleted file mode 100644 index a1f989afa..000000000 --- a/test/src/message/MQMessageExtTest.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "MQMessageExt.h" -#include "MessageSysFlag.h" -#include "SocketUtil.h" -#include "TopicFilterType.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MessageSysFlag; -using rocketmq::MQMessageExt; -using rocketmq::TopicFilterType; - -TEST(messageExt, init) { - MQMessageExt messageExt; - EXPECT_EQ(messageExt.getQueueOffset(), 0); - EXPECT_EQ(messageExt.getCommitLogOffset(), 0); - EXPECT_EQ(messageExt.getBornTimestamp(), 0); - EXPECT_EQ(messageExt.getStoreTimestamp(), 0); - EXPECT_EQ(messageExt.getPreparedTransactionOffset(), 0); - EXPECT_EQ(messageExt.getQueueId(), 0); - EXPECT_EQ(messageExt.getStoreSize(), 0); - EXPECT_EQ(messageExt.getReconsumeTimes(), 3); - EXPECT_EQ(messageExt.getBodyCRC(), 0); - EXPECT_EQ(messageExt.getMsgId(), ""); - EXPECT_EQ(messageExt.getOffsetMsgId(), ""); - - messageExt.setQueueOffset(1); - EXPECT_EQ(messageExt.getQueueOffset(), 1); - - messageExt.setCommitLogOffset(1024); - EXPECT_EQ(messageExt.getCommitLogOffset(), 1024); - - messageExt.setBornTimestamp(1024); - EXPECT_EQ(messageExt.getBornTimestamp(), 1024); - - messageExt.setStoreTimestamp(2048); - EXPECT_EQ(messageExt.getStoreTimestamp(), 2048); - - messageExt.setPreparedTransactionOffset(4096); - EXPECT_EQ(messageExt.getPreparedTransactionOffset(), 4096); - - messageExt.setQueueId(2); - EXPECT_EQ(messageExt.getQueueId(), 2); - - messageExt.setStoreSize(12); - EXPECT_EQ(messageExt.getStoreSize(), 12); - - messageExt.setReconsumeTimes(48); - EXPECT_EQ(messageExt.getReconsumeTimes(), 48); - - messageExt.setBodyCRC(32); - EXPECT_EQ(messageExt.getBodyCRC(), 32); - - messageExt.setMsgId("MsgId"); - EXPECT_EQ(messageExt.getMsgId(), "MsgId"); - - messageExt.setOffsetMsgId("offsetMsgId"); - EXPECT_EQ(messageExt.getOffsetMsgId(), "offsetMsgId"); - - messageExt.setBornTimestamp(1111); - EXPECT_EQ(messageExt.getBornTimestamp(), 1111); - - messageExt.setStoreTimestamp(2222); - EXPECT_EQ(messageExt.getStoreTimestamp(), 2222); - - struct sockaddr_in sa; - sa.sin_family = AF_INET; - sa.sin_port = htons(10091); - sa.sin_addr.s_addr = inet_addr("127.0.0.1"); - - sockaddr bornHost; - memcpy(&bornHost, &sa, sizeof(sockaddr)); - - messageExt.setBornHost(bornHost); - EXPECT_EQ(messageExt.getBornHostNameString(), rocketmq::getHostName(bornHost)); - EXPECT_EQ(messageExt.getBornHostString(), rocketmq::socketAddress2String(bornHost)); - - struct sockaddr_in storeSa; - storeSa.sin_family = AF_INET; - storeSa.sin_port = htons(10092); - storeSa.sin_addr.s_addr = inet_addr("127.0.0.2"); - - sockaddr storeHost; - memcpy(&storeHost, &storeSa, sizeof(sockaddr)); - messageExt.setStoreHost(storeHost); - EXPECT_EQ(messageExt.getStoreHostString(), rocketmq::socketAddress2String(storeHost)); - - MQMessageExt twoMessageExt(2, 1024, bornHost, 2048, storeHost, "msgId"); - EXPECT_EQ(twoMessageExt.getQueueOffset(), 0); - EXPECT_EQ(twoMessageExt.getCommitLogOffset(), 0); - EXPECT_EQ(twoMessageExt.getBornTimestamp(), 1024); - EXPECT_EQ(twoMessageExt.getStoreTimestamp(), 2048); - EXPECT_EQ(twoMessageExt.getPreparedTransactionOffset(), 0); - EXPECT_EQ(twoMessageExt.getQueueId(), 2); - EXPECT_EQ(twoMessageExt.getStoreSize(), 0); - EXPECT_EQ(twoMessageExt.getReconsumeTimes(), 3); - EXPECT_EQ(twoMessageExt.getBodyCRC(), 0); - EXPECT_EQ(twoMessageExt.getMsgId(), "msgId"); - EXPECT_EQ(twoMessageExt.getOffsetMsgId(), ""); - - EXPECT_EQ(twoMessageExt.getBornHostNameString(), rocketmq::getHostName(bornHost)); - EXPECT_EQ(twoMessageExt.getBornHostString(), rocketmq::socketAddress2String(bornHost)); - - EXPECT_EQ(twoMessageExt.getStoreHostString(), rocketmq::socketAddress2String(storeHost)); - - EXPECT_EQ(MQMessageExt::parseTopicFilterType(MessageSysFlag::MultiTagsFlag), TopicFilterType::MULTI_TAG); - - EXPECT_EQ(MQMessageExt::parseTopicFilterType(0), TopicFilterType::SINGLE_TAG); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - - testing::GTEST_FLAG(filter) = "messageExt.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/message/MQMessageIdTest.cpp b/test/src/message/MQMessageIdTest.cpp deleted file mode 100644 index 6e97480f8..000000000 --- a/test/src/message/MQMessageIdTest.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include "MQMessageId.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -using namespace std; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MQMessageId; - -TEST(messageId, id) { - int host; - int port; - sockaddr addr = rocketmq::IPPort2socketAddress(inet_addr("127.0.0.1"), 10091); - MQMessageId id(addr, 1024); - - rocketmq::socketAddress2IPPort(id.getAddress(), host, port); - EXPECT_EQ(host, inet_addr("127.0.0.1")); - EXPECT_EQ(port, 10091); - EXPECT_EQ(id.getOffset(), 1024); - - id.setAddress(rocketmq::IPPort2socketAddress(inet_addr("127.0.0.2"), 10092)); - id.setOffset(2048); - - rocketmq::socketAddress2IPPort(id.getAddress(), host, port); - EXPECT_EQ(host, inet_addr("127.0.0.2")); - EXPECT_EQ(port, 10092); - EXPECT_EQ(id.getOffset(), 2048); - - MQMessageId id2 = id; - EXPECT_EQ(id2.getOffset(), 2048); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - - testing::GTEST_FLAG(filter) = "messageId.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/message/MQMessageQueueTest.cpp b/test/src/message/MQMessageQueueTest.cpp deleted file mode 100644 index c1c542dd7..000000000 --- a/test/src/message/MQMessageQueueTest.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "MQMessageQueue.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MQMessageQueue; - -TEST(messageQueue, init) { - MQMessageQueue messageQueue; - EXPECT_EQ(messageQueue.getBrokerName(), ""); - EXPECT_EQ(messageQueue.getTopic(), ""); - EXPECT_EQ(messageQueue.getQueueId(), -1); - - MQMessageQueue twoMessageQueue("testTopic", "testBroker", 1); - EXPECT_EQ(twoMessageQueue.getBrokerName(), "testBroker"); - EXPECT_EQ(twoMessageQueue.getTopic(), "testTopic"); - EXPECT_EQ(twoMessageQueue.getQueueId(), 1); - - MQMessageQueue threeMessageQueue("threeTestTopic", "threeTestBroker", 2); - MQMessageQueue frouMessageQueue(threeMessageQueue); - EXPECT_EQ(frouMessageQueue.getBrokerName(), "threeTestBroker"); - EXPECT_EQ(frouMessageQueue.getTopic(), "threeTestTopic"); - EXPECT_EQ(frouMessageQueue.getQueueId(), 2); - - frouMessageQueue = twoMessageQueue; - EXPECT_EQ(frouMessageQueue.getBrokerName(), "testBroker"); - EXPECT_EQ(frouMessageQueue.getTopic(), "testTopic"); - EXPECT_EQ(frouMessageQueue.getQueueId(), 1); - - frouMessageQueue.setBrokerName("frouTestBroker"); - frouMessageQueue.setTopic("frouTestTopic"); - frouMessageQueue.setQueueId(4); - EXPECT_EQ(frouMessageQueue.getBrokerName(), "frouTestBroker"); - EXPECT_EQ(frouMessageQueue.getTopic(), "frouTestTopic"); - EXPECT_EQ(frouMessageQueue.getQueueId(), 4); -} - -TEST(messageQueue, operators) { - MQMessageQueue messageQueue; - EXPECT_EQ(messageQueue, messageQueue); - EXPECT_EQ(messageQueue.compareTo(messageQueue), 0); - - MQMessageQueue twoMessageQueue; - EXPECT_EQ(messageQueue, twoMessageQueue); - EXPECT_EQ(messageQueue.compareTo(twoMessageQueue), 0); - - twoMessageQueue.setTopic("testTopic"); - EXPECT_FALSE(messageQueue == twoMessageQueue); - EXPECT_FALSE(messageQueue.compareTo(twoMessageQueue) == 0); - - twoMessageQueue.setQueueId(1); - EXPECT_FALSE(messageQueue == twoMessageQueue); - EXPECT_FALSE(messageQueue.compareTo(twoMessageQueue) == 0); - - twoMessageQueue.setBrokerName("testBroker"); - EXPECT_FALSE(messageQueue == twoMessageQueue); - EXPECT_FALSE(messageQueue.compareTo(twoMessageQueue) == 0); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - - testing::GTEST_FLAG(filter) = "messageQueue.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/message/MQMessageTest.cpp b/test/src/message/MQMessageTest.cpp deleted file mode 100644 index 0f55cbb29..000000000 --- a/test/src/message/MQMessageTest.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include -#include - -#include "MQMessage.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -using namespace std; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MQMessage; - -TEST(message, Init) { - MQMessage messageOne; - EXPECT_EQ(messageOne.getTopic(), ""); - EXPECT_EQ(messageOne.getBody(), ""); - EXPECT_EQ(messageOne.getTags(), ""); - EXPECT_EQ(messageOne.getFlag(), 0); - - MQMessage messageTwo("test", "testBody"); - EXPECT_EQ(messageTwo.getTopic(), "test"); - EXPECT_EQ(messageTwo.getBody(), "testBody"); - EXPECT_EQ(messageTwo.getTags(), ""); - EXPECT_EQ(messageTwo.getFlag(), 0); - - MQMessage messageThree("test", "tagTest", "testBody"); - EXPECT_EQ(messageThree.getTopic(), "test"); - EXPECT_EQ(messageThree.getBody(), "testBody"); - EXPECT_EQ(messageThree.getTags(), "tagTest"); - EXPECT_EQ(messageThree.getFlag(), 0); - - MQMessage messageFour("test", "tagTest", "testKey", "testBody"); - EXPECT_EQ(messageFour.getTopic(), "test"); - EXPECT_EQ(messageFour.getBody(), "testBody"); - EXPECT_EQ(messageFour.getTags(), "tagTest"); - EXPECT_EQ(messageFour.getKeys(), "testKey"); - EXPECT_EQ(messageFour.getFlag(), 0); - - MQMessage messageFive("test", "tagTest", "testKey", 1, "testBody", 2); - EXPECT_EQ(messageFive.getTopic(), "test"); - EXPECT_EQ(messageFive.getBody(), "testBody"); - EXPECT_EQ(messageFive.getTags(), "tagTest"); - EXPECT_EQ(messageFive.getKeys(), "testKey"); - EXPECT_EQ(messageFive.getFlag(), 1); - - MQMessage messageSix(messageFive); - EXPECT_EQ(messageSix.getTopic(), "test"); - EXPECT_EQ(messageSix.getBody(), "testBody"); - EXPECT_EQ(messageSix.getTags(), "tagTest"); - EXPECT_EQ(messageSix.getKeys(), "testKey"); - EXPECT_EQ(messageSix.getFlag(), 1); - - MQMessage messageSeven = messageSix; - EXPECT_EQ(messageSeven.getTopic(), "test"); - EXPECT_EQ(messageSeven.getBody(), "testBody"); - EXPECT_EQ(messageSeven.getTags(), "tagTest"); - EXPECT_EQ(messageSeven.getKeys(), "testKey"); - EXPECT_EQ(messageSeven.getFlag(), 1); -} - -TEST(message, info) { - MQMessage message; - - EXPECT_EQ(message.getTopic(), ""); - message.setTopic("testTopic"); - EXPECT_EQ(message.getTopic(), "testTopic"); - string topic = "testTopic"; - const char* ctopic = topic.c_str(); - message.setTopic(ctopic, 5); - EXPECT_EQ(message.getTopic(), "testT"); - - EXPECT_EQ(message.getBody(), ""); - message.setBody("testBody"); - EXPECT_EQ(message.getBody(), "testBody"); - - string body = "testBody"; - const char* b = body.c_str(); - message.setBody(b, 5); - EXPECT_EQ(message.getBody(), "testB"); - - string tags(message.getTags()); - EXPECT_EQ(tags, ""); - EXPECT_EQ(message.getFlag(), 0); - message.setFlag(2); - EXPECT_EQ(message.getFlag(), 2); - - EXPECT_EQ(message.isWaitStoreMsgOK(), true); - message.setWaitStoreMsgOK(false); - EXPECT_EQ(message.isWaitStoreMsgOK(), false); - message.setWaitStoreMsgOK(true); - EXPECT_EQ(message.isWaitStoreMsgOK(), true); - - string keys(message.getTags()); - EXPECT_EQ(keys, ""); - message.setKeys("testKeys"); - EXPECT_EQ(message.getKeys(), "testKeys"); - - EXPECT_EQ(message.getDelayTimeLevel(), 0); - message.setDelayTimeLevel(1); - EXPECT_EQ(message.getDelayTimeLevel(), 1); - - message.setSysFlag(1); - EXPECT_EQ(message.getSysFlag(), 1); -} - -TEST(message, properties) { - MQMessage message; - EXPECT_EQ(message.getProperties().size(), 1); - EXPECT_STREQ(message.getProperty(MQMessage::PROPERTY_TRANSACTION_PREPARED).c_str(), ""); - - message.setProperty(MQMessage::PROPERTY_TRANSACTION_PREPARED, "true"); - EXPECT_EQ(message.getProperties().size(), 2); - EXPECT_EQ(message.getSysFlag(), 4); - EXPECT_EQ(message.getProperty(MQMessage::PROPERTY_TRANSACTION_PREPARED), "true"); - - message.setProperty(MQMessage::PROPERTY_TRANSACTION_PREPARED, "false"); - EXPECT_EQ(message.getProperties().size(), 2); - EXPECT_EQ(message.getSysFlag(), 0); - EXPECT_EQ(message.getProperty(MQMessage::PROPERTY_TRANSACTION_PREPARED), "false"); - - map newProperties; - - newProperties[MQMessage::PROPERTY_TRANSACTION_PREPARED] = "true"; - message.setProperties(newProperties); - EXPECT_EQ(message.getSysFlag(), 4); - EXPECT_EQ(message.getProperty(MQMessage::PROPERTY_TRANSACTION_PREPARED), "true"); - - newProperties[MQMessage::PROPERTY_TRANSACTION_PREPARED] = "false"; - message.setProperties(newProperties); - EXPECT_EQ(message.getSysFlag(), 0); - EXPECT_EQ(message.getProperty(MQMessage::PROPERTY_TRANSACTION_PREPARED), "false"); -} - -TEST(message, Keys) { - MQMessage message; - vector keys; - keys.push_back("abc"); - keys.push_back("efg"); - keys.push_back("hij"); - message.setKeys(keys); - EXPECT_EQ(message.getKeys(), "abc efg hij"); -} -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - - // testing::GTEST_FLAG(filter) = "message.info"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/producer/DefaultMQProducerImplTest.cpp b/test/src/producer/DefaultMQProducerImplTest.cpp deleted file mode 100644 index b54ec67cd..000000000 --- a/test/src/producer/DefaultMQProducerImplTest.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "DefaultMQProducerImpl.h" -#include "MQClientFactory.h" -#include "TopicPublishInfo.h" - -using namespace std; -using namespace rocketmq; -using rocketmq::DefaultMQProducerImpl; -using rocketmq::MQClientAPIImpl; -using rocketmq::MQClientFactory; -using rocketmq::TopicPublishInfo; -using testing::_; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -class MySendCallback : public SendCallback { - virtual void onSuccess(SendResult& sendResult) {} - virtual void onException(MQException& e) {} -}; - -class MyMessageQueueSelector : public MessageQueueSelector { - virtual MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) { - return MQMessageQueue("TestTopic", "BrokerA", 0); - } -}; -class MockMQClientFactory : public MQClientFactory { - public: - MockMQClientFactory(const string& mqClientId) : MQClientFactory(mqClientId, true, DEFAULT_SSL_PROPERTY_FILE) {} - MOCK_METHOD0(start, void()); - MOCK_METHOD0(shutdown, void()); - MOCK_METHOD0(sendHeartbeatToAllBroker, void()); - MOCK_METHOD0(getMQClientAPIImpl, MQClientAPIImpl*()); - MOCK_METHOD1(registerProducer, bool(MQProducer*)); - MOCK_METHOD1(unregisterProducer, void(MQProducer*)); - MOCK_METHOD1(findBrokerAddressInPublish, string(const string&)); - MOCK_METHOD2(tryToFindTopicPublishInfo, - boost::shared_ptr(const string&, const SessionCredentials&)); -}; -class MockMQClientAPIImpl : public MQClientAPIImpl { - public: - MockMQClientAPIImpl() : MQClientAPIImpl("testMockMQClientAPIImpl", true, DEFAULT_SSL_PROPERTY_FILE) {} - - MOCK_METHOD9(sendMessage, - SendResult(const string&, - const string&, - const MQMessage&, - const SendMessageRequestHeader&, - int, - int, - int, - SendCallback*, - const SessionCredentials&)); -}; -TEST(DefaultMQProducerImplTest, init) { - DefaultMQProducerImpl* impl = new DefaultMQProducerImpl("testMQProducerGroup"); - EXPECT_EQ(impl->getGroupName(), "testMQProducerGroup"); - impl->setUnitName("testUnit"); - EXPECT_EQ(impl->getUnitName(), "testUnit"); - impl->setTcpTransportPullThreadNum(64); - EXPECT_EQ(impl->getTcpTransportPullThreadNum(), 64); - impl->setTcpTransportConnectTimeout(2000); - EXPECT_EQ(impl->getTcpTransportConnectTimeout(), 2000); - impl->setTcpTransportTryLockTimeout(3000); - // need fix the unit - EXPECT_EQ(impl->getTcpTransportTryLockTimeout(), 3); - impl->setRetryTimes4Async(4); - EXPECT_EQ(impl->getRetryTimes4Async(), 4); - impl->setRetryTimes(2); - EXPECT_EQ(impl->getRetryTimes(), 2); - impl->setSendMsgTimeout(1000); - EXPECT_EQ(impl->getSendMsgTimeout(), 1000); - impl->setCompressMsgBodyOverHowmuch(1024); - EXPECT_EQ(impl->getCompressMsgBodyOverHowmuch(), 1024); - impl->setCompressLevel(2); - EXPECT_EQ(impl->getCompressLevel(), 2); - impl->setMaxMessageSize(2048); - EXPECT_EQ(impl->getMaxMessageSize(), 2048); - - impl->setNamesrvAddr("http://rocketmq.nameserver.com"); - EXPECT_EQ(impl->getNamesrvAddr(), "rocketmq.nameserver.com"); - impl->setNameSpace("MQ_INST_NAMESPACE_TEST"); - EXPECT_EQ(impl->getNameSpace(), "MQ_INST_NAMESPACE_TEST"); - impl->setMessageTrace(true); - EXPECT_TRUE(impl->getMessageTrace()); -} -TEST(DefaultMQProducerImplTest, Sends) { - DefaultMQProducerImpl* impl = new DefaultMQProducerImpl("testMockSendMQProducerGroup"); - MockMQClientFactory* mockFactory = new MockMQClientFactory("testClientId"); - MockMQClientAPIImpl* apiImpl = new MockMQClientAPIImpl(); - - impl->setFactory(mockFactory); - impl->setNamesrvAddr("http://rocketmq.nameserver.com"); - - // prepare send - boost::shared_ptr topicPublishInfo = boost::make_shared(); - MQMessageQueue mqA("TestTopic", "BrokerA", 0); - MQMessageQueue mqB("TestTopic", "BrokerB", 0); - topicPublishInfo->updateMessageQueueList(mqA); - topicPublishInfo->updateMessageQueueList(mqB); - - SendResult okMQAResult(SEND_OK, "MSSAGEID", "OFFSETID", mqA, 1024, "DEFAULT_REGION", false); - SendResult okMQBResult(SEND_OK, "MSSAGEID", "OFFSETID", mqB, 2048); - okMQBResult.setRegionId("DEFAULT_REGION"); - okMQBResult.toString(); - SendResult errorMQBResult(SEND_SLAVE_NOT_AVAILABLE, "MSSAGEID", "OFFSETID", mqB, 2048); - - EXPECT_CALL(*mockFactory, start()).Times(1).WillOnce(Return()); - EXPECT_CALL(*mockFactory, shutdown()).Times(1).WillOnce(Return()); - EXPECT_CALL(*mockFactory, registerProducer(_)).Times(1).WillOnce(Return(true)); - EXPECT_CALL(*mockFactory, unregisterProducer(_)).Times(1).WillOnce(Return()); - EXPECT_CALL(*mockFactory, sendHeartbeatToAllBroker()).Times(1).WillOnce(Return()); - EXPECT_CALL(*mockFactory, tryToFindTopicPublishInfo(_, _)).WillRepeatedly(Return(topicPublishInfo)); - EXPECT_CALL(*mockFactory, findBrokerAddressInPublish(_)).WillRepeatedly(Return("BrokerA")); - EXPECT_CALL(*mockFactory, getMQClientAPIImpl()).WillRepeatedly(Return(apiImpl)); - - EXPECT_CALL(*apiImpl, sendMessage(_, _, _, _, _, _, _, _, _)) - .WillOnce(Return(okMQAResult)) - .WillOnce(Return(okMQBResult)) - .WillOnce(Return(errorMQBResult)) - .WillOnce(Return(okMQAResult)) - .WillOnce(Return(okMQAResult)) - .WillRepeatedly(Return(okMQAResult)); - - // Start Producer. - impl->start(); - - MQMessage msg("testTopic", "testTag", "testKey", "testBodysA"); - SendResult s1 = impl->send(msg); - EXPECT_EQ(s1.getSendStatus(), SEND_OK); - EXPECT_EQ(s1.getQueueOffset(), 1024); - SendResult s2 = impl->send(msg, mqB); - EXPECT_EQ(s2.getSendStatus(), SEND_OK); - EXPECT_EQ(s2.getQueueOffset(), 2048); - MessageQueueSelector* pSelect = new MyMessageQueueSelector(); - SendResult s3 = impl->send(msg, pSelect, nullptr, 3, true); - EXPECT_EQ(s3.getSendStatus(), SEND_OK); - EXPECT_EQ(s3.getQueueOffset(), 1024); - SendResult s33 = impl->send(msg, pSelect, nullptr); - EXPECT_EQ(s33.getSendStatus(), SEND_OK); - EXPECT_EQ(s33.getQueueOffset(), 1024); - - SendCallback* pCallback = new MySendCallback(); - EXPECT_NO_THROW(impl->send(msg, pCallback, true)); - EXPECT_NO_THROW(impl->send(msg, pSelect, nullptr, pCallback)); - EXPECT_NO_THROW(impl->send(msg, mqA, pCallback)); - - EXPECT_NO_THROW(impl->sendOneway(msg)); - EXPECT_NO_THROW(impl->sendOneway(msg, mqA)); - EXPECT_NO_THROW(impl->sendOneway(msg, pSelect, nullptr)); - - MQMessage msgB("testTopic", "testTag", "testKey", "testBodysB"); - vector msgs; - msgs.push_back(msg); - msgs.push_back(msgB); - SendResult s4 = impl->send(msgs); - EXPECT_EQ(s4.getSendStatus(), SEND_OK); - EXPECT_EQ(s4.getQueueOffset(), 1024); - SendResult s5 = impl->send(msgs, mqA); - EXPECT_EQ(s5.getSendStatus(), SEND_OK); - EXPECT_EQ(s5.getQueueOffset(), 1024); - - impl->shutdown(); - delete mockFactory; - delete apiImpl; -} -TEST(DefaultMQProducerImplTest, Trace) { - DefaultMQProducerImpl* impl = new DefaultMQProducerImpl("testMockProducerTraceGroup"); - MockMQClientFactory* mockFactory = new MockMQClientFactory("testTraceClientId"); - MockMQClientAPIImpl* apiImpl = new MockMQClientAPIImpl(); - - impl->setFactory(mockFactory); - impl->setNamesrvAddr("http://rocketmq.nameserver.com"); - impl->setMessageTrace(true); - - // prepare send - boost::shared_ptr topicPublishInfo = boost::make_shared(); - MQMessageQueue mqA("TestTraceTopic", "BrokerA", 0); - MQMessageQueue mqB("TestTraceTopic", "BrokerB", 0); - topicPublishInfo->updateMessageQueueList(mqA); - topicPublishInfo->updateMessageQueueList(mqB); - - SendResult okMQAResult(SEND_OK, "MSSAGEID", "OFFSETID", mqA, 1024, "DEFAULT_REGION", false); - - EXPECT_CALL(*mockFactory, start()).Times(1).WillOnce(Return()); - EXPECT_CALL(*mockFactory, shutdown()).Times(1).WillOnce(Return()); - EXPECT_CALL(*mockFactory, registerProducer(_)).Times(1).WillOnce(Return(true)); - EXPECT_CALL(*mockFactory, unregisterProducer(_)).Times(1).WillOnce(Return()); - EXPECT_CALL(*mockFactory, sendHeartbeatToAllBroker()).Times(1).WillOnce(Return()); - EXPECT_CALL(*mockFactory, tryToFindTopicPublishInfo(_, _)).WillRepeatedly(Return(topicPublishInfo)); - EXPECT_CALL(*mockFactory, findBrokerAddressInPublish(_)).WillRepeatedly(Return("BrokerA")); - EXPECT_CALL(*mockFactory, getMQClientAPIImpl()).WillRepeatedly(Return(apiImpl)); - - EXPECT_CALL(*apiImpl, sendMessage(_, _, _, _, _, _, _, _, _)).WillRepeatedly(Return(okMQAResult)); - - // Start Producer. - impl->start(); - - MQMessage msg("TestTraceTopic", "testTag", "testKey", "testBodysA"); - SendResult s1 = impl->send(msg); - EXPECT_EQ(s1.getSendStatus(), SEND_OK); - - impl->shutdown(); - delete mockFactory; - delete apiImpl; -} -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/test/src/producer/StringIdMakerTest.cpp b/test/src/producer/StringIdMakerTest.cpp deleted file mode 100644 index ebe5897f2..000000000 --- a/test/src/producer/StringIdMakerTest.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "StringIdMaker.h" - -using namespace std; -using namespace rocketmq; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -TEST(StringIdMakerTest, get_unique_id) { - string unique_id = StringIdMaker::getInstance().createUniqID(); - cout << "unique_id: " << unique_id << endl; - EXPECT_EQ(unique_id.size(), 32); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/test/src/producer/TopicPublishInfoTest.cpp b/test/src/producer/TopicPublishInfoTest.cpp deleted file mode 100644 index d1548a425..000000000 --- a/test/src/producer/TopicPublishInfoTest.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "MQMessageQueue.h" -#include "TopicPublishInfo.h" - -using namespace std; -using namespace rocketmq; -using rocketmq::MQMessageQueue; -using rocketmq::TopicPublishInfo; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -TEST(TopicPublishInfoTest, testAll) { - TopicPublishInfo* info = new TopicPublishInfo(); - - MQMessageQueue mqA("TestTopicA", "BrokerA", 0); - MQMessageQueue mqB("TestTopicA", "BrokerB", 0); - int index = -1; - EXPECT_EQ(info->getWhichQueue(), 0); - EXPECT_FALSE(info->ok()); - EXPECT_EQ(info->selectOneMessageQueue(mqA, index), MQMessageQueue()); - EXPECT_EQ(info->selectOneActiveMessageQueue(mqA, index), MQMessageQueue()); - info->updateMessageQueueList(mqA); - info->updateMessageQueueList(mqB); - EXPECT_TRUE(info->ok()); - EXPECT_EQ(info->getMessageQueueList().size(), 2); - - EXPECT_EQ(info->selectOneMessageQueue(mqA, index), MQMessageQueue()); - EXPECT_EQ(info->selectOneActiveMessageQueue(mqA, index), MQMessageQueue()); - index = 0; - MQMessageQueue mqSelect1 = info->selectOneMessageQueue(MQMessageQueue(), index); - EXPECT_EQ(index, 0); - EXPECT_EQ(mqSelect1, mqA); - EXPECT_EQ(info->getWhichQueue(), 1); - MQMessageQueue mqSelect2 = info->selectOneMessageQueue(mqSelect1, index); - EXPECT_EQ(index, 1); - EXPECT_EQ(mqSelect2, mqB); - EXPECT_EQ(info->getWhichQueue(), 3); - index = 0; - MQMessageQueue mqActiveSelect1 = info->selectOneActiveMessageQueue(MQMessageQueue(), index); - EXPECT_EQ(index, 0); - EXPECT_EQ(mqActiveSelect1, mqA); - MQMessageQueue mqActiveSelect2 = info->selectOneActiveMessageQueue(mqActiveSelect1, index); - EXPECT_EQ(index, 1); - EXPECT_EQ(mqActiveSelect2, mqB); - EXPECT_EQ(info->getWhichQueue(), 6); - info->updateNonServiceMessageQueue(mqA, 1000); - info->updateNonServiceMessageQueue(mqA, 1000); - index = 0; - MQMessageQueue mqActiveSelect3 = info->selectOneActiveMessageQueue(mqActiveSelect1, index); - EXPECT_EQ(index, 1); - EXPECT_EQ(mqActiveSelect3, mqB); - MQMessageQueue mqActiveSelect4 = info->selectOneActiveMessageQueue(mqActiveSelect2, index); - EXPECT_EQ(index, 1); - EXPECT_EQ(mqActiveSelect4, mqA); - info->updateNonServiceMessageQueue(mqB, 1000); - index = 0; - MQMessageQueue mqSelect3 = info->selectOneMessageQueue(MQMessageQueue(), index); - EXPECT_EQ(index, 0); - EXPECT_EQ(mqSelect3, mqA); - index = 0; - MQMessageQueue mqActiveSelect5 = info->selectOneActiveMessageQueue(MQMessageQueue(), index); - EXPECT_EQ(index, 1); - EXPECT_EQ(mqActiveSelect5, mqA); - index = 0; - MQMessageQueue mqActiveSelect6 = info->selectOneActiveMessageQueue(mqB, index); - EXPECT_EQ(index, 0); - EXPECT_EQ(mqActiveSelect6, mqA); - info->updateMessageQueueList(mqSelect3); - info->resumeNonServiceMessageQueueList(); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/test/src/protocol/CommandHeaderTest.cpp b/test/src/protocol/CommandHeaderTest.cpp deleted file mode 100644 index 8ce2d591d..000000000 --- a/test/src/protocol/CommandHeaderTest.cpp +++ /dev/null @@ -1,558 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "json/value.h" -#include "json/writer.h" - -#include "CommandHeader.h" -#include "dataBlock.h" - -using std::shared_ptr; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using Json::FastWriter; -using Json::Value; - -using rocketmq::CheckTransactionStateRequestHeader; -using rocketmq::CommandHeader; -using rocketmq::ConsumerSendMsgBackRequestHeader; -using rocketmq::CreateTopicRequestHeader; -using rocketmq::EndTransactionRequestHeader; -using rocketmq::GetConsumerListByGroupRequestHeader; -using rocketmq::GetConsumerListByGroupResponseBody; -using rocketmq::GetConsumerListByGroupResponseHeader; -using rocketmq::GetConsumerRunningInfoRequestHeader; -using rocketmq::GetEarliestMsgStoretimeRequestHeader; -using rocketmq::GetEarliestMsgStoretimeResponseHeader; -using rocketmq::GetMaxOffsetRequestHeader; -using rocketmq::GetMaxOffsetResponseHeader; -using rocketmq::GetMinOffsetRequestHeader; -using rocketmq::GetMinOffsetResponseHeader; -using rocketmq::GetRouteInfoRequestHeader; -using rocketmq::MemoryBlock; -using rocketmq::NotifyConsumerIdsChangedRequestHeader; -using rocketmq::PullMessageRequestHeader; -using rocketmq::PullMessageResponseHeader; -using rocketmq::QueryConsumerOffsetRequestHeader; -using rocketmq::QueryConsumerOffsetResponseHeader; -using rocketmq::ResetOffsetRequestHeader; -using rocketmq::SearchOffsetRequestHeader; -using rocketmq::SearchOffsetResponseHeader; -using rocketmq::SendMessageRequestHeader; -using rocketmq::SendMessageRequestHeaderV2; -using rocketmq::SendMessageResponseHeader; -using rocketmq::UnregisterClientRequestHeader; -using rocketmq::UpdateConsumerOffsetRequestHeader; -using rocketmq::ViewMessageRequestHeader; - -TEST(commandHeader, ConsumerSendMsgBackRequestHeader) { - string group = "testGroup"; - int delayLevel = 2; - int64 offset = 3027; - bool unitMode = true; - string originMsgId = "testOriginMsgId"; - string originTopic = "testTopic"; - int maxReconsumeTimes = 12; - ConsumerSendMsgBackRequestHeader header; - header.group = group; - header.delayLevel = delayLevel; - header.offset = offset; - header.unitMode = unitMode; - header.originMsgId = originMsgId; - header.originTopic = originTopic; - header.maxReconsumeTimes = maxReconsumeTimes; - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["group"], group); - EXPECT_EQ(requestMap["delayLevel"], "2"); - EXPECT_EQ(requestMap["offset"], "3027"); - EXPECT_EQ(requestMap["unitMode"], "true"); - EXPECT_EQ(requestMap["originMsgId"], originMsgId); - EXPECT_EQ(requestMap["originTopic"], originTopic); - EXPECT_EQ(requestMap["maxReconsumeTimes"], "12"); - - Value outData; - header.Encode(outData); - EXPECT_EQ(outData["group"], group); - EXPECT_EQ(outData["delayLevel"], 2); - EXPECT_EQ(outData["offset"], "3027"); - EXPECT_EQ(outData["unitMode"], "true"); - EXPECT_EQ(outData["originMsgId"], originMsgId); - EXPECT_EQ(outData["originTopic"], originTopic); - EXPECT_EQ(outData["maxReconsumeTimes"], 12); -} - -TEST(commandHeader, GetRouteInfoRequestHeader) { - GetRouteInfoRequestHeader header("testTopic"); - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["topic"], "testTopic"); - - Value outData; - header.Encode(outData); - EXPECT_EQ(outData["topic"], "testTopic"); -} - -TEST(commandHeader, UnregisterClientRequestHeader) { - UnregisterClientRequestHeader header("testGroup", "testProducer", "testConsumer"); - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["clientID"], "testGroup"); - EXPECT_EQ(requestMap["producerGroup"], "testProducer"); - EXPECT_EQ(requestMap["consumerGroup"], "testConsumer"); - - Value outData; - header.Encode(outData); - EXPECT_EQ(outData["clientID"], "testGroup"); - EXPECT_EQ(outData["producerGroup"], "testProducer"); - EXPECT_EQ(outData["consumerGroup"], "testConsumer"); -} - -TEST(commandHeader, CreateTopicRequestHeader) { - string topic = "testTopic"; - string defaultTopic = "defaultTopic"; - int readQueueNums = 4; - int writeQueueNums = 6; - int perm = 8; - string topicFilterType = "filterType"; - CreateTopicRequestHeader header; - header.topic = topic; - header.defaultTopic = defaultTopic; - header.readQueueNums = readQueueNums; - header.writeQueueNums = writeQueueNums; - header.perm = perm; - header.topicFilterType = topicFilterType; - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["topic"], topic); - EXPECT_EQ(requestMap["defaultTopic"], defaultTopic); - EXPECT_EQ(requestMap["readQueueNums"], "4"); - EXPECT_EQ(requestMap["writeQueueNums"], "6"); - EXPECT_EQ(requestMap["perm"], "8"); - EXPECT_EQ(requestMap["topicFilterType"], topicFilterType); - - Value outData; - header.Encode(outData); - EXPECT_EQ(outData["topic"], topic); - EXPECT_EQ(outData["defaultTopic"], defaultTopic); - EXPECT_EQ(outData["readQueueNums"], readQueueNums); - EXPECT_EQ(outData["writeQueueNums"], writeQueueNums); - EXPECT_EQ(outData["perm"], perm); - EXPECT_EQ(outData["topicFilterType"], topicFilterType); -} - -TEST(commandHeader, CheckTransactionStateRequestHeader) { - CheckTransactionStateRequestHeader header(2000, 1000, "ABC", "DEF", "GHI"); - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["msgId"], "ABC"); - EXPECT_EQ(requestMap["transactionId"], "DEF"); - EXPECT_EQ(requestMap["offsetMsgId"], "GHI"); - EXPECT_EQ(requestMap["commitLogOffset"], "1000"); - EXPECT_EQ(requestMap["tranStateTableOffset"], "2000"); - - Value value; - value["msgId"] = "ABC"; - value["transactionId"] = "DEF"; - value["offsetMsgId"] = "GHI"; - value["commitLogOffset"] = "1000"; - value["tranStateTableOffset"] = "2000"; - shared_ptr headerDecode( - static_cast(CheckTransactionStateRequestHeader::Decode(value))); - EXPECT_EQ(headerDecode->m_msgId, "ABC"); - EXPECT_EQ(headerDecode->m_commitLogOffset, 1000); - EXPECT_EQ(headerDecode->m_tranStateTableOffset, 2000); - EXPECT_EQ(headerDecode->toString(), header.toString()); -} - -TEST(commandHeader, EndTransactionRequestHeader) { - EndTransactionRequestHeader header("testProducer", 1000, 2000, 3000, true, "ABC", "DEF"); - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["msgId"], "ABC"); - EXPECT_EQ(requestMap["transactionId"], "DEF"); - EXPECT_EQ(requestMap["producerGroup"], "testProducer"); - EXPECT_EQ(requestMap["tranStateTableOffset"], "1000"); - EXPECT_EQ(requestMap["commitLogOffset"], "2000"); - EXPECT_EQ(requestMap["commitOrRollback"], "3000"); - EXPECT_EQ(requestMap["fromTransactionCheck"], "true"); - - Value outData; - header.Encode(outData); - EXPECT_EQ(outData["msgId"], "ABC"); - EXPECT_EQ(outData["transactionId"], "DEF"); - EXPECT_EQ(outData["producerGroup"], "testProducer"); - EXPECT_EQ(outData["tranStateTableOffset"], "1000"); - EXPECT_EQ(outData["commitLogOffset"], "2000"); - EXPECT_EQ(outData["commitOrRollback"], "3000"); - EXPECT_EQ(outData["fromTransactionCheck"], "true"); - - EXPECT_NO_THROW(header.toString()); -} - -TEST(commandHeader, SendMessageRequestHeader) { - string producerGroup = "testProducer"; - string topic = "testTopic"; - string defaultTopic = "defaultTopic"; - int defaultTopicQueueNums = 1; - int queueId = 2; - int sysFlag = 3; - int64 bornTimestamp = 4; - int flag = 5; - string properties = "testProperty"; - int reconsumeTimes = 6; - bool unitMode = true; - bool batch = false; - - SendMessageRequestHeader header; - header.producerGroup = producerGroup; - header.topic = topic; - header.defaultTopic = defaultTopic; - header.defaultTopicQueueNums = defaultTopicQueueNums; - header.queueId = queueId; - header.sysFlag = sysFlag; - header.bornTimestamp = bornTimestamp; - header.flag = flag; - header.properties = properties; - header.reconsumeTimes = reconsumeTimes; - header.unitMode = unitMode; - header.batch = batch; - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["topic"], topic); - EXPECT_EQ(requestMap["producerGroup"], producerGroup); - EXPECT_EQ(requestMap["defaultTopic"], defaultTopic); - EXPECT_EQ(requestMap["defaultTopicQueueNums"], "1"); - EXPECT_EQ(requestMap["queueId"], "2"); - EXPECT_EQ(requestMap["sysFlag"], "3"); - EXPECT_EQ(requestMap["bornTimestamp"], "4"); - EXPECT_EQ(requestMap["flag"], "5"); - EXPECT_EQ(requestMap["properties"], properties); - EXPECT_EQ(requestMap["reconsumeTimes"], "6"); - EXPECT_EQ(requestMap["unitMode"], "true"); - EXPECT_EQ(requestMap["batch"], "false"); - - Value outData; - header.Encode(outData); - EXPECT_EQ(outData["topic"], topic); - EXPECT_EQ(outData["producerGroup"], producerGroup); - EXPECT_EQ(outData["defaultTopic"], defaultTopic); - EXPECT_EQ(outData["defaultTopicQueueNums"], defaultTopicQueueNums); - EXPECT_EQ(outData["queueId"], queueId); - EXPECT_EQ(outData["sysFlag"], sysFlag); - EXPECT_EQ(outData["bornTimestamp"], "4"); - EXPECT_EQ(outData["flag"], flag); - EXPECT_EQ(outData["properties"], properties); - EXPECT_EQ(outData["reconsumeTimes"], "6"); - EXPECT_EQ(outData["unitMode"], "true"); - EXPECT_EQ(outData["batch"], "false"); -} - -TEST(commandHeader, SendMessageRequestHeaderV2) { - string producerGroup = "testProducer"; - string topic = "testTopic"; - string defaultTopic = "defaultTopic"; - int defaultTopicQueueNums = 1; - int queueId = 2; - int sysFlag = 3; - int64 bornTimestamp = 4; - int flag = 5; - string properties = "testProperty"; - int reconsumeTimes = 6; - bool unitMode = true; - bool batch = false; - - SendMessageRequestHeaderV2 header; - header.a = producerGroup; - header.b = topic; - header.c = defaultTopic; - header.d = defaultTopicQueueNums; - header.e = queueId; - header.f = sysFlag; - header.g = bornTimestamp; - header.h = flag; - header.i = properties; - header.j = reconsumeTimes; - header.k = unitMode; - header.m = batch; - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["a"], producerGroup); - EXPECT_EQ(requestMap["b"], topic); - EXPECT_EQ(requestMap["c"], defaultTopic); - EXPECT_EQ(requestMap["d"], "1"); - EXPECT_EQ(requestMap["e"], "2"); - EXPECT_EQ(requestMap["f"], "3"); - EXPECT_EQ(requestMap["g"], "4"); - EXPECT_EQ(requestMap["h"], "5"); - EXPECT_EQ(requestMap["i"], properties); - EXPECT_EQ(requestMap["j"], "6"); - EXPECT_EQ(requestMap["k"], "true"); - EXPECT_EQ(requestMap["m"], "false"); - - Value outData; - header.Encode(outData); - EXPECT_EQ(outData["a"], producerGroup); - EXPECT_EQ(outData["b"], topic); - EXPECT_EQ(outData["c"], defaultTopic); - EXPECT_EQ(outData["d"], defaultTopicQueueNums); - EXPECT_EQ(outData["e"], queueId); - EXPECT_EQ(outData["f"], sysFlag); - EXPECT_EQ(outData["g"], "4"); - EXPECT_EQ(outData["h"], flag); - EXPECT_EQ(outData["i"], properties); - EXPECT_EQ(outData["j"], "6"); - EXPECT_EQ(outData["k"], "true"); - EXPECT_EQ(outData["m"], "false"); - - SendMessageRequestHeader v1; - header.CreateSendMessageRequestHeaderV1(v1); - EXPECT_EQ(v1.producerGroup, producerGroup); - EXPECT_EQ(v1.queueId, queueId); - EXPECT_EQ(v1.batch, batch); - - SendMessageRequestHeaderV2 v2(v1); - EXPECT_EQ(header.a, v2.a); - EXPECT_EQ(header.e, v2.e); - EXPECT_EQ(header.m, v2.m); - EXPECT_EQ(header.g, v2.g); -} - -TEST(commandHeader, SendMessageResponseHeader) { - SendMessageResponseHeader header; - header.msgId = "ABCDEFG"; - header.queueId = 1; - header.queueOffset = 2; - header.transactionId = "ID"; - header.regionId = "public"; - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["msgId"], "ABCDEFG"); - EXPECT_EQ(requestMap["queueId"], "1"); - EXPECT_EQ(requestMap["queueOffset"], "2"); - EXPECT_EQ(requestMap["transactionId"], "ID"); - EXPECT_EQ(requestMap["MSG_REGION"], "public"); - - Value value; - value["msgId"] = "EFGHIJK"; - value["queueId"] = "3"; - value["queueOffset"] = "4"; - value["transactionId"] = "transactionId"; - value["MSG_REGION"] = "MSG_REGION"; - shared_ptr headerDecode( - static_cast(SendMessageResponseHeader::Decode(value))); - EXPECT_EQ(headerDecode->msgId, "EFGHIJK"); - EXPECT_EQ(headerDecode->queueId, 3); - EXPECT_EQ(headerDecode->queueOffset, 4); - EXPECT_EQ(headerDecode->transactionId, "transactionId"); - EXPECT_EQ(headerDecode->regionId, "MSG_REGION"); -} - -TEST(commandHeader, PullMessageRequestHeader) { - PullMessageRequestHeader header; - header.consumerGroup = "testConsumer"; - header.topic = "testTopic"; - header.queueId = 1; - header.maxMsgNums = 2; - header.sysFlag = 3; - header.subscription = "testSub"; - header.queueOffset = 4; - header.commitOffset = 5; - header.suspendTimeoutMillis = 6; - header.subVersion = 7; - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["consumerGroup"], "testConsumer"); - EXPECT_EQ(requestMap["topic"], "testTopic"); - EXPECT_EQ(requestMap["queueId"], "1"); - EXPECT_EQ(requestMap["maxMsgNums"], "2"); - EXPECT_EQ(requestMap["sysFlag"], "3"); - EXPECT_EQ(requestMap["subscription"], "testSub"); - EXPECT_EQ(requestMap["queueOffset"], "4"); - EXPECT_EQ(requestMap["commitOffset"], "5"); - EXPECT_EQ(requestMap["suspendTimeoutMillis"], "6"); - EXPECT_EQ(requestMap["subVersion"], "7"); - - Value outData; - header.Encode(outData); - EXPECT_EQ(outData["consumerGroup"], "testConsumer"); - EXPECT_EQ(outData["topic"], "testTopic"); - EXPECT_EQ(outData["queueId"], 1); - EXPECT_EQ(outData["maxMsgNums"], 2); - EXPECT_EQ(outData["sysFlag"], 3); - EXPECT_EQ(outData["subscription"], "testSub"); - EXPECT_EQ(outData["queueOffset"], "4"); - EXPECT_EQ(outData["commitOffset"], "5"); - EXPECT_EQ(outData["suspendTimeoutMillis"], "6"); - EXPECT_EQ(outData["subVersion"], "7"); -} - -TEST(commandHeader, PullMessageResponseHeader) { - PullMessageResponseHeader header; - header.suggestWhichBrokerId = 100; - header.nextBeginOffset = 200; - header.minOffset = 3000; - header.maxOffset = 5000; - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["suggestWhichBrokerId"], "100"); - EXPECT_EQ(requestMap["nextBeginOffset"], "200"); - EXPECT_EQ(requestMap["minOffset"], "3000"); - EXPECT_EQ(requestMap["maxOffset"], "5000"); - - Value value; - value["suggestWhichBrokerId"] = "5"; - value["nextBeginOffset"] = "102400"; - value["minOffset"] = "1"; - value["maxOffset"] = "123456789"; - shared_ptr headerDecode( - static_cast(PullMessageResponseHeader::Decode(value))); - EXPECT_EQ(headerDecode->suggestWhichBrokerId, 5); - EXPECT_EQ(headerDecode->nextBeginOffset, 102400); - EXPECT_EQ(headerDecode->minOffset, 1); - EXPECT_EQ(headerDecode->maxOffset, 123456789); -} - -TEST(commandHeader, GetConsumerListByGroupResponseBody) { - Value value; - value[0] = "body"; - value[1] = 1; - - Value root; - root["consumerIdList"] = value; - - FastWriter writer; - string data = writer.write(root); - - MemoryBlock* mem = new MemoryBlock(data.c_str(), data.size()); - vector cids; - GetConsumerListByGroupResponseBody::Decode(mem, cids); - - EXPECT_EQ(cids.size(), 1); - - delete mem; -} - -TEST(commandHeader, ResetOffsetRequestHeader) { - ResetOffsetRequestHeader header; - - header.setTopic("testTopic"); - EXPECT_EQ(header.getTopic(), "testTopic"); - - header.setGroup("testGroup"); - EXPECT_EQ(header.getGroup(), "testGroup"); - - header.setTimeStamp(123); - EXPECT_EQ(header.getTimeStamp(), 123); - - header.setForceFlag(true); - EXPECT_TRUE(header.getForceFlag()); - - Value value; - value["isForce"] = "false"; - shared_ptr headersh( - static_cast(ResetOffsetRequestHeader::Decode(value))); - EXPECT_EQ(headersh->getTopic(), ""); - EXPECT_EQ(headersh->getGroup(), ""); - // EXPECT_EQ(headersh->getTimeStamp(), 0); - EXPECT_FALSE(headersh->getForceFlag()); - value["topic"] = "testTopic"; - headersh.reset(static_cast(ResetOffsetRequestHeader::Decode(value))); - EXPECT_EQ(headersh->getTopic(), "testTopic"); - EXPECT_EQ(headersh->getGroup(), ""); - // EXPECT_EQ(headersh->getTimeStamp(), 0); - EXPECT_FALSE(headersh->getForceFlag()); - - value["topic"] = "testTopic"; - value["group"] = "testGroup"; - headersh.reset(static_cast(ResetOffsetRequestHeader::Decode(value))); - EXPECT_EQ(headersh->getTopic(), "testTopic"); - EXPECT_EQ(headersh->getGroup(), "testGroup"); - // EXPECT_EQ(headersh->getTimeStamp(), 0); - EXPECT_FALSE(headersh->getForceFlag()); - - value["topic"] = "testTopic"; - value["group"] = "testGroup"; - value["timestamp"] = "123"; - headersh.reset(static_cast(ResetOffsetRequestHeader::Decode(value))); - EXPECT_EQ(headersh->getTopic(), "testTopic"); - EXPECT_EQ(headersh->getGroup(), "testGroup"); - EXPECT_EQ(headersh->getTimeStamp(), 123); - EXPECT_FALSE(headersh->getForceFlag()); - - value["topic"] = "testTopic"; - value["group"] = "testGroup"; - value["timestamp"] = "123"; - value["isForce"] = "1"; - headersh.reset(static_cast(ResetOffsetRequestHeader::Decode(value))); - EXPECT_EQ(headersh->getTopic(), "testTopic"); - EXPECT_EQ(headersh->getGroup(), "testGroup"); - EXPECT_EQ(headersh->getTimeStamp(), 123); - EXPECT_TRUE(headersh->getForceFlag()); -} - -TEST(commandHeader, GetConsumerRunningInfoRequestHeader) { - GetConsumerRunningInfoRequestHeader header; - header.setClientId("testClientId"); - header.setConsumerGroup("testConsumer"); - header.setJstackEnable(true); - - map requestMap; - header.SetDeclaredFieldOfCommandHeader(requestMap); - EXPECT_EQ(requestMap["clientId"], "testClientId"); - EXPECT_EQ(requestMap["consumerGroup"], "testConsumer"); - EXPECT_EQ(requestMap["jstackEnable"], "true"); - - Value outData; - header.Encode(outData); - EXPECT_EQ(outData["clientId"], "testClientId"); - EXPECT_EQ(outData["consumerGroup"], "testConsumer"); - EXPECT_TRUE(outData["jstackEnable"].asBool()); - - shared_ptr decodeHeader( - static_cast(GetConsumerRunningInfoRequestHeader::Decode(outData))); - EXPECT_EQ(decodeHeader->getClientId(), "testClientId"); - EXPECT_EQ(decodeHeader->getConsumerGroup(), "testConsumer"); - EXPECT_TRUE(decodeHeader->isJstackEnable()); -} - -TEST(commandHeader, NotifyConsumerIdsChangedRequestHeader) { - Json::Value ext; - shared_ptr header( - static_cast(NotifyConsumerIdsChangedRequestHeader::Decode(ext))); - EXPECT_EQ(header->getGroup(), ""); - - ext["consumerGroup"] = "testGroup"; - header.reset(static_cast(NotifyConsumerIdsChangedRequestHeader::Decode(ext))); - EXPECT_EQ(header->getGroup(), "testGroup"); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "commandHeader.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/protocol/ConsumerRunningInfoTest.cpp b/test/src/protocol/ConsumerRunningInfoTest.cpp deleted file mode 100644 index 44604ec4b..000000000 --- a/test/src/protocol/ConsumerRunningInfoTest.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include "map" -#include "string" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "json/reader.h" -#include "json/value.h" - -#include "ConsumeStats.h" -#include "ConsumerRunningInfo.h" -#include "MessageQueue.h" -#include "ProcessQueueInfo.h" -#include "SubscriptionData.h" - -using std::map; -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using Json::Reader; -using Json::Value; - -using rocketmq::ConsumerRunningInfo; -using rocketmq::ConsumeStats; -using rocketmq::MessageQueue; -using rocketmq::ProcessQueueInfo; -using rocketmq::SubscriptionData; - -TEST(ConsumerRunningInfo, init) { - ConsumerRunningInfo info; - - // jstack - info.setJstack("jstack"); - EXPECT_EQ(info.getJstack(), "jstack"); - - // property - EXPECT_TRUE(info.getProperties().empty()); - info.setProperty("testKey", "testValue"); - map properties = info.getProperties(); - EXPECT_EQ(properties["testKey"], "testValue"); - info.setProperties(map()); - EXPECT_TRUE(info.getProperties().empty()); - info.setProperty("testKey", "testValue"); - map properties2 = info.getProperties(); - EXPECT_EQ(properties2["testKey"], "testValue"); - - // subscription - EXPECT_TRUE(info.getSubscriptionSet().empty()); - vector subscriptionSet; - SubscriptionData sData1("testTopic", "testSub"); - sData1.putTagsSet("testTag"); - sData1.putCodeSet("11234"); - subscriptionSet.push_back(sData1); - SubscriptionData sData2("testTopic2", "testSub2"); - sData2.putTagsSet("testTag2"); - sData2.putCodeSet("21234"); - subscriptionSet.push_back(sData2); - info.setSubscriptionSet(subscriptionSet); - EXPECT_EQ(info.getSubscriptionSet().size(), 2); - - // mqtable - EXPECT_TRUE(info.getMqTable().empty()); - - MessageQueue messageQueue1("testTopic", "testBrokerA", 3); - ProcessQueueInfo processQueueInfo1; - processQueueInfo1.commitOffset = 1024; - info.setMqTable(messageQueue1, processQueueInfo1); - MessageQueue messageQueue2("testTopic", "testBrokerB", 4); - ProcessQueueInfo processQueueInfo2; - processQueueInfo2.cachedMsgCount = 1023; - info.setMqTable(messageQueue2, processQueueInfo2); - map mqTable = info.getMqTable(); - EXPECT_EQ(mqTable.size(), 2); - EXPECT_EQ(mqTable[messageQueue1].commitOffset, 1024); - EXPECT_EQ(mqTable[messageQueue2].cachedMsgCount, 1023); - // consumeStats - EXPECT_TRUE(info.getStatusTable().empty()); - - ConsumeStats consumeStats; - consumeStats.pullTPS = 22.5; - ConsumeStats consumeStats2; - consumeStats2.consumeOKTPS = 3.168; - info.setStatusTable("TopicA", consumeStats); - info.setStatusTable("TopicB", consumeStats2); - map statsTable = info.getStatusTable(); - EXPECT_EQ(statsTable.size(), 2); - EXPECT_EQ(statsTable["TopicA"].pullTPS, 22.5); - EXPECT_EQ(statsTable["TopicB"].consumeOKTPS, 3.168); - - // encode start - info.setProperty(ConsumerRunningInfo::PROP_NAMESERVER_ADDR, "127.0.0.1:9876"); - info.setProperty(ConsumerRunningInfo::PROP_THREADPOOL_CORE_SIZE, "core_size"); - info.setProperty(ConsumerRunningInfo::PROP_CONSUME_ORDERLY, "consume_orderly"); - info.setProperty(ConsumerRunningInfo::PROP_CONSUME_TYPE, "consume_type"); - info.setProperty(ConsumerRunningInfo::PROP_CLIENT_VERSION, "client_version"); - info.setProperty(ConsumerRunningInfo::PROP_CONSUMER_START_TIMESTAMP, "127"); - info.setProperty(ConsumerRunningInfo::PROP_CLIENT_SDK_VERSION, "sdk_version"); - - // encode - string outStr = info.encode(); - EXPECT_GT(outStr.length(), 1); - EXPECT_NE(outStr.find("testTopic"), string::npos); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "ConsumerRunningInfo.*"; - int iTests = RUN_ALL_TESTS(); - return iTests; -} diff --git a/test/src/protocol/HeartbeatDataTest.cpp b/test/src/protocol/HeartbeatDataTest.cpp deleted file mode 100644 index c153ed58a..000000000 --- a/test/src/protocol/HeartbeatDataTest.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "vector" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "ConsumeType.h" -#include "HeartbeatData.h" -#include "SubscriptionData.h" - -using std::vector; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::ConsumeFromWhere; -using rocketmq::ConsumerData; -using rocketmq::ConsumeType; -using rocketmq::HeartbeatData; -using rocketmq::MessageModel; -using rocketmq::ProducerData; -using rocketmq::SubscriptionData; - -TEST(heartbeatData, ProducerData) { - ProducerData producerData; - producerData.groupName = "testGroup"; - - Json::Value outJson = producerData.toJson(); - EXPECT_EQ(outJson["groupName"], "testGroup"); -} - -TEST(heartbeatData, ConsumerData) { - ConsumerData consumerData; - consumerData.groupName = "testGroup"; - consumerData.consumeType = ConsumeType::CONSUME_ACTIVELY; - consumerData.messageModel = MessageModel::BROADCASTING; - consumerData.consumeFromWhere = ConsumeFromWhere::CONSUME_FROM_TIMESTAMP; - - vector subs; - subs.push_back(SubscriptionData("testTopic", "sub")); - - consumerData.subscriptionDataSet = subs; - - Json::Value outJson = consumerData.toJson(); - - EXPECT_EQ(outJson["groupName"], "testGroup"); - - EXPECT_EQ(outJson["consumeType"].asInt(), ConsumeType::CONSUME_ACTIVELY); - EXPECT_EQ(outJson["messageModel"].asInt(), MessageModel::BROADCASTING); - EXPECT_EQ(outJson["consumeFromWhere"].asInt(), ConsumeFromWhere::CONSUME_FROM_TIMESTAMP); - - Json::Value subsValue = outJson["subscriptionDataSet"]; - EXPECT_EQ(subsValue[0]["topic"], "testTopic"); - EXPECT_EQ(subsValue[0]["subString"], "sub"); -} - -TEST(heartbeatData, HeartbeatData) { - HeartbeatData heartbeatData; - heartbeatData.setClientID("testClientId"); - - ProducerData producerData; - producerData.groupName = "testGroup"; - - EXPECT_TRUE(heartbeatData.isProducerDataSetEmpty()); - heartbeatData.insertDataToProducerDataSet(producerData); - EXPECT_FALSE(heartbeatData.isProducerDataSetEmpty()); - - ConsumerData consumerData; - consumerData.groupName = "testGroup"; - consumerData.consumeType = ConsumeType::CONSUME_ACTIVELY; - consumerData.messageModel = MessageModel::BROADCASTING; - consumerData.consumeFromWhere = ConsumeFromWhere::CONSUME_FROM_TIMESTAMP; - - vector subs; - subs.push_back(SubscriptionData("testTopic", "sub")); - - consumerData.subscriptionDataSet = subs; - EXPECT_TRUE(heartbeatData.isConsumerDataSetEmpty()); - heartbeatData.insertDataToConsumerDataSet(consumerData); - EXPECT_FALSE(heartbeatData.isConsumerDataSetEmpty()); - - string outData; - heartbeatData.Encode(outData); - - Json::Value root; - Json::Reader reader; - reader.parse(outData, root); - - EXPECT_EQ(root["clientID"], "testClientId"); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "heartbeatData.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/protocol/KVTableTest.cpp b/test/src/protocol/KVTableTest.cpp deleted file mode 100644 index b6fd50493..000000000 --- a/test/src/protocol/KVTableTest.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "KVTable.h" - -using std::map; -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::KVTable; - -TEST(KVTable, init) { - KVTable table; - - EXPECT_EQ(table.getTable().size(), 0); - - map maps; - maps["string"] = "string"; - table.setTable(maps); - EXPECT_EQ(table.getTable().size(), 1); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "KVTable.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/protocol/LockBatchBodyTest.cpp b/test/src/protocol/LockBatchBodyTest.cpp deleted file mode 100644 index 81f5e4f9a..000000000 --- a/test/src/protocol/LockBatchBodyTest.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "vector" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "LockBatchBody.h" -#include "MQMessageQueue.h" -#include "dataBlock.h" - -using std::vector; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::LockBatchRequestBody; -using rocketmq::LockBatchResponseBody; -using rocketmq::MemoryBlock; -using rocketmq::MQMessageQueue; -using rocketmq::UnlockBatchRequestBody; - -TEST(lockBatchBody, LockBatchRequestBody) { - LockBatchRequestBody lockBatchRequestBody; - - lockBatchRequestBody.setClientId("testClientId"); - EXPECT_EQ(lockBatchRequestBody.getClientId(), "testClientId"); - - lockBatchRequestBody.setConsumerGroup("testGroup"); - EXPECT_EQ(lockBatchRequestBody.getConsumerGroup(), "testGroup"); - - vector vectorMessageQueue; - vectorMessageQueue.push_back(MQMessageQueue()); - vectorMessageQueue.push_back(MQMessageQueue()); - - lockBatchRequestBody.setMqSet(vectorMessageQueue); - EXPECT_EQ(lockBatchRequestBody.getMqSet(), vectorMessageQueue); - - Json::Value outJson = lockBatchRequestBody.toJson(MQMessageQueue("topicTest", "testBroker", 10)); - EXPECT_EQ(outJson["topic"], "topicTest"); - EXPECT_EQ(outJson["brokerName"], "testBroker"); - EXPECT_EQ(outJson["queueId"], 10); - - string outData; - lockBatchRequestBody.Encode(outData); - - Json::Value root; - Json::Reader reader; - reader.parse(outData, root); - EXPECT_EQ(root["clientId"], "testClientId"); - EXPECT_EQ(root["consumerGroup"], "testGroup"); -} - -TEST(lockBatchBody, UnlockBatchRequestBody) { - UnlockBatchRequestBody uRB; - uRB.setClientId("testClient"); - EXPECT_EQ(uRB.getClientId(), "testClient"); - uRB.setConsumerGroup("testGroup"); - EXPECT_EQ(uRB.getConsumerGroup(), "testGroup"); - - // message queue - EXPECT_TRUE(uRB.getMqSet().empty()); - vector mqs; - MQMessageQueue mqA("testTopic", "testBrokerA", 1); - mqs.push_back(mqA); - MQMessageQueue mqB("testTopic", "testBrokerB", 2); - mqs.push_back(mqB); - uRB.setMqSet(mqs); - EXPECT_EQ(uRB.getMqSet().size(), 2); - string outData; - uRB.Encode(outData); - EXPECT_GT(outData.length(), 1); - EXPECT_NE(outData.find("testTopic"), string::npos); -} - -TEST(lockBatchBody, LockBatchResponseBody) { - Json::Value root; - Json::Value mqs; - - Json::Value mq; - mq["topic"] = "testTopic"; - mq["brokerName"] = "testBroker"; - mq["queueId"] = 1; - mqs[0] = mq; - root["lockOKMQSet"] = mqs; - - Json::FastWriter fastwrite; - string outData = fastwrite.write(root); - - MemoryBlock* mem = new MemoryBlock(outData.c_str(), outData.size()); - - LockBatchResponseBody lockBatchResponseBody; - - vector messageQueues; - LockBatchResponseBody::Decode(mem, messageQueues); - - MQMessageQueue messageQueue("testTopic", "testBroker", 1); - EXPECT_EQ(messageQueue, messageQueues[0]); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "lockBatchBody.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/protocol/MessageQueueTest.cpp b/test/src/protocol/MessageQueueTest.cpp deleted file mode 100644 index eb943d206..000000000 --- a/test/src/protocol/MessageQueueTest.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/*" - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "MessageQueue.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MessageQueue; - -TEST(messageExt, init) { - MessageQueue messageQueue; - EXPECT_EQ(messageQueue.getQueueId(), -1); - - MessageQueue twoMessageQueue("testTopic", "testBroker", 1); - EXPECT_EQ(twoMessageQueue.getQueueId(), 1); - EXPECT_EQ(twoMessageQueue.getTopic(), "testTopic"); - EXPECT_EQ(twoMessageQueue.getBrokerName(), "testBroker"); - - MessageQueue threeMessageQueue(twoMessageQueue); - EXPECT_EQ(threeMessageQueue.getQueueId(), 1); - EXPECT_EQ(threeMessageQueue.getTopic(), "testTopic"); - EXPECT_EQ(threeMessageQueue.getBrokerName(), "testBroker"); - - Json::Value outJson = twoMessageQueue.toJson(); - EXPECT_EQ(outJson["queueId"], 1); - EXPECT_EQ(outJson["topic"], "testTopic"); - EXPECT_EQ(outJson["brokerName"], "testBroker"); - - MessageQueue fiveMessageQueue = threeMessageQueue; - EXPECT_EQ(fiveMessageQueue.getQueueId(), 1); - EXPECT_EQ(fiveMessageQueue.getTopic(), "testTopic"); - EXPECT_EQ(fiveMessageQueue.getBrokerName(), "testBroker"); -} - -TEST(messageExt, info) { - MessageQueue messageQueue; - - messageQueue.setQueueId(11); - EXPECT_EQ(messageQueue.getQueueId(), 11); - - messageQueue.setTopic("testTopic"); - EXPECT_EQ(messageQueue.getTopic(), "testTopic"); - - messageQueue.setBrokerName("testBroker"); - EXPECT_EQ(messageQueue.getBrokerName(), "testBroker"); -} - -TEST(messageExt, operators) { - MessageQueue messageQueue; - EXPECT_TRUE(messageQueue == messageQueue); - - MessageQueue twoMessageQueue; - EXPECT_TRUE(messageQueue == twoMessageQueue); - - twoMessageQueue.setTopic("testTopic"); - EXPECT_FALSE(messageQueue == twoMessageQueue); - - messageQueue.setQueueId(11); - EXPECT_FALSE(messageQueue == twoMessageQueue); - - messageQueue.setBrokerName("testBroker"); - EXPECT_FALSE(messageQueue == twoMessageQueue); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "messageExt.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/protocol/ProcessQueueInfoTest.cpp b/test/src/protocol/ProcessQueueInfoTest.cpp deleted file mode 100644 index bb5e7141e..000000000 --- a/test/src/protocol/ProcessQueueInfoTest.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "MQMessageExt.h" -#include "ProcessQueueInfo.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::MQMessageExt; -using rocketmq::ProcessQueueInfo; - -TEST(processQueueInfo, init) { - ProcessQueueInfo processQueueInfo; - EXPECT_EQ(processQueueInfo.commitOffset, 0); - EXPECT_EQ(processQueueInfo.cachedMsgMinOffset, 0); - EXPECT_EQ(processQueueInfo.cachedMsgMaxOffset, 0); - EXPECT_EQ(processQueueInfo.cachedMsgCount, 0); - EXPECT_EQ(processQueueInfo.transactionMsgMinOffset, 0); - EXPECT_EQ(processQueueInfo.transactionMsgMaxOffset, 0); - EXPECT_EQ(processQueueInfo.transactionMsgCount, 0); - EXPECT_EQ(processQueueInfo.locked, false); - EXPECT_EQ(processQueueInfo.tryUnlockTimes, 0); - EXPECT_EQ(processQueueInfo.lastLockTimestamp, 123); - EXPECT_EQ(processQueueInfo.droped, false); - EXPECT_EQ(processQueueInfo.lastPullTimestamp, 0); - EXPECT_EQ(processQueueInfo.lastConsumeTimestamp, 0); - - processQueueInfo.setLocked(true); - EXPECT_EQ(processQueueInfo.isLocked(), true); - - processQueueInfo.setDroped(true); - EXPECT_EQ(processQueueInfo.isDroped(), true); - - processQueueInfo.setCommitOffset(456); - EXPECT_EQ(processQueueInfo.getCommitOffset(), 456); - - Json::Value outJson = processQueueInfo.toJson(); - - EXPECT_EQ(outJson["commitOffset"], "456"); - EXPECT_EQ(outJson["cachedMsgMinOffset"], "0"); - EXPECT_EQ(outJson["cachedMsgMaxOffset"], "0"); - EXPECT_EQ(outJson["cachedMsgCount"].asInt(), 0); - EXPECT_EQ(outJson["transactionMsgMinOffset"], "0"); - EXPECT_EQ(outJson["transactionMsgMaxOffset"], "0"); - EXPECT_EQ(outJson["transactionMsgCount"].asInt(), 0); - EXPECT_EQ(outJson["locked"].asBool(), true); - EXPECT_EQ(outJson["tryUnlockTimes"].asInt(), 0); - EXPECT_EQ(outJson["lastLockTimestamp"], "123"); - EXPECT_EQ(outJson["droped"].asBool(), true); - EXPECT_EQ(outJson["lastPullTimestamp"], "0"); - EXPECT_EQ(outJson["lastConsumeTimestamp"], "0"); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "processQueueInfo.init"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/protocol/RemotingCommandTest.cpp b/test/src/protocol/RemotingCommandTest.cpp deleted file mode 100644 index ca28aa3f4..000000000 --- a/test/src/protocol/RemotingCommandTest.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "CommandHeader.h" -#include "MQProtos.h" -#include "MQVersion.h" -#include "MemoryOutputStream.h" -#include "RemotingCommand.h" -#include "dataBlock.h" - -using std::shared_ptr; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::CommandHeader; -using rocketmq::GetConsumerRunningInfoRequestHeader; -using rocketmq::GetEarliestMsgStoretimeResponseHeader; -using rocketmq::GetMaxOffsetResponseHeader; -using rocketmq::GetMinOffsetResponseHeader; -using rocketmq::GetRouteInfoRequestHeader; -using rocketmq::MemoryBlock; -using rocketmq::MemoryOutputStream; -using rocketmq::MQRequestCode; -using rocketmq::MQVersion; -using rocketmq::NotifyConsumerIdsChangedRequestHeader; -using rocketmq::PullMessageResponseHeader; -using rocketmq::QueryConsumerOffsetResponseHeader; -using rocketmq::RemotingCommand; -using rocketmq::ResetOffsetRequestHeader; -using rocketmq::SearchOffsetResponseHeader; -using rocketmq::SendMessageResponseHeader; - -TEST(remotingCommand, init) { - RemotingCommand remotingCommand; - EXPECT_EQ(remotingCommand.getCode(), 0); - - RemotingCommand twoRemotingCommand(13); - EXPECT_EQ(twoRemotingCommand.getCode(), 13); - EXPECT_EQ(twoRemotingCommand.getOpaque(), 0); - EXPECT_EQ(twoRemotingCommand.getRemark(), ""); - EXPECT_EQ(twoRemotingCommand.getVersion(), MQVersion::s_CurrentVersion); - // EXPECT_EQ(twoRemotingCommand.getFlag() , 0); - EXPECT_EQ(twoRemotingCommand.getMsgBody(), ""); - EXPECT_TRUE(twoRemotingCommand.getCommandHeader() == nullptr); - - RemotingCommand threeRemotingCommand(13, new GetRouteInfoRequestHeader("topic")); - EXPECT_FALSE(threeRemotingCommand.getCommandHeader() == nullptr); - - RemotingCommand frouRemotingCommand(13, "CPP", MQVersion::s_CurrentVersion, 12, 3, "remark", - new GetRouteInfoRequestHeader("topic")); - EXPECT_EQ(frouRemotingCommand.getCode(), 13); - EXPECT_EQ(frouRemotingCommand.getOpaque(), 12); - EXPECT_EQ(frouRemotingCommand.getRemark(), "remark"); - EXPECT_EQ(frouRemotingCommand.getVersion(), MQVersion::s_CurrentVersion); - EXPECT_EQ(frouRemotingCommand.getFlag(), 3); - EXPECT_EQ(frouRemotingCommand.getMsgBody(), ""); - EXPECT_FALSE(frouRemotingCommand.getCommandHeader() == nullptr); - - RemotingCommand sixRemotingCommand(frouRemotingCommand); - EXPECT_EQ(sixRemotingCommand.getCode(), 13); - EXPECT_EQ(sixRemotingCommand.getOpaque(), 12); - EXPECT_EQ(sixRemotingCommand.getRemark(), "remark"); - EXPECT_EQ(sixRemotingCommand.getVersion(), MQVersion::s_CurrentVersion); - EXPECT_EQ(sixRemotingCommand.getFlag(), 3); - EXPECT_EQ(sixRemotingCommand.getMsgBody(), ""); - EXPECT_TRUE(sixRemotingCommand.getCommandHeader() == nullptr); - - RemotingCommand* sevenRemotingCommand = &sixRemotingCommand; - EXPECT_EQ(sevenRemotingCommand->getCode(), 13); - EXPECT_EQ(sevenRemotingCommand->getOpaque(), 12); - EXPECT_EQ(sevenRemotingCommand->getRemark(), "remark"); - EXPECT_EQ(sevenRemotingCommand->getVersion(), MQVersion::s_CurrentVersion); - EXPECT_EQ(sevenRemotingCommand->getFlag(), 3); - EXPECT_EQ(sevenRemotingCommand->getMsgBody(), ""); - EXPECT_TRUE(sevenRemotingCommand->getCommandHeader() == nullptr); -} - -TEST(remotingCommand, info) { - RemotingCommand remotingCommand; - - remotingCommand.setCode(13); - EXPECT_EQ(remotingCommand.getCode(), 13); - - remotingCommand.setOpaque(12); - EXPECT_EQ(remotingCommand.getOpaque(), 12); - - remotingCommand.setRemark("123"); - EXPECT_EQ(remotingCommand.getRemark(), "123"); - - remotingCommand.setMsgBody("msgBody"); - EXPECT_EQ(remotingCommand.getMsgBody(), "msgBody"); - - remotingCommand.addExtField("key", "value"); -} - -TEST(remotingCommand, flag) { - RemotingCommand remotingCommand(13, "CPP", MQVersion::s_CurrentVersion, 12, 0, "remark", - new GetRouteInfoRequestHeader("topic")); - ; - EXPECT_EQ(remotingCommand.getFlag(), 0); - - remotingCommand.markResponseType(); - int bits = 1 << 0; - int flag = 0; - flag |= bits; - EXPECT_EQ(remotingCommand.getFlag(), flag); - EXPECT_TRUE(remotingCommand.isResponseType()); - - bits = 1 << 1; - flag |= bits; - remotingCommand.markOnewayRPC(); - EXPECT_EQ(remotingCommand.getFlag(), flag); - EXPECT_TRUE(remotingCommand.isOnewayRPC()); -} - -TEST(remotingCommand, encodeAndDecode) { - RemotingCommand remotingCommand(13, "CPP", MQVersion::s_CurrentVersion, 12, 3, "remark", NULL); - remotingCommand.SetBody("123123", 6); - remotingCommand.Encode(); - // no delete - const MemoryBlock* head = remotingCommand.GetHead(); - const MemoryBlock* body = remotingCommand.GetBody(); - - unique_ptr result(new MemoryOutputStream(1024)); - result->write(head->getData() + 4, head->getSize() - 4); - result->write(body->getData(), body->getSize()); - - shared_ptr decodeRemtingCommand(RemotingCommand::Decode(result->getMemoryBlock())); - EXPECT_EQ(remotingCommand.getCode(), decodeRemtingCommand->getCode()); - EXPECT_EQ(remotingCommand.getOpaque(), decodeRemtingCommand->getOpaque()); - EXPECT_EQ(remotingCommand.getRemark(), decodeRemtingCommand->getRemark()); - EXPECT_EQ(remotingCommand.getVersion(), decodeRemtingCommand->getVersion()); - EXPECT_EQ(remotingCommand.getFlag(), decodeRemtingCommand->getFlag()); - EXPECT_TRUE(decodeRemtingCommand->getCommandHeader() == NULL); - - // ~RemotingCommand delete - GetConsumerRunningInfoRequestHeader* requestHeader = new GetConsumerRunningInfoRequestHeader(); - requestHeader->setClientId("client"); - requestHeader->setConsumerGroup("consumerGroup"); - requestHeader->setJstackEnable(false); - - RemotingCommand encodeRemotingCommand(307, "CPP", MQVersion::s_CurrentVersion, 12, 3, "remark", requestHeader); - encodeRemotingCommand.SetBody("123123", 6); - encodeRemotingCommand.Encode(); - - // no delete - const MemoryBlock* phead = encodeRemotingCommand.GetHead(); - const MemoryBlock* pbody = encodeRemotingCommand.GetBody(); - - unique_ptr results(new MemoryOutputStream(1024)); - results->write(phead->getData() + 4, phead->getSize() - 4); - results->write(pbody->getData(), pbody->getSize()); - - shared_ptr decodeRemtingCommandTwo(RemotingCommand::Decode(results->getMemoryBlock())); - - decodeRemtingCommandTwo->SetExtHeader(encodeRemotingCommand.getCode()); - GetConsumerRunningInfoRequestHeader* header = - reinterpret_cast(decodeRemtingCommandTwo->getCommandHeader()); - EXPECT_EQ(requestHeader->getClientId(), header->getClientId()); - EXPECT_EQ(requestHeader->getConsumerGroup(), header->getConsumerGroup()); -} - -TEST(remotingCommand, SetExtHeader) { - shared_ptr remotingCommand(new RemotingCommand()); - - remotingCommand->SetExtHeader(-1); - EXPECT_TRUE(remotingCommand->getCommandHeader() == NULL); - - Json::Value object; - Json::Value extFields; - extFields["id"] = 1; - object["extFields"] = extFields; - remotingCommand->setParsedJson(object); - - remotingCommand->SetExtHeader(MQRequestCode::SEND_MESSAGE); - SendMessageResponseHeader* sendMessageResponseHeader = - reinterpret_cast(remotingCommand->getCommandHeader()); - EXPECT_EQ(sendMessageResponseHeader->msgId, ""); - - remotingCommand->SetExtHeader(MQRequestCode::PULL_MESSAGE); - PullMessageResponseHeader* pullMessageResponseHeader = - reinterpret_cast(remotingCommand->getCommandHeader()); - EXPECT_EQ(pullMessageResponseHeader->suggestWhichBrokerId, 0); - - remotingCommand->SetExtHeader(MQRequestCode::GET_MIN_OFFSET); - GetMinOffsetResponseHeader* getMinOffsetResponseHeader = - reinterpret_cast(remotingCommand->getCommandHeader()); - EXPECT_EQ(getMinOffsetResponseHeader->offset, 0); - - remotingCommand->SetExtHeader(MQRequestCode::GET_MAX_OFFSET); - GetMaxOffsetResponseHeader* getMaxOffsetResponseHeader = - reinterpret_cast(remotingCommand->getCommandHeader()); - EXPECT_EQ(getMaxOffsetResponseHeader->offset, 0); - - remotingCommand->SetExtHeader(MQRequestCode::SEARCH_OFFSET_BY_TIMESTAMP); - SearchOffsetResponseHeader* searchOffsetResponseHeader = - reinterpret_cast(remotingCommand->getCommandHeader()); - EXPECT_EQ(searchOffsetResponseHeader->offset, 0); - - remotingCommand->SetExtHeader(MQRequestCode::GET_EARLIEST_MSG_STORETIME); - GetEarliestMsgStoretimeResponseHeader* getEarliestMsgStoretimeResponseHeader = - reinterpret_cast(remotingCommand->getCommandHeader()); - EXPECT_EQ(getEarliestMsgStoretimeResponseHeader->timestamp, 0); - - remotingCommand->SetExtHeader(MQRequestCode::QUERY_CONSUMER_OFFSET); - QueryConsumerOffsetResponseHeader* queryConsumerOffsetResponseHeader = - reinterpret_cast(remotingCommand->getCommandHeader()); - EXPECT_EQ(queryConsumerOffsetResponseHeader->offset, 0); - - extFields["isForce"] = "true"; - object["extFields"] = extFields; - remotingCommand->setParsedJson(object); - remotingCommand->SetExtHeader(MQRequestCode::RESET_CONSUMER_CLIENT_OFFSET); - ResetOffsetRequestHeader* resetOffsetRequestHeader = - reinterpret_cast(remotingCommand->getCommandHeader()); - resetOffsetRequestHeader->setGroup("group"); - - remotingCommand->SetExtHeader(MQRequestCode::GET_CONSUMER_RUNNING_INFO); - GetConsumerRunningInfoRequestHeader* getConsumerRunningInfoRequestHeader = - reinterpret_cast(remotingCommand->getCommandHeader()); - getConsumerRunningInfoRequestHeader->setClientId("id"); - - remotingCommand->SetExtHeader(MQRequestCode::NOTIFY_CONSUMER_IDS_CHANGED); - NotifyConsumerIdsChangedRequestHeader* notifyConsumerIdsChangedRequestHeader = - reinterpret_cast(remotingCommand->getCommandHeader()); - notifyConsumerIdsChangedRequestHeader->setGroup("group"); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "remotingCommand.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/protocol/TopicRouteDataTest.cpp b/test/src/protocol/TopicRouteDataTest.cpp deleted file mode 100644 index cf88ac106..000000000 --- a/test/src/protocol/TopicRouteDataTest.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "TopicRouteData.h" -#include "dataBlock.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::BrokerData; -using rocketmq::MemoryBlock; -using rocketmq::QueueData; -using rocketmq::TopicRouteData; - -TEST(topicRouteData, topicRouteData) { - Json::Value root; - root["orderTopicConf"] = "orderTopicConf"; - - Json::Value queueDatas; - Json::Value queueData; - - queueData["brokerName"] = "brokerTest"; - queueData["readQueueNums"] = 8; - queueData["writeQueueNums"] = 8; - queueData["perm"] = 7; - queueDatas[0] = queueData; - - root["queueDatas"] = queueDatas; - - Json::Value brokerDatas; - Json::Value brokerData; - brokerData["brokerName"] = "testBroker"; - - Json::Value brokerAddrs; - brokerAddrs["0"] = "127.0.0.1:10091"; - brokerAddrs["1"] = "127.0.0.2:10092"; - brokerData["brokerAddrs"] = brokerAddrs; - - brokerDatas[0] = brokerData; - - root["brokerDatas"] = brokerDatas; - string out = root.toStyledString(); - - MemoryBlock* block = new MemoryBlock(out.c_str(), out.size()); - TopicRouteData* topicRouteData = TopicRouteData::Decode(block); - EXPECT_EQ(root["orderTopicConf"], topicRouteData->getOrderTopicConf()); - - BrokerData broker; - broker.brokerName = "testBroker"; - broker.brokerAddrs[0] = "127.0.0.1:10091"; - broker.brokerAddrs[1] = "127.0.0.2:10092"; - - vector brokerDataSt = topicRouteData->getBrokerDatas(); - EXPECT_EQ(broker, brokerDataSt[0]); - - QueueData queue; - queue.brokerName = "brokerTest"; - queue.readQueueNums = 8; - queue.writeQueueNums = 8; - queue.perm = 7; - vector queueDataSt = topicRouteData->getQueueDatas(); - EXPECT_EQ(queue, queueDataSt[0]); - - EXPECT_EQ(topicRouteData->selectBrokerAddr(), "127.0.0.1:10091"); - - delete block; - delete topicRouteData; -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "topicRouteData.topicRouteData"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/trace/TraceBeanTest.cpp b/test/src/trace/TraceBeanTest.cpp deleted file mode 100644 index 7fcbe32c0..000000000 --- a/test/src/trace/TraceBeanTest.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "TraceBean.h" - -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::TraceBean; -using rocketmq::TraceMessageType; - -TEST(TraceBean, Init) { - std::string m_topic("topic"); - std::string m_msgId("msgid"); - std::string m_offsetMsgId("offsetmsgid"); - std::string m_tags("tag"); - std::string m_keys("ksy"); - std::string m_storeHost("storehost"); - std::string m_clientHost("clienthost"); - TraceMessageType m_msgType = TraceMessageType::TRACE_NORMAL_MSG; - long long m_storeTime = 100; - int m_retryTimes = 2; - int m_bodyLength = 1024; - TraceBean bean; - bean.setTopic(m_topic); - bean.setMsgId(m_msgId); - bean.setOffsetMsgId(m_offsetMsgId); - bean.setTags(m_tags); - bean.setKeys(m_keys); - bean.setStoreHost(m_storeHost); - bean.setClientHost(m_clientHost); - bean.setMsgType(m_msgType); - bean.setStoreTime(m_storeTime); - bean.setRetryTimes(m_retryTimes); - bean.setBodyLength(m_bodyLength); - EXPECT_EQ(bean.getTopic(), m_topic); - EXPECT_EQ(bean.getMsgId(), m_msgId); - EXPECT_EQ(bean.getOffsetMsgId(), m_offsetMsgId); - EXPECT_EQ(bean.getTags(), m_tags); - EXPECT_EQ(bean.getKeys(), m_keys); - EXPECT_EQ(bean.getStoreHost(), m_storeHost); - EXPECT_EQ(bean.getClientHost(), m_clientHost); - EXPECT_EQ(bean.getMsgType(), m_msgType); - EXPECT_EQ(bean.getStoreTime(), m_storeTime); - EXPECT_EQ(bean.getRetryTimes(), m_retryTimes); - EXPECT_EQ(bean.getBodyLength(), m_bodyLength); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "TraceBean.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/trace/TraceUtilTest.cpp b/test/src/trace/TraceUtilTest.cpp deleted file mode 100644 index 8fa6cff41..000000000 --- a/test/src/trace/TraceUtilTest.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "TraceConstant.h" -#include "TraceUtil.h" - -using std::string; - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::TraceBean; -using rocketmq::TraceConstant; -using rocketmq::TraceContext; -using rocketmq::TraceMessageType; -using rocketmq::TraceTransferBean; -using rocketmq::TraceType; -using rocketmq::TraceUtil; - -TEST(TraceUtil, CovertTraceTypeToString) { - EXPECT_EQ(TraceUtil::CovertTraceTypeToString(TraceType::Pub), TraceConstant::TRACE_TYPE_PUB); - EXPECT_EQ(TraceUtil::CovertTraceTypeToString(TraceType::SubBefore), TraceConstant::TRACE_TYPE_BEFORE); - EXPECT_EQ(TraceUtil::CovertTraceTypeToString(TraceType::SubAfter), TraceConstant::TRACE_TYPE_AFTER); - EXPECT_EQ(TraceUtil::CovertTraceTypeToString((TraceType)5), TraceConstant::TRACE_TYPE_PUB); -} -TEST(TraceUtil, CovertTraceContextToTransferBean) { - TraceContext context; - TraceBean bean; - bean.setMsgType(TraceMessageType::TRACE_NORMAL_MSG); - bean.setMsgId("MessageID"); - bean.setKeys("MessageKey"); - context.setRegionId("region"); - context.setMsgType(TraceMessageType::TRACE_TRANS_COMMIT_MSG); - context.setTraceType(TraceType::Pub); - context.setGroupName("PubGroup"); - context.setCostTime(50); - context.setStatus(true); - context.setTraceBean(bean); - context.setTraceBeanIndex(1); - TraceTransferBean beanPub = TraceUtil::CovertTraceContextToTransferBean(&context); - EXPECT_GT(beanPub.getTransKey().size(), 0); - context.setTraceType(TraceType::SubBefore); - TraceTransferBean beanBefore = TraceUtil::CovertTraceContextToTransferBean(&context); - EXPECT_GT(beanBefore.getTransKey().size(), 0); - - context.setTraceType(TraceType::SubAfter); - TraceTransferBean beanAfter = TraceUtil::CovertTraceContextToTransferBean(&context); - EXPECT_GT(beanAfter.getTransKey().size(), 0); - - TraceContext contextFailed("testGroup"); - contextFailed.setMsgType(context.getMsgType()); - contextFailed.setTraceType((TraceType)5); - contextFailed.setRequestId(context.getRegionId()); - contextFailed.setTimeStamp(context.getTimeStamp()); - contextFailed.setTraceBeanIndex(context.getTraceBeanIndex()); - TraceTransferBean beanWrong = TraceUtil::CovertTraceContextToTransferBean(&contextFailed); - EXPECT_EQ(beanWrong.getTransKey().size(), 0); -} -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "TraceUtil.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/transport/ClientRemotingProcessorTest.cpp b/test/src/transport/ClientRemotingProcessorTest.cpp deleted file mode 100644 index 716150d1c..000000000 --- a/test/src/transport/ClientRemotingProcessorTest.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "map" -#include "string.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "json/value.h" -#include "json/writer.h" - -#include "ClientRPCHook.h" -#include "ClientRemotingProcessor.h" -#include "ConsumerRunningInfo.h" -#include "MQClientFactory.h" -#include "MQMessageQueue.h" -#include "MQProtos.h" -#include "RemotingCommand.h" -#include "SessionCredentials.h" -#include "UtilAll.h" -#include "dataBlock.h" - -using std::map; -using std::string; - -using ::testing::_; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Mock; -using testing::Return; -using testing::SetArgReferee; - -using Json::FastWriter; -using Json::Value; - -using rocketmq::ClientRemotingProcessor; -using rocketmq::ClientRPCHook; -using rocketmq::ConsumerRunningInfo; -using rocketmq::GetConsumerRunningInfoRequestHeader; -using rocketmq::MemoryBlock; -using rocketmq::MQClientFactory; -using rocketmq::MQMessageQueue; -using rocketmq::MQRequestCode; -using rocketmq::MQResponseCode; -using rocketmq::NotifyConsumerIdsChangedRequestHeader; -using rocketmq::RemotingCommand; -using rocketmq::ResetOffsetBody; -using rocketmq::ResetOffsetRequestHeader; -using rocketmq::SessionCredentials; -using rocketmq::UtilAll; - -class MockClientRemotingProcessor : public ClientRemotingProcessor { - public: - MockClientRemotingProcessor(MQClientFactory* factrory) : ClientRemotingProcessor(factrory) {} - MOCK_METHOD1(resetOffset, RemotingCommand*(RemotingCommand* request)); - MOCK_METHOD1(getConsumerRunningInfo, RemotingCommand*(RemotingCommand* request)); - MOCK_METHOD1(notifyConsumerIdsChanged, RemotingCommand*(RemotingCommand* request)); -}; - -class MockMQClientFactory : public MQClientFactory { - public: - MockMQClientFactory(const string& clientID, - int pullThreadNum, - uint64_t tcpConnectTimeout, - uint64_t tcpTransportTryLockTimeout, - string unitName) - : MQClientFactory(clientID, - pullThreadNum, - tcpConnectTimeout, - tcpTransportTryLockTimeout, - unitName, - true, - rocketmq::DEFAULT_SSL_PROPERTY_FILE) {} - - MOCK_METHOD3(resetOffset, - void(const string& group, const string& topic, const map& offsetTable)); - MOCK_METHOD1(consumerRunningInfo, ConsumerRunningInfo*(const string& consumerGroup)); - MOCK_METHOD2(getSessionCredentialFromConsumer, - bool(const string& consumerGroup, SessionCredentials& sessionCredentials)); - MOCK_METHOD1(doRebalanceByConsumerGroup, void(const string& consumerGroup)); -}; - -TEST(clientRemotingProcessor, processRequest) { - MockMQClientFactory* factory = new MockMQClientFactory("testClientId", 4, 3000, 4000, "a"); - ClientRemotingProcessor clientRemotingProcessor(factory); - - string addr = "127.0.0.1:9876"; - RemotingCommand* command = new RemotingCommand(); - RemotingCommand* pResponse = new RemotingCommand(13); - - pResponse->setCode(MQRequestCode::RESET_CONSUMER_CLIENT_OFFSET); - command->setCode(MQRequestCode::RESET_CONSUMER_CLIENT_OFFSET); - EXPECT_TRUE(clientRemotingProcessor.processRequest(addr, command) == nullptr); - EXPECT_EQ(nullptr, clientRemotingProcessor.processRequest(addr, command)); - - NotifyConsumerIdsChangedRequestHeader* header = new NotifyConsumerIdsChangedRequestHeader(); - header->setGroup("testGroup"); - RemotingCommand* twoCommand = new RemotingCommand(MQRequestCode::NOTIFY_CONSUMER_IDS_CHANGED, header); - - EXPECT_EQ(NULL, clientRemotingProcessor.processRequest(addr, twoCommand)); - - command->setCode(MQRequestCode::GET_CONSUMER_RUNNING_INFO); - // EXPECT_EQ(NULL , clientRemotingProcessor.processRequest(addr, command)); - - command->setCode(MQRequestCode::CHECK_TRANSACTION_STATE); - EXPECT_TRUE(clientRemotingProcessor.processRequest(addr, command) == nullptr); - - command->setCode(MQRequestCode::GET_CONSUMER_STATUS_FROM_CLIENT); - EXPECT_TRUE(clientRemotingProcessor.processRequest(addr, command) == nullptr); - - command->setCode(MQRequestCode::CONSUME_MESSAGE_DIRECTLY); - EXPECT_TRUE(clientRemotingProcessor.processRequest(addr, command) == nullptr); - - command->setCode(1); - EXPECT_TRUE(clientRemotingProcessor.processRequest(addr, command) == nullptr); - - delete twoCommand; - delete command; - delete pResponse; -} - -TEST(clientRemotingProcessor, resetOffset) { - MockMQClientFactory* factory = new MockMQClientFactory("testClientId", 4, 3000, 4000, "a"); - Mock::AllowLeak(factory); - ClientRemotingProcessor clientRemotingProcessor(factory); - Value root; - Value messageQueues; - Value messageQueue; - messageQueue["brokerName"] = "testBroker"; - messageQueue["queueId"] = 4; - messageQueue["topic"] = "testTopic"; - messageQueue["offset"] = 1024; - - messageQueues.append(messageQueue); - root["offsetTable"] = messageQueues; - - FastWriter wrtier; - string strData = wrtier.write(root); - - ResetOffsetRequestHeader* header = new ResetOffsetRequestHeader(); - RemotingCommand* request = new RemotingCommand(13, header); - - EXPECT_CALL(*factory, resetOffset(_, _, _)).Times(1); - clientRemotingProcessor.resetOffset(request); - - request->SetBody(strData.c_str(), strData.size() - 2); - clientRemotingProcessor.resetOffset(request); - - request->SetBody(strData.c_str(), strData.size()); - clientRemotingProcessor.resetOffset(request); - - // here header no need delete, it will managered by RemotingCommand - // delete header; - delete request; -} - -TEST(clientRemotingProcessor, getConsumerRunningInfoFailed) { - MockMQClientFactory* factory = new MockMQClientFactory("testClientId", 4, 3000, 4000, "a"); - Mock::AllowLeak(factory); - ConsumerRunningInfo* info = new ConsumerRunningInfo(); - EXPECT_CALL(*factory, consumerRunningInfo(_)).Times(2).WillOnce(Return(info)).WillOnce(Return(info)); - EXPECT_CALL(*factory, getSessionCredentialFromConsumer(_, _)) - .Times(2); //.WillRepeatedly(SetArgReferee<1>(sessionCredentials)); - ClientRemotingProcessor clientRemotingProcessor(factory); - - GetConsumerRunningInfoRequestHeader* header = new GetConsumerRunningInfoRequestHeader(); - header->setConsumerGroup("testGroup"); - header->setClientId("testClientId"); - header->setJstackEnable(false); - - RemotingCommand* request = new RemotingCommand(14, header); - - RemotingCommand* command = clientRemotingProcessor.getConsumerRunningInfo("127.0.0.1:9876", request); - EXPECT_EQ(command->getCode(), MQResponseCode::SYSTEM_ERROR); - EXPECT_EQ(command->getRemark(), "The Consumer Group not exist in this consumer"); - delete command; - delete request; -} - -TEST(clientRemotingProcessor, notifyConsumerIdsChanged) { - MockMQClientFactory* factory = new MockMQClientFactory("testClientId", 4, 3000, 4000, "a"); - Mock::AllowLeak(factory); - ClientRemotingProcessor clientRemotingProcessor(factory); - NotifyConsumerIdsChangedRequestHeader* header = new NotifyConsumerIdsChangedRequestHeader(); - header->setGroup("testGroup"); - RemotingCommand* request = new RemotingCommand(14, header); - - EXPECT_CALL(*factory, doRebalanceByConsumerGroup(_)).Times(1); - clientRemotingProcessor.notifyConsumerIdsChanged(request); - - delete request; -} - -TEST(clientRemotingProcessor, resetOffsetBody) { - MockMQClientFactory* factory = new MockMQClientFactory("testClientId", 4, 3000, 4000, "a"); - ClientRemotingProcessor clientRemotingProcessor(factory); - - Value root; - Value messageQueues; - Value messageQueue; - messageQueue["brokerName"] = "testBroker"; - messageQueue["queueId"] = 4; - messageQueue["topic"] = "testTopic"; - messageQueue["offset"] = 1024; - - messageQueues.append(messageQueue); - root["offsetTable"] = messageQueues; - - FastWriter wrtier; - string strData = wrtier.write(root); - - MemoryBlock* mem = new MemoryBlock(strData.c_str(), strData.size()); - - ResetOffsetBody* resetOffset = ResetOffsetBody::Decode(mem); - - map map = resetOffset->getOffsetTable(); - MQMessageQueue mqmq("testTopic", "testBroker", 4); - EXPECT_EQ(map[mqmq], 1024); - Mock::AllowLeak(factory); - delete resetOffset; - delete mem; -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "clientRemotingProcessor.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/transport/ResponseFutureTest.cpp b/test/src/transport/ResponseFutureTest.cpp deleted file mode 100644 index 058dbe7cd..000000000 --- a/test/src/transport/ResponseFutureTest.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "AsyncCallback.h" -#include "AsyncCallbackWrap.h" -#include "MQClientAPIImpl.h" -#include "MQMessage.h" -#include "RemotingCommand.h" -#include "ResponseFuture.h" -#include "TcpRemotingClient.h" -#include "UtilAll.h" - -using ::testing::_; -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -using rocketmq::AsyncCallback; -using rocketmq::AsyncCallbackStatus; -using rocketmq::asyncCallBackType; -using rocketmq::AsyncCallbackWrap; -using rocketmq::MQClientAPIImpl; -using rocketmq::MQMessage; -using rocketmq::RemotingCommand; -using rocketmq::ResponseFuture; -using rocketmq::SendCallbackWrap; -using rocketmq::TcpRemotingClient; -using rocketmq::UtilAll; - -class MockAsyncCallbackWrap : public SendCallbackWrap { - public: - MockAsyncCallbackWrap(AsyncCallback* pAsyncCallback, MQClientAPIImpl* pclientAPI) - : SendCallbackWrap("", MQMessage(), pAsyncCallback, pclientAPI) {} - - MOCK_METHOD2(operationComplete, void(ResponseFuture*, bool)); - MOCK_METHOD0(onException, void()); - asyncCallBackType getCallbackType() { return asyncCallBackType::sendCallbackWrap; } -}; - -TEST(responseFuture, init) { - ResponseFuture responseFuture(13, 4, NULL, 1000); - EXPECT_EQ(responseFuture.getRequestCode(), 13); - EXPECT_EQ(responseFuture.getOpaque(), 4); - - EXPECT_EQ(responseFuture.getRequestCommand().getCode(), 0); - EXPECT_FALSE(responseFuture.isSendRequestOK()); - EXPECT_EQ(responseFuture.getMaxRetrySendTimes(), 1); - EXPECT_EQ(responseFuture.getRetrySendTimes(), 1); - EXPECT_EQ(responseFuture.getBrokerAddr(), ""); - - EXPECT_FALSE(responseFuture.getAsyncFlag()); - EXPECT_TRUE(responseFuture.getAsyncCallbackWrap() == nullptr); - - // ~ResponseFuture delete pcall - std::shared_ptr callBack = std::make_shared("", MQMessage(), nullptr, nullptr); - ResponseFuture twoResponseFuture(13, 4, nullptr, 1000, true, callBack); - EXPECT_TRUE(twoResponseFuture.getAsyncFlag()); - EXPECT_FALSE(twoResponseFuture.getAsyncCallbackWrap() == nullptr); -} - -TEST(responseFuture, info) { - ResponseFuture responseFuture(13, 4, NULL, 1000); - - responseFuture.setBrokerAddr("127.0.0.1:9876"); - EXPECT_EQ(responseFuture.getBrokerAddr(), "127.0.0.1:9876"); - - responseFuture.setMaxRetrySendTimes(3000); - EXPECT_EQ(responseFuture.getMaxRetrySendTimes(), 3000); - - responseFuture.setRetrySendTimes(3000); - EXPECT_EQ(responseFuture.getRetrySendTimes(), 3000); - - responseFuture.setSendRequestOK(true); - EXPECT_TRUE(responseFuture.isSendRequestOK()); -} - -TEST(responseFuture, response) { - // m_bAsync = false m_syncResponse - ResponseFuture responseFuture(13, 4, NULL, 1000); - - EXPECT_FALSE(responseFuture.getAsyncFlag()); - - RemotingCommand* pResponseCommand = NULL; - responseFuture.setResponse(pResponseCommand); - EXPECT_EQ(responseFuture.getRequestCommand().getCode(), 0); - - // m_bAsync = true m_syncResponse - ResponseFuture twoResponseFuture(13, 4, NULL, 1000, true); - EXPECT_TRUE(twoResponseFuture.getAsyncFlag()); - - ResponseFuture threeResponseFuture(13, 4, NULL, 1000); - - uint64_t millis = UtilAll::currentTimeMillis(); - RemotingCommand* remotingCommand = threeResponseFuture.waitResponse(10); - uint64_t useTime = UtilAll::currentTimeMillis() - millis; - EXPECT_LT(useTime, 30); - - EXPECT_EQ(NULL, remotingCommand); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "responseFuture.*"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/test/src/transport/SocketUtilTest.cpp b/test/src/transport/SocketUtilTest.cpp deleted file mode 100644 index 09560b241..000000000 --- a/test/src/transport/SocketUtilTest.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "SocketUtil.h" - -using ::testing::InitGoogleMock; -using ::testing::InitGoogleTest; -using testing::Return; - -TEST(socketUtil, init) { - sockaddr addr = rocketmq::IPPort2socketAddress(inet_addr("127.0.0.1"), 10091); - - EXPECT_EQ(rocketmq::socketAddress2IPPort(addr), "1.0.0.127:10091"); - - int host; - int port; - - rocketmq::socketAddress2IPPort(addr, host, port); - EXPECT_EQ(host, inet_addr("127.0.0.1")); - EXPECT_EQ(port, 10091); - - EXPECT_EQ(rocketmq::socketAddress2String(addr), "1.0.0.127"); -} - -int main(int argc, char* argv[]) { - InitGoogleMock(&argc, argv); - testing::GTEST_FLAG(throw_on_failure) = true; - testing::GTEST_FLAG(filter) = "socketUtil.init"; - int itestts = RUN_ALL_TESTS(); - return itestts; -} diff --git a/third_party/BUILD.bazel b/third_party/BUILD.bazel new file mode 100644 index 000000000..28e0699af --- /dev/null +++ b/third_party/BUILD.bazel @@ -0,0 +1,16 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt new file mode 100644 index 000000000..f3aa57438 --- /dev/null +++ b/third_party/CMakeLists.txt @@ -0,0 +1,10 @@ +add_subdirectory(asio/1.18.2) +add_subdirectory(filesystem/1.5.12) +add_subdirectory(fmt/9.0.0) +add_subdirectory(spdlog/1.10.0) + +add_subdirectory(httplib/0.11.1) + +include(OpenCensusHelpers) +add_subdirectory(opencensus/0.5.0-alpha) +add_subdirectory(opencensus/proto) \ No newline at end of file diff --git a/third_party/ThreadPool.BUILD b/third_party/ThreadPool.BUILD new file mode 100644 index 000000000..616a34ee9 --- /dev/null +++ b/third_party/ThreadPool.BUILD @@ -0,0 +1,11 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "ThreadPool", + hdrs = [ + "ThreadPool.h", + ], + visibility = [ + "//visibility:public" + ], +) \ No newline at end of file diff --git a/third_party/asio.BUILD b/third_party/asio.BUILD new file mode 100644 index 000000000..98f590dd0 --- /dev/null +++ b/third_party/asio.BUILD @@ -0,0 +1,17 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "asio", + hdrs = glob(["include/**/*.hpp", "include/**/*.ipp"]), + visibility = ["//visibility:public"], + includes = [ + "include", + ], + defines = [ + "ASIO_STANDALONE", + "ASIO_HAS_STD_ADDRESSOF", + "ASIO_HAS_STD_ARRAY", + "ASIO_HAS_CSTDINT", + "ASIO_HAS_STD_SHARED_PTR", + "ASIO_HAS_STD_TYPE_TRAITS", + ], +) \ No newline at end of file diff --git a/third_party/asio/1.18.2/CMakeLists.txt b/third_party/asio/1.18.2/CMakeLists.txt new file mode 100644 index 000000000..cec071a09 --- /dev/null +++ b/third_party/asio/1.18.2/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(asio INTERFACE) +target_include_directories(asio + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include) \ No newline at end of file diff --git a/third_party/asio/1.18.2/include/Makefile.am b/third_party/asio/1.18.2/include/Makefile.am new file mode 100644 index 000000000..5ff1d260e --- /dev/null +++ b/third_party/asio/1.18.2/include/Makefile.am @@ -0,0 +1,566 @@ +# find . -name "*.*pp" | sed -e 's/^\.\///' | sed -e 's/^.*$/ & \\/' | sort +nobase_include_HEADERS = \ + asio/any_io_executor.hpp \ + asio/associated_allocator.hpp \ + asio/associated_executor.hpp \ + asio/async_result.hpp \ + asio/awaitable.hpp \ + asio/basic_datagram_socket.hpp \ + asio/basic_deadline_timer.hpp \ + asio/basic_io_object.hpp \ + asio/basic_raw_socket.hpp \ + asio/basic_seq_packet_socket.hpp \ + asio/basic_serial_port.hpp \ + asio/basic_signal_set.hpp \ + asio/basic_socket_acceptor.hpp \ + asio/basic_socket.hpp \ + asio/basic_socket_iostream.hpp \ + asio/basic_socket_streambuf.hpp \ + asio/basic_streambuf_fwd.hpp \ + asio/basic_streambuf.hpp \ + asio/basic_stream_socket.hpp \ + asio/basic_waitable_timer.hpp \ + asio/bind_executor.hpp \ + asio/buffered_read_stream_fwd.hpp \ + asio/buffered_read_stream.hpp \ + asio/buffered_stream_fwd.hpp \ + asio/buffered_stream.hpp \ + asio/buffered_write_stream_fwd.hpp \ + asio/buffered_write_stream.hpp \ + asio/buffer.hpp \ + asio/buffers_iterator.hpp \ + asio/co_spawn.hpp \ + asio/completion_condition.hpp \ + asio/compose.hpp \ + asio/connect.hpp \ + asio/coroutine.hpp \ + asio/deadline_timer.hpp \ + asio/defer.hpp \ + asio/detached.hpp \ + asio/detail/array_fwd.hpp \ + asio/detail/array.hpp \ + asio/detail/assert.hpp \ + asio/detail/atomic_count.hpp \ + asio/detail/base_from_completion_cond.hpp \ + asio/detail/bind_handler.hpp \ + asio/detail/blocking_executor_op.hpp \ + asio/detail/buffered_stream_storage.hpp \ + asio/detail/buffer_resize_guard.hpp \ + asio/detail/buffer_sequence_adapter.hpp \ + asio/detail/bulk_executor_op.hpp \ + asio/detail/call_stack.hpp \ + asio/detail/chrono.hpp \ + asio/detail/chrono_time_traits.hpp \ + asio/detail/completion_handler.hpp \ + asio/detail/concurrency_hint.hpp \ + asio/detail/conditionally_enabled_event.hpp \ + asio/detail/conditionally_enabled_mutex.hpp \ + asio/detail/config.hpp \ + asio/detail/consuming_buffers.hpp \ + asio/detail/cstddef.hpp \ + asio/detail/cstdint.hpp \ + asio/detail/date_time_fwd.hpp \ + asio/detail/deadline_timer_service.hpp \ + asio/detail/dependent_type.hpp \ + asio/detail/descriptor_ops.hpp \ + asio/detail/descriptor_read_op.hpp \ + asio/detail/descriptor_write_op.hpp \ + asio/detail/dev_poll_reactor.hpp \ + asio/detail/epoll_reactor.hpp \ + asio/detail/eventfd_select_interrupter.hpp \ + asio/detail/event.hpp \ + asio/detail/executor_function.hpp \ + asio/detail/executor_op.hpp \ + asio/detail/fd_set_adapter.hpp \ + asio/detail/fenced_block.hpp \ + asio/detail/functional.hpp \ + asio/detail/future.hpp \ + asio/detail/gcc_arm_fenced_block.hpp \ + asio/detail/gcc_hppa_fenced_block.hpp \ + asio/detail/gcc_sync_fenced_block.hpp \ + asio/detail/gcc_x86_fenced_block.hpp \ + asio/detail/global.hpp \ + asio/detail/handler_alloc_helpers.hpp \ + asio/detail/handler_cont_helpers.hpp \ + asio/detail/handler_invoke_helpers.hpp \ + asio/detail/handler_tracking.hpp \ + asio/detail/handler_type_requirements.hpp \ + asio/detail/handler_work.hpp \ + asio/detail/hash_map.hpp \ + asio/detail/impl/buffer_sequence_adapter.ipp \ + asio/detail/impl/descriptor_ops.ipp \ + asio/detail/impl/dev_poll_reactor.hpp \ + asio/detail/impl/dev_poll_reactor.ipp \ + asio/detail/impl/epoll_reactor.hpp \ + asio/detail/impl/epoll_reactor.ipp \ + asio/detail/impl/eventfd_select_interrupter.ipp \ + asio/detail/impl/handler_tracking.ipp \ + asio/detail/impl/kqueue_reactor.hpp \ + asio/detail/impl/kqueue_reactor.ipp \ + asio/detail/impl/null_event.ipp \ + asio/detail/impl/pipe_select_interrupter.ipp \ + asio/detail/impl/posix_event.ipp \ + asio/detail/impl/posix_mutex.ipp \ + asio/detail/impl/posix_thread.ipp \ + asio/detail/impl/posix_tss_ptr.ipp \ + asio/detail/impl/reactive_descriptor_service.ipp \ + asio/detail/impl/reactive_serial_port_service.ipp \ + asio/detail/impl/reactive_socket_service_base.ipp \ + asio/detail/impl/resolver_service_base.ipp \ + asio/detail/impl/scheduler.ipp \ + asio/detail/impl/select_reactor.hpp \ + asio/detail/impl/select_reactor.ipp \ + asio/detail/impl/service_registry.hpp \ + asio/detail/impl/service_registry.ipp \ + asio/detail/impl/signal_set_service.ipp \ + asio/detail/impl/socket_ops.ipp \ + asio/detail/impl/socket_select_interrupter.ipp \ + asio/detail/impl/strand_executor_service.hpp \ + asio/detail/impl/strand_executor_service.ipp \ + asio/detail/impl/strand_service.hpp \ + asio/detail/impl/strand_service.ipp \ + asio/detail/impl/thread_context.ipp \ + asio/detail/impl/throw_error.ipp \ + asio/detail/impl/timer_queue_ptime.ipp \ + asio/detail/impl/timer_queue_set.ipp \ + asio/detail/impl/win_event.ipp \ + asio/detail/impl/win_iocp_handle_service.ipp \ + asio/detail/impl/win_iocp_io_context.hpp \ + asio/detail/impl/win_iocp_io_context.ipp \ + asio/detail/impl/win_iocp_serial_port_service.ipp \ + asio/detail/impl/win_iocp_socket_service_base.ipp \ + asio/detail/impl/win_mutex.ipp \ + asio/detail/impl/win_object_handle_service.ipp \ + asio/detail/impl/winrt_ssocket_service_base.ipp \ + asio/detail/impl/winrt_timer_scheduler.hpp \ + asio/detail/impl/winrt_timer_scheduler.ipp \ + asio/detail/impl/winsock_init.ipp \ + asio/detail/impl/win_static_mutex.ipp \ + asio/detail/impl/win_thread.ipp \ + asio/detail/impl/win_tss_ptr.ipp \ + asio/detail/io_control.hpp \ + asio/detail/io_object_impl.hpp \ + asio/detail/is_buffer_sequence.hpp \ + asio/detail/is_executor.hpp \ + asio/detail/keyword_tss_ptr.hpp \ + asio/detail/kqueue_reactor.hpp \ + asio/detail/limits.hpp \ + asio/detail/local_free_on_block_exit.hpp \ + asio/detail/macos_fenced_block.hpp \ + asio/detail/memory.hpp \ + asio/detail/mutex.hpp \ + asio/detail/non_const_lvalue.hpp \ + asio/detail/noncopyable.hpp \ + asio/detail/null_event.hpp \ + asio/detail/null_fenced_block.hpp \ + asio/detail/null_global.hpp \ + asio/detail/null_mutex.hpp \ + asio/detail/null_reactor.hpp \ + asio/detail/null_signal_blocker.hpp \ + asio/detail/null_socket_service.hpp \ + asio/detail/null_static_mutex.hpp \ + asio/detail/null_thread.hpp \ + asio/detail/null_tss_ptr.hpp \ + asio/detail/object_pool.hpp \ + asio/detail/old_win_sdk_compat.hpp \ + asio/detail/operation.hpp \ + asio/detail/op_queue.hpp \ + asio/detail/pipe_select_interrupter.hpp \ + asio/detail/pop_options.hpp \ + asio/detail/posix_event.hpp \ + asio/detail/posix_fd_set_adapter.hpp \ + asio/detail/posix_global.hpp \ + asio/detail/posix_mutex.hpp \ + asio/detail/posix_signal_blocker.hpp \ + asio/detail/posix_static_mutex.hpp \ + asio/detail/posix_thread.hpp \ + asio/detail/posix_tss_ptr.hpp \ + asio/detail/push_options.hpp \ + asio/detail/reactive_descriptor_service.hpp \ + asio/detail/reactive_null_buffers_op.hpp \ + asio/detail/reactive_serial_port_service.hpp \ + asio/detail/reactive_socket_accept_op.hpp \ + asio/detail/reactive_socket_connect_op.hpp \ + asio/detail/reactive_socket_recvfrom_op.hpp \ + asio/detail/reactive_socket_recvmsg_op.hpp \ + asio/detail/reactive_socket_recv_op.hpp \ + asio/detail/reactive_socket_send_op.hpp \ + asio/detail/reactive_socket_sendto_op.hpp \ + asio/detail/reactive_socket_service_base.hpp \ + asio/detail/reactive_socket_service.hpp \ + asio/detail/reactive_wait_op.hpp \ + asio/detail/reactor_fwd.hpp \ + asio/detail/reactor.hpp \ + asio/detail/reactor_op.hpp \ + asio/detail/reactor_op_queue.hpp \ + asio/detail/recycling_allocator.hpp \ + asio/detail/regex_fwd.hpp \ + asio/detail/resolve_endpoint_op.hpp \ + asio/detail/resolve_op.hpp \ + asio/detail/resolve_query_op.hpp \ + asio/detail/resolver_service_base.hpp \ + asio/detail/resolver_service.hpp \ + asio/detail/scheduler.hpp \ + asio/detail/scheduler_operation.hpp \ + asio/detail/scheduler_thread_info.hpp \ + asio/detail/scoped_lock.hpp \ + asio/detail/scoped_ptr.hpp \ + asio/detail/select_interrupter.hpp \ + asio/detail/select_reactor.hpp \ + asio/detail/service_registry.hpp \ + asio/detail/signal_blocker.hpp \ + asio/detail/signal_handler.hpp \ + asio/detail/signal_init.hpp \ + asio/detail/signal_op.hpp \ + asio/detail/signal_set_service.hpp \ + asio/detail/socket_holder.hpp \ + asio/detail/socket_ops.hpp \ + asio/detail/socket_option.hpp \ + asio/detail/socket_select_interrupter.hpp \ + asio/detail/socket_types.hpp \ + asio/detail/solaris_fenced_block.hpp \ + asio/detail/source_location.hpp \ + asio/detail/static_mutex.hpp \ + asio/detail/std_event.hpp \ + asio/detail/std_fenced_block.hpp \ + asio/detail/std_global.hpp \ + asio/detail/std_mutex.hpp \ + asio/detail/std_static_mutex.hpp \ + asio/detail/std_thread.hpp \ + asio/detail/strand_executor_service.hpp \ + asio/detail/strand_service.hpp \ + asio/detail/string_view.hpp \ + asio/detail/thread_context.hpp \ + asio/detail/thread_group.hpp \ + asio/detail/thread.hpp \ + asio/detail/thread_info_base.hpp \ + asio/detail/throw_error.hpp \ + asio/detail/throw_exception.hpp \ + asio/detail/timer_queue_base.hpp \ + asio/detail/timer_queue.hpp \ + asio/detail/timer_queue_ptime.hpp \ + asio/detail/timer_queue_set.hpp \ + asio/detail/timer_scheduler_fwd.hpp \ + asio/detail/timer_scheduler.hpp \ + asio/detail/tss_ptr.hpp \ + asio/detail/type_traits.hpp \ + asio/detail/variadic_templates.hpp \ + asio/detail/wait_handler.hpp \ + asio/detail/wait_op.hpp \ + asio/detail/winapp_thread.hpp \ + asio/detail/wince_thread.hpp \ + asio/detail/win_event.hpp \ + asio/detail/win_fd_set_adapter.hpp \ + asio/detail/win_fenced_block.hpp \ + asio/detail/win_global.hpp \ + asio/detail/win_iocp_handle_read_op.hpp \ + asio/detail/win_iocp_handle_service.hpp \ + asio/detail/win_iocp_handle_write_op.hpp \ + asio/detail/win_iocp_io_context.hpp \ + asio/detail/win_iocp_null_buffers_op.hpp \ + asio/detail/win_iocp_operation.hpp \ + asio/detail/win_iocp_overlapped_op.hpp \ + asio/detail/win_iocp_overlapped_ptr.hpp \ + asio/detail/win_iocp_serial_port_service.hpp \ + asio/detail/win_iocp_socket_accept_op.hpp \ + asio/detail/win_iocp_socket_connect_op.hpp \ + asio/detail/win_iocp_socket_recvfrom_op.hpp \ + asio/detail/win_iocp_socket_recvmsg_op.hpp \ + asio/detail/win_iocp_socket_recv_op.hpp \ + asio/detail/win_iocp_socket_send_op.hpp \ + asio/detail/win_iocp_socket_service_base.hpp \ + asio/detail/win_iocp_socket_service.hpp \ + asio/detail/win_iocp_thread_info.hpp \ + asio/detail/win_iocp_wait_op.hpp \ + asio/detail/win_mutex.hpp \ + asio/detail/win_object_handle_service.hpp \ + asio/detail/winrt_async_manager.hpp \ + asio/detail/winrt_async_op.hpp \ + asio/detail/winrt_resolve_op.hpp \ + asio/detail/winrt_resolver_service.hpp \ + asio/detail/winrt_socket_connect_op.hpp \ + asio/detail/winrt_socket_recv_op.hpp \ + asio/detail/winrt_socket_send_op.hpp \ + asio/detail/winrt_ssocket_service_base.hpp \ + asio/detail/winrt_ssocket_service.hpp \ + asio/detail/winrt_timer_scheduler.hpp \ + asio/detail/winrt_utils.hpp \ + asio/detail/winsock_init.hpp \ + asio/detail/win_static_mutex.hpp \ + asio/detail/win_thread.hpp \ + asio/detail/win_tss_ptr.hpp \ + asio/detail/work_dispatcher.hpp \ + asio/detail/wrapped_handler.hpp \ + asio/dispatch.hpp \ + asio/error_code.hpp \ + asio/error.hpp \ + asio/execution.hpp \ + asio/execution_context.hpp \ + asio/execution/allocator.hpp \ + asio/execution/any_executor.hpp \ + asio/execution/bad_executor.hpp \ + asio/execution/blocking.hpp \ + asio/execution/blocking_adaptation.hpp \ + asio/execution/bulk_execute.hpp \ + asio/execution/bulk_guarantee.hpp \ + asio/execution/connect.hpp \ + asio/execution/context.hpp \ + asio/execution/context_as.hpp \ + asio/execution/detail/as_invocable.hpp \ + asio/execution/detail/as_operation.hpp \ + asio/execution/detail/as_receiver.hpp \ + asio/execution/detail/bulk_sender.hpp \ + asio/execution/detail/void_receiver.hpp \ + asio/execution/detail/submit_receiver.hpp \ + asio/execution/execute.hpp \ + asio/execution/executor.hpp \ + asio/execution/impl/bad_executor.ipp \ + asio/execution/impl/receiver_invocation_error.ipp \ + asio/execution/invocable_archetype.hpp \ + asio/execution/mapping.hpp \ + asio/execution/occupancy.hpp \ + asio/execution/operation_state.hpp \ + asio/execution/outstanding_work.hpp \ + asio/execution/prefer_only.hpp \ + asio/execution/receiver.hpp \ + asio/execution/receiver_invocation_error.hpp \ + asio/execution/relationship.hpp \ + asio/execution/schedule.hpp \ + asio/execution/scheduler.hpp \ + asio/execution/sender.hpp \ + asio/execution/set_done.hpp \ + asio/execution/set_error.hpp \ + asio/execution/set_value.hpp \ + asio/execution/start.hpp \ + asio/execution/submit.hpp \ + asio/executor.hpp \ + asio/executor_work_guard.hpp \ + asio/experimental/as_single.hpp \ + asio/experimental/impl/as_single.hpp \ + asio/generic/basic_endpoint.hpp \ + asio/generic/datagram_protocol.hpp \ + asio/generic/detail/endpoint.hpp \ + asio/generic/detail/impl/endpoint.ipp \ + asio/generic/raw_protocol.hpp \ + asio/generic/seq_packet_protocol.hpp \ + asio/generic/stream_protocol.hpp \ + asio/handler_alloc_hook.hpp \ + asio/handler_continuation_hook.hpp \ + asio/handler_invoke_hook.hpp \ + asio/high_resolution_timer.hpp \ + asio.hpp \ + asio/impl/awaitable.hpp \ + asio/impl/buffered_read_stream.hpp \ + asio/impl/buffered_write_stream.hpp \ + asio/impl/co_spawn.hpp \ + asio/impl/compose.hpp \ + asio/impl/connect.hpp \ + asio/impl/defer.hpp \ + asio/impl/detached.hpp \ + asio/impl/dispatch.hpp \ + asio/impl/error_code.ipp \ + asio/impl/error.ipp \ + asio/impl/execution_context.hpp \ + asio/impl/execution_context.ipp \ + asio/impl/executor.hpp \ + asio/impl/executor.ipp \ + asio/impl/handler_alloc_hook.ipp \ + asio/impl/io_context.hpp \ + asio/impl/io_context.ipp \ + asio/impl/multiple_exceptions.ipp \ + asio/impl/post.hpp \ + asio/impl/read_at.hpp \ + asio/impl/read.hpp \ + asio/impl/read_until.hpp \ + asio/impl/redirect_error.hpp \ + asio/impl/serial_port_base.hpp \ + asio/impl/serial_port_base.ipp \ + asio/impl/spawn.hpp \ + asio/impl/src.hpp \ + asio/impl/system_context.hpp \ + asio/impl/system_context.ipp \ + asio/impl/system_executor.hpp \ + asio/impl/thread_pool.hpp \ + asio/impl/thread_pool.ipp \ + asio/impl/use_awaitable.hpp \ + asio/impl/use_future.hpp \ + asio/impl/write_at.hpp \ + asio/impl/write.hpp \ + asio/io_context.hpp \ + asio/io_context_strand.hpp \ + asio/io_service.hpp \ + asio/io_service_strand.hpp \ + asio/ip/address.hpp \ + asio/ip/address_v4.hpp \ + asio/ip/address_v4_iterator.hpp \ + asio/ip/address_v4_range.hpp \ + asio/ip/address_v6.hpp \ + asio/ip/address_v6_iterator.hpp \ + asio/ip/address_v6_range.hpp \ + asio/ip/bad_address_cast.hpp \ + asio/ip/basic_endpoint.hpp \ + asio/ip/basic_resolver_entry.hpp \ + asio/ip/basic_resolver.hpp \ + asio/ip/basic_resolver_iterator.hpp \ + asio/ip/basic_resolver_query.hpp \ + asio/ip/basic_resolver_results.hpp \ + asio/ip/detail/endpoint.hpp \ + asio/ip/detail/impl/endpoint.ipp \ + asio/ip/detail/socket_option.hpp \ + asio/ip/host_name.hpp \ + asio/ip/icmp.hpp \ + asio/ip/impl/address.hpp \ + asio/ip/impl/address.ipp \ + asio/ip/impl/address_v4.hpp \ + asio/ip/impl/address_v4.ipp \ + asio/ip/impl/address_v6.hpp \ + asio/ip/impl/address_v6.ipp \ + asio/ip/impl/basic_endpoint.hpp \ + asio/ip/impl/host_name.ipp \ + asio/ip/impl/network_v4.hpp \ + asio/ip/impl/network_v4.ipp \ + asio/ip/impl/network_v6.hpp \ + asio/ip/impl/network_v6.ipp \ + asio/ip/multicast.hpp \ + asio/ip/network_v4.hpp \ + asio/ip/network_v6.hpp \ + asio/ip/resolver_base.hpp \ + asio/ip/resolver_query_base.hpp \ + asio/ip/tcp.hpp \ + asio/ip/udp.hpp \ + asio/ip/unicast.hpp \ + asio/ip/v6_only.hpp \ + asio/is_applicable_property.hpp \ + asio/is_executor.hpp \ + asio/is_read_buffered.hpp \ + asio/is_write_buffered.hpp \ + asio/local/basic_endpoint.hpp \ + asio/local/connect_pair.hpp \ + asio/local/datagram_protocol.hpp \ + asio/local/detail/endpoint.hpp \ + asio/local/detail/impl/endpoint.ipp \ + asio/local/stream_protocol.hpp \ + asio/multiple_exceptions.hpp \ + asio/packaged_task.hpp \ + asio/placeholders.hpp \ + asio/posix/basic_descriptor.hpp \ + asio/posix/basic_stream_descriptor.hpp \ + asio/posix/descriptor_base.hpp \ + asio/posix/descriptor.hpp \ + asio/posix/stream_descriptor.hpp \ + asio/post.hpp \ + asio/prefer.hpp \ + asio/query.hpp \ + asio/read_at.hpp \ + asio/read.hpp \ + asio/read_until.hpp \ + asio/redirect_error.hpp \ + asio/require.hpp \ + asio/require_concept.hpp \ + asio/serial_port_base.hpp \ + asio/serial_port.hpp \ + asio/signal_set.hpp \ + asio/socket_base.hpp \ + asio/spawn.hpp \ + asio/ssl/context_base.hpp \ + asio/ssl/context.hpp \ + asio/ssl/detail/buffered_handshake_op.hpp \ + asio/ssl/detail/engine.hpp \ + asio/ssl/detail/handshake_op.hpp \ + asio/ssl/detail/impl/engine.ipp \ + asio/ssl/detail/impl/openssl_init.ipp \ + asio/ssl/detail/io.hpp \ + asio/ssl/detail/openssl_init.hpp \ + asio/ssl/detail/openssl_types.hpp \ + asio/ssl/detail/password_callback.hpp \ + asio/ssl/detail/read_op.hpp \ + asio/ssl/detail/shutdown_op.hpp \ + asio/ssl/detail/stream_core.hpp \ + asio/ssl/detail/verify_callback.hpp \ + asio/ssl/detail/write_op.hpp \ + asio/ssl/error.hpp \ + asio/ssl.hpp \ + asio/ssl/host_name_verification.hpp \ + asio/ssl/impl/context.hpp \ + asio/ssl/impl/context.ipp \ + asio/ssl/impl/error.ipp \ + asio/ssl/impl/host_name_verification.ipp \ + asio/ssl/impl/rfc2818_verification.ipp \ + asio/ssl/impl/src.hpp \ + asio/ssl/rfc2818_verification.hpp \ + asio/ssl/stream_base.hpp \ + asio/ssl/stream.hpp \ + asio/ssl/verify_context.hpp \ + asio/ssl/verify_mode.hpp \ + asio/static_thread_pool.hpp \ + asio/steady_timer.hpp \ + asio/strand.hpp \ + asio/streambuf.hpp \ + asio/system_context.hpp \ + asio/system_error.hpp \ + asio/system_executor.hpp \ + asio/system_timer.hpp \ + asio/this_coro.hpp \ + asio/thread.hpp \ + asio/thread_pool.hpp \ + asio/time_traits.hpp \ + asio/traits/bulk_execute_free.hpp \ + asio/traits/bulk_execute_member.hpp \ + asio/traits/connect_free.hpp \ + asio/traits/connect_member.hpp \ + asio/traits/equality_comparable.hpp \ + asio/traits/execute_free.hpp \ + asio/traits/execute_member.hpp \ + asio/traits/prefer_free.hpp \ + asio/traits/prefer_member.hpp \ + asio/traits/query_free.hpp \ + asio/traits/query_member.hpp \ + asio/traits/query_static_constexpr_member.hpp \ + asio/traits/require_concept_free.hpp \ + asio/traits/require_concept_member.hpp \ + asio/traits/require_free.hpp \ + asio/traits/require_member.hpp \ + asio/traits/schedule_free.hpp \ + asio/traits/schedule_member.hpp \ + asio/traits/set_done_free.hpp \ + asio/traits/set_done_member.hpp \ + asio/traits/set_error_free.hpp \ + asio/traits/set_error_member.hpp \ + asio/traits/set_value_free.hpp \ + asio/traits/set_value_member.hpp \ + asio/traits/start_free.hpp \ + asio/traits/start_member.hpp \ + asio/traits/static_query.hpp \ + asio/traits/static_require.hpp \ + asio/traits/static_require_concept.hpp \ + asio/traits/submit_free.hpp \ + asio/traits/submit_member.hpp \ + asio/ts/buffer.hpp \ + asio/ts/executor.hpp \ + asio/ts/internet.hpp \ + asio/ts/io_context.hpp \ + asio/ts/netfwd.hpp \ + asio/ts/net.hpp \ + asio/ts/socket.hpp \ + asio/ts/timer.hpp \ + asio/unyield.hpp \ + asio/use_awaitable.hpp \ + asio/use_future.hpp \ + asio/uses_executor.hpp \ + asio/version.hpp \ + asio/wait_traits.hpp \ + asio/windows/basic_object_handle.hpp \ + asio/windows/basic_overlapped_handle.hpp \ + asio/windows/basic_random_access_handle.hpp \ + asio/windows/basic_stream_handle.hpp \ + asio/windows/object_handle.hpp \ + asio/windows/overlapped_handle.hpp \ + asio/windows/overlapped_ptr.hpp \ + asio/windows/random_access_handle.hpp \ + asio/windows/stream_handle.hpp \ + asio/write_at.hpp \ + asio/write.hpp \ + asio/yield.hpp + +MAINTAINERCLEANFILES = \ + $(srcdir)/Makefile.in diff --git a/third_party/asio/1.18.2/include/Makefile.in b/third_party/asio/1.18.2/include/Makefile.in new file mode 100644 index 000000000..fc25627e0 --- /dev/null +++ b/third_party/asio/1.18.2/include/Makefile.in @@ -0,0 +1,1105 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(nobase_include_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(includedir)" +HEADERS = $(nobase_include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# find . -name "*.*pp" | sed -e 's/^\.\///' | sed -e 's/^.*$/ & \\/' | sort +nobase_include_HEADERS = \ + asio/any_io_executor.hpp \ + asio/associated_allocator.hpp \ + asio/associated_executor.hpp \ + asio/async_result.hpp \ + asio/awaitable.hpp \ + asio/basic_datagram_socket.hpp \ + asio/basic_deadline_timer.hpp \ + asio/basic_io_object.hpp \ + asio/basic_raw_socket.hpp \ + asio/basic_seq_packet_socket.hpp \ + asio/basic_serial_port.hpp \ + asio/basic_signal_set.hpp \ + asio/basic_socket_acceptor.hpp \ + asio/basic_socket.hpp \ + asio/basic_socket_iostream.hpp \ + asio/basic_socket_streambuf.hpp \ + asio/basic_streambuf_fwd.hpp \ + asio/basic_streambuf.hpp \ + asio/basic_stream_socket.hpp \ + asio/basic_waitable_timer.hpp \ + asio/bind_executor.hpp \ + asio/buffered_read_stream_fwd.hpp \ + asio/buffered_read_stream.hpp \ + asio/buffered_stream_fwd.hpp \ + asio/buffered_stream.hpp \ + asio/buffered_write_stream_fwd.hpp \ + asio/buffered_write_stream.hpp \ + asio/buffer.hpp \ + asio/buffers_iterator.hpp \ + asio/co_spawn.hpp \ + asio/completion_condition.hpp \ + asio/compose.hpp \ + asio/connect.hpp \ + asio/coroutine.hpp \ + asio/deadline_timer.hpp \ + asio/defer.hpp \ + asio/detached.hpp \ + asio/detail/array_fwd.hpp \ + asio/detail/array.hpp \ + asio/detail/assert.hpp \ + asio/detail/atomic_count.hpp \ + asio/detail/base_from_completion_cond.hpp \ + asio/detail/bind_handler.hpp \ + asio/detail/blocking_executor_op.hpp \ + asio/detail/buffered_stream_storage.hpp \ + asio/detail/buffer_resize_guard.hpp \ + asio/detail/buffer_sequence_adapter.hpp \ + asio/detail/bulk_executor_op.hpp \ + asio/detail/call_stack.hpp \ + asio/detail/chrono.hpp \ + asio/detail/chrono_time_traits.hpp \ + asio/detail/completion_handler.hpp \ + asio/detail/concurrency_hint.hpp \ + asio/detail/conditionally_enabled_event.hpp \ + asio/detail/conditionally_enabled_mutex.hpp \ + asio/detail/config.hpp \ + asio/detail/consuming_buffers.hpp \ + asio/detail/cstddef.hpp \ + asio/detail/cstdint.hpp \ + asio/detail/date_time_fwd.hpp \ + asio/detail/deadline_timer_service.hpp \ + asio/detail/dependent_type.hpp \ + asio/detail/descriptor_ops.hpp \ + asio/detail/descriptor_read_op.hpp \ + asio/detail/descriptor_write_op.hpp \ + asio/detail/dev_poll_reactor.hpp \ + asio/detail/epoll_reactor.hpp \ + asio/detail/eventfd_select_interrupter.hpp \ + asio/detail/event.hpp \ + asio/detail/executor_function.hpp \ + asio/detail/executor_op.hpp \ + asio/detail/fd_set_adapter.hpp \ + asio/detail/fenced_block.hpp \ + asio/detail/functional.hpp \ + asio/detail/future.hpp \ + asio/detail/gcc_arm_fenced_block.hpp \ + asio/detail/gcc_hppa_fenced_block.hpp \ + asio/detail/gcc_sync_fenced_block.hpp \ + asio/detail/gcc_x86_fenced_block.hpp \ + asio/detail/global.hpp \ + asio/detail/handler_alloc_helpers.hpp \ + asio/detail/handler_cont_helpers.hpp \ + asio/detail/handler_invoke_helpers.hpp \ + asio/detail/handler_tracking.hpp \ + asio/detail/handler_type_requirements.hpp \ + asio/detail/handler_work.hpp \ + asio/detail/hash_map.hpp \ + asio/detail/impl/buffer_sequence_adapter.ipp \ + asio/detail/impl/descriptor_ops.ipp \ + asio/detail/impl/dev_poll_reactor.hpp \ + asio/detail/impl/dev_poll_reactor.ipp \ + asio/detail/impl/epoll_reactor.hpp \ + asio/detail/impl/epoll_reactor.ipp \ + asio/detail/impl/eventfd_select_interrupter.ipp \ + asio/detail/impl/handler_tracking.ipp \ + asio/detail/impl/kqueue_reactor.hpp \ + asio/detail/impl/kqueue_reactor.ipp \ + asio/detail/impl/null_event.ipp \ + asio/detail/impl/pipe_select_interrupter.ipp \ + asio/detail/impl/posix_event.ipp \ + asio/detail/impl/posix_mutex.ipp \ + asio/detail/impl/posix_thread.ipp \ + asio/detail/impl/posix_tss_ptr.ipp \ + asio/detail/impl/reactive_descriptor_service.ipp \ + asio/detail/impl/reactive_serial_port_service.ipp \ + asio/detail/impl/reactive_socket_service_base.ipp \ + asio/detail/impl/resolver_service_base.ipp \ + asio/detail/impl/scheduler.ipp \ + asio/detail/impl/select_reactor.hpp \ + asio/detail/impl/select_reactor.ipp \ + asio/detail/impl/service_registry.hpp \ + asio/detail/impl/service_registry.ipp \ + asio/detail/impl/signal_set_service.ipp \ + asio/detail/impl/socket_ops.ipp \ + asio/detail/impl/socket_select_interrupter.ipp \ + asio/detail/impl/strand_executor_service.hpp \ + asio/detail/impl/strand_executor_service.ipp \ + asio/detail/impl/strand_service.hpp \ + asio/detail/impl/strand_service.ipp \ + asio/detail/impl/thread_context.ipp \ + asio/detail/impl/throw_error.ipp \ + asio/detail/impl/timer_queue_ptime.ipp \ + asio/detail/impl/timer_queue_set.ipp \ + asio/detail/impl/win_event.ipp \ + asio/detail/impl/win_iocp_handle_service.ipp \ + asio/detail/impl/win_iocp_io_context.hpp \ + asio/detail/impl/win_iocp_io_context.ipp \ + asio/detail/impl/win_iocp_serial_port_service.ipp \ + asio/detail/impl/win_iocp_socket_service_base.ipp \ + asio/detail/impl/win_mutex.ipp \ + asio/detail/impl/win_object_handle_service.ipp \ + asio/detail/impl/winrt_ssocket_service_base.ipp \ + asio/detail/impl/winrt_timer_scheduler.hpp \ + asio/detail/impl/winrt_timer_scheduler.ipp \ + asio/detail/impl/winsock_init.ipp \ + asio/detail/impl/win_static_mutex.ipp \ + asio/detail/impl/win_thread.ipp \ + asio/detail/impl/win_tss_ptr.ipp \ + asio/detail/io_control.hpp \ + asio/detail/io_object_impl.hpp \ + asio/detail/is_buffer_sequence.hpp \ + asio/detail/is_executor.hpp \ + asio/detail/keyword_tss_ptr.hpp \ + asio/detail/kqueue_reactor.hpp \ + asio/detail/limits.hpp \ + asio/detail/local_free_on_block_exit.hpp \ + asio/detail/macos_fenced_block.hpp \ + asio/detail/memory.hpp \ + asio/detail/mutex.hpp \ + asio/detail/non_const_lvalue.hpp \ + asio/detail/noncopyable.hpp \ + asio/detail/null_event.hpp \ + asio/detail/null_fenced_block.hpp \ + asio/detail/null_global.hpp \ + asio/detail/null_mutex.hpp \ + asio/detail/null_reactor.hpp \ + asio/detail/null_signal_blocker.hpp \ + asio/detail/null_socket_service.hpp \ + asio/detail/null_static_mutex.hpp \ + asio/detail/null_thread.hpp \ + asio/detail/null_tss_ptr.hpp \ + asio/detail/object_pool.hpp \ + asio/detail/old_win_sdk_compat.hpp \ + asio/detail/operation.hpp \ + asio/detail/op_queue.hpp \ + asio/detail/pipe_select_interrupter.hpp \ + asio/detail/pop_options.hpp \ + asio/detail/posix_event.hpp \ + asio/detail/posix_fd_set_adapter.hpp \ + asio/detail/posix_global.hpp \ + asio/detail/posix_mutex.hpp \ + asio/detail/posix_signal_blocker.hpp \ + asio/detail/posix_static_mutex.hpp \ + asio/detail/posix_thread.hpp \ + asio/detail/posix_tss_ptr.hpp \ + asio/detail/push_options.hpp \ + asio/detail/reactive_descriptor_service.hpp \ + asio/detail/reactive_null_buffers_op.hpp \ + asio/detail/reactive_serial_port_service.hpp \ + asio/detail/reactive_socket_accept_op.hpp \ + asio/detail/reactive_socket_connect_op.hpp \ + asio/detail/reactive_socket_recvfrom_op.hpp \ + asio/detail/reactive_socket_recvmsg_op.hpp \ + asio/detail/reactive_socket_recv_op.hpp \ + asio/detail/reactive_socket_send_op.hpp \ + asio/detail/reactive_socket_sendto_op.hpp \ + asio/detail/reactive_socket_service_base.hpp \ + asio/detail/reactive_socket_service.hpp \ + asio/detail/reactive_wait_op.hpp \ + asio/detail/reactor_fwd.hpp \ + asio/detail/reactor.hpp \ + asio/detail/reactor_op.hpp \ + asio/detail/reactor_op_queue.hpp \ + asio/detail/recycling_allocator.hpp \ + asio/detail/regex_fwd.hpp \ + asio/detail/resolve_endpoint_op.hpp \ + asio/detail/resolve_op.hpp \ + asio/detail/resolve_query_op.hpp \ + asio/detail/resolver_service_base.hpp \ + asio/detail/resolver_service.hpp \ + asio/detail/scheduler.hpp \ + asio/detail/scheduler_operation.hpp \ + asio/detail/scheduler_thread_info.hpp \ + asio/detail/scoped_lock.hpp \ + asio/detail/scoped_ptr.hpp \ + asio/detail/select_interrupter.hpp \ + asio/detail/select_reactor.hpp \ + asio/detail/service_registry.hpp \ + asio/detail/signal_blocker.hpp \ + asio/detail/signal_handler.hpp \ + asio/detail/signal_init.hpp \ + asio/detail/signal_op.hpp \ + asio/detail/signal_set_service.hpp \ + asio/detail/socket_holder.hpp \ + asio/detail/socket_ops.hpp \ + asio/detail/socket_option.hpp \ + asio/detail/socket_select_interrupter.hpp \ + asio/detail/socket_types.hpp \ + asio/detail/solaris_fenced_block.hpp \ + asio/detail/source_location.hpp \ + asio/detail/static_mutex.hpp \ + asio/detail/std_event.hpp \ + asio/detail/std_fenced_block.hpp \ + asio/detail/std_global.hpp \ + asio/detail/std_mutex.hpp \ + asio/detail/std_static_mutex.hpp \ + asio/detail/std_thread.hpp \ + asio/detail/strand_executor_service.hpp \ + asio/detail/strand_service.hpp \ + asio/detail/string_view.hpp \ + asio/detail/thread_context.hpp \ + asio/detail/thread_group.hpp \ + asio/detail/thread.hpp \ + asio/detail/thread_info_base.hpp \ + asio/detail/throw_error.hpp \ + asio/detail/throw_exception.hpp \ + asio/detail/timer_queue_base.hpp \ + asio/detail/timer_queue.hpp \ + asio/detail/timer_queue_ptime.hpp \ + asio/detail/timer_queue_set.hpp \ + asio/detail/timer_scheduler_fwd.hpp \ + asio/detail/timer_scheduler.hpp \ + asio/detail/tss_ptr.hpp \ + asio/detail/type_traits.hpp \ + asio/detail/variadic_templates.hpp \ + asio/detail/wait_handler.hpp \ + asio/detail/wait_op.hpp \ + asio/detail/winapp_thread.hpp \ + asio/detail/wince_thread.hpp \ + asio/detail/win_event.hpp \ + asio/detail/win_fd_set_adapter.hpp \ + asio/detail/win_fenced_block.hpp \ + asio/detail/win_global.hpp \ + asio/detail/win_iocp_handle_read_op.hpp \ + asio/detail/win_iocp_handle_service.hpp \ + asio/detail/win_iocp_handle_write_op.hpp \ + asio/detail/win_iocp_io_context.hpp \ + asio/detail/win_iocp_null_buffers_op.hpp \ + asio/detail/win_iocp_operation.hpp \ + asio/detail/win_iocp_overlapped_op.hpp \ + asio/detail/win_iocp_overlapped_ptr.hpp \ + asio/detail/win_iocp_serial_port_service.hpp \ + asio/detail/win_iocp_socket_accept_op.hpp \ + asio/detail/win_iocp_socket_connect_op.hpp \ + asio/detail/win_iocp_socket_recvfrom_op.hpp \ + asio/detail/win_iocp_socket_recvmsg_op.hpp \ + asio/detail/win_iocp_socket_recv_op.hpp \ + asio/detail/win_iocp_socket_send_op.hpp \ + asio/detail/win_iocp_socket_service_base.hpp \ + asio/detail/win_iocp_socket_service.hpp \ + asio/detail/win_iocp_thread_info.hpp \ + asio/detail/win_iocp_wait_op.hpp \ + asio/detail/win_mutex.hpp \ + asio/detail/win_object_handle_service.hpp \ + asio/detail/winrt_async_manager.hpp \ + asio/detail/winrt_async_op.hpp \ + asio/detail/winrt_resolve_op.hpp \ + asio/detail/winrt_resolver_service.hpp \ + asio/detail/winrt_socket_connect_op.hpp \ + asio/detail/winrt_socket_recv_op.hpp \ + asio/detail/winrt_socket_send_op.hpp \ + asio/detail/winrt_ssocket_service_base.hpp \ + asio/detail/winrt_ssocket_service.hpp \ + asio/detail/winrt_timer_scheduler.hpp \ + asio/detail/winrt_utils.hpp \ + asio/detail/winsock_init.hpp \ + asio/detail/win_static_mutex.hpp \ + asio/detail/win_thread.hpp \ + asio/detail/win_tss_ptr.hpp \ + asio/detail/work_dispatcher.hpp \ + asio/detail/wrapped_handler.hpp \ + asio/dispatch.hpp \ + asio/error_code.hpp \ + asio/error.hpp \ + asio/execution.hpp \ + asio/execution_context.hpp \ + asio/execution/allocator.hpp \ + asio/execution/any_executor.hpp \ + asio/execution/bad_executor.hpp \ + asio/execution/blocking.hpp \ + asio/execution/blocking_adaptation.hpp \ + asio/execution/bulk_execute.hpp \ + asio/execution/bulk_guarantee.hpp \ + asio/execution/connect.hpp \ + asio/execution/context.hpp \ + asio/execution/context_as.hpp \ + asio/execution/detail/as_invocable.hpp \ + asio/execution/detail/as_operation.hpp \ + asio/execution/detail/as_receiver.hpp \ + asio/execution/detail/bulk_sender.hpp \ + asio/execution/detail/void_receiver.hpp \ + asio/execution/detail/submit_receiver.hpp \ + asio/execution/execute.hpp \ + asio/execution/executor.hpp \ + asio/execution/impl/bad_executor.ipp \ + asio/execution/impl/receiver_invocation_error.ipp \ + asio/execution/invocable_archetype.hpp \ + asio/execution/mapping.hpp \ + asio/execution/occupancy.hpp \ + asio/execution/operation_state.hpp \ + asio/execution/outstanding_work.hpp \ + asio/execution/prefer_only.hpp \ + asio/execution/receiver.hpp \ + asio/execution/receiver_invocation_error.hpp \ + asio/execution/relationship.hpp \ + asio/execution/schedule.hpp \ + asio/execution/scheduler.hpp \ + asio/execution/sender.hpp \ + asio/execution/set_done.hpp \ + asio/execution/set_error.hpp \ + asio/execution/set_value.hpp \ + asio/execution/start.hpp \ + asio/execution/submit.hpp \ + asio/executor.hpp \ + asio/executor_work_guard.hpp \ + asio/experimental/as_single.hpp \ + asio/experimental/impl/as_single.hpp \ + asio/generic/basic_endpoint.hpp \ + asio/generic/datagram_protocol.hpp \ + asio/generic/detail/endpoint.hpp \ + asio/generic/detail/impl/endpoint.ipp \ + asio/generic/raw_protocol.hpp \ + asio/generic/seq_packet_protocol.hpp \ + asio/generic/stream_protocol.hpp \ + asio/handler_alloc_hook.hpp \ + asio/handler_continuation_hook.hpp \ + asio/handler_invoke_hook.hpp \ + asio/high_resolution_timer.hpp \ + asio.hpp \ + asio/impl/awaitable.hpp \ + asio/impl/buffered_read_stream.hpp \ + asio/impl/buffered_write_stream.hpp \ + asio/impl/co_spawn.hpp \ + asio/impl/compose.hpp \ + asio/impl/connect.hpp \ + asio/impl/defer.hpp \ + asio/impl/detached.hpp \ + asio/impl/dispatch.hpp \ + asio/impl/error_code.ipp \ + asio/impl/error.ipp \ + asio/impl/execution_context.hpp \ + asio/impl/execution_context.ipp \ + asio/impl/executor.hpp \ + asio/impl/executor.ipp \ + asio/impl/handler_alloc_hook.ipp \ + asio/impl/io_context.hpp \ + asio/impl/io_context.ipp \ + asio/impl/multiple_exceptions.ipp \ + asio/impl/post.hpp \ + asio/impl/read_at.hpp \ + asio/impl/read.hpp \ + asio/impl/read_until.hpp \ + asio/impl/redirect_error.hpp \ + asio/impl/serial_port_base.hpp \ + asio/impl/serial_port_base.ipp \ + asio/impl/spawn.hpp \ + asio/impl/src.hpp \ + asio/impl/system_context.hpp \ + asio/impl/system_context.ipp \ + asio/impl/system_executor.hpp \ + asio/impl/thread_pool.hpp \ + asio/impl/thread_pool.ipp \ + asio/impl/use_awaitable.hpp \ + asio/impl/use_future.hpp \ + asio/impl/write_at.hpp \ + asio/impl/write.hpp \ + asio/io_context.hpp \ + asio/io_context_strand.hpp \ + asio/io_service.hpp \ + asio/io_service_strand.hpp \ + asio/ip/address.hpp \ + asio/ip/address_v4.hpp \ + asio/ip/address_v4_iterator.hpp \ + asio/ip/address_v4_range.hpp \ + asio/ip/address_v6.hpp \ + asio/ip/address_v6_iterator.hpp \ + asio/ip/address_v6_range.hpp \ + asio/ip/bad_address_cast.hpp \ + asio/ip/basic_endpoint.hpp \ + asio/ip/basic_resolver_entry.hpp \ + asio/ip/basic_resolver.hpp \ + asio/ip/basic_resolver_iterator.hpp \ + asio/ip/basic_resolver_query.hpp \ + asio/ip/basic_resolver_results.hpp \ + asio/ip/detail/endpoint.hpp \ + asio/ip/detail/impl/endpoint.ipp \ + asio/ip/detail/socket_option.hpp \ + asio/ip/host_name.hpp \ + asio/ip/icmp.hpp \ + asio/ip/impl/address.hpp \ + asio/ip/impl/address.ipp \ + asio/ip/impl/address_v4.hpp \ + asio/ip/impl/address_v4.ipp \ + asio/ip/impl/address_v6.hpp \ + asio/ip/impl/address_v6.ipp \ + asio/ip/impl/basic_endpoint.hpp \ + asio/ip/impl/host_name.ipp \ + asio/ip/impl/network_v4.hpp \ + asio/ip/impl/network_v4.ipp \ + asio/ip/impl/network_v6.hpp \ + asio/ip/impl/network_v6.ipp \ + asio/ip/multicast.hpp \ + asio/ip/network_v4.hpp \ + asio/ip/network_v6.hpp \ + asio/ip/resolver_base.hpp \ + asio/ip/resolver_query_base.hpp \ + asio/ip/tcp.hpp \ + asio/ip/udp.hpp \ + asio/ip/unicast.hpp \ + asio/ip/v6_only.hpp \ + asio/is_applicable_property.hpp \ + asio/is_executor.hpp \ + asio/is_read_buffered.hpp \ + asio/is_write_buffered.hpp \ + asio/local/basic_endpoint.hpp \ + asio/local/connect_pair.hpp \ + asio/local/datagram_protocol.hpp \ + asio/local/detail/endpoint.hpp \ + asio/local/detail/impl/endpoint.ipp \ + asio/local/stream_protocol.hpp \ + asio/multiple_exceptions.hpp \ + asio/packaged_task.hpp \ + asio/placeholders.hpp \ + asio/posix/basic_descriptor.hpp \ + asio/posix/basic_stream_descriptor.hpp \ + asio/posix/descriptor_base.hpp \ + asio/posix/descriptor.hpp \ + asio/posix/stream_descriptor.hpp \ + asio/post.hpp \ + asio/prefer.hpp \ + asio/query.hpp \ + asio/read_at.hpp \ + asio/read.hpp \ + asio/read_until.hpp \ + asio/redirect_error.hpp \ + asio/require.hpp \ + asio/require_concept.hpp \ + asio/serial_port_base.hpp \ + asio/serial_port.hpp \ + asio/signal_set.hpp \ + asio/socket_base.hpp \ + asio/spawn.hpp \ + asio/ssl/context_base.hpp \ + asio/ssl/context.hpp \ + asio/ssl/detail/buffered_handshake_op.hpp \ + asio/ssl/detail/engine.hpp \ + asio/ssl/detail/handshake_op.hpp \ + asio/ssl/detail/impl/engine.ipp \ + asio/ssl/detail/impl/openssl_init.ipp \ + asio/ssl/detail/io.hpp \ + asio/ssl/detail/openssl_init.hpp \ + asio/ssl/detail/openssl_types.hpp \ + asio/ssl/detail/password_callback.hpp \ + asio/ssl/detail/read_op.hpp \ + asio/ssl/detail/shutdown_op.hpp \ + asio/ssl/detail/stream_core.hpp \ + asio/ssl/detail/verify_callback.hpp \ + asio/ssl/detail/write_op.hpp \ + asio/ssl/error.hpp \ + asio/ssl.hpp \ + asio/ssl/host_name_verification.hpp \ + asio/ssl/impl/context.hpp \ + asio/ssl/impl/context.ipp \ + asio/ssl/impl/error.ipp \ + asio/ssl/impl/host_name_verification.ipp \ + asio/ssl/impl/rfc2818_verification.ipp \ + asio/ssl/impl/src.hpp \ + asio/ssl/rfc2818_verification.hpp \ + asio/ssl/stream_base.hpp \ + asio/ssl/stream.hpp \ + asio/ssl/verify_context.hpp \ + asio/ssl/verify_mode.hpp \ + asio/static_thread_pool.hpp \ + asio/steady_timer.hpp \ + asio/strand.hpp \ + asio/streambuf.hpp \ + asio/system_context.hpp \ + asio/system_error.hpp \ + asio/system_executor.hpp \ + asio/system_timer.hpp \ + asio/this_coro.hpp \ + asio/thread.hpp \ + asio/thread_pool.hpp \ + asio/time_traits.hpp \ + asio/traits/bulk_execute_free.hpp \ + asio/traits/bulk_execute_member.hpp \ + asio/traits/connect_free.hpp \ + asio/traits/connect_member.hpp \ + asio/traits/equality_comparable.hpp \ + asio/traits/execute_free.hpp \ + asio/traits/execute_member.hpp \ + asio/traits/prefer_free.hpp \ + asio/traits/prefer_member.hpp \ + asio/traits/query_free.hpp \ + asio/traits/query_member.hpp \ + asio/traits/query_static_constexpr_member.hpp \ + asio/traits/require_concept_free.hpp \ + asio/traits/require_concept_member.hpp \ + asio/traits/require_free.hpp \ + asio/traits/require_member.hpp \ + asio/traits/schedule_free.hpp \ + asio/traits/schedule_member.hpp \ + asio/traits/set_done_free.hpp \ + asio/traits/set_done_member.hpp \ + asio/traits/set_error_free.hpp \ + asio/traits/set_error_member.hpp \ + asio/traits/set_value_free.hpp \ + asio/traits/set_value_member.hpp \ + asio/traits/start_free.hpp \ + asio/traits/start_member.hpp \ + asio/traits/static_query.hpp \ + asio/traits/static_require.hpp \ + asio/traits/static_require_concept.hpp \ + asio/traits/submit_free.hpp \ + asio/traits/submit_member.hpp \ + asio/ts/buffer.hpp \ + asio/ts/executor.hpp \ + asio/ts/internet.hpp \ + asio/ts/io_context.hpp \ + asio/ts/netfwd.hpp \ + asio/ts/net.hpp \ + asio/ts/socket.hpp \ + asio/ts/timer.hpp \ + asio/unyield.hpp \ + asio/use_awaitable.hpp \ + asio/use_future.hpp \ + asio/uses_executor.hpp \ + asio/version.hpp \ + asio/wait_traits.hpp \ + asio/windows/basic_object_handle.hpp \ + asio/windows/basic_overlapped_handle.hpp \ + asio/windows/basic_random_access_handle.hpp \ + asio/windows/basic_stream_handle.hpp \ + asio/windows/object_handle.hpp \ + asio/windows/overlapped_handle.hpp \ + asio/windows/overlapped_ptr.hpp \ + asio/windows/random_access_handle.hpp \ + asio/windows/stream_handle.hpp \ + asio/write_at.hpp \ + asio/write.hpp \ + asio/yield.hpp + +MAINTAINERCLEANFILES = \ + $(srcdir)/Makefile.in + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-nobase_includeHEADERS: $(nobase_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-nobase_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-nobase_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + cscopelist-am ctags ctags-am distclean distclean-generic \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-nobase_includeHEADERS install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-nobase_includeHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/third_party/asio/1.18.2/include/asio.hpp b/third_party/asio/1.18.2/include/asio.hpp new file mode 100644 index 000000000..31ce82dde --- /dev/null +++ b/third_party/asio/1.18.2/include/asio.hpp @@ -0,0 +1,182 @@ +// +// asio.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_HPP +#define ASIO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/async_result.hpp" +#include "asio/awaitable.hpp" +#include "asio/basic_datagram_socket.hpp" +#include "asio/basic_deadline_timer.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/basic_raw_socket.hpp" +#include "asio/basic_seq_packet_socket.hpp" +#include "asio/basic_serial_port.hpp" +#include "asio/basic_signal_set.hpp" +#include "asio/basic_socket.hpp" +#include "asio/basic_socket_acceptor.hpp" +#include "asio/basic_socket_iostream.hpp" +#include "asio/basic_socket_streambuf.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/basic_streambuf.hpp" +#include "asio/basic_waitable_timer.hpp" +#include "asio/bind_executor.hpp" +#include "asio/buffer.hpp" +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffered_read_stream.hpp" +#include "asio/buffered_stream_fwd.hpp" +#include "asio/buffered_stream.hpp" +#include "asio/buffered_write_stream_fwd.hpp" +#include "asio/buffered_write_stream.hpp" +#include "asio/buffers_iterator.hpp" +#include "asio/co_spawn.hpp" +#include "asio/completion_condition.hpp" +#include "asio/compose.hpp" +#include "asio/connect.hpp" +#include "asio/coroutine.hpp" +#include "asio/deadline_timer.hpp" +#include "asio/defer.hpp" +#include "asio/detached.hpp" +#include "asio/dispatch.hpp" +#include "asio/error.hpp" +#include "asio/error_code.hpp" +#include "asio/execution.hpp" +#include "asio/execution/allocator.hpp" +#include "asio/execution/any_executor.hpp" +#include "asio/execution/blocking.hpp" +#include "asio/execution/blocking_adaptation.hpp" +#include "asio/execution/bulk_execute.hpp" +#include "asio/execution/bulk_guarantee.hpp" +#include "asio/execution/connect.hpp" +#include "asio/execution/context.hpp" +#include "asio/execution/context_as.hpp" +#include "asio/execution/execute.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/invocable_archetype.hpp" +#include "asio/execution/mapping.hpp" +#include "asio/execution/occupancy.hpp" +#include "asio/execution/operation_state.hpp" +#include "asio/execution/outstanding_work.hpp" +#include "asio/execution/prefer_only.hpp" +#include "asio/execution/receiver.hpp" +#include "asio/execution/receiver_invocation_error.hpp" +#include "asio/execution/relationship.hpp" +#include "asio/execution/schedule.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/execution/set_done.hpp" +#include "asio/execution/set_error.hpp" +#include "asio/execution/set_value.hpp" +#include "asio/execution/start.hpp" +#include "asio/execution_context.hpp" +#include "asio/executor.hpp" +#include "asio/executor_work_guard.hpp" +#include "asio/generic/basic_endpoint.hpp" +#include "asio/generic/datagram_protocol.hpp" +#include "asio/generic/raw_protocol.hpp" +#include "asio/generic/seq_packet_protocol.hpp" +#include "asio/generic/stream_protocol.hpp" +#include "asio/handler_alloc_hook.hpp" +#include "asio/handler_continuation_hook.hpp" +#include "asio/handler_invoke_hook.hpp" +#include "asio/high_resolution_timer.hpp" +#include "asio/io_context.hpp" +#include "asio/io_context_strand.hpp" +#include "asio/io_service.hpp" +#include "asio/io_service_strand.hpp" +#include "asio/ip/address.hpp" +#include "asio/ip/address_v4.hpp" +#include "asio/ip/address_v4_iterator.hpp" +#include "asio/ip/address_v4_range.hpp" +#include "asio/ip/address_v6.hpp" +#include "asio/ip/address_v6_iterator.hpp" +#include "asio/ip/address_v6_range.hpp" +#include "asio/ip/network_v4.hpp" +#include "asio/ip/network_v6.hpp" +#include "asio/ip/bad_address_cast.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver.hpp" +#include "asio/ip/basic_resolver_entry.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/host_name.hpp" +#include "asio/ip/icmp.hpp" +#include "asio/ip/multicast.hpp" +#include "asio/ip/resolver_base.hpp" +#include "asio/ip/resolver_query_base.hpp" +#include "asio/ip/tcp.hpp" +#include "asio/ip/udp.hpp" +#include "asio/ip/unicast.hpp" +#include "asio/ip/v6_only.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/is_executor.hpp" +#include "asio/is_read_buffered.hpp" +#include "asio/is_write_buffered.hpp" +#include "asio/local/basic_endpoint.hpp" +#include "asio/local/connect_pair.hpp" +#include "asio/local/datagram_protocol.hpp" +#include "asio/local/stream_protocol.hpp" +#include "asio/multiple_exceptions.hpp" +#include "asio/packaged_task.hpp" +#include "asio/placeholders.hpp" +#include "asio/posix/basic_descriptor.hpp" +#include "asio/posix/basic_stream_descriptor.hpp" +#include "asio/posix/descriptor.hpp" +#include "asio/posix/descriptor_base.hpp" +#include "asio/posix/stream_descriptor.hpp" +#include "asio/post.hpp" +#include "asio/prefer.hpp" +#include "asio/query.hpp" +#include "asio/read.hpp" +#include "asio/read_at.hpp" +#include "asio/read_until.hpp" +#include "asio/redirect_error.hpp" +#include "asio/require.hpp" +#include "asio/require_concept.hpp" +#include "asio/serial_port.hpp" +#include "asio/serial_port_base.hpp" +#include "asio/signal_set.hpp" +#include "asio/socket_base.hpp" +#include "asio/static_thread_pool.hpp" +#include "asio/steady_timer.hpp" +#include "asio/strand.hpp" +#include "asio/streambuf.hpp" +#include "asio/system_context.hpp" +#include "asio/system_error.hpp" +#include "asio/system_executor.hpp" +#include "asio/system_timer.hpp" +#include "asio/this_coro.hpp" +#include "asio/thread.hpp" +#include "asio/thread_pool.hpp" +#include "asio/time_traits.hpp" +#include "asio/use_awaitable.hpp" +#include "asio/use_future.hpp" +#include "asio/uses_executor.hpp" +#include "asio/version.hpp" +#include "asio/wait_traits.hpp" +#include "asio/windows/basic_object_handle.hpp" +#include "asio/windows/basic_overlapped_handle.hpp" +#include "asio/windows/basic_random_access_handle.hpp" +#include "asio/windows/basic_stream_handle.hpp" +#include "asio/windows/object_handle.hpp" +#include "asio/windows/overlapped_handle.hpp" +#include "asio/windows/overlapped_ptr.hpp" +#include "asio/windows/random_access_handle.hpp" +#include "asio/windows/stream_handle.hpp" +#include "asio/write.hpp" +#include "asio/write_at.hpp" + +#endif // ASIO_HPP diff --git a/third_party/asio/1.18.2/include/asio/any_io_executor.hpp b/third_party/asio/1.18.2/include/asio/any_io_executor.hpp new file mode 100644 index 000000000..5f2c13f01 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/any_io_executor.hpp @@ -0,0 +1,300 @@ +// +// any_io_executor.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ANY_IO_EXECUTOR_HPP +#define ASIO_ANY_IO_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#if defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT) +# include "asio/executor.hpp" +#else // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT) +# include "asio/execution.hpp" +# include "asio/execution_context.hpp" +#endif // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT) + +typedef executor any_io_executor; + +#else // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT) + +/// Polymorphic executor type for use with I/O objects. +/** + * The @c any_io_executor type is a polymorphic executor that supports the set + * of properties required by I/O objects. It is defined as the + * execution::any_executor class template parameterised as follows: + * @code execution::any_executor< + * execution::context_as_t, + * execution::blocking_t::never_t, + * execution::prefer_only, + * execution::prefer_only, + * execution::prefer_only, + * execution::prefer_only, + * execution::prefer_only + * > @endcode + */ +class any_io_executor : +#if defined(GENERATING_DOCUMENTATION) + public execution::any_executor<...> +#else // defined(GENERATING_DOCUMENTATION) + public execution::any_executor< + execution::context_as_t, + execution::blocking_t::never_t, + execution::prefer_only, + execution::prefer_only, + execution::prefer_only, + execution::prefer_only, + execution::prefer_only + > +#endif // defined(GENERATING_DOCUMENTATION) +{ +public: +#if !defined(GENERATING_DOCUMENTATION) + typedef execution::any_executor< + execution::context_as_t, + execution::blocking_t::never_t, + execution::prefer_only, + execution::prefer_only, + execution::prefer_only, + execution::prefer_only, + execution::prefer_only + > base_type; + + typedef void supportable_properties_type( + execution::context_as_t, + execution::blocking_t::never_t, + execution::prefer_only, + execution::prefer_only, + execution::prefer_only, + execution::prefer_only, + execution::prefer_only + ); +#endif // !defined(GENERATING_DOCUMENTATION) + + /// Default constructor. + any_io_executor() ASIO_NOEXCEPT + : base_type() + { + } + + /// Construct in an empty state. Equivalent effects to default constructor. + any_io_executor(nullptr_t) ASIO_NOEXCEPT + : base_type(nullptr_t()) + { + } + + /// Copy constructor. + any_io_executor(const any_io_executor& e) ASIO_NOEXCEPT + : base_type(static_cast(e)) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move constructor. + any_io_executor(any_io_executor&& e) ASIO_NOEXCEPT + : base_type(static_cast(e)) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Construct to point to the same target as another any_executor. +#if defined(GENERATING_DOCUMENTATION) + template + any_io_executor(execution::any_executor e); +#else // defined(GENERATING_DOCUMENTATION) + template + any_io_executor(OtherAnyExecutor e, + typename constraint< + conditional< + !is_same::value + && is_base_of::value, + typename execution::detail::supportable_properties< + 0, supportable_properties_type>::template + is_valid_target, + false_type + >::type::value + >::type = 0) + : base_type(ASIO_MOVE_CAST(OtherAnyExecutor)(e)) + { + } +#endif // defined(GENERATING_DOCUMENTATION) + + /// Construct a polymorphic wrapper for the specified executor. +#if defined(GENERATING_DOCUMENTATION) + template + any_io_executor(Executor e); +#else // defined(GENERATING_DOCUMENTATION) + template + any_io_executor(Executor e, + typename constraint< + conditional< + !is_same::value + && !is_base_of::value, + execution::detail::is_valid_target_executor< + Executor, supportable_properties_type>, + false_type + >::type::value + >::type = 0) + : base_type(ASIO_MOVE_CAST(Executor)(e)) + { + } +#endif // defined(GENERATING_DOCUMENTATION) + + /// Assignment operator. + any_io_executor& operator=(const any_io_executor& e) ASIO_NOEXCEPT + { + base_type::operator=(static_cast(e)); + return *this; + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move assignment operator. + any_io_executor& operator=(any_io_executor&& e) ASIO_NOEXCEPT + { + base_type::operator=(static_cast(e)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Assignment operator that sets the polymorphic wrapper to the empty state. + any_io_executor& operator=(nullptr_t) + { + base_type::operator=(nullptr_t()); + return *this; + } + + /// Destructor. + ~any_io_executor() + { + } + + /// Swap targets with another polymorphic wrapper. + void swap(any_io_executor& other) ASIO_NOEXCEPT + { + static_cast(*this).swap(static_cast(other)); + } + + /// Obtain a polymorphic wrapper with the specified property. + /** + * Do not call this function directly. It is intended for use with the + * asio::require and asio::prefer customisation points. + * + * For example: + * @code any_io_executor ex = ...; + * auto ex2 = asio::require(ex, execution::blocking.possibly); @endcode + */ + template + any_io_executor require(const Property& p, + typename constraint< + traits::require_member::is_valid + >::type = 0) const + { + return static_cast(*this).require(p); + } + + /// Obtain a polymorphic wrapper with the specified property. + /** + * Do not call this function directly. It is intended for use with the + * asio::prefer customisation point. + * + * For example: + * @code any_io_executor ex = ...; + * auto ex2 = asio::prefer(ex, execution::blocking.possibly); @endcode + */ + template + any_io_executor prefer(const Property& p, + typename constraint< + traits::prefer_member::is_valid + >::type = 0) const + { + return static_cast(*this).prefer(p); + } +}; + +#if !defined(GENERATING_DOCUMENTATION) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) + +template <> +struct equality_comparable +{ + static const bool is_valid = true; + static const bool is_noexcept = true; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + +template +struct execute_member +{ + static const bool is_valid = true; + static const bool is_noexcept = false; + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + +template +struct query_member : + query_member +{ +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT) + +template +struct require_member : + require_member +{ + typedef any_io_executor result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT) + +template +struct prefer_member : + prefer_member +{ + typedef any_io_executor result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT) + +} // namespace traits + +#endif // !defined(GENERATING_DOCUMENTATION) + +#endif // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ANY_IO_EXECUTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/associated_allocator.hpp b/third_party/asio/1.18.2/include/asio/associated_allocator.hpp new file mode 100644 index 000000000..8e7a3452d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/associated_allocator.hpp @@ -0,0 +1,125 @@ +// +// associated_allocator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ASSOCIATED_ALLOCATOR_HPP +#define ASIO_ASSOCIATED_ALLOCATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct associated_allocator_impl +{ + typedef E type; + + static type get(const T&, const E& e) ASIO_NOEXCEPT + { + return e; + } +}; + +template +struct associated_allocator_impl::type> +{ + typedef typename T::allocator_type type; + + static type get(const T& t, const E&) ASIO_NOEXCEPT + { + return t.get_allocator(); + } +}; + +} // namespace detail + +/// Traits type used to obtain the allocator associated with an object. +/** + * A program may specialise this traits type if the @c T template parameter in + * the specialisation is a user-defined type. The template parameter @c + * Allocator shall be a type meeting the Allocator requirements. + * + * Specialisations shall meet the following requirements, where @c t is a const + * reference to an object of type @c T, and @c a is an object of type @c + * Allocator. + * + * @li Provide a nested typedef @c type that identifies a type meeting the + * Allocator requirements. + * + * @li Provide a noexcept static member function named @c get, callable as @c + * get(t) and with return type @c type. + * + * @li Provide a noexcept static member function named @c get, callable as @c + * get(t,a) and with return type @c type. + */ +template > +struct associated_allocator +{ + /// If @c T has a nested type @c allocator_type, T::allocator_type. + /// Otherwise @c Allocator. +#if defined(GENERATING_DOCUMENTATION) + typedef see_below type; +#else // defined(GENERATING_DOCUMENTATION) + typedef typename detail::associated_allocator_impl::type type; +#endif // defined(GENERATING_DOCUMENTATION) + + /// If @c T has a nested type @c allocator_type, returns + /// t.get_allocator(). Otherwise returns @c a. + static type get(const T& t, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return detail::associated_allocator_impl::get(t, a); + } +}; + +/// Helper function to obtain an object's associated allocator. +/** + * @returns associated_allocator::get(t) + */ +template +inline typename associated_allocator::type +get_associated_allocator(const T& t) ASIO_NOEXCEPT +{ + return associated_allocator::get(t); +} + +/// Helper function to obtain an object's associated allocator. +/** + * @returns associated_allocator::get(t, a) + */ +template +inline typename associated_allocator::type +get_associated_allocator(const T& t, const Allocator& a) ASIO_NOEXCEPT +{ + return associated_allocator::get(t, a); +} + +#if defined(ASIO_HAS_ALIAS_TEMPLATES) + +template > +using associated_allocator_t + = typename associated_allocator::type; + +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ASSOCIATED_ALLOCATOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/associated_executor.hpp b/third_party/asio/1.18.2/include/asio/associated_executor.hpp new file mode 100644 index 000000000..3da1a6b8a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/associated_executor.hpp @@ -0,0 +1,166 @@ +// +// associated_executor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ASSOCIATED_EXECUTOR_HPP +#define ASIO_ASSOCIATED_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/executor.hpp" +#include "asio/is_executor.hpp" +#include "asio/system_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct associated_executor_impl +{ + typedef void asio_associated_executor_is_unspecialised; + + typedef E type; + + static type get(const T&, const E& e = E()) ASIO_NOEXCEPT + { + return e; + } +}; + +template +struct associated_executor_impl::type> +{ + typedef typename T::executor_type type; + + static type get(const T& t, const E& = E()) ASIO_NOEXCEPT + { + return t.get_executor(); + } +}; + +} // namespace detail + +/// Traits type used to obtain the executor associated with an object. +/** + * A program may specialise this traits type if the @c T template parameter in + * the specialisation is a user-defined type. The template parameter @c + * Executor shall be a type meeting the Executor requirements. + * + * Specialisations shall meet the following requirements, where @c t is a const + * reference to an object of type @c T, and @c e is an object of type @c + * Executor. + * + * @li Provide a nested typedef @c type that identifies a type meeting the + * Executor requirements. + * + * @li Provide a noexcept static member function named @c get, callable as @c + * get(t) and with return type @c type. + * + * @li Provide a noexcept static member function named @c get, callable as @c + * get(t,e) and with return type @c type. + */ +template +struct associated_executor +#if !defined(GENERATING_DOCUMENTATION) + : detail::associated_executor_impl +#endif // !defined(GENERATING_DOCUMENTATION) +{ +#if defined(GENERATING_DOCUMENTATION) + /// If @c T has a nested type @c executor_type, T::executor_type. + /// Otherwise @c Executor. + typedef see_below type; + + /// If @c T has a nested type @c executor_type, returns + /// t.get_executor(). Otherwise returns @c ex. + static type get(const T& t, + const Executor& ex = Executor()) ASIO_NOEXCEPT; +#endif // defined(GENERATING_DOCUMENTATION) +}; + +/// Helper function to obtain an object's associated executor. +/** + * @returns associated_executor::get(t) + */ +template +inline typename associated_executor::type +get_associated_executor(const T& t) ASIO_NOEXCEPT +{ + return associated_executor::get(t); +} + +/// Helper function to obtain an object's associated executor. +/** + * @returns associated_executor::get(t, ex) + */ +template +inline typename associated_executor::type +get_associated_executor(const T& t, const Executor& ex, + typename constraint< + is_executor::value || execution::is_executor::value + >::type = 0) ASIO_NOEXCEPT +{ + return associated_executor::get(t, ex); +} + +/// Helper function to obtain an object's associated executor. +/** + * @returns associated_executor::get(t, ctx.get_executor()) + */ +template +inline typename associated_executor::type +get_associated_executor(const T& t, ExecutionContext& ctx, + typename constraint::value>::type = 0) ASIO_NOEXCEPT +{ + return associated_executor::get(t, ctx.get_executor()); +} + +#if defined(ASIO_HAS_ALIAS_TEMPLATES) + +template +using associated_executor_t = typename associated_executor::type; + +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + +namespace detail { + +template +struct associated_executor_forwarding_base +{ +}; + +template +struct associated_executor_forwarding_base::asio_associated_executor_is_unspecialised, + void + >::value + >::type> +{ + typedef void asio_associated_executor_is_unspecialised; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ASSOCIATED_EXECUTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/async_result.hpp b/third_party/asio/1.18.2/include/asio/async_result.hpp new file mode 100644 index 000000000..56bdcc333 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/async_result.hpp @@ -0,0 +1,582 @@ +// +// async_result.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ASYNC_RESULT_HPP +#define ASIO_ASYNC_RESULT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/detail/variadic_templates.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(ASIO_HAS_CONCEPTS) \ + && defined(ASIO_HAS_VARIADIC_TEMPLATES) \ + && defined(ASIO_HAS_DECLTYPE) + +namespace detail { + +template +struct is_completion_signature : false_type +{ +}; + +template +struct is_completion_signature : true_type +{ +}; + +template +ASIO_CONCEPT callable_with = requires(T t, Args&&... args) +{ + t(static_cast(args)...); +}; + +template +struct is_completion_handler_for : false_type +{ +}; + +template +struct is_completion_handler_for + : integral_constant)> +{ +}; + +} // namespace detail + +template +ASIO_CONCEPT completion_signature = + detail::is_completion_signature::value; + +#define ASIO_COMPLETION_SIGNATURE \ + ::asio::completion_signature + +template +ASIO_CONCEPT completion_handler_for = + detail::is_completion_signature::value + && detail::is_completion_handler_for::value; + +#define ASIO_COMPLETION_HANDLER_FOR(s) \ + ::asio::completion_handler_for + +#else // defined(ASIO_HAS_CONCEPTS) + // && defined(ASIO_HAS_VARIADIC_TEMPLATES) + // && defined(ASIO_HAS_DECLTYPE) + +#define ASIO_COMPLETION_SIGNATURE typename +#define ASIO_COMPLETION_HANDLER_FOR(s) typename + +#endif // defined(ASIO_HAS_CONCEPTS) + // && defined(ASIO_HAS_VARIADIC_TEMPLATES) + // && defined(ASIO_HAS_DECLTYPE) + +/// An interface for customising the behaviour of an initiating function. +/** + * The async_result traits class is used for determining: + * + * @li the concrete completion handler type to be called at the end of the + * asynchronous operation; + * + * @li the initiating function return type; and + * + * @li how the return value of the initiating function is obtained. + * + * The trait allows the handler and return types to be determined at the point + * where the specific completion handler signature is known. + * + * This template may be specialised for user-defined completion token types. + * The primary template assumes that the CompletionToken is the completion + * handler. + */ +template +class async_result +{ +public: + /// The concrete completion handler type for the specific signature. + typedef CompletionToken completion_handler_type; + + /// The return type of the initiating function. + typedef void return_type; + + /// Construct an async result from a given handler. + /** + * When using a specalised async_result, the constructor has an opportunity + * to initialise some state associated with the completion handler, which is + * then returned from the initiating function. + */ + explicit async_result(completion_handler_type& h) + { + (void)h; + } + + /// Obtain the value to be returned from the initiating function. + return_type get() + { + } + +#if defined(GENERATING_DOCUMENTATION) + + /// Initiate the asynchronous operation that will produce the result, and + /// obtain the value to be returned from the initiating function. + template + static return_type initiate( + ASIO_MOVE_ARG(Initiation) initiation, + ASIO_MOVE_ARG(RawCompletionToken) token, + ASIO_MOVE_ARG(Args)... args); + +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + static return_type initiate( + ASIO_MOVE_ARG(Initiation) initiation, + ASIO_MOVE_ARG(RawCompletionToken) token, + ASIO_MOVE_ARG(Args)... args) + { + ASIO_MOVE_CAST(Initiation)(initiation)( + ASIO_MOVE_CAST(RawCompletionToken)(token), + ASIO_MOVE_CAST(Args)(args)...); + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + static return_type initiate( + ASIO_MOVE_ARG(Initiation) initiation, + ASIO_MOVE_ARG(RawCompletionToken) token) + { + ASIO_MOVE_CAST(Initiation)(initiation)( + ASIO_MOVE_CAST(RawCompletionToken)(token)); + } + +#define ASIO_PRIVATE_INITIATE_DEF(n) \ + template \ + static return_type initiate( \ + ASIO_MOVE_ARG(Initiation) initiation, \ + ASIO_MOVE_ARG(RawCompletionToken) token, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + ASIO_MOVE_CAST(Initiation)(initiation)( \ + ASIO_MOVE_CAST(RawCompletionToken)(token), \ + ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_INITIATE_DEF) +#undef ASIO_PRIVATE_INITIATE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +private: + async_result(const async_result&) ASIO_DELETED; + async_result& operator=(const async_result&) ASIO_DELETED; +}; + +#if !defined(GENERATING_DOCUMENTATION) + +template +class async_result +{ + // Empty. +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +/// Helper template to deduce the handler type from a CompletionToken, capture +/// a local copy of the handler, and then create an async_result for the +/// handler. +template +struct async_completion +{ + /// The real handler type to be used for the asynchronous operation. + typedef typename asio::async_result< + typename decay::type, + Signature>::completion_handler_type completion_handler_type; + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Constructor. + /** + * The constructor creates the concrete completion handler and makes the link + * between the handler and the asynchronous result. + */ + explicit async_completion(CompletionToken& token) + : completion_handler(static_cast::value, + completion_handler_type&, CompletionToken&&>::type>(token)), + result(completion_handler) + { + } +#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + explicit async_completion(typename decay::type& token) + : completion_handler(token), + result(completion_handler) + { + } + + explicit async_completion(const typename decay::type& token) + : completion_handler(token), + result(completion_handler) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// A copy of, or reference to, a real handler object. +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + typename conditional< + is_same::value, + completion_handler_type&, completion_handler_type>::type completion_handler; +#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + completion_handler_type completion_handler; +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// The result of the asynchronous operation's initiating function. + async_result::type, Signature> result; +}; + +namespace detail { + +template +struct async_result_helper + : async_result::type, Signature> +{ +}; + +struct async_result_memfns_base +{ + void initiate(); +}; + +template +struct async_result_memfns_derived + : T, async_result_memfns_base +{ +}; + +template +struct async_result_memfns_check +{ +}; + +template +char (&async_result_initiate_memfn_helper(...))[2]; + +template +char async_result_initiate_memfn_helper( + async_result_memfns_check< + void (async_result_memfns_base::*)(), + &async_result_memfns_derived::initiate>*); + +template +struct async_result_has_initiate_memfn + : integral_constant::type, Signature> + >(0)) != 1> +{ +}; + +} // namespace detail + +#if defined(GENERATING_DOCUMENTATION) +# define ASIO_INITFN_RESULT_TYPE(ct, sig) \ + void_or_deduced +#elif defined(_MSC_VER) && (_MSC_VER < 1500) +# define ASIO_INITFN_RESULT_TYPE(ct, sig) \ + typename ::asio::detail::async_result_helper< \ + ct, sig>::return_type +#define ASIO_HANDLER_TYPE(ct, sig) \ + typename ::asio::detail::async_result_helper< \ + ct, sig>::completion_handler_type +#else +# define ASIO_INITFN_RESULT_TYPE(ct, sig) \ + typename ::asio::async_result< \ + typename ::asio::decay::type, sig>::return_type +#define ASIO_HANDLER_TYPE(ct, sig) \ + typename ::asio::async_result< \ + typename ::asio::decay::type, sig>::completion_handler_type +#endif + +#if defined(GENERATING_DOCUMENTATION) +# define ASIO_INITFN_AUTO_RESULT_TYPE(ct, sig) \ + auto +#elif defined(ASIO_HAS_RETURN_TYPE_DEDUCTION) +# define ASIO_INITFN_AUTO_RESULT_TYPE(ct, sig) \ + auto +#else +# define ASIO_INITFN_AUTO_RESULT_TYPE(ct, sig) \ + ASIO_INITFN_RESULT_TYPE(ct, sig) +#endif + +#if defined(GENERATING_DOCUMENTATION) +# define ASIO_INITFN_DEDUCED_RESULT_TYPE(ct, sig, expr) \ + void_or_deduced +#elif defined(ASIO_HAS_DECLTYPE) +# define ASIO_INITFN_DEDUCED_RESULT_TYPE(ct, sig, expr) \ + decltype expr +#else +# define ASIO_INITFN_DEDUCED_RESULT_TYPE(ct, sig, expr) \ + ASIO_INITFN_RESULT_TYPE(ct, sig) +#endif + +#if defined(GENERATING_DOCUMENTATION) + +template +void_or_deduced async_initiate( + ASIO_MOVE_ARG(Initiation) initiation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken), + ASIO_MOVE_ARG(Args)... args); + +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +inline typename constraint< + detail::async_result_has_initiate_memfn::value, + ASIO_INITFN_DEDUCED_RESULT_TYPE(CompletionToken, Signature, + (async_result::type, + Signature>::initiate(declval(), + declval(), + declval()...)))>::type +async_initiate(ASIO_MOVE_ARG(Initiation) initiation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, + ASIO_MOVE_ARG(Args)... args) +{ + return async_result::type, + Signature>::initiate(ASIO_MOVE_CAST(Initiation)(initiation), + ASIO_MOVE_CAST(CompletionToken)(token), + ASIO_MOVE_CAST(Args)(args)...); +} + +template +inline typename constraint< + !detail::async_result_has_initiate_memfn::value, + ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature)>::type +async_initiate(ASIO_MOVE_ARG(Initiation) initiation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, + ASIO_MOVE_ARG(Args)... args) +{ + async_completion completion(token); + + ASIO_MOVE_CAST(Initiation)(initiation)( + ASIO_MOVE_CAST(ASIO_HANDLER_TYPE(CompletionToken, + Signature))(completion.completion_handler), + ASIO_MOVE_CAST(Args)(args)...); + + return completion.result.get(); +} + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +inline typename constraint< + detail::async_result_has_initiate_memfn::value, + ASIO_INITFN_DEDUCED_RESULT_TYPE(CompletionToken, Signature, + (async_result::type, + Signature>::initiate(declval(), + declval())))>::type +async_initiate(ASIO_MOVE_ARG(Initiation) initiation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token) +{ + return async_result::type, + Signature>::initiate(ASIO_MOVE_CAST(Initiation)(initiation), + ASIO_MOVE_CAST(CompletionToken)(token)); +} + +template +inline typename constraint< + !detail::async_result_has_initiate_memfn::value, + ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature)>::type +async_initiate(ASIO_MOVE_ARG(Initiation) initiation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token) +{ + async_completion completion(token); + + ASIO_MOVE_CAST(Initiation)(initiation)( + ASIO_MOVE_CAST(ASIO_HANDLER_TYPE(CompletionToken, + Signature))(completion.completion_handler)); + + return completion.result.get(); +} + +#define ASIO_PRIVATE_INITIATE_DEF(n) \ + template \ + inline typename constraint< \ + detail::async_result_has_initiate_memfn< \ + CompletionToken, Signature>::value, \ + ASIO_INITFN_DEDUCED_RESULT_TYPE(CompletionToken, Signature, \ + (async_result::type, \ + Signature>::initiate(declval(), \ + declval(), \ + ASIO_VARIADIC_MOVE_DECLVAL(n))))>::type \ + async_initiate(ASIO_MOVE_ARG(Initiation) initiation, \ + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + return async_result::type, \ + Signature>::initiate(ASIO_MOVE_CAST(Initiation)(initiation), \ + ASIO_MOVE_CAST(CompletionToken)(token), \ + ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + \ + template \ + inline typename constraint< \ + !detail::async_result_has_initiate_memfn< \ + CompletionToken, Signature>::value, \ + ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature)>::type \ + async_initiate(ASIO_MOVE_ARG(Initiation) initiation, \ + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + async_completion completion(token); \ + \ + ASIO_MOVE_CAST(Initiation)(initiation)( \ + ASIO_MOVE_CAST(ASIO_HANDLER_TYPE(CompletionToken, \ + Signature))(completion.completion_handler), \ + ASIO_VARIADIC_MOVE_ARGS(n)); \ + \ + return completion.result.get(); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_INITIATE_DEF) +#undef ASIO_PRIVATE_INITIATE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#if defined(ASIO_HAS_CONCEPTS) \ + && defined(ASIO_HAS_VARIADIC_TEMPLATES) \ + && defined(ASIO_HAS_DECLTYPE) + +namespace detail { + +template +struct initiation_archetype +{ + template CompletionHandler> + void operator()(CompletionHandler&&) const + { + } +}; + +} // namespace detail + +template +ASIO_CONCEPT completion_token_for = + detail::is_completion_signature::value + && + requires(T&& t) + { + async_initiate(detail::initiation_archetype{}, t); + }; + +#define ASIO_COMPLETION_TOKEN_FOR(s) \ + ::asio::completion_token_for + +#else // defined(ASIO_HAS_CONCEPTS) + // && defined(ASIO_HAS_VARIADIC_TEMPLATES) + // && defined(ASIO_HAS_DECLTYPE) + +#define ASIO_COMPLETION_TOKEN_FOR(s) typename + +#endif // defined(ASIO_HAS_CONCEPTS) + // && defined(ASIO_HAS_VARIADIC_TEMPLATES) + // && defined(ASIO_HAS_DECLTYPE) + +namespace detail { + +template +struct default_completion_token_impl +{ + typedef void type; +}; + +template +struct default_completion_token_impl::type> +{ + typedef typename T::default_completion_token_type type; +}; + +} // namespace detail + +#if defined(GENERATING_DOCUMENTATION) + +/// Traits type used to determine the default completion token type associated +/// with a type (such as an executor). +/** + * A program may specialise this traits type if the @c T template parameter in + * the specialisation is a user-defined type. + * + * Specialisations of this trait may provide a nested typedef @c type, which is + * a default-constructible completion token type. + */ +template +struct default_completion_token +{ + /// If @c T has a nested type @c default_completion_token_type, + /// T::default_completion_token_type. Otherwise the typedef @c type + /// is not defined. + typedef see_below type; +}; +#else +template +struct default_completion_token + : detail::default_completion_token_impl +{ +}; +#endif + +#if defined(ASIO_HAS_ALIAS_TEMPLATES) + +template +using default_completion_token_t = typename default_completion_token::type; + +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + +#if defined(ASIO_HAS_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS) + +#define ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(e) \ + = typename ::asio::default_completion_token::type +#define ASIO_DEFAULT_COMPLETION_TOKEN(e) \ + = typename ::asio::default_completion_token::type() + +#else // defined(ASIO_HAS_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS) + +#define ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(e) +#define ASIO_DEFAULT_COMPLETION_TOKEN(e) + +#endif // defined(ASIO_HAS_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ASYNC_RESULT_HPP diff --git a/third_party/asio/1.18.2/include/asio/awaitable.hpp b/third_party/asio/1.18.2/include/asio/awaitable.hpp new file mode 100644 index 000000000..489704985 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/awaitable.hpp @@ -0,0 +1,133 @@ +// +// awaitable.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_AWAITABLE_HPP +#define ASIO_AWAITABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) + +#if defined(ASIO_HAS_STD_COROUTINE) +# include +#else // defined(ASIO_HAS_STD_COROUTINE) +# include +#endif // defined(ASIO_HAS_STD_COROUTINE) + +#include "asio/any_io_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_STD_COROUTINE) +using std::coroutine_handle; +using std::suspend_always; +#else // defined(ASIO_HAS_STD_COROUTINE) +using std::experimental::coroutine_handle; +using std::experimental::suspend_always; +#endif // defined(ASIO_HAS_STD_COROUTINE) + +template class awaitable_thread; +template class awaitable_frame; + +} // namespace detail + +/// The return type of a coroutine or asynchronous operation. +template +class awaitable +{ +public: + /// The type of the awaited value. + typedef T value_type; + + /// The executor type that will be used for the coroutine. + typedef Executor executor_type; + + /// Default constructor. + constexpr awaitable() noexcept + : frame_(nullptr) + { + } + + /// Move constructor. + awaitable(awaitable&& other) noexcept + : frame_(std::exchange(other.frame_, nullptr)) + { + } + + /// Destructor + ~awaitable() + { + if (frame_) + frame_->destroy(); + } + + /// Checks if the awaitable refers to a future result. + bool valid() const noexcept + { + return !!frame_; + } + +#if !defined(GENERATING_DOCUMENTATION) + + // Support for co_await keyword. + bool await_ready() const noexcept + { + return false; + } + + // Support for co_await keyword. + template + void await_suspend( + detail::coroutine_handle> h) + { + frame_->push_frame(&h.promise()); + } + + // Support for co_await keyword. + T await_resume() + { + return awaitable(static_cast(*this)).frame_->get(); + } + +#endif // !defined(GENERATING_DOCUMENTATION) + +private: + template friend class detail::awaitable_thread; + template friend class detail::awaitable_frame; + + // Not copy constructible or copy assignable. + awaitable(const awaitable&) = delete; + awaitable& operator=(const awaitable&) = delete; + + // Construct the awaitable from a coroutine's frame object. + explicit awaitable(detail::awaitable_frame* a) + : frame_(a) + { + } + + detail::awaitable_frame* frame_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/awaitable.hpp" + +#endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_AWAITABLE_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_datagram_socket.hpp b/third_party/asio/1.18.2/include/asio/basic_datagram_socket.hpp new file mode 100644 index 000000000..d9fba8a88 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_datagram_socket.hpp @@ -0,0 +1,1223 @@ +// +// basic_datagram_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_DATAGRAM_SOCKET_HPP +#define ASIO_BASIC_DATAGRAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/basic_socket.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if !defined(ASIO_BASIC_DATAGRAM_SOCKET_FWD_DECL) +#define ASIO_BASIC_DATAGRAM_SOCKET_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_datagram_socket; + +#endif // !defined(ASIO_BASIC_DATAGRAM_SOCKET_FWD_DECL) + +/// Provides datagram-oriented socket functionality. +/** + * The basic_datagram_socket class template provides asynchronous and blocking + * datagram-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * Synchronous @c send, @c send_to, @c receive, @c receive_from, and @c connect + * operations are thread safe with respect to each other, if the underlying + * operating system calls are also thread safe. This means that it is permitted + * to perform concurrent calls to these synchronous operations on a single + * socket object. Other synchronous operations, such as @c open or @c close, are + * not thread safe. + */ +template +class basic_datagram_socket + : public basic_socket +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_datagram_socket other; + }; + + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_datagram_socket without opening it. + /** + * This constructor creates a datagram socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_datagram_socket(const executor_type& ex) + : basic_socket(ex) + { + } + + /// Construct a basic_datagram_socket without opening it. + /** + * This constructor creates a datagram socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template + explicit basic_datagram_socket(ExecutionContext& context, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context) + { + } + + /// Construct and open a basic_datagram_socket. + /** + * This constructor creates and opens a datagram socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_datagram_socket(const executor_type& ex, const protocol_type& protocol) + : basic_socket(ex, protocol) + { + } + + /// Construct and open a basic_datagram_socket. + /** + * This constructor creates and opens a datagram socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_datagram_socket(ExecutionContext& context, + const protocol_type& protocol, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : basic_socket(context, protocol) + { + } + + /// Construct a basic_datagram_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a datagram socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_datagram_socket(const executor_type& ex, const endpoint_type& endpoint) + : basic_socket(ex, endpoint) + { + } + + /// Construct a basic_datagram_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a datagram socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_datagram_socket(ExecutionContext& context, + const endpoint_type& endpoint, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context, endpoint) + { + } + + /// Construct a basic_datagram_socket on an existing native socket. + /** + * This constructor creates a datagram socket object to hold an existing + * native socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_datagram_socket(const executor_type& ex, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_socket(ex, protocol, native_socket) + { + } + + /// Construct a basic_datagram_socket on an existing native socket. + /** + * This constructor creates a datagram socket object to hold an existing + * native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_datagram_socket(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_socket, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context, protocol, native_socket) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_datagram_socket from another. + /** + * This constructor moves a datagram socket from one object to another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. + */ + basic_datagram_socket(basic_datagram_socket&& other) ASIO_NOEXCEPT + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_datagram_socket from another. + /** + * This assignment operator moves a datagram socket from one object to + * another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. + */ + basic_datagram_socket& operator=(basic_datagram_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } + + /// Move-construct a basic_datagram_socket from a socket of another protocol + /// type. + /** + * This constructor moves a datagram socket from one object to another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. + */ + template + basic_datagram_socket(basic_datagram_socket&& other, + typename constraint< + is_convertible::value + && is_convertible::value + >::type = 0) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_datagram_socket from a socket of another protocol + /// type. + /** + * This assignment operator moves a datagram socket from one object to + * another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. + */ + template + typename constraint< + is_convertible::value + && is_convertible::value, + basic_datagram_socket& + >::type operator=(basic_datagram_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_datagram_socket() + { + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code socket.send(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to asynchronously send data on the datagram socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send(this), handler, + buffers, socket_base::message_flags(0)); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to asynchronously send data on the datagram socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send(this), handler, buffers, flags); + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.send_to(asio::buffer(data, size), destination); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send_to( + this->impl_.get_implementation(), buffers, destination, 0, ec); + asio::detail::throw_error(ec, "send_to"); + return s; + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send_to( + this->impl_.get_implementation(), buffers, destination, flags, ec); + asio::detail::throw_error(ec, "send_to"); + return s; + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->impl_.get_service().send_to(this->impl_.get_implementation(), + buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_send_to( + * asio::buffer(data, size), destination, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send_to(this), handler, buffers, + destination, socket_base::message_flags(0)); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send_to(this), handler, buffers, destination, flags); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.receive(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive(this), handler, + buffers, socket_base::message_flags(0)); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive(this), handler, buffers, flags); + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * asio::ip::udp::endpoint sender_endpoint; + * socket.receive_from( + * asio::buffer(data, size), sender_endpoint); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, 0, ec); + asio::detail::throw_error(ec, "receive_from"); + return s; + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, flags, ec); + asio::detail::throw_error(ec, "receive_from"); + return s; + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.async_receive_from( + * asio::buffer(data, size), sender_endpoint, handler); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive_from(this), handler, buffers, + &sender_endpoint, socket_base::message_flags(0)); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive_from(this), handler, + buffers, &sender_endpoint, flags); + } + +private: + // Disallow copying and assignment. + basic_datagram_socket(const basic_datagram_socket&) ASIO_DELETED; + basic_datagram_socket& operator=( + const basic_datagram_socket&) ASIO_DELETED; + + class initiate_async_send + { + public: + typedef Executor executor_type; + + explicit initiate_async_send(basic_datagram_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + const ConstBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_send( + self_->impl_.get_implementation(), buffers, flags, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_datagram_socket* self_; + }; + + class initiate_async_send_to + { + public: + typedef Executor executor_type; + + explicit initiate_async_send_to(basic_datagram_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_send_to( + self_->impl_.get_implementation(), buffers, destination, + flags, handler2.value, self_->impl_.get_executor()); + } + + private: + basic_datagram_socket* self_; + }; + + class initiate_async_receive + { + public: + typedef Executor executor_type; + + explicit initiate_async_receive(basic_datagram_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + const MutableBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_receive( + self_->impl_.get_implementation(), buffers, flags, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_datagram_socket* self_; + }; + + class initiate_async_receive_from + { + public: + typedef Executor executor_type; + + explicit initiate_async_receive_from(basic_datagram_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + const MutableBufferSequence& buffers, endpoint_type* sender_endpoint, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_receive_from( + self_->impl_.get_implementation(), buffers, *sender_endpoint, + flags, handler2.value, self_->impl_.get_executor()); + } + + private: + basic_datagram_socket* self_; + }; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_DATAGRAM_SOCKET_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_deadline_timer.hpp b/third_party/asio/1.18.2/include/asio/basic_deadline_timer.hpp new file mode 100644 index 000000000..1104c6143 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_deadline_timer.hpp @@ -0,0 +1,693 @@ +// +// basic_deadline_timer.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_DEADLINE_TIMER_HPP +#define ASIO_BASIC_DEADLINE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/any_io_executor.hpp" +#include "asio/detail/deadline_timer_service.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/time_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides waitable timer functionality. +/** + * The basic_deadline_timer class template provides the ability to perform a + * blocking or asynchronous wait for a timer to expire. + * + * A deadline timer is always in one of two states: "expired" or "not expired". + * If the wait() or async_wait() function is called on an expired timer, the + * wait operation will complete immediately. + * + * Most applications will use the asio::deadline_timer typedef. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Examples + * Performing a blocking wait: + * @code + * // Construct a timer without setting an expiry time. + * asio::deadline_timer timer(my_context); + * + * // Set an expiry time relative to now. + * timer.expires_from_now(boost::posix_time::seconds(5)); + * + * // Wait for the timer to expire. + * timer.wait(); + * @endcode + * + * @par + * Performing an asynchronous wait: + * @code + * void handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Timer expired. + * } + * } + * + * ... + * + * // Construct a timer with an absolute expiry time. + * asio::deadline_timer timer(my_context, + * boost::posix_time::time_from_string("2005-12-07 23:59:59.000")); + * + * // Start an asynchronous wait. + * timer.async_wait(handler); + * @endcode + * + * @par Changing an active deadline_timer's expiry time + * + * Changing the expiry time of a timer while there are pending asynchronous + * waits causes those wait operations to be cancelled. To ensure that the action + * associated with the timer is performed only once, use something like this: + * used: + * + * @code + * void on_some_event() + * { + * if (my_timer.expires_from_now(seconds(5)) > 0) + * { + * // We managed to cancel the timer. Start new asynchronous wait. + * my_timer.async_wait(on_timeout); + * } + * else + * { + * // Too late, timer has already expired! + * } + * } + * + * void on_timeout(const asio::error_code& e) + * { + * if (e != asio::error::operation_aborted) + * { + * // Timer was not cancelled, take necessary action. + * } + * } + * @endcode + * + * @li The asio::basic_deadline_timer::expires_from_now() function + * cancels any pending asynchronous waits, and returns the number of + * asynchronous waits that were cancelled. If it returns 0 then you were too + * late and the wait handler has already been executed, or will soon be + * executed. If it returns 1 then the wait handler was successfully cancelled. + * + * @li If a wait handler is cancelled, the asio::error_code passed to + * it contains the value asio::error::operation_aborted. + */ +template , + typename Executor = any_io_executor> +class basic_deadline_timer +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the timer type to another executor. + template + struct rebind_executor + { + /// The timer type when rebound to the specified executor. + typedef basic_deadline_timer other; + }; + + /// The time traits type. + typedef TimeTraits traits_type; + + /// The time type. + typedef typename traits_type::time_type time_type; + + /// The duration type. + typedef typename traits_type::duration_type duration_type; + + /// Constructor. + /** + * This constructor creates a timer without setting an expiry time. The + * expires_at() or expires_from_now() functions must be called to set an + * expiry time before the timer can be waited on. + * + * @param ex The I/O executor that the timer will use, by default, to + * dispatch handlers for any asynchronous operations performed on the timer. + */ + explicit basic_deadline_timer(const executor_type& ex) + : impl_(0, ex) + { + } + + /// Constructor. + /** + * This constructor creates a timer without setting an expiry time. The + * expires_at() or expires_from_now() functions must be called to set an + * expiry time before the timer can be waited on. + * + * @param context An execution context which provides the I/O executor that + * the timer will use, by default, to dispatch handlers for any asynchronous + * operations performed on the timer. + */ + template + explicit basic_deadline_timer(ExecutionContext& context, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + } + + /// Constructor to set a particular expiry time as an absolute time. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param ex The I/O executor that the timer will use, by default, to + * dispatch handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, expressed + * as an absolute time. + */ + basic_deadline_timer(const executor_type& ex, const time_type& expiry_time) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().expires_at(impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + } + + /// Constructor to set a particular expiry time as an absolute time. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param context An execution context which provides the I/O executor that + * the timer will use, by default, to dispatch handlers for any asynchronous + * operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, expressed + * as an absolute time. + */ + template + basic_deadline_timer(ExecutionContext& context, const time_type& expiry_time, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().expires_at(impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + } + + /// Constructor to set a particular expiry time relative to now. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param ex The I/O executor that the timer will use, by default, to + * dispatch handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, relative to + * now. + */ + basic_deadline_timer(const executor_type& ex, + const duration_type& expiry_time) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().expires_from_now( + impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_from_now"); + } + + /// Constructor to set a particular expiry time relative to now. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param context An execution context which provides the I/O executor that + * the timer will use, by default, to dispatch handlers for any asynchronous + * operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, relative to + * now. + */ + template + basic_deadline_timer(ExecutionContext& context, + const duration_type& expiry_time, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().expires_from_now( + impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_from_now"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_deadline_timer from another. + /** + * This constructor moves a timer from one object to another. + * + * @param other The other basic_deadline_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_deadline_timer(const executor_type&) + * constructor. + */ + basic_deadline_timer(basic_deadline_timer&& other) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_deadline_timer from another. + /** + * This assignment operator moves a timer from one object to another. Cancels + * any outstanding asynchronous operations associated with the target object. + * + * @param other The other basic_deadline_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_deadline_timer(const executor_type&) + * constructor. + */ + basic_deadline_timer& operator=(basic_deadline_timer&& other) + { + impl_ = std::move(other.impl_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the timer. + /** + * This function destroys the timer, cancelling any outstanding asynchronous + * wait operations associated with the timer as if by calling @c cancel. + */ + ~basic_deadline_timer() + { + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + + /// Cancel any asynchronous operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel() + { + asio::error_code ec; + std::size_t s = impl_.get_service().cancel(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + return s; + } + + /// Cancel any asynchronous operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel(asio::error_code& ec) + { + return impl_.get_service().cancel(impl_.get_implementation(), ec); + } + + /// Cancels one asynchronous operation that is waiting on the timer. + /** + * This function forces the completion of one pending asynchronous wait + * operation against the timer. Handlers are cancelled in FIFO order. The + * handler for the cancelled operation will be invoked with the + * asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. That is, + * either 0 or 1. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when cancel_one() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel_one() + { + asio::error_code ec; + std::size_t s = impl_.get_service().cancel_one( + impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "cancel_one"); + return s; + } + + /// Cancels one asynchronous operation that is waiting on the timer. + /** + * This function forces the completion of one pending asynchronous wait + * operation against the timer. Handlers are cancelled in FIFO order. The + * handler for the cancelled operation will be invoked with the + * asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. That is, + * either 0 or 1. + * + * @note If the timer has already expired when cancel_one() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel_one(asio::error_code& ec) + { + return impl_.get_service().cancel_one(impl_.get_implementation(), ec); + } + + /// Get the timer's expiry time as an absolute time. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + time_type expires_at() const + { + return impl_.get_service().expires_at(impl_.get_implementation()); + } + + /// Set the timer's expiry time as an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when expires_at() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_at(const time_type& expiry_time) + { + asio::error_code ec; + std::size_t s = impl_.get_service().expires_at( + impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + return s; + } + + /// Set the timer's expiry time as an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when expires_at() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_at(const time_type& expiry_time, + asio::error_code& ec) + { + return impl_.get_service().expires_at( + impl_.get_implementation(), expiry_time, ec); + } + + /// Get the timer's expiry time relative to now. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + duration_type expires_from_now() const + { + return impl_.get_service().expires_from_now(impl_.get_implementation()); + } + + /// Set the timer's expiry time relative to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when expires_from_now() is called, + * then the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_from_now(const duration_type& expiry_time) + { + asio::error_code ec; + std::size_t s = impl_.get_service().expires_from_now( + impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_from_now"); + return s; + } + + /// Set the timer's expiry time relative to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when expires_from_now() is called, + * then the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_from_now(const duration_type& expiry_time, + asio::error_code& ec) + { + return impl_.get_service().expires_from_now( + impl_.get_implementation(), expiry_time, ec); + } + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @throws asio::system_error Thrown on failure. + */ + void wait() + { + asio::error_code ec; + impl_.get_service().wait(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @param ec Set to indicate what error occurred, if any. + */ + void wait(asio::error_code& ec) + { + impl_.get_service().wait(impl_.get_implementation(), ec); + } + + /// Start an asynchronous wait on the timer. + /** + * This function may be used to initiate an asynchronous wait against the + * timer. It always returns immediately. + * + * For each call to async_wait(), the supplied handler will be called exactly + * once. The handler will be called when: + * + * @li The timer has expired. + * + * @li The timer was cancelled, in which case the handler is passed the error + * code asio::error::operation_aborted. + * + * @param handler The handler to be called when the timer expires. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + */ + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code)) + WaitHandler ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait( + ASIO_MOVE_ARG(WaitHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_wait(this), handler); + } + +private: + // Disallow copying and assignment. + basic_deadline_timer(const basic_deadline_timer&) ASIO_DELETED; + basic_deadline_timer& operator=( + const basic_deadline_timer&) ASIO_DELETED; + + class initiate_async_wait + { + public: + typedef Executor executor_type; + + explicit initiate_async_wait(basic_deadline_timer* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WaitHandler) handler) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_wait( + self_->impl_.get_implementation(), + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_deadline_timer* self_; + }; + + detail::io_object_impl< + detail::deadline_timer_service, Executor> impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_BASIC_DEADLINE_TIMER_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_io_object.hpp b/third_party/asio/1.18.2/include/asio/basic_io_object.hpp new file mode 100644 index 000000000..733557e3e --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_io_object.hpp @@ -0,0 +1,290 @@ +// +// basic_io_object.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_IO_OBJECT_HPP +#define ASIO_BASIC_IO_OBJECT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(ASIO_HAS_MOVE) +namespace detail +{ + // Type trait used to determine whether a service supports move. + template + class service_has_move + { + private: + typedef IoObjectService service_type; + typedef typename service_type::implementation_type implementation_type; + + template + static auto asio_service_has_move_eval(T* t, U* u) + -> decltype(t->move_construct(*u, *u), char()); + static char (&asio_service_has_move_eval(...))[2]; + + public: + static const bool value = + sizeof(asio_service_has_move_eval( + static_cast(0), + static_cast(0))) == 1; + }; +} +#endif // defined(ASIO_HAS_MOVE) + +/// Base class for all I/O objects. +/** + * @note All I/O objects are non-copyable. However, when using C++0x, certain + * I/O objects do support move construction and move assignment. + */ +#if !defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) +template +#else +template ::value> +#endif +class basic_io_object +{ +public: + /// The type of the service that will be used to provide I/O operations. + typedef IoObjectService service_type; + + /// The underlying implementation type of I/O object. + typedef typename service_type::implementation_type implementation_type; + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_context() + { + return service_.get_io_context(); + } + + /// (Deprecated: Use get_executor().) Get the io_context associated with the + /// object. + /** + * This function may be used to obtain the io_context object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_context object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_context& get_io_service() + { + return service_.get_io_context(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// The type of the executor associated with the object. + typedef asio::io_context::executor_type executor_type; + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return service_.get_io_context().get_executor(); + } + +protected: + /// Construct a basic_io_object. + /** + * Performs: + * @code get_service().construct(get_implementation()); @endcode + */ + explicit basic_io_object(asio::io_context& io_context) + : service_(asio::use_service(io_context)) + { + service_.construct(implementation_); + } + +#if defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_io_object. + /** + * Performs: + * @code get_service().move_construct( + * get_implementation(), other.get_implementation()); @endcode + * + * @note Available only for services that support movability, + */ + basic_io_object(basic_io_object&& other); + + /// Move-assign a basic_io_object. + /** + * Performs: + * @code get_service().move_assign(get_implementation(), + * other.get_service(), other.get_implementation()); @endcode + * + * @note Available only for services that support movability, + */ + basic_io_object& operator=(basic_io_object&& other); + + /// Perform a converting move-construction of a basic_io_object. + template + basic_io_object(IoObjectService1& other_service, + typename IoObjectService1::implementation_type& other_implementation); +#endif // defined(GENERATING_DOCUMENTATION) + + /// Protected destructor to prevent deletion through this type. + /** + * Performs: + * @code get_service().destroy(get_implementation()); @endcode + */ + ~basic_io_object() + { + service_.destroy(implementation_); + } + + /// Get the service associated with the I/O object. + service_type& get_service() + { + return service_; + } + + /// Get the service associated with the I/O object. + const service_type& get_service() const + { + return service_; + } + + /// Get the underlying implementation of the I/O object. + implementation_type& get_implementation() + { + return implementation_; + } + + /// Get the underlying implementation of the I/O object. + const implementation_type& get_implementation() const + { + return implementation_; + } + +private: + basic_io_object(const basic_io_object&); + basic_io_object& operator=(const basic_io_object&); + + // The service associated with the I/O object. + service_type& service_; + + /// The underlying implementation of the I/O object. + implementation_type implementation_; +}; + +#if defined(ASIO_HAS_MOVE) +// Specialisation for movable objects. +template +class basic_io_object +{ +public: + typedef IoObjectService service_type; + typedef typename service_type::implementation_type implementation_type; + +#if !defined(ASIO_NO_DEPRECATED) + asio::io_context& get_io_context() + { + return service_->get_io_context(); + } + + asio::io_context& get_io_service() + { + return service_->get_io_context(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + typedef asio::io_context::executor_type executor_type; + + executor_type get_executor() ASIO_NOEXCEPT + { + return service_->get_io_context().get_executor(); + } + +protected: + explicit basic_io_object(asio::io_context& io_context) + : service_(&asio::use_service(io_context)) + { + service_->construct(implementation_); + } + + basic_io_object(basic_io_object&& other) + : service_(&other.get_service()) + { + service_->move_construct(implementation_, other.implementation_); + } + + template + basic_io_object(IoObjectService1& other_service, + typename IoObjectService1::implementation_type& other_implementation) + : service_(&asio::use_service( + other_service.get_io_context())) + { + service_->converting_move_construct(implementation_, + other_service, other_implementation); + } + + ~basic_io_object() + { + service_->destroy(implementation_); + } + + basic_io_object& operator=(basic_io_object&& other) + { + service_->move_assign(implementation_, + *other.service_, other.implementation_); + service_ = other.service_; + return *this; + } + + service_type& get_service() + { + return *service_; + } + + const service_type& get_service() const + { + return *service_; + } + + implementation_type& get_implementation() + { + return implementation_; + } + + const implementation_type& get_implementation() const + { + return implementation_; + } + +private: + basic_io_object(const basic_io_object&); + void operator=(const basic_io_object&); + + IoObjectService* service_; + implementation_type implementation_; +}; +#endif // defined(ASIO_HAS_MOVE) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_IO_OBJECT_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_raw_socket.hpp b/third_party/asio/1.18.2/include/asio/basic_raw_socket.hpp new file mode 100644 index 000000000..60801e3fa --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_raw_socket.hpp @@ -0,0 +1,1214 @@ +// +// basic_raw_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_RAW_SOCKET_HPP +#define ASIO_BASIC_RAW_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/basic_socket.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if !defined(ASIO_BASIC_RAW_SOCKET_FWD_DECL) +#define ASIO_BASIC_RAW_SOCKET_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_raw_socket; + +#endif // !defined(ASIO_BASIC_RAW_SOCKET_FWD_DECL) + +/// Provides raw-oriented socket functionality. +/** + * The basic_raw_socket class template provides asynchronous and blocking + * raw-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * Synchronous @c send, @c send_to, @c receive, @c receive_from, and @c connect + * operations are thread safe with respect to each other, if the underlying + * operating system calls are also thread safe. This means that it is permitted + * to perform concurrent calls to these synchronous operations on a single + * socket object. Other synchronous operations, such as @c open or @c close, are + * not thread safe. + */ +template +class basic_raw_socket + : public basic_socket +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_raw_socket other; + }; + + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_raw_socket without opening it. + /** + * This constructor creates a raw socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_raw_socket(const executor_type& ex) + : basic_socket(ex) + { + } + + /// Construct a basic_raw_socket without opening it. + /** + * This constructor creates a raw socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template + explicit basic_raw_socket(ExecutionContext& context, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context) + { + } + + /// Construct and open a basic_raw_socket. + /** + * This constructor creates and opens a raw socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_raw_socket(const executor_type& ex, const protocol_type& protocol) + : basic_socket(ex, protocol) + { + } + + /// Construct and open a basic_raw_socket. + /** + * This constructor creates and opens a raw socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_raw_socket(ExecutionContext& context, const protocol_type& protocol, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : basic_socket(context, protocol) + { + } + + /// Construct a basic_raw_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a raw socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the raw + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_raw_socket(const executor_type& ex, const endpoint_type& endpoint) + : basic_socket(ex, endpoint) + { + } + + /// Construct a basic_raw_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a raw socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the raw + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_raw_socket(ExecutionContext& context, const endpoint_type& endpoint, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context, endpoint) + { + } + + /// Construct a basic_raw_socket on an existing native socket. + /** + * This constructor creates a raw socket object to hold an existing + * native socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_raw_socket(const executor_type& ex, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_socket(ex, protocol, native_socket) + { + } + + /// Construct a basic_raw_socket on an existing native socket. + /** + * This constructor creates a raw socket object to hold an existing + * native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_raw_socket(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_socket, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context, protocol, native_socket) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_raw_socket from another. + /** + * This constructor moves a raw socket from one object to another. + * + * @param other The other basic_raw_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_raw_socket(const executor_type&) + * constructor. + */ + basic_raw_socket(basic_raw_socket&& other) ASIO_NOEXCEPT + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_raw_socket from another. + /** + * This assignment operator moves a raw socket from one object to another. + * + * @param other The other basic_raw_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_raw_socket(const executor_type&) + * constructor. + */ + basic_raw_socket& operator=(basic_raw_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } + + /// Move-construct a basic_raw_socket from a socket of another protocol + /// type. + /** + * This constructor moves a raw socket from one object to another. + * + * @param other The other basic_raw_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_raw_socket(const executor_type&) + * constructor. + */ + template + basic_raw_socket(basic_raw_socket&& other, + typename constraint< + is_convertible::value + && is_convertible::value + >::type = 0) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_raw_socket from a socket of another protocol type. + /** + * This assignment operator moves a raw socket from one object to another. + * + * @param other The other basic_raw_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_raw_socket(const executor_type&) + * constructor. + */ + template + typename constraint< + is_convertible::value + && is_convertible::value, + basic_raw_socket& + >::type operator=(basic_raw_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_raw_socket() + { + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected raw socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code socket.send(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected raw socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected raw socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected raw + * socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send(this), handler, + buffers, socket_base::message_flags(0)); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected raw + * socket. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send(this), handler, buffers, flags); + } + + /// Send raw data to the specified endpoint. + /** + * This function is used to send raw data to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.send_to(asio::buffer(data, size), destination); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send_to( + this->impl_.get_implementation(), buffers, destination, 0, ec); + asio::detail::throw_error(ec, "send_to"); + return s; + } + + /// Send raw data to the specified endpoint. + /** + * This function is used to send raw data to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send_to( + this->impl_.get_implementation(), buffers, destination, flags, ec); + asio::detail::throw_error(ec, "send_to"); + return s; + } + + /// Send raw data to the specified endpoint. + /** + * This function is used to send raw data to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->impl_.get_service().send_to(this->impl_.get_implementation(), + buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send raw data to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_send_to( + * asio::buffer(data, size), destination, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send_to(this), handler, buffers, + destination, socket_base::message_flags(0)); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send raw data to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send_to(this), handler, buffers, destination, flags); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the raw socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected raw + * socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.receive(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the raw socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected raw + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the raw socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected raw + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the raw + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * raw socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive(this), handler, + buffers, socket_base::message_flags(0)); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the raw + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * raw socket. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive(this), handler, buffers, flags); + } + + /// Receive raw data with the endpoint of the sender. + /** + * This function is used to receive raw data. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * asio::ip::udp::endpoint sender_endpoint; + * socket.receive_from( + * asio::buffer(data, size), sender_endpoint); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, 0, ec); + asio::detail::throw_error(ec, "receive_from"); + return s; + } + + /// Receive raw data with the endpoint of the sender. + /** + * This function is used to receive raw data. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, flags, ec); + asio::detail::throw_error(ec, "receive_from"); + return s; + } + + /// Receive raw data with the endpoint of the sender. + /** + * This function is used to receive raw data. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive raw data. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.async_receive_from( + * asio::buffer(data, size), 0, sender_endpoint, handler); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive_from(this), handler, buffers, + &sender_endpoint, socket_base::message_flags(0)); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive raw data. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive_from(this), handler, + buffers, &sender_endpoint, flags); + } + +private: + // Disallow copying and assignment. + basic_raw_socket(const basic_raw_socket&) ASIO_DELETED; + basic_raw_socket& operator=(const basic_raw_socket&) ASIO_DELETED; + + class initiate_async_send + { + public: + typedef Executor executor_type; + + explicit initiate_async_send(basic_raw_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + const ConstBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_send( + self_->impl_.get_implementation(), buffers, flags, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_raw_socket* self_; + }; + + class initiate_async_send_to + { + public: + typedef Executor executor_type; + + explicit initiate_async_send_to(basic_raw_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_send_to( + self_->impl_.get_implementation(), buffers, destination, + flags, handler2.value, self_->impl_.get_executor()); + } + + private: + basic_raw_socket* self_; + }; + + class initiate_async_receive + { + public: + typedef Executor executor_type; + + explicit initiate_async_receive(basic_raw_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + const MutableBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_receive( + self_->impl_.get_implementation(), buffers, flags, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_raw_socket* self_; + }; + + class initiate_async_receive_from + { + public: + typedef Executor executor_type; + + explicit initiate_async_receive_from(basic_raw_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + const MutableBufferSequence& buffers, endpoint_type* sender_endpoint, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_receive_from( + self_->impl_.get_implementation(), buffers, *sender_endpoint, + flags, handler2.value, self_->impl_.get_executor()); + } + + private: + basic_raw_socket* self_; + }; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_RAW_SOCKET_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_seq_packet_socket.hpp b/third_party/asio/1.18.2/include/asio/basic_seq_packet_socket.hpp new file mode 100644 index 000000000..684261b79 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_seq_packet_socket.hpp @@ -0,0 +1,768 @@ +// +// basic_seq_packet_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SEQ_PACKET_SOCKET_HPP +#define ASIO_BASIC_SEQ_PACKET_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/basic_socket.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if !defined(ASIO_BASIC_SEQ_PACKET_SOCKET_FWD_DECL) +#define ASIO_BASIC_SEQ_PACKET_SOCKET_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_seq_packet_socket; + +#endif // !defined(ASIO_BASIC_SEQ_PACKET_SOCKET_FWD_DECL) + +/// Provides sequenced packet socket functionality. +/** + * The basic_seq_packet_socket class template provides asynchronous and blocking + * sequenced packet socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * Synchronous @c send, @c receive, and @c connect operations are thread safe + * with respect to each other, if the underlying operating system calls are + * also thread safe. This means that it is permitted to perform concurrent + * calls to these synchronous operations on a single socket object. Other + * synchronous operations, such as @c open or @c close, are not thread safe. + */ +template +class basic_seq_packet_socket + : public basic_socket +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_seq_packet_socket other; + }; + + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_seq_packet_socket without opening it. + /** + * This constructor creates a sequenced packet socket without opening it. The + * socket needs to be opened and then connected or accepted before data can + * be sent or received on it. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_seq_packet_socket(const executor_type& ex) + : basic_socket(ex) + { + } + + /// Construct a basic_seq_packet_socket without opening it. + /** + * This constructor creates a sequenced packet socket without opening it. The + * socket needs to be opened and then connected or accepted before data can + * be sent or received on it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template + explicit basic_seq_packet_socket(ExecutionContext& context, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context) + { + } + + /// Construct and open a basic_seq_packet_socket. + /** + * This constructor creates and opens a sequenced_packet socket. The socket + * needs to be connected or accepted before data can be sent or received on + * it. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_seq_packet_socket(const executor_type& ex, + const protocol_type& protocol) + : basic_socket(ex, protocol) + { + } + + /// Construct and open a basic_seq_packet_socket. + /** + * This constructor creates and opens a sequenced_packet socket. The socket + * needs to be connected or accepted before data can be sent or received on + * it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_seq_packet_socket(ExecutionContext& context, + const protocol_type& protocol, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : basic_socket(context, protocol) + { + } + + /// Construct a basic_seq_packet_socket, opening it and binding it to the + /// given local endpoint. + /** + * This constructor creates a sequenced packet socket and automatically opens + * it bound to the specified endpoint on the local machine. The protocol used + * is the protocol associated with the given endpoint. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the sequenced + * packet socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_seq_packet_socket(const executor_type& ex, + const endpoint_type& endpoint) + : basic_socket(ex, endpoint) + { + } + + /// Construct a basic_seq_packet_socket, opening it and binding it to the + /// given local endpoint. + /** + * This constructor creates a sequenced packet socket and automatically opens + * it bound to the specified endpoint on the local machine. The protocol used + * is the protocol associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the sequenced + * packet socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_seq_packet_socket(ExecutionContext& context, + const endpoint_type& endpoint, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context, endpoint) + { + } + + /// Construct a basic_seq_packet_socket on an existing native socket. + /** + * This constructor creates a sequenced packet socket object to hold an + * existing native socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_seq_packet_socket(const executor_type& ex, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_socket(ex, protocol, native_socket) + { + } + + /// Construct a basic_seq_packet_socket on an existing native socket. + /** + * This constructor creates a sequenced packet socket object to hold an + * existing native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_seq_packet_socket(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_socket, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context, protocol, native_socket) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_seq_packet_socket from another. + /** + * This constructor moves a sequenced packet socket from one object to + * another. + * + * @param other The other basic_seq_packet_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_seq_packet_socket(const executor_type&) + * constructor. + */ + basic_seq_packet_socket(basic_seq_packet_socket&& other) ASIO_NOEXCEPT + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_seq_packet_socket from another. + /** + * This assignment operator moves a sequenced packet socket from one object to + * another. + * + * @param other The other basic_seq_packet_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_seq_packet_socket(const executor_type&) + * constructor. + */ + basic_seq_packet_socket& operator=(basic_seq_packet_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } + + /// Move-construct a basic_seq_packet_socket from a socket of another protocol + /// type. + /** + * This constructor moves a sequenced packet socket from one object to + * another. + * + * @param other The other basic_seq_packet_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_seq_packet_socket(const executor_type&) + * constructor. + */ + template + basic_seq_packet_socket(basic_seq_packet_socket&& other, + typename constraint< + is_convertible::value + && is_convertible::value + >::type = 0) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_seq_packet_socket from a socket of another protocol + /// type. + /** + * This assignment operator moves a sequenced packet socket from one object to + * another. + * + * @param other The other basic_seq_packet_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_seq_packet_socket(const executor_type&) + * constructor. + */ + template + typename constraint< + is_convertible::value + && is_convertible::value, + basic_seq_packet_socket& + >::type operator=(basic_seq_packet_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_seq_packet_socket() + { + } + + /// Send some data on the socket. + /** + * This function is used to send data on the sequenced packet socket. The + * function call will block until the data has been sent successfully, or an + * until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on the socket. + /** + * This function is used to send data on the sequenced packet socket. The + * function call will block the data has been sent successfully, or an until + * error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. Returns 0 if an error occurred. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the sequenced packet + * socket. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send(this), handler, buffers, flags); + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the sequenced packet socket. The + * function call will block until data has been received successfully, or + * until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param out_flags After the receive call completes, contains flags + * associated with the received data. For example, if the + * socket_base::message_end_of_record bit is set then the received data marks + * the end of a record. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size), out_flags); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags& out_flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive_with_flags( + this->impl_.get_implementation(), buffers, 0, out_flags, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the sequenced packet socket. The + * function call will block until data has been received successfully, or + * until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param in_flags Flags specifying how the receive call is to be made. + * + * @param out_flags After the receive call completes, contains flags + * associated with the received data. For example, if the + * socket_base::message_end_of_record bit is set then the received data marks + * the end of a record. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size), 0, out_flags); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive_with_flags( + this->impl_.get_implementation(), buffers, in_flags, out_flags, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the sequenced packet socket. The + * function call will block until data has been received successfully, or + * until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param in_flags Flags specifying how the receive call is to be made. + * + * @param out_flags After the receive call completes, contains flags + * associated with the received data. For example, if the + * socket_base::message_end_of_record bit is set then the received data marks + * the end of a record. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. Returns 0 if an error occurred. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { + return this->impl_.get_service().receive_with_flags( + this->impl_.get_implementation(), buffers, in_flags, out_flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the sequenced + * packet socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param out_flags Once the asynchronous operation completes, contains flags + * associated with the received data. For example, if the + * socket_base::message_end_of_record bit is set then the received data marks + * the end of a record. The caller must guarantee that the referenced + * variable remains valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), out_flags, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags& out_flags, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive_with_flags(this), handler, + buffers, socket_base::message_flags(0), &out_flags); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the sequenced + * data socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param in_flags Flags specifying how the receive call is to be made. + * + * @param out_flags Once the asynchronous operation completes, contains flags + * associated with the received data. For example, if the + * socket_base::message_end_of_record bit is set then the received data marks + * the end of a record. The caller must guarantee that the referenced + * variable remains valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive( + * asio::buffer(data, size), + * 0, out_flags, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive_with_flags(this), + handler, buffers, in_flags, &out_flags); + } + +private: + // Disallow copying and assignment. + basic_seq_packet_socket(const basic_seq_packet_socket&) ASIO_DELETED; + basic_seq_packet_socket& operator=( + const basic_seq_packet_socket&) ASIO_DELETED; + + class initiate_async_send + { + public: + typedef Executor executor_type; + + explicit initiate_async_send(basic_seq_packet_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + const ConstBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_send( + self_->impl_.get_implementation(), buffers, flags, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_seq_packet_socket* self_; + }; + + class initiate_async_receive_with_flags + { + public: + typedef Executor executor_type; + + explicit initiate_async_receive_with_flags(basic_seq_packet_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags* out_flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_receive_with_flags( + self_->impl_.get_implementation(), buffers, in_flags, + *out_flags, handler2.value, self_->impl_.get_executor()); + } + + private: + basic_seq_packet_socket* self_; + }; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SEQ_PACKET_SOCKET_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_serial_port.hpp b/third_party/asio/1.18.2/include/asio/basic_serial_port.hpp new file mode 100644 index 000000000..1c0c63697 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_serial_port.hpp @@ -0,0 +1,907 @@ +// +// basic_serial_port.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SERIAL_PORT_HPP +#define ASIO_BASIC_SERIAL_PORT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) \ + || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/any_io_executor.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/serial_port_base.hpp" +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_serial_port_service.hpp" +#else +# include "asio/detail/reactive_serial_port_service.hpp" +#endif + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides serial port functionality. +/** + * The basic_serial_port class provides a wrapper over serial port + * functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_serial_port + : public serial_port_base +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the serial port type to another executor. + template + struct rebind_executor + { + /// The serial port type when rebound to the specified executor. + typedef basic_serial_port other; + }; + + /// The native representation of a serial port. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#elif defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_serial_port_service::native_handle_type + native_handle_type; +#else + typedef detail::reactive_serial_port_service::native_handle_type + native_handle_type; +#endif + + /// A basic_basic_serial_port is always the lowest layer. + typedef basic_serial_port lowest_layer_type; + + /// Construct a basic_serial_port without opening it. + /** + * This constructor creates a serial port without opening it. + * + * @param ex The I/O executor that the serial port will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * serial port. + */ + explicit basic_serial_port(const executor_type& ex) + : impl_(0, ex) + { + } + + /// Construct a basic_serial_port without opening it. + /** + * This constructor creates a serial port without opening it. + * + * @param context An execution context which provides the I/O executor that + * the serial port will use, by default, to dispatch handlers for any + * asynchronous operations performed on the serial port. + */ + template + explicit basic_serial_port(ExecutionContext& context, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : impl_(0, 0, context) + { + } + + /// Construct and open a basic_serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param ex The I/O executor that the serial port will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * serial port. + * + * @param device The platform-specific device name for this serial + * port. + */ + basic_serial_port(const executor_type& ex, const char* device) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct and open a basic_serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param context An execution context which provides the I/O executor that + * the serial port will use, by default, to dispatch handlers for any + * asynchronous operations performed on the serial port. + * + * @param device The platform-specific device name for this serial + * port. + */ + template + basic_serial_port(ExecutionContext& context, const char* device, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct and open a basic_serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param ex The I/O executor that the serial port will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * serial port. + * + * @param device The platform-specific device name for this serial + * port. + */ + basic_serial_port(const executor_type& ex, const std::string& device) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct and open a basic_serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param context An execution context which provides the I/O executor that + * the serial port will use, by default, to dispatch handlers for any + * asynchronous operations performed on the serial port. + * + * @param device The platform-specific device name for this serial + * port. + */ + template + basic_serial_port(ExecutionContext& context, const std::string& device, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct a basic_serial_port on an existing native serial port. + /** + * This constructor creates a serial port object to hold an existing native + * serial port. + * + * @param ex The I/O executor that the serial port will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * serial port. + * + * @param native_serial_port A native serial port. + * + * @throws asio::system_error Thrown on failure. + */ + basic_serial_port(const executor_type& ex, + const native_handle_type& native_serial_port) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + native_serial_port, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Construct a basic_serial_port on an existing native serial port. + /** + * This constructor creates a serial port object to hold an existing native + * serial port. + * + * @param context An execution context which provides the I/O executor that + * the serial port will use, by default, to dispatch handlers for any + * asynchronous operations performed on the serial port. + * + * @param native_serial_port A native serial port. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_serial_port(ExecutionContext& context, + const native_handle_type& native_serial_port, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + native_serial_port, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_serial_port from another. + /** + * This constructor moves a serial port from one object to another. + * + * @param other The other basic_serial_port object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_serial_port(const executor_type&) + * constructor. + */ + basic_serial_port(basic_serial_port&& other) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_serial_port from another. + /** + * This assignment operator moves a serial port from one object to another. + * + * @param other The other basic_serial_port object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_serial_port(const executor_type&) + * constructor. + */ + basic_serial_port& operator=(basic_serial_port&& other) + { + impl_ = std::move(other.impl_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the serial port. + /** + * This function destroys the serial port, cancelling any outstanding + * asynchronous wait operations associated with the serial port as if by + * calling @c cancel. + */ + ~basic_serial_port() + { + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_serial_port cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since a basic_serial_port cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } + + /// Open the serial port using the specified device name. + /** + * This function opens the serial port for the specified device name. + * + * @param device The platform-specific device name. + * + * @throws asio::system_error Thrown on failure. + */ + void open(const std::string& device) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Open the serial port using the specified device name. + /** + * This function opens the serial port using the given platform-specific + * device name. + * + * @param device The platform-specific device name. + * + * @param ec Set the indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID open(const std::string& device, + asio::error_code& ec) + { + impl_.get_service().open(impl_.get_implementation(), device, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native serial port to the serial port. + /* + * This function opens the serial port to hold an existing native serial port. + * + * @param native_serial_port A native serial port. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_handle_type& native_serial_port) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + native_serial_port, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native serial port to the serial port. + /* + * This function opens the serial port to hold an existing native serial port. + * + * @param native_serial_port A native serial port. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const native_handle_type& native_serial_port, + asio::error_code& ec) + { + impl_.get_service().assign(impl_.get_implementation(), + native_serial_port, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the serial port is open. + bool is_open() const + { + return impl_.get_service().is_open(impl_.get_implementation()); + } + + /// Close the serial port. + /** + * This function is used to close the serial port. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + impl_.get_service().close(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the serial port. + /** + * This function is used to close the serial port. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + impl_.get_service().close(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the native serial port representation. + /** + * This function may be used to obtain the underlying representation of the + * serial port. This is intended to allow access to native serial port + * functionality that is not otherwise provided. + */ + native_handle_type native_handle() + { + return impl_.get_service().native_handle(impl_.get_implementation()); + } + + /// Cancel all asynchronous operations associated with the serial port. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + impl_.get_service().cancel(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the serial port. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + impl_.get_service().cancel(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Send a break sequence to the serial port. + /** + * This function causes a break sequence of platform-specific duration to be + * sent out the serial port. + * + * @throws asio::system_error Thrown on failure. + */ + void send_break() + { + asio::error_code ec; + impl_.get_service().send_break(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "send_break"); + } + + /// Send a break sequence to the serial port. + /** + * This function causes a break sequence of platform-specific duration to be + * sent out the serial port. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID send_break(asio::error_code& ec) + { + impl_.get_service().send_break(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Set an option on the serial port. + /** + * This function is used to set an option on the serial port. + * + * @param option The option value to be set on the serial port. + * + * @throws asio::system_error Thrown on failure. + * + * @sa SettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + void set_option(const SettableSerialPortOption& option) + { + asio::error_code ec; + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + asio::detail::throw_error(ec, "set_option"); + } + + /// Set an option on the serial port. + /** + * This function is used to set an option on the serial port. + * + * @param option The option value to be set on the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + ASIO_SYNC_OP_VOID set_option(const SettableSerialPortOption& option, + asio::error_code& ec) + { + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get an option from the serial port. + /** + * This function is used to get the current value of an option on the serial + * port. + * + * @param option The option value to be obtained from the serial port. + * + * @throws asio::system_error Thrown on failure. + * + * @sa GettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + void get_option(GettableSerialPortOption& option) const + { + asio::error_code ec; + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + asio::detail::throw_error(ec, "get_option"); + } + + /// Get an option from the serial port. + /** + * This function is used to get the current value of an option on the serial + * port. + * + * @param option The option value to be obtained from the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + ASIO_SYNC_OP_VOID get_option(GettableSerialPortOption& option, + asio::error_code& ec) const + { + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Write some data to the serial port. + /** + * This function is used to write data to the serial port. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the serial port. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * basic_serial_port.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = impl_.get_service().write_some( + impl_.get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "write_some"); + return s; + } + + /// Write some data to the serial port. + /** + * This function is used to write data to the serial port. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return impl_.get_service().write_some( + impl_.get_implementation(), buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the serial port. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the serial port. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * basic_serial_port.async_write_some( + * asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_write_some(this), handler, buffers); + } + + /// Read some data from the serial port. + /** + * This function is used to read data from the serial port. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * basic_serial_port.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = impl_.get_service().read_some( + impl_.get_implementation(), buffers, ec); + asio::detail::throw_error(ec, "read_some"); + return s; + } + + /// Read some data from the serial port. + /** + * This function is used to read data from the serial port. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return impl_.get_service().read_some( + impl_.get_implementation(), buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the serial port. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * basic_serial_port.async_read_some( + * asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_read_some(this), handler, buffers); + } + +private: + // Disallow copying and assignment. + basic_serial_port(const basic_serial_port&) ASIO_DELETED; + basic_serial_port& operator=(const basic_serial_port&) ASIO_DELETED; + + class initiate_async_write_some + { + public: + typedef Executor executor_type; + + explicit initiate_async_write_some(basic_serial_port* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + const ConstBufferSequence& buffers) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_write_some( + self_->impl_.get_implementation(), buffers, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_serial_port* self_; + }; + + class initiate_async_read_some + { + public: + typedef Executor executor_type; + + explicit initiate_async_read_some(basic_serial_port* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + const MutableBufferSequence& buffers) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_read_some( + self_->impl_.get_implementation(), buffers, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_serial_port* self_; + }; + +#if defined(ASIO_HAS_IOCP) + detail::io_object_impl impl_; +#else + detail::io_object_impl impl_; +#endif +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_SERIAL_PORT) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_BASIC_SERIAL_PORT_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_signal_set.hpp b/third_party/asio/1.18.2/include/asio/basic_signal_set.hpp new file mode 100644 index 000000000..25144346c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_signal_set.hpp @@ -0,0 +1,576 @@ +// +// basic_signal_set.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SIGNAL_SET_HPP +#define ASIO_BASIC_SIGNAL_SET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/any_io_executor.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/signal_set_service.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Provides signal functionality. +/** + * The basic_signal_set class provides the ability to perform an asynchronous + * wait for one or more signals to occur. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example + * Performing an asynchronous wait: + * @code + * void handler( + * const asio::error_code& error, + * int signal_number) + * { + * if (!error) + * { + * // A signal occurred. + * } + * } + * + * ... + * + * // Construct a signal set registered for process termination. + * asio::signal_set signals(my_context, SIGINT, SIGTERM); + * + * // Start an asynchronous wait for one of the signals to occur. + * signals.async_wait(handler); + * @endcode + * + * @par Queueing of signal notifications + * + * If a signal is registered with a signal_set, and the signal occurs when + * there are no waiting handlers, then the signal notification is queued. The + * next async_wait operation on that signal_set will dequeue the notification. + * If multiple notifications are queued, subsequent async_wait operations + * dequeue them one at a time. Signal notifications are dequeued in order of + * ascending signal number. + * + * If a signal number is removed from a signal_set (using the @c remove or @c + * erase member functions) then any queued notifications for that signal are + * discarded. + * + * @par Multiple registration of signals + * + * The same signal number may be registered with different signal_set objects. + * When the signal occurs, one handler is called for each signal_set object. + * + * Note that multiple registration only works for signals that are registered + * using Asio. The application must not also register a signal handler using + * functions such as @c signal() or @c sigaction(). + * + * @par Signal masking on POSIX platforms + * + * POSIX allows signals to be blocked using functions such as @c sigprocmask() + * and @c pthread_sigmask(). For signals to be delivered, programs must ensure + * that any signals registered using signal_set objects are unblocked in at + * least one thread. + */ +template +class basic_signal_set +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the signal set type to another executor. + template + struct rebind_executor + { + /// The signal set type when rebound to the specified executor. + typedef basic_signal_set other; + }; + + /// Construct a signal set without adding any signals. + /** + * This constructor creates a signal set without registering for any signals. + * + * @param ex The I/O executor that the signal set will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * signal set. + */ + explicit basic_signal_set(const executor_type& ex) + : impl_(0, ex) + { + } + + /// Construct a signal set without adding any signals. + /** + * This constructor creates a signal set without registering for any signals. + * + * @param context An execution context which provides the I/O executor that + * the signal set will use, by default, to dispatch handlers for any + * asynchronous operations performed on the signal set. + */ + template + explicit basic_signal_set(ExecutionContext& context, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : impl_(0, 0, context) + { + } + + /// Construct a signal set and add one signal. + /** + * This constructor creates a signal set and registers for one signal. + * + * @param ex The I/O executor that the signal set will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * signal set. + * + * @param signal_number_1 The signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(ex); + * signals.add(signal_number_1); @endcode + */ + basic_signal_set(const executor_type& ex, int signal_number_1) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Construct a signal set and add one signal. + /** + * This constructor creates a signal set and registers for one signal. + * + * @param context An execution context which provides the I/O executor that + * the signal set will use, by default, to dispatch handlers for any + * asynchronous operations performed on the signal set. + * + * @param signal_number_1 The signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(context); + * signals.add(signal_number_1); @endcode + */ + template + basic_signal_set(ExecutionContext& context, int signal_number_1, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Construct a signal set and add two signals. + /** + * This constructor creates a signal set and registers for two signals. + * + * @param ex The I/O executor that the signal set will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * signal set. + * + * @param signal_number_1 The first signal number to be added. + * + * @param signal_number_2 The second signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(ex); + * signals.add(signal_number_1); + * signals.add(signal_number_2); @endcode + */ + basic_signal_set(const executor_type& ex, int signal_number_1, + int signal_number_2) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + impl_.get_service().add(impl_.get_implementation(), signal_number_2, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Construct a signal set and add two signals. + /** + * This constructor creates a signal set and registers for two signals. + * + * @param context An execution context which provides the I/O executor that + * the signal set will use, by default, to dispatch handlers for any + * asynchronous operations performed on the signal set. + * + * @param signal_number_1 The first signal number to be added. + * + * @param signal_number_2 The second signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(context); + * signals.add(signal_number_1); + * signals.add(signal_number_2); @endcode + */ + template + basic_signal_set(ExecutionContext& context, int signal_number_1, + int signal_number_2, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + impl_.get_service().add(impl_.get_implementation(), signal_number_2, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Construct a signal set and add three signals. + /** + * This constructor creates a signal set and registers for three signals. + * + * @param ex The I/O executor that the signal set will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * signal set. + * + * @param signal_number_1 The first signal number to be added. + * + * @param signal_number_2 The second signal number to be added. + * + * @param signal_number_3 The third signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(ex); + * signals.add(signal_number_1); + * signals.add(signal_number_2); + * signals.add(signal_number_3); @endcode + */ + basic_signal_set(const executor_type& ex, int signal_number_1, + int signal_number_2, int signal_number_3) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + impl_.get_service().add(impl_.get_implementation(), signal_number_2, ec); + asio::detail::throw_error(ec, "add"); + impl_.get_service().add(impl_.get_implementation(), signal_number_3, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Construct a signal set and add three signals. + /** + * This constructor creates a signal set and registers for three signals. + * + * @param context An execution context which provides the I/O executor that + * the signal set will use, by default, to dispatch handlers for any + * asynchronous operations performed on the signal set. + * + * @param signal_number_1 The first signal number to be added. + * + * @param signal_number_2 The second signal number to be added. + * + * @param signal_number_3 The third signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(context); + * signals.add(signal_number_1); + * signals.add(signal_number_2); + * signals.add(signal_number_3); @endcode + */ + template + basic_signal_set(ExecutionContext& context, int signal_number_1, + int signal_number_2, int signal_number_3, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + impl_.get_service().add(impl_.get_implementation(), signal_number_2, ec); + asio::detail::throw_error(ec, "add"); + impl_.get_service().add(impl_.get_implementation(), signal_number_3, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Destroys the signal set. + /** + * This function destroys the signal set, cancelling any outstanding + * asynchronous wait operations associated with the signal set as if by + * calling @c cancel. + */ + ~basic_signal_set() + { + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + + /// Add a signal to a signal_set. + /** + * This function adds the specified signal to the set. It has no effect if the + * signal is already in the set. + * + * @param signal_number The signal to be added to the set. + * + * @throws asio::system_error Thrown on failure. + */ + void add(int signal_number) + { + asio::error_code ec; + impl_.get_service().add(impl_.get_implementation(), signal_number, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Add a signal to a signal_set. + /** + * This function adds the specified signal to the set. It has no effect if the + * signal is already in the set. + * + * @param signal_number The signal to be added to the set. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID add(int signal_number, + asio::error_code& ec) + { + impl_.get_service().add(impl_.get_implementation(), signal_number, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Remove a signal from a signal_set. + /** + * This function removes the specified signal from the set. It has no effect + * if the signal is not in the set. + * + * @param signal_number The signal to be removed from the set. + * + * @throws asio::system_error Thrown on failure. + * + * @note Removes any notifications that have been queued for the specified + * signal number. + */ + void remove(int signal_number) + { + asio::error_code ec; + impl_.get_service().remove(impl_.get_implementation(), signal_number, ec); + asio::detail::throw_error(ec, "remove"); + } + + /// Remove a signal from a signal_set. + /** + * This function removes the specified signal from the set. It has no effect + * if the signal is not in the set. + * + * @param signal_number The signal to be removed from the set. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Removes any notifications that have been queued for the specified + * signal number. + */ + ASIO_SYNC_OP_VOID remove(int signal_number, + asio::error_code& ec) + { + impl_.get_service().remove(impl_.get_implementation(), signal_number, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Remove all signals from a signal_set. + /** + * This function removes all signals from the set. It has no effect if the set + * is already empty. + * + * @throws asio::system_error Thrown on failure. + * + * @note Removes all queued notifications. + */ + void clear() + { + asio::error_code ec; + impl_.get_service().clear(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "clear"); + } + + /// Remove all signals from a signal_set. + /** + * This function removes all signals from the set. It has no effect if the set + * is already empty. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Removes all queued notifications. + */ + ASIO_SYNC_OP_VOID clear(asio::error_code& ec) + { + impl_.get_service().clear(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Cancel all operations associated with the signal set. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the signal set. The handler for each cancelled + * operation will be invoked with the asio::error::operation_aborted + * error code. + * + * Cancellation does not alter the set of registered signals. + * + * @throws asio::system_error Thrown on failure. + * + * @note If a registered signal occurred before cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + void cancel() + { + asio::error_code ec; + impl_.get_service().cancel(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all operations associated with the signal set. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the signal set. The handler for each cancelled + * operation will be invoked with the asio::error::operation_aborted + * error code. + * + * Cancellation does not alter the set of registered signals. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note If a registered signal occurred before cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + impl_.get_service().cancel(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous operation to wait for a signal to be delivered. + /** + * This function may be used to initiate an asynchronous wait against the + * signal set. It always returns immediately. + * + * For each call to async_wait(), the supplied handler will be called exactly + * once. The handler will be called when: + * + * @li One of the registered signals in the signal set occurs; or + * + * @li The signal set was cancelled, in which case the handler is passed the + * error code asio::error::operation_aborted. + * + * @param handler The handler to be called when the signal occurs. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * int signal_number // Indicates which signal occurred. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + */ + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code, int)) + SignalHandler ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(SignalHandler, + void (asio::error_code, int)) + async_wait( + ASIO_MOVE_ARG(SignalHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_wait(this), handler); + } + +private: + // Disallow copying and assignment. + basic_signal_set(const basic_signal_set&) ASIO_DELETED; + basic_signal_set& operator=(const basic_signal_set&) ASIO_DELETED; + + class initiate_async_wait + { + public: + typedef Executor executor_type; + + explicit initiate_async_wait(basic_signal_set* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(SignalHandler) handler) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a SignalHandler. + ASIO_SIGNAL_HANDLER_CHECK(SignalHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_wait( + self_->impl_.get_implementation(), + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_signal_set* self_; + }; + + detail::io_object_impl impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SIGNAL_SET_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_socket.hpp b/third_party/asio/1.18.2/include/asio/basic_socket.hpp new file mode 100644 index 000000000..a0404ca2a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_socket.hpp @@ -0,0 +1,1895 @@ +// +// basic_socket.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_HPP +#define ASIO_BASIC_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/any_io_executor.hpp" +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/post.hpp" +#include "asio/socket_base.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_socket_service.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +#else +# include "asio/detail/reactive_socket_service.hpp" +#endif + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if !defined(ASIO_BASIC_SOCKET_FWD_DECL) +#define ASIO_BASIC_SOCKET_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_socket; + +#endif // !defined(ASIO_BASIC_SOCKET_FWD_DECL) + +/// Provides socket functionality. +/** + * The basic_socket class template provides functionality that is common to both + * stream-oriented and datagram-oriented sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_socket + : public socket_base +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_socket other; + }; + + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#elif defined(ASIO_WINDOWS_RUNTIME) + typedef typename detail::null_socket_service< + Protocol>::native_handle_type native_handle_type; +#elif defined(ASIO_HAS_IOCP) + typedef typename detail::win_iocp_socket_service< + Protocol>::native_handle_type native_handle_type; +#else + typedef typename detail::reactive_socket_service< + Protocol>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +#if !defined(ASIO_NO_EXTENSIONS) + /// A basic_socket is always the lowest layer. + typedef basic_socket lowest_layer_type; +#endif // !defined(ASIO_NO_EXTENSIONS) + + /// Construct a basic_socket without opening it. + /** + * This constructor creates a socket without opening it. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_socket(const executor_type& ex) + : impl_(0, ex) + { + } + + /// Construct a basic_socket without opening it. + /** + * This constructor creates a socket without opening it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template + explicit basic_socket(ExecutionContext& context, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + } + + /// Construct and open a basic_socket. + /** + * This constructor creates and opens a socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket(const executor_type& ex, const protocol_type& protocol) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct and open a basic_socket. + /** + * This constructor creates and opens a socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_socket(ExecutionContext& context, const protocol_type& protocol, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct a basic_socket, opening it and binding it to the given local + /// endpoint. + /** + * This constructor creates a socket and automatically opens it bound to the + * specified endpoint on the local machine. The protocol used is the protocol + * associated with the given endpoint. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket(const executor_type& ex, const endpoint_type& endpoint) + : impl_(0, ex) + { + asio::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + } + + /// Construct a basic_socket, opening it and binding it to the given local + /// endpoint. + /** + * This constructor creates a socket and automatically opens it bound to the + * specified endpoint on the local machine. The protocol used is the protocol + * associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_socket(ExecutionContext& context, const endpoint_type& endpoint, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + } + + /// Construct a basic_socket on an existing native socket. + /** + * This constructor creates a socket object to hold an existing native socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket A native socket. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket(const executor_type& ex, const protocol_type& protocol, + const native_handle_type& native_socket) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Construct a basic_socket on an existing native socket. + /** + * This constructor creates a socket object to hold an existing native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket A native socket. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_socket(ExecutionContext& context, const protocol_type& protocol, + const native_handle_type& native_socket, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_socket from another. + /** + * This constructor moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + basic_socket(basic_socket&& other) ASIO_NOEXCEPT + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_socket from another. + /** + * This assignment operator moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + basic_socket& operator=(basic_socket&& other) + { + impl_ = std::move(other.impl_); + return *this; + } + + // All sockets have access to each other's implementations. + template + friend class basic_socket; + + /// Move-construct a basic_socket from a socket of another protocol type. + /** + * This constructor moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + template + basic_socket(basic_socket&& other, + typename constraint< + is_convertible::value + && is_convertible::value + >::type = 0) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_socket from a socket of another protocol type. + /** + * This assignment operator moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + template + typename constraint< + is_convertible::value + && is_convertible::value, + basic_socket& + >::type operator=(basic_socket && other) + { + basic_socket tmp(std::move(other)); + impl_ = std::move(tmp.impl_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + +#if !defined(ASIO_NO_EXTENSIONS) + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_socket cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since a basic_socket cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } +#endif // !defined(ASIO_NO_EXTENSIONS) + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * socket.open(asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * asio::error_code ec; + * socket.open(asio::ip::tcp::v4(), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID open(const protocol_type& protocol, + asio::error_code& ec) + { + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const protocol_type& protocol, + const native_handle_type& native_socket) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const protocol_type& protocol, + const native_handle_type& native_socket, asio::error_code& ec) + { + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is open. + bool is_open() const + { + return impl_.get_service().is_open(impl_.get_implementation()); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + * + * @note For portable behaviour with respect to graceful closure of a + * connected socket, call shutdown() before closing the socket. + */ + void close() + { + asio::error_code ec; + impl_.get_service().close(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::error_code ec; + * socket.close(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + * + * @note For portable behaviour with respect to graceful closure of a + * connected socket, call shutdown() before closing the socket. + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + impl_.get_service().close(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying native socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. Ownership + * of the native socket is then transferred to the caller. + * + * @throws asio::system_error Thrown on failure. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release() + { + asio::error_code ec; + native_handle_type s = impl_.get_service().release( + impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "release"); + return s; + } + + /// Release ownership of the underlying native socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. Ownership + * of the native socket is then transferred to the caller. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release(asio::error_code& ec) + { + return impl_.get_service().release(impl_.get_implementation(), ec); + } + + /// Get the native socket representation. + /** + * This function may be used to obtain the underlying representation of the + * socket. This is intended to allow access to native socket functionality + * that is not otherwise provided. + */ + native_handle_type native_handle() + { + return impl_.get_service().native_handle(impl_.get_implementation()); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + * + * @note Calls to cancel() will always fail with + * asio::error::operation_not_supported when run on Windows XP, Windows + * Server 2003, and earlier versions of Windows, unless + * ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has + * two issues that should be considered before enabling its use: + * + * @li It will only cancel asynchronous operations that were initiated in the + * current thread. + * + * @li It can appear to complete without error, but the request to cancel the + * unfinished operations may be silently ignored by the operating system. + * Whether it works or not seems to depend on the drivers that are installed. + * + * For portable cancellation, consider using one of the following + * alternatives: + * + * @li Disable asio's I/O completion port backend by defining + * ASIO_DISABLE_IOCP. + * + * @li Use the close() function to simultaneously cancel the outstanding + * operations and close the socket. + * + * When running on Windows Vista, Windows Server 2008, and later, the + * CancelIoEx function is always used. This function does not have the + * problems described above. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ + && !defined(ASIO_ENABLE_CANCELIO) + __declspec(deprecated("By default, this function always fails with " + "operation_not_supported when used on Windows XP, Windows Server 2003, " + "or earlier. Consult documentation for details.")) +#endif + void cancel() + { + asio::error_code ec; + impl_.get_service().cancel(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls to cancel() will always fail with + * asio::error::operation_not_supported when run on Windows XP, Windows + * Server 2003, and earlier versions of Windows, unless + * ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has + * two issues that should be considered before enabling its use: + * + * @li It will only cancel asynchronous operations that were initiated in the + * current thread. + * + * @li It can appear to complete without error, but the request to cancel the + * unfinished operations may be silently ignored by the operating system. + * Whether it works or not seems to depend on the drivers that are installed. + * + * For portable cancellation, consider using one of the following + * alternatives: + * + * @li Disable asio's I/O completion port backend by defining + * ASIO_DISABLE_IOCP. + * + * @li Use the close() function to simultaneously cancel the outstanding + * operations and close the socket. + * + * When running on Windows Vista, Windows Server 2008, and later, the + * CancelIoEx function is always used. This function does not have the + * problems described above. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ + && !defined(ASIO_ENABLE_CANCELIO) + __declspec(deprecated("By default, this function always fails with " + "operation_not_supported when used on Windows XP, Windows Server 2003, " + "or earlier. Consult documentation for details.")) +#endif + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + impl_.get_service().cancel(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + /** + * This function is used to check whether the socket input is currently + * positioned at the out-of-band data mark. + * + * @return A bool indicating whether the socket is at the out-of-band data + * mark. + * + * @throws asio::system_error Thrown on failure. + */ + bool at_mark() const + { + asio::error_code ec; + bool b = impl_.get_service().at_mark(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "at_mark"); + return b; + } + + /// Determine whether the socket is at the out-of-band data mark. + /** + * This function is used to check whether the socket input is currently + * positioned at the out-of-band data mark. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return A bool indicating whether the socket is at the out-of-band data + * mark. + */ + bool at_mark(asio::error_code& ec) const + { + return impl_.get_service().at_mark(impl_.get_implementation(), ec); + } + + /// Determine the number of bytes available for reading. + /** + * This function is used to determine the number of bytes that may be read + * without blocking. + * + * @return The number of bytes that may be read without blocking, or 0 if an + * error occurs. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t available() const + { + asio::error_code ec; + std::size_t s = impl_.get_service().available( + impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "available"); + return s; + } + + /// Determine the number of bytes available for reading. + /** + * This function is used to determine the number of bytes that may be read + * without blocking. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of bytes that may be read without blocking, or 0 if an + * error occurs. + */ + std::size_t available(asio::error_code& ec) const + { + return impl_.get_service().available(impl_.get_implementation(), ec); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * socket.open(asio::ip::tcp::v4()); + * socket.bind(asio::ip::tcp::endpoint( + * asio::ip::tcp::v4(), 12345)); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + asio::error_code ec; + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * socket.open(asio::ip::tcp::v4()); + * asio::error_code ec; + * socket.bind(asio::ip::tcp::endpoint( + * asio::ip::tcp::v4(), 12345), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID bind(const endpoint_type& endpoint, + asio::error_code& ec) + { + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.connect(endpoint); + * @endcode + */ + void connect(const endpoint_type& peer_endpoint) + { + asio::error_code ec; + if (!is_open()) + { + impl_.get_service().open(impl_.get_implementation(), + peer_endpoint.protocol(), ec); + asio::detail::throw_error(ec, "connect"); + } + impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); + asio::detail::throw_error(ec, "connect"); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * asio::error_code ec; + * socket.connect(endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID connect(const endpoint_type& peer_endpoint, + asio::error_code& ec) + { + if (!is_open()) + { + impl_.get_service().open(impl_.get_implementation(), + peer_endpoint.protocol(), ec); + if (ec) + { + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous connect. + /** + * This function is used to asynchronously connect a socket to the specified + * remote endpoint. The function call always returns immediately. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. Copies will be made of the endpoint object as required. + * + * @param handler The handler to be called when the connection operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void connect_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Connect succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::socket socket(my_context); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_connect(endpoint, connect_handler); + * @endcode + */ + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code)) + ConnectHandler ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(ConnectHandler, + void (asio::error_code)) + async_connect(const endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(ConnectHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + asio::error_code open_ec; + if (!is_open()) + { + const protocol_type protocol = peer_endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, open_ec); + } + + return async_initiate( + initiate_async_connect(this), handler, peer_endpoint, open_ec); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @sa SettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::ip::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + */ + template + void set_option(const SettableSocketOption& option) + { + asio::error_code ec; + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + asio::detail::throw_error(ec, "set_option"); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::ip::tcp::no_delay option(true); + * asio::error_code ec; + * socket.set_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + ASIO_SYNC_OP_VOID set_option(const SettableSocketOption& option, + asio::error_code& ec) + { + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @sa GettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::ip::tcp::socket::keep_alive option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + */ + template + void get_option(GettableSocketOption& option) const + { + asio::error_code ec; + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + asio::detail::throw_error(ec, "get_option"); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::ip::tcp::socket::keep_alive option; + * asio::error_code ec; + * socket.get_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * bool is_set = option.value(); + * @endcode + */ + template + ASIO_SYNC_OP_VOID get_option(GettableSocketOption& option, + asio::error_code& ec) const + { + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * asio::socket_base::bytes_readable @n + * asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::ip::tcp::socket::bytes_readable command; + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IoControlCommand& command) + { + asio::error_code ec; + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + asio::detail::throw_error(ec, "io_control"); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * asio::socket_base::bytes_readable @n + * asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::ip::tcp::socket::bytes_readable command; + * asio::error_code ec; + * socket.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, + asio::error_code& ec) + { + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the socket. + /** + * @returns @c true if the socket's synchronous operations will fail with + * asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + bool non_blocking() const + { + return impl_.get_service().non_blocking(impl_.get_implementation()); + } + + /// Sets the non-blocking mode of the socket. + /** + * @param mode If @c true, the socket's synchronous operations will fail with + * asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @throws asio::system_error Thrown on failure. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + void non_blocking(bool mode) + { + asio::error_code ec; + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + asio::detail::throw_error(ec, "non_blocking"); + } + + /// Sets the non-blocking mode of the socket. + /** + * @param mode If @c true, the socket's synchronous operations will fail with + * asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + ASIO_SYNC_OP_VOID non_blocking( + bool mode, asio::error_code& ec) + { + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native socket implementation. + /** + * This function is used to retrieve the non-blocking mode of the underlying + * native socket. This mode has no effect on the behaviour of the socket + * object's synchronous operations. + * + * @returns @c true if the underlying socket is in non-blocking mode and + * direct system calls may fail with asio::error::would_block (or the + * equivalent system error). + * + * @note The current non-blocking mode is cached by the socket object. + * Consequently, the return value may be incorrect if the non-blocking mode + * was set directly on the native socket. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(asio::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = asio::error_code(n < 0 ? errno : 0, + * asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == asio::error::would_block + * || ec == asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + bool native_non_blocking() const + { + return impl_.get_service().native_non_blocking(impl_.get_implementation()); + } + + /// Sets the non-blocking mode of the native socket implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native socket. It has no effect on the behaviour of the socket object's + * synchronous operations. + * + * @param mode If @c true, the underlying socket is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @throws asio::system_error Thrown on failure. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(asio::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = asio::error_code(n < 0 ? errno : 0, + * asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == asio::error::would_block + * || ec == asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + void native_non_blocking(bool mode) + { + asio::error_code ec; + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + asio::detail::throw_error(ec, "native_non_blocking"); + } + + /// Sets the non-blocking mode of the native socket implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native socket. It has no effect on the behaviour of the socket object's + * synchronous operations. + * + * @param mode If @c true, the underlying socket is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @param ec Set to indicate what error occurred, if any. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(asio::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = asio::error_code(n < 0 ? errno : 0, + * asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == asio::error::would_block + * || ec == asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + ASIO_SYNC_OP_VOID native_non_blocking( + bool mode, asio::error_code& ec) + { + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @returns An object that represents the local endpoint of the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + asio::error_code ec; + endpoint_type ep = impl_.get_service().local_endpoint( + impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "local_endpoint"); + return ep; + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the local endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::error_code ec; + * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type local_endpoint(asio::error_code& ec) const + { + return impl_.get_service().local_endpoint(impl_.get_implementation(), ec); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @returns An object that represents the remote endpoint of the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); + * @endcode + */ + endpoint_type remote_endpoint() const + { + asio::error_code ec; + endpoint_type ep = impl_.get_service().remote_endpoint( + impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "remote_endpoint"); + return ep; + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the remote endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::error_code ec; + * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type remote_endpoint(asio::error_code& ec) const + { + return impl_.get_service().remote_endpoint(impl_.get_implementation(), ec); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * Shutting down the send side of the socket: + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * socket.shutdown(asio::ip::tcp::socket::shutdown_send); + * @endcode + */ + void shutdown(shutdown_type what) + { + asio::error_code ec; + impl_.get_service().shutdown(impl_.get_implementation(), what, ec); + asio::detail::throw_error(ec, "shutdown"); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Shutting down the send side of the socket: + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::error_code ec; + * socket.shutdown(asio::ip::tcp::socket::shutdown_send, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID shutdown(shutdown_type what, + asio::error_code& ec) + { + impl_.get_service().shutdown(impl_.get_implementation(), what, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @par Example + * Waiting for a socket to become readable. + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * socket.wait(asio::ip::tcp::socket::wait_read); + * @endcode + */ + void wait(wait_type w) + { + asio::error_code ec; + impl_.get_service().wait(impl_.get_implementation(), w, ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Waiting for a socket to become readable. + * @code + * asio::ip::tcp::socket socket(my_context); + * ... + * asio::error_code ec; + * socket.wait(asio::ip::tcp::socket::wait_read, ec); + * @endcode + */ + ASIO_SYNC_OP_VOID wait(wait_type w, asio::error_code& ec) + { + impl_.get_service().wait(impl_.get_implementation(), w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the socket to become ready to read, ready to + /// write, or to have pending error conditions. + /** + * This function is used to perform an asynchronous wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @param handler The handler to be called when the wait operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void wait_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Wait succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::socket socket(my_context); + * ... + * socket.async_wait(asio::ip::tcp::socket::wait_read, wait_handler); + * @endcode + */ + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code)) + WaitHandler ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(wait_type w, + ASIO_MOVE_ARG(WaitHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_wait(this), handler, w); + } + +protected: + /// Protected destructor to prevent deletion through this type. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_socket() + { + } + +#if defined(ASIO_WINDOWS_RUNTIME) + detail::io_object_impl< + detail::null_socket_service, Executor> impl_; +#elif defined(ASIO_HAS_IOCP) + detail::io_object_impl< + detail::win_iocp_socket_service, Executor> impl_; +#else + detail::io_object_impl< + detail::reactive_socket_service, Executor> impl_; +#endif + +private: + // Disallow copying and assignment. + basic_socket(const basic_socket&) ASIO_DELETED; + basic_socket& operator=(const basic_socket&) ASIO_DELETED; + + class initiate_async_connect + { + public: + typedef Executor executor_type; + + explicit initiate_async_connect(basic_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(ConnectHandler) handler, + const endpoint_type& peer_endpoint, + const asio::error_code& open_ec) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ConnectHandler. + ASIO_CONNECT_HANDLER_CHECK(ConnectHandler, handler) type_check; + + if (open_ec) + { + asio::post(self_->impl_.get_executor(), + asio::detail::bind_handler( + ASIO_MOVE_CAST(ConnectHandler)(handler), open_ec)); + } + else + { + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_connect( + self_->impl_.get_implementation(), peer_endpoint, + handler2.value, self_->impl_.get_executor()); + } + } + + private: + basic_socket* self_; + }; + + class initiate_async_wait + { + public: + typedef Executor executor_type; + + explicit initiate_async_wait(basic_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WaitHandler) handler, wait_type w) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_wait( + self_->impl_.get_implementation(), w, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_socket* self_; + }; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SOCKET_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_socket_acceptor.hpp b/third_party/asio/1.18.2/include/asio/basic_socket_acceptor.hpp new file mode 100644 index 000000000..2633f8cce --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_socket_acceptor.hpp @@ -0,0 +1,2508 @@ +// +// basic_socket_acceptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_ACCEPTOR_HPP +#define ASIO_BASIC_SOCKET_ACCEPTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/any_io_executor.hpp" +#include "asio/basic_socket.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/socket_base.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_socket_service.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +#else +# include "asio/detail/reactive_socket_service.hpp" +#endif + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if !defined(ASIO_BASIC_SOCKET_ACCEPTOR_FWD_DECL) +#define ASIO_BASIC_SOCKET_ACCEPTOR_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_socket_acceptor; + +#endif // !defined(ASIO_BASIC_SOCKET_ACCEPTOR_FWD_DECL) + +/// Provides the ability to accept new connections. +/** + * The basic_socket_acceptor class template is used for accepting new socket + * connections. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * Synchronous @c accept operations are thread safe, if the underlying + * operating system calls are also thread safe. This means that it is permitted + * to perform concurrent calls to synchronous @c accept operations on a single + * socket object. Other synchronous operations, such as @c open or @c close, are + * not thread safe. + * + * @par Example + * Opening a socket acceptor with the SO_REUSEADDR option enabled: + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port); + * acceptor.open(endpoint.protocol()); + * acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ +template +class basic_socket_acceptor + : public socket_base +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the acceptor type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_socket_acceptor other; + }; + + /// The native representation of an acceptor. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#elif defined(ASIO_WINDOWS_RUNTIME) + typedef typename detail::null_socket_service< + Protocol>::native_handle_type native_handle_type; +#elif defined(ASIO_HAS_IOCP) + typedef typename detail::win_iocp_socket_service< + Protocol>::native_handle_type native_handle_type; +#else + typedef typename detail::reactive_socket_service< + Protocol>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct an acceptor without opening it. + /** + * This constructor creates an acceptor without opening it to listen for new + * connections. The open() function must be called before the acceptor can + * accept new socket connections. + * + * @param ex The I/O executor that the acceptor will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + */ + explicit basic_socket_acceptor(const executor_type& ex) + : impl_(0, ex) + { + } + + /// Construct an acceptor without opening it. + /** + * This constructor creates an acceptor without opening it to listen for new + * connections. The open() function must be called before the acceptor can + * accept new socket connections. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + */ + template + explicit basic_socket_acceptor(ExecutionContext& context, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + } + + /// Construct an open acceptor. + /** + * This constructor creates an acceptor and automatically opens it. + * + * @param ex The I/O executor that the acceptor will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket_acceptor(const executor_type& ex, const protocol_type& protocol) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct an open acceptor. + /** + * This constructor creates an acceptor and automatically opens it. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_socket_acceptor(ExecutionContext& context, + const protocol_type& protocol, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct an acceptor opened on the given endpoint. + /** + * This constructor creates an acceptor and automatically opens it to listen + * for new connections on the specified endpoint. + * + * @param ex The I/O executor that the acceptor will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param endpoint An endpoint on the local machine on which the acceptor + * will listen for new connections. + * + * @param reuse_addr Whether the constructor should set the socket option + * socket_base::reuse_address. + * + * @throws asio::system_error Thrown on failure. + * + * @note This constructor is equivalent to the following code: + * @code + * basic_socket_acceptor acceptor(my_context); + * acceptor.open(endpoint.protocol()); + * if (reuse_addr) + * acceptor.set_option(socket_base::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ + basic_socket_acceptor(const executor_type& ex, + const endpoint_type& endpoint, bool reuse_addr = true) + : impl_(0, ex) + { + asio::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + if (reuse_addr) + { + impl_.get_service().set_option(impl_.get_implementation(), + socket_base::reuse_address(true), ec); + asio::detail::throw_error(ec, "set_option"); + } + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + impl_.get_service().listen(impl_.get_implementation(), + socket_base::max_listen_connections, ec); + asio::detail::throw_error(ec, "listen"); + } + + /// Construct an acceptor opened on the given endpoint. + /** + * This constructor creates an acceptor and automatically opens it to listen + * for new connections on the specified endpoint. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + * + * @param endpoint An endpoint on the local machine on which the acceptor + * will listen for new connections. + * + * @param reuse_addr Whether the constructor should set the socket option + * socket_base::reuse_address. + * + * @throws asio::system_error Thrown on failure. + * + * @note This constructor is equivalent to the following code: + * @code + * basic_socket_acceptor acceptor(my_context); + * acceptor.open(endpoint.protocol()); + * if (reuse_addr) + * acceptor.set_option(socket_base::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ + template + basic_socket_acceptor(ExecutionContext& context, + const endpoint_type& endpoint, bool reuse_addr = true, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + if (reuse_addr) + { + impl_.get_service().set_option(impl_.get_implementation(), + socket_base::reuse_address(true), ec); + asio::detail::throw_error(ec, "set_option"); + } + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + impl_.get_service().listen(impl_.get_implementation(), + socket_base::max_listen_connections, ec); + asio::detail::throw_error(ec, "listen"); + } + + /// Construct a basic_socket_acceptor on an existing native acceptor. + /** + * This constructor creates an acceptor object to hold an existing native + * acceptor. + * + * @param ex The I/O executor that the acceptor will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket_acceptor(const executor_type& ex, + const protocol_type& protocol, const native_handle_type& native_acceptor) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_acceptor, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Construct a basic_socket_acceptor on an existing native acceptor. + /** + * This constructor creates an acceptor object to hold an existing native + * acceptor. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_socket_acceptor(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_acceptor, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_acceptor, ec); + asio::detail::throw_error(ec, "assign"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_socket_acceptor from another. + /** + * This constructor moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. + */ + basic_socket_acceptor(basic_socket_acceptor&& other) ASIO_NOEXCEPT + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_socket_acceptor from another. + /** + * This assignment operator moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. + */ + basic_socket_acceptor& operator=(basic_socket_acceptor&& other) + { + impl_ = std::move(other.impl_); + return *this; + } + + // All socket acceptors have access to each other's implementations. + template + friend class basic_socket_acceptor; + + /// Move-construct a basic_socket_acceptor from an acceptor of another + /// protocol type. + /** + * This constructor moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. + */ + template + basic_socket_acceptor(basic_socket_acceptor&& other, + typename constraint< + is_convertible::value + && is_convertible::value + >::type = 0) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_socket_acceptor from an acceptor of another protocol + /// type. + /** + * This assignment operator moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. + */ + template + typename constraint< + is_convertible::value + && is_convertible::value, + basic_socket_acceptor& + >::type operator=(basic_socket_acceptor&& other) + { + basic_socket_acceptor tmp(std::move(other)); + impl_ = std::move(tmp.impl_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the acceptor. + /** + * This function destroys the acceptor, cancelling any outstanding + * asynchronous operations associated with the acceptor as if by calling + * @c cancel. + */ + ~basic_socket_acceptor() + { + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * acceptor.open(asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * asio::error_code ec; + * acceptor.open(asio::ip::tcp::v4(), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID open(const protocol_type& protocol, + asio::error_code& ec) + { + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assigns an existing native acceptor to the acceptor. + /* + * This function opens the acceptor to hold an existing native acceptor. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const protocol_type& protocol, + const native_handle_type& native_acceptor) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_acceptor, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Assigns an existing native acceptor to the acceptor. + /* + * This function opens the acceptor to hold an existing native acceptor. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_acceptor A native acceptor. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID assign(const protocol_type& protocol, + const native_handle_type& native_acceptor, asio::error_code& ec) + { + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_acceptor, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the acceptor is open. + bool is_open() const + { + return impl_.get_service().is_open(impl_.get_implementation()); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), 12345); + * acceptor.open(endpoint.protocol()); + * acceptor.bind(endpoint); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + asio::error_code ec; + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), 12345); + * acceptor.open(endpoint.protocol()); + * asio::error_code ec; + * acceptor.bind(endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID bind(const endpoint_type& endpoint, + asio::error_code& ec) + { + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. + * + * @throws asio::system_error Thrown on failure. + */ + void listen(int backlog = socket_base::max_listen_connections) + { + asio::error_code ec; + impl_.get_service().listen(impl_.get_implementation(), backlog, ec); + asio::detail::throw_error(ec, "listen"); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::error_code ec; + * acceptor.listen(asio::socket_base::max_listen_connections, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID listen(int backlog, asio::error_code& ec) + { + impl_.get_service().listen(impl_.get_implementation(), backlog, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + impl_.get_service().close(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "close"); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::error_code ec; + * acceptor.close(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + impl_.get_service().close(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying native acceptor. + /** + * This function causes all outstanding asynchronous accept operations to + * finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. Ownership of the + * native acceptor is then transferred to the caller. + * + * @throws asio::system_error Thrown on failure. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release() + { + asio::error_code ec; + native_handle_type s = impl_.get_service().release( + impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "release"); + return s; + } + + /// Release ownership of the underlying native acceptor. + /** + * This function causes all outstanding asynchronous accept operations to + * finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. Ownership of the + * native acceptor is then transferred to the caller. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release(asio::error_code& ec) + { + return impl_.get_service().release(impl_.get_implementation(), ec); + } + + /// Get the native acceptor representation. + /** + * This function may be used to obtain the underlying representation of the + * acceptor. This is intended to allow access to native acceptor functionality + * that is not otherwise provided. + */ + native_handle_type native_handle() + { + return impl_.get_service().native_handle(impl_.get_implementation()); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + impl_.get_service().cancel(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) + { + impl_.get_service().cancel(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa SettableSocketOption @n + * asio::socket_base::reuse_address + * asio::socket_base::enable_connection_aborted + * + * @par Example + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::acceptor::reuse_address option(true); + * acceptor.set_option(option); + * @endcode + */ + template + void set_option(const SettableSocketOption& option) + { + asio::error_code ec; + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + asio::detail::throw_error(ec, "set_option"); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSocketOption @n + * asio::socket_base::reuse_address + * asio::socket_base::enable_connection_aborted + * + * @par Example + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::acceptor::reuse_address option(true); + * asio::error_code ec; + * acceptor.set_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + ASIO_SYNC_OP_VOID set_option(const SettableSocketOption& option, + asio::error_code& ec) + { + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa GettableSocketOption @n + * asio::socket_base::reuse_address + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::acceptor::reuse_address option; + * acceptor.get_option(option); + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(GettableSocketOption& option) const + { + asio::error_code ec; + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + asio::detail::throw_error(ec, "get_option"); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSocketOption @n + * asio::socket_base::reuse_address + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::acceptor::reuse_address option; + * asio::error_code ec; + * acceptor.get_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * bool is_set = option.get(); + * @endcode + */ + template + ASIO_SYNC_OP_VOID get_option(GettableSocketOption& option, + asio::error_code& ec) const + { + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the acceptor. + /** + * This function is used to execute an IO control command on the acceptor. + * + * @param command The IO control command to be performed on the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::acceptor::non_blocking_io command(true); + * socket.io_control(command); + * @endcode + */ + template + void io_control(IoControlCommand& command) + { + asio::error_code ec; + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + asio::detail::throw_error(ec, "io_control"); + } + + /// Perform an IO control command on the acceptor. + /** + * This function is used to execute an IO control command on the acceptor. + * + * @param command The IO control command to be performed on the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::acceptor::non_blocking_io command(true); + * asio::error_code ec; + * socket.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, + asio::error_code& ec) + { + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the acceptor. + /** + * @returns @c true if the acceptor's synchronous operations will fail with + * asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + bool non_blocking() const + { + return impl_.get_service().non_blocking(impl_.get_implementation()); + } + + /// Sets the non-blocking mode of the acceptor. + /** + * @param mode If @c true, the acceptor's synchronous operations will fail + * with asio::error::would_block if they are unable to perform the + * requested operation immediately. If @c false, synchronous operations will + * block until complete. + * + * @throws asio::system_error Thrown on failure. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + void non_blocking(bool mode) + { + asio::error_code ec; + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + asio::detail::throw_error(ec, "non_blocking"); + } + + /// Sets the non-blocking mode of the acceptor. + /** + * @param mode If @c true, the acceptor's synchronous operations will fail + * with asio::error::would_block if they are unable to perform the + * requested operation immediately. If @c false, synchronous operations will + * block until complete. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * asio::error::would_block. + */ + ASIO_SYNC_OP_VOID non_blocking( + bool mode, asio::error_code& ec) + { + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native acceptor implementation. + /** + * This function is used to retrieve the non-blocking mode of the underlying + * native acceptor. This mode has no effect on the behaviour of the acceptor + * object's synchronous operations. + * + * @returns @c true if the underlying acceptor is in non-blocking mode and + * direct system calls may fail with asio::error::would_block (or the + * equivalent system error). + * + * @note The current non-blocking mode is cached by the acceptor object. + * Consequently, the return value may be incorrect if the non-blocking mode + * was set directly on the native acceptor. + */ + bool native_non_blocking() const + { + return impl_.get_service().native_non_blocking(impl_.get_implementation()); + } + + /// Sets the non-blocking mode of the native acceptor implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native acceptor. It has no effect on the behaviour of the acceptor object's + * synchronous operations. + * + * @param mode If @c true, the underlying acceptor is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @throws asio::system_error Thrown on failure. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + */ + void native_non_blocking(bool mode) + { + asio::error_code ec; + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + asio::detail::throw_error(ec, "native_non_blocking"); + } + + /// Sets the non-blocking mode of the native acceptor implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native acceptor. It has no effect on the behaviour of the acceptor object's + * synchronous operations. + * + * @param mode If @c true, the underlying acceptor is put into non-blocking + * mode and direct system calls may fail with asio::error::would_block + * (or the equivalent system error). + * + * @param ec Set to indicate what error occurred, if any. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with asio::error::invalid_argument, as the + * combination does not make sense. + */ + ASIO_SYNC_OP_VOID native_non_blocking( + bool mode, asio::error_code& ec) + { + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the acceptor. + * + * @returns An object that represents the local endpoint of the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + asio::error_code ec; + endpoint_type ep = impl_.get_service().local_endpoint( + impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "local_endpoint"); + return ep; + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the local endpoint of the acceptor. + * Returns a default-constructed endpoint object if an error occurred and the + * error handler did not throw an exception. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::error_code ec; + * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type local_endpoint(asio::error_code& ec) const + { + return impl_.get_service().local_endpoint(impl_.get_implementation(), ec); + } + + /// Wait for the acceptor to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for an acceptor to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired acceptor state. + * + * @par Example + * Waiting for an acceptor to become readable. + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.wait(asio::ip::tcp::acceptor::wait_read); + * @endcode + */ + void wait(wait_type w) + { + asio::error_code ec; + impl_.get_service().wait(impl_.get_implementation(), w, ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Wait for the acceptor to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for an acceptor to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired acceptor state. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Waiting for an acceptor to become readable. + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::error_code ec; + * acceptor.wait(asio::ip::tcp::acceptor::wait_read, ec); + * @endcode + */ + ASIO_SYNC_OP_VOID wait(wait_type w, asio::error_code& ec) + { + impl_.get_service().wait(impl_.get_implementation(), w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the acceptor to become ready to read, ready to + /// write, or to have pending error conditions. + /** + * This function is used to perform an asynchronous wait for an acceptor to + * enter a ready to read, write or error condition state. + * + * @param w Specifies the desired acceptor state. + * + * @param handler The handler to be called when the wait operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void wait_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Wait succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.async_wait( + * asio::ip::tcp::acceptor::wait_read, + * wait_handler); + * @endcode + */ + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code)) + WaitHandler ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait(wait_type w, + ASIO_MOVE_ARG(WaitHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_wait(this), handler, w); + } + +#if !defined(ASIO_NO_EXTENSIONS) + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(my_context); + * acceptor.accept(socket); + * @endcode + */ + template + void accept(basic_socket& peer, + typename constraint< + is_convertible::value + >::type = 0) + { + asio::error_code ec; + impl_.get_service().accept(impl_.get_implementation(), + peer, static_cast(0), ec); + asio::detail::throw_error(ec, "accept"); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(my_context); + * asio::error_code ec; + * acceptor.accept(socket, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + ASIO_SYNC_OP_VOID accept( + basic_socket& peer, asio::error_code& ec, + typename constraint< + is_convertible::value + >::type = 0) + { + impl_.get_service().accept(impl_.get_implementation(), + peer, static_cast(0), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket. The function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(my_context); + * acceptor.async_accept(socket, accept_handler); + * @endcode + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(AcceptHandler, + void (asio::error_code)) + async_accept(basic_socket& peer, + ASIO_MOVE_ARG(AcceptHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type), + typename constraint< + is_convertible::value + >::type = 0) + { + return async_initiate( + initiate_async_accept(this), handler, + &peer, static_cast(0)); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(my_context); + * asio::ip::tcp::endpoint endpoint; + * acceptor.accept(socket, endpoint); + * @endcode + */ + template + void accept(basic_socket& peer, + endpoint_type& peer_endpoint) + { + asio::error_code ec; + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + asio::detail::throw_error(ec, "accept"); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(my_context); + * asio::ip::tcp::endpoint endpoint; + * asio::error_code ec; + * acceptor.accept(socket, endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + ASIO_SYNC_OP_VOID accept(basic_socket& peer, + endpoint_type& peer_endpoint, asio::error_code& ec) + { + impl_.get_service().accept( + impl_.get_implementation(), peer, &peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket, and additionally obtain the endpoint of the remote peer. The + * function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(AcceptHandler, + void (asio::error_code)) + async_accept(basic_socket& peer, + endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(AcceptHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_accept(this), handler, &peer, &peer_endpoint); + } +#endif // !defined(ASIO_NO_EXTENSIONS) + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept()); + * @endcode + */ + typename Protocol::socket::template rebind_executor::other + accept() + { + asio::error_code ec; + typename Protocol::socket::template rebind_executor< + executor_type>::other peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept(ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + typename Protocol::socket::template rebind_executor::other + accept(asio::error_code& ec) + { + typename Protocol::socket::template rebind_executor< + executor_type>::other peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + return peer; + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * // On success, the newly accepted socket. + * typename Protocol::socket::template + * rebind_executor::other peer + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.async_accept(accept_handler); + * @endcode + */ + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code, + typename Protocol::socket::template rebind_executor< + executor_type>::other)) MoveAcceptHandler + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, + typename Protocol::socket::template + rebind_executor::other)) + async_accept( + ASIO_MOVE_ARG(MoveAcceptHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate::other)>( + initiate_async_move_accept(this), handler, + impl_.get_executor(), static_cast(0), + static_cast::other*>(0)); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly + * accepted socket. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept()); + * @endcode + */ + template + typename Protocol::socket::template rebind_executor::other + accept(const Executor1& ex, + typename constraint< + is_executor::value + || execution::is_executor::value + >::type = 0) + { + asio::error_code ec; + typename Protocol::socket::template + rebind_executor::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept()); + * @endcode + */ + template + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, + typename constraint< + is_convertible::value + >::type = 0) + { + asio::error_code ec; + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly accepted + * socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept(my_context2, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + typename Protocol::socket::template rebind_executor::other + accept(const Executor1& ex, asio::error_code& ec, + typename constraint< + is_executor::value + || execution::is_executor::value + >::type = 0) + { + typename Protocol::socket::template + rebind_executor::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept(my_context2, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, asio::error_code& ec, + typename constraint< + is_convertible::value + >::type = 0) + { + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + return peer; + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly accepted + * socket. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * typename Protocol::socket::template rebind_executor< + * Executor1>::other peer // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.async_accept(my_context2, accept_handler); + * @endcode + */ + template ::other)) MoveAcceptHandler + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, + typename Protocol::socket::template rebind_executor< + Executor1>::other)) + async_accept(const Executor1& ex, + ASIO_MOVE_ARG(MoveAcceptHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type), + typename constraint< + is_executor::value + || execution::is_executor::value + >::type = 0) + { + typedef typename Protocol::socket::template rebind_executor< + Executor1>::other other_socket_type; + + return async_initiate( + initiate_async_move_accept(this), handler, + ex, static_cast(0), + static_cast(0)); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * typename Protocol::socket::template rebind_executor< + * typename ExecutionContext::executor_type>::other peer + * // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.async_accept(my_context2, accept_handler); + * @endcode + */ + template ::other)) MoveAcceptHandler + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other)) + async_accept(ExecutionContext& context, + ASIO_MOVE_ARG(MoveAcceptHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type), + typename constraint< + is_convertible::value + >::type = 0) + { + typedef typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other other_socket_type; + + return async_initiate( + initiate_async_move_accept(this), handler, + context.get_executor(), static_cast(0), + static_cast(0)); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket(acceptor.accept(endpoint)); + * @endcode + */ + typename Protocol::socket::template rebind_executor::other + accept(endpoint_type& peer_endpoint) + { + asio::error_code ec; + typename Protocol::socket::template rebind_executor< + executor_type>::other peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket(acceptor.accept(endpoint, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + typename Protocol::socket::template rebind_executor::other + accept(endpoint_type& peer_endpoint, asio::error_code& ec) + { + typename Protocol::socket::template rebind_executor< + executor_type>::other peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + return peer; + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * // On success, the newly accepted socket. + * typename Protocol::socket::template + * rebind_executor::other peer + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * acceptor.async_accept(endpoint, accept_handler); + * @endcode + */ + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code, + typename Protocol::socket::template rebind_executor< + executor_type>::other)) MoveAcceptHandler + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, + typename Protocol::socket::template + rebind_executor::other)) + async_accept(endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(MoveAcceptHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate::other)>( + initiate_async_move_accept(this), handler, + impl_.get_executor(), &peer_endpoint, + static_cast::other*>(0)); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly accepted + * socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket( + * acceptor.accept(my_context2, endpoint)); + * @endcode + */ + template + typename Protocol::socket::template rebind_executor::other + accept(const Executor1& ex, endpoint_type& peer_endpoint, + typename constraint< + is_executor::value + || execution::is_executor::value + >::type = 0) + { + asio::error_code ec; + typename Protocol::socket::template + rebind_executor::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket( + * acceptor.accept(my_context2, endpoint)); + * @endcode + */ + template + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, endpoint_type& peer_endpoint, + typename constraint< + is_convertible::value + >::type = 0) + { + asio::error_code ec; + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly accepted + * socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket( + * acceptor.accept(my_context2, endpoint, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + typename Protocol::socket::template rebind_executor::other + accept(const executor_type& ex, + endpoint_type& peer_endpoint, asio::error_code& ec, + typename constraint< + is_executor::value + || execution::is_executor::value + >::type = 0) + { + typename Protocol::socket::template + rebind_executor::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket( + * acceptor.accept(my_context2, endpoint, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, + endpoint_type& peer_endpoint, asio::error_code& ec, + typename constraint< + is_convertible::value + >::type = 0) + { + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + return peer; + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly accepted + * socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * typename Protocol::socket::template rebind_executor< + * Executor1>::other peer // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * acceptor.async_accept(my_context2, endpoint, accept_handler); + * @endcode + */ + template ::other)) MoveAcceptHandler + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, + typename Protocol::socket::template rebind_executor< + Executor1>::other)) + async_accept(const Executor1& ex, endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(MoveAcceptHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type), + typename constraint< + is_executor::value + || execution::is_executor::value + >::type = 0) + { + typedef typename Protocol::socket::template rebind_executor< + Executor1>::other other_socket_type; + + return async_initiate( + initiate_async_move_accept(this), handler, + ex, &peer_endpoint, + static_cast(0)); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * typename Protocol::socket::template rebind_executor< + * typename ExecutionContext::executor_type>::other peer + * // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * acceptor.async_accept(my_context2, endpoint, accept_handler); + * @endcode + */ + template ::other)) MoveAcceptHandler + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other)) + async_accept(ExecutionContext& context, + endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(MoveAcceptHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type), + typename constraint< + is_convertible::value + >::type = 0) + { + typedef typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other other_socket_type; + + return async_initiate( + initiate_async_move_accept(this), handler, + context.get_executor(), &peer_endpoint, + static_cast(0)); + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +private: + // Disallow copying and assignment. + basic_socket_acceptor(const basic_socket_acceptor&) ASIO_DELETED; + basic_socket_acceptor& operator=( + const basic_socket_acceptor&) ASIO_DELETED; + + class initiate_async_wait + { + public: + typedef Executor executor_type; + + explicit initiate_async_wait(basic_socket_acceptor* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WaitHandler) handler, wait_type w) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_wait( + self_->impl_.get_implementation(), w, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_socket_acceptor* self_; + }; + + class initiate_async_accept + { + public: + typedef Executor executor_type; + + explicit initiate_async_accept(basic_socket_acceptor* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(AcceptHandler) handler, + basic_socket* peer, + endpoint_type* peer_endpoint) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a AcceptHandler. + ASIO_ACCEPT_HANDLER_CHECK(AcceptHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_accept( + self_->impl_.get_implementation(), *peer, peer_endpoint, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_socket_acceptor* self_; + }; + + class initiate_async_move_accept + { + public: + typedef Executor executor_type; + + explicit initiate_async_move_accept(basic_socket_acceptor* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(MoveAcceptHandler) handler, + const Executor1& peer_ex, endpoint_type* peer_endpoint, Socket*) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a MoveAcceptHandler. + ASIO_MOVE_ACCEPT_HANDLER_CHECK( + MoveAcceptHandler, handler, Socket) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_move_accept( + self_->impl_.get_implementation(), peer_ex, peer_endpoint, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_socket_acceptor* self_; + }; + +#if defined(ASIO_WINDOWS_RUNTIME) + detail::io_object_impl< + detail::null_socket_service, Executor> impl_; +#elif defined(ASIO_HAS_IOCP) + detail::io_object_impl< + detail::win_iocp_socket_service, Executor> impl_; +#else + detail::io_object_impl< + detail::reactive_socket_service, Executor> impl_; +#endif +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SOCKET_ACCEPTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_socket_iostream.hpp b/third_party/asio/1.18.2/include/asio/basic_socket_iostream.hpp new file mode 100644 index 000000000..b6fd88880 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_socket_iostream.hpp @@ -0,0 +1,407 @@ +// +// basic_socket_iostream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_IOSTREAM_HPP +#define ASIO_BASIC_SOCKET_IOSTREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_NO_IOSTREAM) + +#include +#include +#include "asio/basic_socket_streambuf.hpp" + +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +# include "asio/detail/variadic_templates.hpp" + +// A macro that should expand to: +// template +// explicit basic_socket_iostream(T1 x1, ..., Tn xn) +// : std::basic_iostream( +// &this->detail::socket_iostream_base< +// Protocol, Clock, WaitTraits>::streambuf_) +// { +// if (rdbuf()->connect(x1, ..., xn) == 0) +// this->setstate(std::ios_base::failbit); +// } +// This macro should only persist within this file. + +# define ASIO_PRIVATE_CTR_DEF(n) \ + template \ + explicit basic_socket_iostream(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ + : std::basic_iostream( \ + &this->detail::socket_iostream_base< \ + Protocol, Clock, WaitTraits>::streambuf_) \ + { \ + this->setf(std::ios_base::unitbuf); \ + if (rdbuf()->connect(ASIO_VARIADIC_BYVAL_ARGS(n)) == 0) \ + this->setstate(std::ios_base::failbit); \ + } \ + /**/ + +// A macro that should expand to: +// template +// void connect(T1 x1, ..., Tn xn) +// { +// if (rdbuf()->connect(x1, ..., xn) == 0) +// this->setstate(std::ios_base::failbit); +// } +// This macro should only persist within this file. + +# define ASIO_PRIVATE_CONNECT_DEF(n) \ + template \ + void connect(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ + { \ + if (rdbuf()->connect(ASIO_VARIADIC_BYVAL_ARGS(n)) == 0) \ + this->setstate(std::ios_base::failbit); \ + } \ + /**/ + +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// A separate base class is used to ensure that the streambuf is initialised +// prior to the basic_socket_iostream's basic_iostream base class. +template +class socket_iostream_base +{ +protected: + socket_iostream_base() + { + } + +#if defined(ASIO_HAS_MOVE) + socket_iostream_base(socket_iostream_base&& other) + : streambuf_(std::move(other.streambuf_)) + { + } + + socket_iostream_base(basic_stream_socket s) + : streambuf_(std::move(s)) + { + } + + socket_iostream_base& operator=(socket_iostream_base&& other) + { + streambuf_ = std::move(other.streambuf_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + basic_socket_streambuf streambuf_; +}; + +} // namespace detail + +#if !defined(ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL) +#define ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL + +// Forward declaration with defaulted arguments. +template > +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typename Clock = chrono::steady_clock, + typename WaitTraits = wait_traits > +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) +class basic_socket_iostream; + +#endif // !defined(ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL) + +/// Iostream interface for a socket. +#if defined(GENERATING_DOCUMENTATION) +template > +#else // defined(GENERATING_DOCUMENTATION) +template +#endif // defined(GENERATING_DOCUMENTATION) +class basic_socket_iostream + : private detail::socket_iostream_base, + public std::basic_iostream +{ +private: + // These typedefs are intended keep this class's implementation independent + // of whether it's using Boost.DateClock, Boost.Chrono or std::chrono. +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typedef WaitTraits traits_helper; +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typedef detail::chrono_time_traits traits_helper; +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The clock type. + typedef Clock clock_type; + +#if defined(GENERATING_DOCUMENTATION) + /// (Deprecated: Use time_point.) The time type. + typedef typename WaitTraits::time_type time_type; + + /// The time type. + typedef typename WaitTraits::time_point time_point; + + /// (Deprecated: Use duration.) The duration type. + typedef typename WaitTraits::duration_type duration_type; + + /// The duration type. + typedef typename WaitTraits::duration duration; +#else +# if !defined(ASIO_NO_DEPRECATED) + typedef typename traits_helper::time_type time_type; + typedef typename traits_helper::duration_type duration_type; +# endif // !defined(ASIO_NO_DEPRECATED) + typedef typename traits_helper::time_type time_point; + typedef typename traits_helper::duration_type duration; +#endif + + /// Construct a basic_socket_iostream without establishing a connection. + basic_socket_iostream() + : std::basic_iostream( + &this->detail::socket_iostream_base< + Protocol, Clock, WaitTraits>::streambuf_) + { + this->setf(std::ios_base::unitbuf); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Construct a basic_socket_iostream from the supplied socket. + explicit basic_socket_iostream(basic_stream_socket s) + : detail::socket_iostream_base< + Protocol, Clock, WaitTraits>(std::move(s)), + std::basic_iostream( + &this->detail::socket_iostream_base< + Protocol, Clock, WaitTraits>::streambuf_) + { + this->setf(std::ios_base::unitbuf); + } + +#if defined(ASIO_HAS_STD_IOSTREAM_MOVE) \ + || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_socket_iostream from another. + basic_socket_iostream(basic_socket_iostream&& other) + : detail::socket_iostream_base< + Protocol, Clock, WaitTraits>(std::move(other)), + std::basic_iostream(std::move(other)) + { + this->set_rdbuf(&this->detail::socket_iostream_base< + Protocol, Clock, WaitTraits>::streambuf_); + } + + /// Move-assign a basic_socket_iostream from another. + basic_socket_iostream& operator=(basic_socket_iostream&& other) + { + std::basic_iostream::operator=(std::move(other)); + detail::socket_iostream_base< + Protocol, Clock, WaitTraits>::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_STD_IOSTREAM_MOVE) + // || defined(GENERATING_DOCUMENTATION) +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection to an endpoint corresponding to a resolver query. + /** + * This constructor automatically establishes a connection based on the + * supplied resolver query parameters. The arguments are used to construct + * a resolver query object. + */ + template + explicit basic_socket_iostream(T1 t1, ..., TN tn); +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + template + explicit basic_socket_iostream(T... x) + : std::basic_iostream( + &this->detail::socket_iostream_base< + Protocol, Clock, WaitTraits>::streambuf_) + { + this->setf(std::ios_base::unitbuf); + if (rdbuf()->connect(x...) == 0) + this->setstate(std::ios_base::failbit); + } +#else + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CTR_DEF) +#endif + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection to an endpoint corresponding to a resolver query. + /** + * This function automatically establishes a connection based on the supplied + * resolver query parameters. The arguments are used to construct a resolver + * query object. + */ + template + void connect(T1 t1, ..., TN tn); +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + template + void connect(T... x) + { + if (rdbuf()->connect(x...) == 0) + this->setstate(std::ios_base::failbit); + } +#else + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CONNECT_DEF) +#endif + + /// Close the connection. + void close() + { + if (rdbuf()->close() == 0) + this->setstate(std::ios_base::failbit); + } + + /// Return a pointer to the underlying streambuf. + basic_socket_streambuf* rdbuf() const + { + return const_cast*>( + &this->detail::socket_iostream_base< + Protocol, Clock, WaitTraits>::streambuf_); + } + + /// Get a reference to the underlying socket. + basic_socket& socket() + { + return rdbuf()->socket(); + } + + /// Get the last error associated with the stream. + /** + * @return An \c error_code corresponding to the last error from the stream. + * + * @par Example + * To print the error associated with a failure to establish a connection: + * @code tcp::iostream s("www.boost.org", "http"); + * if (!s) + * { + * std::cout << "Error: " << s.error().message() << std::endl; + * } @endcode + */ + const asio::error_code& error() const + { + return rdbuf()->error(); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use expiry().) Get the stream's expiry time as an absolute + /// time. + /** + * @return An absolute time value representing the stream's expiry time. + */ + time_point expires_at() const + { + return rdbuf()->expires_at(); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the stream's expiry time as an absolute time. + /** + * @return An absolute time value representing the stream's expiry time. + */ + time_point expiry() const + { + return rdbuf()->expiry(); + } + + /// Set the stream's expiry time as an absolute time. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the stream. + */ + void expires_at(const time_point& expiry_time) + { + rdbuf()->expires_at(expiry_time); + } + + /// Set the stream's expiry time relative to now. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the timer. + */ + void expires_after(const duration& expiry_time) + { + rdbuf()->expires_after(expiry_time); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use expiry().) Get the stream's expiry time relative to now. + /** + * @return A relative time value representing the stream's expiry time. + */ + duration expires_from_now() const + { + return rdbuf()->expires_from_now(); + } + + /// (Deprecated: Use expires_after().) Set the stream's expiry time relative + /// to now. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the timer. + */ + void expires_from_now(const duration& expiry_time) + { + rdbuf()->expires_from_now(expiry_time); + } +#endif // !defined(ASIO_NO_DEPRECATED) + +private: + // Disallow copying and assignment. + basic_socket_iostream(const basic_socket_iostream&) ASIO_DELETED; + basic_socket_iostream& operator=( + const basic_socket_iostream&) ASIO_DELETED; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) +# undef ASIO_PRIVATE_CTR_DEF +# undef ASIO_PRIVATE_CONNECT_DEF +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_socket_streambuf.hpp b/third_party/asio/1.18.2/include/asio/basic_socket_streambuf.hpp new file mode 100644 index 000000000..11efcce10 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_socket_streambuf.hpp @@ -0,0 +1,687 @@ +// +// basic_socket_streambuf.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_STREAMBUF_HPP +#define ASIO_BASIC_SOCKET_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_NO_IOSTREAM) + +#include +#include +#include "asio/basic_socket.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/io_context.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) +# include "asio/detail/deadline_timer_service.hpp" +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) +# include "asio/steady_timer.hpp" +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +# include "asio/detail/variadic_templates.hpp" + +// A macro that should expand to: +// template +// basic_socket_streambuf* connect(T1 x1, ..., Tn xn) +// { +// init_buffers(); +// typedef typename Protocol::resolver resolver_type; +// resolver_type resolver(socket().get_executor()); +// connect_to_endpoints( +// resolver.resolve(x1, ..., xn, ec_)); +// return !ec_ ? this : 0; +// } +// This macro should only persist within this file. + +# define ASIO_PRIVATE_CONNECT_DEF(n) \ + template \ + basic_socket_streambuf* connect(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ + { \ + init_buffers(); \ + typedef typename Protocol::resolver resolver_type; \ + resolver_type resolver(socket().get_executor()); \ + connect_to_endpoints( \ + resolver.resolve(ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \ + return !ec_ ? this : 0; \ + } \ + /**/ + +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// A separate base class is used to ensure that the io_context member is +// initialised prior to the basic_socket_streambuf's basic_socket base class. +class socket_streambuf_io_context +{ +protected: + socket_streambuf_io_context(io_context* ctx) + : default_io_context_(ctx) + { + } + + shared_ptr default_io_context_; +}; + +// A separate base class is used to ensure that the dynamically allocated +// buffers are constructed prior to the basic_socket_streambuf's basic_socket +// base class. This makes moving the socket is the last potentially throwing +// step in the streambuf's move constructor, giving the constructor a strong +// exception safety guarantee. +class socket_streambuf_buffers +{ +protected: + socket_streambuf_buffers() + : get_buffer_(buffer_size), + put_buffer_(buffer_size) + { + } + + enum { buffer_size = 512 }; + std::vector get_buffer_; + std::vector put_buffer_; +}; + +} // namespace detail + +#if !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL) +#define ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL + +// Forward declaration with defaulted arguments. +template > +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typename Clock = chrono::steady_clock, + typename WaitTraits = wait_traits > +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) +class basic_socket_streambuf; + +#endif // !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL) + +/// Iostream streambuf for a socket. +#if defined(GENERATING_DOCUMENTATION) +template > +#else // defined(GENERATING_DOCUMENTATION) +template +#endif // defined(GENERATING_DOCUMENTATION) +class basic_socket_streambuf + : public std::streambuf, + private detail::socket_streambuf_io_context, + private detail::socket_streambuf_buffers, +#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + private basic_socket +#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + public basic_socket +#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) +{ +private: + // These typedefs are intended keep this class's implementation independent + // of whether it's using Boost.DateClock, Boost.Chrono or std::chrono. +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typedef WaitTraits traits_helper; +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + typedef detail::chrono_time_traits traits_helper; +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The clock type. + typedef Clock clock_type; + +#if defined(GENERATING_DOCUMENTATION) + /// (Deprecated: Use time_point.) The time type. + typedef typename WaitTraits::time_type time_type; + + /// The time type. + typedef typename WaitTraits::time_point time_point; + + /// (Deprecated: Use duration.) The duration type. + typedef typename WaitTraits::duration_type duration_type; + + /// The duration type. + typedef typename WaitTraits::duration duration; +#else +# if !defined(ASIO_NO_DEPRECATED) + typedef typename traits_helper::time_type time_type; + typedef typename traits_helper::duration_type duration_type; +# endif // !defined(ASIO_NO_DEPRECATED) + typedef typename traits_helper::time_type time_point; + typedef typename traits_helper::duration_type duration; +#endif + + /// Construct a basic_socket_streambuf without establishing a connection. + basic_socket_streambuf() + : detail::socket_streambuf_io_context(new io_context), + basic_socket(*default_io_context_), + expiry_time_(max_expiry_time()) + { + init_buffers(); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Construct a basic_socket_streambuf from the supplied socket. + explicit basic_socket_streambuf(basic_stream_socket s) + : detail::socket_streambuf_io_context(0), + basic_socket(std::move(s)), + expiry_time_(max_expiry_time()) + { + init_buffers(); + } + + /// Move-construct a basic_socket_streambuf from another. + basic_socket_streambuf(basic_socket_streambuf&& other) + : detail::socket_streambuf_io_context(other), + basic_socket(std::move(other.socket())), + ec_(other.ec_), + expiry_time_(other.expiry_time_) + { + get_buffer_.swap(other.get_buffer_); + put_buffer_.swap(other.put_buffer_); + setg(other.eback(), other.gptr(), other.egptr()); + setp(other.pptr(), other.epptr()); + other.ec_ = asio::error_code(); + other.expiry_time_ = max_expiry_time(); + other.init_buffers(); + } + + /// Move-assign a basic_socket_streambuf from another. + basic_socket_streambuf& operator=(basic_socket_streambuf&& other) + { + this->close(); + socket() = std::move(other.socket()); + detail::socket_streambuf_io_context::operator=(other); + ec_ = other.ec_; + expiry_time_ = other.expiry_time_; + get_buffer_.swap(other.get_buffer_); + put_buffer_.swap(other.put_buffer_); + setg(other.eback(), other.gptr(), other.egptr()); + setp(other.pptr(), other.epptr()); + other.ec_ = asio::error_code(); + other.expiry_time_ = max_expiry_time(); + other.put_buffer_.resize(buffer_size); + other.init_buffers(); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destructor flushes buffered data. + virtual ~basic_socket_streambuf() + { + if (pptr() != pbase()) + overflow(traits_type::eof()); + } + + /// Establish a connection. + /** + * This function establishes a connection to the specified endpoint. + * + * @return \c this if a connection was successfully established, a null + * pointer otherwise. + */ + basic_socket_streambuf* connect(const endpoint_type& endpoint) + { + init_buffers(); + ec_ = asio::error_code(); + this->connect_to_endpoints(&endpoint, &endpoint + 1); + return !ec_ ? this : 0; + } + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection. + /** + * This function automatically establishes a connection based on the supplied + * resolver query parameters. The arguments are used to construct a resolver + * query object. + * + * @return \c this if a connection was successfully established, a null + * pointer otherwise. + */ + template + basic_socket_streambuf* connect(T1 t1, ..., TN tn); +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + template + basic_socket_streambuf* connect(T... x) + { + init_buffers(); + typedef typename Protocol::resolver resolver_type; + resolver_type resolver(socket().get_executor()); + connect_to_endpoints(resolver.resolve(x..., ec_)); + return !ec_ ? this : 0; + } +#else + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CONNECT_DEF) +#endif + + /// Close the connection. + /** + * @return \c this if a connection was successfully established, a null + * pointer otherwise. + */ + basic_socket_streambuf* close() + { + sync(); + socket().close(ec_); + if (!ec_) + init_buffers(); + return !ec_ ? this : 0; + } + + /// Get a reference to the underlying socket. + basic_socket& socket() + { + return *this; + } + + /// Get the last error associated with the stream buffer. + /** + * @return An \c error_code corresponding to the last error from the stream + * buffer. + */ + const asio::error_code& error() const + { + return ec_; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use error().) Get the last error associated with the stream + /// buffer. + /** + * @return An \c error_code corresponding to the last error from the stream + * buffer. + */ + const asio::error_code& puberror() const + { + return error(); + } + + /// (Deprecated: Use expiry().) Get the stream buffer's expiry time as an + /// absolute time. + /** + * @return An absolute time value representing the stream buffer's expiry + * time. + */ + time_point expires_at() const + { + return expiry_time_; + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the stream buffer's expiry time as an absolute time. + /** + * @return An absolute time value representing the stream buffer's expiry + * time. + */ + time_point expiry() const + { + return expiry_time_; + } + + /// Set the stream buffer's expiry time as an absolute time. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the stream. + */ + void expires_at(const time_point& expiry_time) + { + expiry_time_ = expiry_time; + } + + /// Set the stream buffer's expiry time relative to now. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the timer. + */ + void expires_after(const duration& expiry_time) + { + expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time); + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use expiry().) Get the stream buffer's expiry time relative + /// to now. + /** + * @return A relative time value representing the stream buffer's expiry time. + */ + duration expires_from_now() const + { + return traits_helper::subtract(expires_at(), traits_helper::now()); + } + + /// (Deprecated: Use expires_after().) Set the stream buffer's expiry time + /// relative to now. + /** + * This function sets the expiry time associated with the stream. Stream + * operations performed after this time (where the operations cannot be + * completed using the internal buffers) will fail with the error + * asio::error::operation_aborted. + * + * @param expiry_time The expiry time to be used for the timer. + */ + void expires_from_now(const duration& expiry_time) + { + expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time); + } +#endif // !defined(ASIO_NO_DEPRECATED) + +protected: + int_type underflow() + { +#if defined(ASIO_WINDOWS_RUNTIME) + ec_ = asio::error::operation_not_supported; + return traits_type::eof(); +#else // defined(ASIO_WINDOWS_RUNTIME) + if (gptr() != egptr()) + return traits_type::eof(); + + for (;;) + { + // Check if we are past the expiry time. + if (traits_helper::less_than(expiry_time_, traits_helper::now())) + { + ec_ = asio::error::timed_out; + return traits_type::eof(); + } + + // Try to complete the operation without blocking. + if (!socket().native_non_blocking()) + socket().native_non_blocking(true, ec_); + detail::buffer_sequence_adapter + bufs(asio::buffer(get_buffer_) + putback_max); + detail::signed_size_type bytes = detail::socket_ops::recv( + socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_); + + // Check if operation succeeded. + if (bytes > 0) + { + setg(&get_buffer_[0], &get_buffer_[0] + putback_max, + &get_buffer_[0] + putback_max + bytes); + return traits_type::to_int_type(*gptr()); + } + + // Check for EOF. + if (bytes == 0) + { + ec_ = asio::error::eof; + return traits_type::eof(); + } + + // Operation failed. + if (ec_ != asio::error::would_block + && ec_ != asio::error::try_again) + return traits_type::eof(); + + // Wait for socket to become ready. + if (detail::socket_ops::poll_read( + socket().native_handle(), 0, timeout(), ec_) < 0) + return traits_type::eof(); + } +#endif // defined(ASIO_WINDOWS_RUNTIME) + } + + int_type overflow(int_type c) + { +#if defined(ASIO_WINDOWS_RUNTIME) + ec_ = asio::error::operation_not_supported; + return traits_type::eof(); +#else // defined(ASIO_WINDOWS_RUNTIME) + char_type ch = traits_type::to_char_type(c); + + // Determine what needs to be sent. + const_buffer output_buffer; + if (put_buffer_.empty()) + { + if (traits_type::eq_int_type(c, traits_type::eof())) + return traits_type::not_eof(c); // Nothing to do. + output_buffer = asio::buffer(&ch, sizeof(char_type)); + } + else + { + output_buffer = asio::buffer(pbase(), + (pptr() - pbase()) * sizeof(char_type)); + } + + while (output_buffer.size() > 0) + { + // Check if we are past the expiry time. + if (traits_helper::less_than(expiry_time_, traits_helper::now())) + { + ec_ = asio::error::timed_out; + return traits_type::eof(); + } + + // Try to complete the operation without blocking. + if (!socket().native_non_blocking()) + socket().native_non_blocking(true, ec_); + detail::buffer_sequence_adapter< + const_buffer, const_buffer> bufs(output_buffer); + detail::signed_size_type bytes = detail::socket_ops::send( + socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_); + + // Check if operation succeeded. + if (bytes > 0) + { + output_buffer += static_cast(bytes); + continue; + } + + // Operation failed. + if (ec_ != asio::error::would_block + && ec_ != asio::error::try_again) + return traits_type::eof(); + + // Wait for socket to become ready. + if (detail::socket_ops::poll_write( + socket().native_handle(), 0, timeout(), ec_) < 0) + return traits_type::eof(); + } + + if (!put_buffer_.empty()) + { + setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); + + // If the new character is eof then our work here is done. + if (traits_type::eq_int_type(c, traits_type::eof())) + return traits_type::not_eof(c); + + // Add the new character to the output buffer. + *pptr() = ch; + pbump(1); + } + + return c; +#endif // defined(ASIO_WINDOWS_RUNTIME) + } + + int sync() + { + return overflow(traits_type::eof()); + } + + std::streambuf* setbuf(char_type* s, std::streamsize n) + { + if (pptr() == pbase() && s == 0 && n == 0) + { + put_buffer_.clear(); + setp(0, 0); + sync(); + return this; + } + + return 0; + } + +private: + // Disallow copying and assignment. + basic_socket_streambuf(const basic_socket_streambuf&) ASIO_DELETED; + basic_socket_streambuf& operator=( + const basic_socket_streambuf&) ASIO_DELETED; + + void init_buffers() + { + setg(&get_buffer_[0], + &get_buffer_[0] + putback_max, + &get_buffer_[0] + putback_max); + + if (put_buffer_.empty()) + setp(0, 0); + else + setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); + } + + int timeout() const + { + int64_t msec = traits_helper::to_posix_duration( + traits_helper::subtract(expiry_time_, + traits_helper::now())).total_milliseconds(); + if (msec > (std::numeric_limits::max)()) + msec = (std::numeric_limits::max)(); + else if (msec < 0) + msec = 0; + return static_cast(msec); + } + + template + void connect_to_endpoints(const EndpointSequence& endpoints) + { + this->connect_to_endpoints(endpoints.begin(), endpoints.end()); + } + + template + void connect_to_endpoints(EndpointIterator begin, EndpointIterator end) + { +#if defined(ASIO_WINDOWS_RUNTIME) + ec_ = asio::error::operation_not_supported; +#else // defined(ASIO_WINDOWS_RUNTIME) + if (ec_) + return; + + ec_ = asio::error::not_found; + for (EndpointIterator i = begin; i != end; ++i) + { + // Check if we are past the expiry time. + if (traits_helper::less_than(expiry_time_, traits_helper::now())) + { + ec_ = asio::error::timed_out; + return; + } + + // Close and reopen the socket. + typename Protocol::endpoint ep(*i); + socket().close(ec_); + socket().open(ep.protocol(), ec_); + if (ec_) + continue; + + // Try to complete the operation without blocking. + if (!socket().native_non_blocking()) + socket().native_non_blocking(true, ec_); + detail::socket_ops::connect(socket().native_handle(), + ep.data(), ep.size(), ec_); + + // Check if operation succeeded. + if (!ec_) + return; + + // Operation failed. + if (ec_ != asio::error::in_progress + && ec_ != asio::error::would_block) + continue; + + // Wait for socket to become ready. + if (detail::socket_ops::poll_connect( + socket().native_handle(), timeout(), ec_) < 0) + continue; + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (detail::socket_ops::getsockopt(socket().native_handle(), 0, + SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec_) + == detail::socket_error_retval) + return; + + // Check the result of the connect operation. + ec_ = asio::error_code(connect_error, + asio::error::get_system_category()); + if (!ec_) + return; + } +#endif // defined(ASIO_WINDOWS_RUNTIME) + } + + // Helper function to get the maximum expiry time. + static time_point max_expiry_time() + { +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + return boost::posix_time::pos_infin; +#else // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + return (time_point::max)(); +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) + } + + enum { putback_max = 8 }; + asio::error_code ec_; + time_point expiry_time_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) +# undef ASIO_PRIVATE_CONNECT_DEF +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_stream_socket.hpp b/third_party/asio/1.18.2/include/asio/basic_stream_socket.hpp new file mode 100644 index 000000000..c622c859d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_stream_socket.hpp @@ -0,0 +1,1060 @@ +// +// basic_stream_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_STREAM_SOCKET_HPP +#define ASIO_BASIC_STREAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/basic_socket.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if !defined(ASIO_BASIC_STREAM_SOCKET_FWD_DECL) +#define ASIO_BASIC_STREAM_SOCKET_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_stream_socket; + +#endif // !defined(ASIO_BASIC_STREAM_SOCKET_FWD_DECL) + +/// Provides stream-oriented socket functionality. +/** + * The basic_stream_socket class template provides asynchronous and blocking + * stream-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * Synchronous @c send, @c receive, and @c connect operations are thread safe + * with respect to each other, if the underlying operating system calls are + * also thread safe. This means that it is permitted to perform concurrent + * calls to these synchronous operations on a single socket object. Other + * synchronous operations, such as @c open or @c close, are not thread safe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class basic_stream_socket + : public basic_socket +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_stream_socket other; + }; + + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_stream_socket without opening it. + /** + * This constructor creates a stream socket without opening it. The socket + * needs to be opened and then connected or accepted before data can be sent + * or received on it. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_stream_socket(const executor_type& ex) + : basic_socket(ex) + { + } + + /// Construct a basic_stream_socket without opening it. + /** + * This constructor creates a stream socket without opening it. The socket + * needs to be opened and then connected or accepted before data can be sent + * or received on it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template + explicit basic_stream_socket(ExecutionContext& context, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context) + { + } + + /// Construct and open a basic_stream_socket. + /** + * This constructor creates and opens a stream socket. The socket needs to be + * connected or accepted before data can be sent or received on it. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_socket(const executor_type& ex, const protocol_type& protocol) + : basic_socket(ex, protocol) + { + } + + /// Construct and open a basic_stream_socket. + /** + * This constructor creates and opens a stream socket. The socket needs to be + * connected or accepted before data can be sent or received on it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_stream_socket(ExecutionContext& context, const protocol_type& protocol, + typename constraint< + is_convertible::value, + defaulted_constraint + >::type = defaulted_constraint()) + : basic_socket(context, protocol) + { + } + + /// Construct a basic_stream_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a stream socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the stream + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_socket(const executor_type& ex, const endpoint_type& endpoint) + : basic_socket(ex, endpoint) + { + } + + /// Construct a basic_stream_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a stream socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the stream + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_stream_socket(ExecutionContext& context, const endpoint_type& endpoint, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context, endpoint) + { + } + + /// Construct a basic_stream_socket on an existing native socket. + /** + * This constructor creates a stream socket object to hold an existing native + * socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_socket(const executor_type& ex, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_socket(ex, protocol, native_socket) + { + } + + /// Construct a basic_stream_socket on an existing native socket. + /** + * This constructor creates a stream socket object to hold an existing native + * socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_stream_socket(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_socket, + typename constraint< + is_convertible::value + >::type = 0) + : basic_socket(context, protocol, native_socket) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_stream_socket from another. + /** + * This constructor moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(const executor_type&) + * constructor. + */ + basic_stream_socket(basic_stream_socket&& other) ASIO_NOEXCEPT + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_stream_socket from another. + /** + * This assignment operator moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(const executor_type&) + * constructor. + */ + basic_stream_socket& operator=(basic_stream_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } + + /// Move-construct a basic_stream_socket from a socket of another protocol + /// type. + /** + * This constructor moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(const executor_type&) + * constructor. + */ + template + basic_stream_socket(basic_stream_socket&& other, + typename constraint< + is_convertible::value + && is_convertible::value + >::type = 0) + : basic_socket(std::move(other)) + { + } + + /// Move-assign a basic_stream_socket from a socket of another protocol type. + /** + * This assignment operator moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(const executor_type&) + * constructor. + */ + template + typename constraint< + is_convertible::value + && is_convertible::value, + basic_stream_socket& + >::type operator=(basic_stream_socket&& other) + { + basic_socket::operator=(std::move(other)); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_stream_socket() + { + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. Returns 0 if an error occurred. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send(this), handler, + buffers, socket_base::message_flags(0)); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send(this), handler, buffers, flags); + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); + asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. Returns 0 if an error occurred. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive(this), handler, + buffers, socket_base::message_flags(0)); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive(this), handler, buffers, flags); + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "write_some"); + return s; + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the socket. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_send(this), handler, + buffers, socket_base::message_flags(0)); + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); + asio::detail::throw_error(ec, "read_some"); + return s; + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_receive(this), handler, + buffers, socket_base::message_flags(0)); + } + +private: + // Disallow copying and assignment. + basic_stream_socket(const basic_stream_socket&) ASIO_DELETED; + basic_stream_socket& operator=(const basic_stream_socket&) ASIO_DELETED; + + class initiate_async_send + { + public: + typedef Executor executor_type; + + explicit initiate_async_send(basic_stream_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + const ConstBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_send( + self_->impl_.get_implementation(), buffers, flags, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_stream_socket* self_; + }; + + class initiate_async_receive + { + public: + typedef Executor executor_type; + + explicit initiate_async_receive(basic_stream_socket* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + const MutableBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_receive( + self_->impl_.get_implementation(), buffers, flags, + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_stream_socket* self_; + }; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_STREAM_SOCKET_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_streambuf.hpp b/third_party/asio/1.18.2/include/asio/basic_streambuf.hpp new file mode 100644 index 000000000..3e0c6e4a2 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_streambuf.hpp @@ -0,0 +1,452 @@ +// +// basic_streambuf.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_STREAMBUF_HPP +#define ASIO_BASIC_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_NO_IOSTREAM) + +#include +#include +#include +#include +#include +#include "asio/basic_streambuf_fwd.hpp" +#include "asio/buffer.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/throw_exception.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Automatically resizable buffer class based on std::streambuf. +/** + * The @c basic_streambuf class is derived from @c std::streambuf to associate + * the streambuf's input and output sequences with one or more character + * arrays. These character arrays are internal to the @c basic_streambuf + * object, but direct access to the array elements is provided to permit them + * to be used efficiently with I/O operations. Characters written to the output + * sequence of a @c basic_streambuf object are appended to the input sequence + * of the same object. + * + * The @c basic_streambuf class's public interface is intended to permit the + * following implementation strategies: + * + * @li A single contiguous character array, which is reallocated as necessary + * to accommodate changes in the size of the character sequence. This is the + * implementation approach currently used in Asio. + * + * @li A sequence of one or more character arrays, where each array is of the + * same size. Additional character array objects are appended to the sequence + * to accommodate changes in the size of the character sequence. + * + * @li A sequence of one or more character arrays of varying sizes. Additional + * character array objects are appended to the sequence to accommodate changes + * in the size of the character sequence. + * + * The constructor for basic_streambuf accepts a @c size_t argument specifying + * the maximum of the sum of the sizes of the input sequence and output + * sequence. During the lifetime of the @c basic_streambuf object, the following + * invariant holds: + * @code size() <= max_size()@endcode + * Any member function that would, if successful, cause the invariant to be + * violated shall throw an exception of class @c std::length_error. + * + * The constructor for @c basic_streambuf takes an Allocator argument. A copy + * of this argument is used for any memory allocation performed, by the + * constructor and by all member functions, during the lifetime of each @c + * basic_streambuf object. + * + * @par Examples + * Writing directly from an streambuf to a socket: + * @code + * asio::streambuf b; + * std::ostream os(&b); + * os << "Hello, World!\n"; + * + * // try sending some data in input sequence + * size_t n = sock.send(b.data()); + * + * b.consume(n); // sent data is removed from input sequence + * @endcode + * + * Reading from a socket directly into a streambuf: + * @code + * asio::streambuf b; + * + * // reserve 512 bytes in output sequence + * asio::streambuf::mutable_buffers_type bufs = b.prepare(512); + * + * size_t n = sock.receive(bufs); + * + * // received data is "committed" from output sequence to input sequence + * b.commit(n); + * + * std::istream is(&b); + * std::string s; + * is >> s; + * @endcode + */ +#if defined(GENERATING_DOCUMENTATION) +template > +#else +template +#endif +class basic_streambuf + : public std::streambuf, + private noncopyable +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The type used to represent the input sequence as a list of buffers. + typedef implementation_defined const_buffers_type; + + /// The type used to represent the output sequence as a list of buffers. + typedef implementation_defined mutable_buffers_type; +#else + typedef ASIO_CONST_BUFFER const_buffers_type; + typedef ASIO_MUTABLE_BUFFER mutable_buffers_type; +#endif + + /// Construct a basic_streambuf object. + /** + * Constructs a streambuf with the specified maximum size. The initial size + * of the streambuf's input sequence is 0. + */ + explicit basic_streambuf( + std::size_t maximum_size = (std::numeric_limits::max)(), + const Allocator& allocator = Allocator()) + : max_size_(maximum_size), + buffer_(allocator) + { + std::size_t pend = (std::min)(max_size_, buffer_delta); + buffer_.resize((std::max)(pend, 1)); + setg(&buffer_[0], &buffer_[0], &buffer_[0]); + setp(&buffer_[0], &buffer_[0] + pend); + } + + /// Get the size of the input sequence. + /** + * @returns The size of the input sequence. The value is equal to that + * calculated for @c s in the following code: + * @code + * size_t s = 0; + * const_buffers_type bufs = data(); + * const_buffers_type::const_iterator i = bufs.begin(); + * while (i != bufs.end()) + * { + * const_buffer buf(*i++); + * s += buf.size(); + * } + * @endcode + */ + std::size_t size() const ASIO_NOEXCEPT + { + return pptr() - gptr(); + } + + /// Get the maximum size of the basic_streambuf. + /** + * @returns The allowed maximum of the sum of the sizes of the input sequence + * and output sequence. + */ + std::size_t max_size() const ASIO_NOEXCEPT + { + return max_size_; + } + + /// Get the current capacity of the basic_streambuf. + /** + * @returns The current total capacity of the streambuf, i.e. for both the + * input sequence and output sequence. + */ + std::size_t capacity() const ASIO_NOEXCEPT + { + return buffer_.capacity(); + } + + /// Get a list of buffers that represents the input sequence. + /** + * @returns An object of type @c const_buffers_type that satisfies + * ConstBufferSequence requirements, representing all character arrays in the + * input sequence. + * + * @note The returned object is invalidated by any @c basic_streambuf member + * function that modifies the input sequence or output sequence. + */ + const_buffers_type data() const ASIO_NOEXCEPT + { + return asio::buffer(asio::const_buffer(gptr(), + (pptr() - gptr()) * sizeof(char_type))); + } + + /// Get a list of buffers that represents the output sequence, with the given + /// size. + /** + * Ensures that the output sequence can accommodate @c n characters, + * reallocating character array objects as necessary. + * + * @returns An object of type @c mutable_buffers_type that satisfies + * MutableBufferSequence requirements, representing character array objects + * at the start of the output sequence such that the sum of the buffer sizes + * is @c n. + * + * @throws std::length_error If size() + n > max_size(). + * + * @note The returned object is invalidated by any @c basic_streambuf member + * function that modifies the input sequence or output sequence. + */ + mutable_buffers_type prepare(std::size_t n) + { + reserve(n); + return asio::buffer(asio::mutable_buffer( + pptr(), n * sizeof(char_type))); + } + + /// Move characters from the output sequence to the input sequence. + /** + * Appends @c n characters from the start of the output sequence to the input + * sequence. The beginning of the output sequence is advanced by @c n + * characters. + * + * Requires a preceding call prepare(x) where x >= n, and + * no intervening operations that modify the input or output sequence. + * + * @note If @c n is greater than the size of the output sequence, the entire + * output sequence is moved to the input sequence and no error is issued. + */ + void commit(std::size_t n) + { + n = std::min(n, epptr() - pptr()); + pbump(static_cast(n)); + setg(eback(), gptr(), pptr()); + } + + /// Remove characters from the input sequence. + /** + * Removes @c n characters from the beginning of the input sequence. + * + * @note If @c n is greater than the size of the input sequence, the entire + * input sequence is consumed and no error is issued. + */ + void consume(std::size_t n) + { + if (egptr() < pptr()) + setg(&buffer_[0], gptr(), pptr()); + if (gptr() + n > pptr()) + n = pptr() - gptr(); + gbump(static_cast(n)); + } + +protected: + enum { buffer_delta = 128 }; + + /// Override std::streambuf behaviour. + /** + * Behaves according to the specification of @c std::streambuf::underflow(). + */ + int_type underflow() + { + if (gptr() < pptr()) + { + setg(&buffer_[0], gptr(), pptr()); + return traits_type::to_int_type(*gptr()); + } + else + { + return traits_type::eof(); + } + } + + /// Override std::streambuf behaviour. + /** + * Behaves according to the specification of @c std::streambuf::overflow(), + * with the specialisation that @c std::length_error is thrown if appending + * the character to the input sequence would require the condition + * size() > max_size() to be true. + */ + int_type overflow(int_type c) + { + if (!traits_type::eq_int_type(c, traits_type::eof())) + { + if (pptr() == epptr()) + { + std::size_t buffer_size = pptr() - gptr(); + if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta) + { + reserve(max_size_ - buffer_size); + } + else + { + reserve(buffer_delta); + } + } + + *pptr() = traits_type::to_char_type(c); + pbump(1); + return c; + } + + return traits_type::not_eof(c); + } + + void reserve(std::size_t n) + { + // Get current stream positions as offsets. + std::size_t gnext = gptr() - &buffer_[0]; + std::size_t pnext = pptr() - &buffer_[0]; + std::size_t pend = epptr() - &buffer_[0]; + + // Check if there is already enough space in the put area. + if (n <= pend - pnext) + { + return; + } + + // Shift existing contents of get area to start of buffer. + if (gnext > 0) + { + pnext -= gnext; + std::memmove(&buffer_[0], &buffer_[0] + gnext, pnext); + } + + // Ensure buffer is large enough to hold at least the specified size. + if (n > pend - pnext) + { + if (n <= max_size_ && pnext <= max_size_ - n) + { + pend = pnext + n; + buffer_.resize((std::max)(pend, 1)); + } + else + { + std::length_error ex("asio::streambuf too long"); + asio::detail::throw_exception(ex); + } + } + + // Update stream positions. + setg(&buffer_[0], &buffer_[0], &buffer_[0] + pnext); + setp(&buffer_[0] + pnext, &buffer_[0] + pend); + } + +private: + std::size_t max_size_; + std::vector buffer_; + + // Helper function to get the preferred size for reading data. + friend std::size_t read_size_helper( + basic_streambuf& sb, std::size_t max_size) + { + return std::min( + std::max(512, sb.buffer_.capacity() - sb.size()), + std::min(max_size, sb.max_size() - sb.size())); + } +}; + +/// Adapts basic_streambuf to the dynamic buffer sequence type requirements. +#if defined(GENERATING_DOCUMENTATION) +template > +#else +template +#endif +class basic_streambuf_ref +{ +public: + /// The type used to represent the input sequence as a list of buffers. + typedef typename basic_streambuf::const_buffers_type + const_buffers_type; + + /// The type used to represent the output sequence as a list of buffers. + typedef typename basic_streambuf::mutable_buffers_type + mutable_buffers_type; + + /// Construct a basic_streambuf_ref for the given basic_streambuf object. + explicit basic_streambuf_ref(basic_streambuf& sb) + : sb_(sb) + { + } + + /// Copy construct a basic_streambuf_ref. + basic_streambuf_ref(const basic_streambuf_ref& other) ASIO_NOEXCEPT + : sb_(other.sb_) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move construct a basic_streambuf_ref. + basic_streambuf_ref(basic_streambuf_ref&& other) ASIO_NOEXCEPT + : sb_(other.sb_) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Get the size of the input sequence. + std::size_t size() const ASIO_NOEXCEPT + { + return sb_.size(); + } + + /// Get the maximum size of the dynamic buffer. + std::size_t max_size() const ASIO_NOEXCEPT + { + return sb_.max_size(); + } + + /// Get the current capacity of the dynamic buffer. + std::size_t capacity() const ASIO_NOEXCEPT + { + return sb_.capacity(); + } + + /// Get a list of buffers that represents the input sequence. + const_buffers_type data() const ASIO_NOEXCEPT + { + return sb_.data(); + } + + /// Get a list of buffers that represents the output sequence, with the given + /// size. + mutable_buffers_type prepare(std::size_t n) + { + return sb_.prepare(n); + } + + /// Move bytes from the output sequence to the input sequence. + void commit(std::size_t n) + { + return sb_.commit(n); + } + + /// Remove characters from the input sequence. + void consume(std::size_t n) + { + return sb_.consume(n); + } + +private: + basic_streambuf& sb_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_BASIC_STREAMBUF_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_streambuf_fwd.hpp b/third_party/asio/1.18.2/include/asio/basic_streambuf_fwd.hpp new file mode 100644 index 000000000..5856c9664 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_streambuf_fwd.hpp @@ -0,0 +1,36 @@ +// +// basic_streambuf_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_STREAMBUF_FWD_HPP +#define ASIO_BASIC_STREAMBUF_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_NO_IOSTREAM) + +#include + +namespace asio { + +template > +class basic_streambuf; + +template > +class basic_streambuf_ref; + +} // namespace asio + +#endif // !defined(ASIO_NO_IOSTREAM) + +#endif // ASIO_BASIC_STREAMBUF_FWD_HPP diff --git a/third_party/asio/1.18.2/include/asio/basic_waitable_timer.hpp b/third_party/asio/1.18.2/include/asio/basic_waitable_timer.hpp new file mode 100644 index 000000000..417b818c0 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/basic_waitable_timer.hpp @@ -0,0 +1,811 @@ +// +// basic_waitable_timer.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_WAITABLE_TIMER_HPP +#define ASIO_BASIC_WAITABLE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/any_io_executor.hpp" +#include "asio/detail/chrono_time_traits.hpp" +#include "asio/detail/deadline_timer_service.hpp" +#include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/wait_traits.hpp" + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL) +#define ASIO_BASIC_WAITABLE_TIMER_FWD_DECL + +// Forward declaration with defaulted arguments. +template , + typename Executor = any_io_executor> +class basic_waitable_timer; + +#endif // !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL) + +/// Provides waitable timer functionality. +/** + * The basic_waitable_timer class template provides the ability to perform a + * blocking or asynchronous wait for a timer to expire. + * + * A waitable timer is always in one of two states: "expired" or "not expired". + * If the wait() or async_wait() function is called on an expired timer, the + * wait operation will complete immediately. + * + * Most applications will use one of the asio::steady_timer, + * asio::system_timer or asio::high_resolution_timer typedefs. + * + * @note This waitable timer functionality is for use with the C++11 standard + * library's @c <chrono> facility, or with the Boost.Chrono library. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Examples + * Performing a blocking wait (C++11): + * @code + * // Construct a timer without setting an expiry time. + * asio::steady_timer timer(my_context); + * + * // Set an expiry time relative to now. + * timer.expires_after(std::chrono::seconds(5)); + * + * // Wait for the timer to expire. + * timer.wait(); + * @endcode + * + * @par + * Performing an asynchronous wait (C++11): + * @code + * void handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Timer expired. + * } + * } + * + * ... + * + * // Construct a timer with an absolute expiry time. + * asio::steady_timer timer(my_context, + * std::chrono::steady_clock::now() + std::chrono::seconds(60)); + * + * // Start an asynchronous wait. + * timer.async_wait(handler); + * @endcode + * + * @par Changing an active waitable timer's expiry time + * + * Changing the expiry time of a timer while there are pending asynchronous + * waits causes those wait operations to be cancelled. To ensure that the action + * associated with the timer is performed only once, use something like this: + * used: + * + * @code + * void on_some_event() + * { + * if (my_timer.expires_after(seconds(5)) > 0) + * { + * // We managed to cancel the timer. Start new asynchronous wait. + * my_timer.async_wait(on_timeout); + * } + * else + * { + * // Too late, timer has already expired! + * } + * } + * + * void on_timeout(const asio::error_code& e) + * { + * if (e != asio::error::operation_aborted) + * { + * // Timer was not cancelled, take necessary action. + * } + * } + * @endcode + * + * @li The asio::basic_waitable_timer::expires_after() function + * cancels any pending asynchronous waits, and returns the number of + * asynchronous waits that were cancelled. If it returns 0 then you were too + * late and the wait handler has already been executed, or will soon be + * executed. If it returns 1 then the wait handler was successfully cancelled. + * + * @li If a wait handler is cancelled, the asio::error_code passed to + * it contains the value asio::error::operation_aborted. + */ +template +class basic_waitable_timer +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the timer type to another executor. + template + struct rebind_executor + { + /// The timer type when rebound to the specified executor. + typedef basic_waitable_timer other; + }; + + /// The clock type. + typedef Clock clock_type; + + /// The duration type of the clock. + typedef typename clock_type::duration duration; + + /// The time point type of the clock. + typedef typename clock_type::time_point time_point; + + /// The wait traits type. + typedef WaitTraits traits_type; + + /// Constructor. + /** + * This constructor creates a timer without setting an expiry time. The + * expires_at() or expires_after() functions must be called to set an expiry + * time before the timer can be waited on. + * + * @param ex The I/O executor that the timer will use, by default, to + * dispatch handlers for any asynchronous operations performed on the timer. + */ + explicit basic_waitable_timer(const executor_type& ex) + : impl_(0, ex) + { + } + + /// Constructor. + /** + * This constructor creates a timer without setting an expiry time. The + * expires_at() or expires_after() functions must be called to set an expiry + * time before the timer can be waited on. + * + * @param context An execution context which provides the I/O executor that + * the timer will use, by default, to dispatch handlers for any asynchronous + * operations performed on the timer. + */ + template + explicit basic_waitable_timer(ExecutionContext& context, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + } + + /// Constructor to set a particular expiry time as an absolute time. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param ex The I/O executor object that the timer will use, by default, to + * dispatch handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, expressed + * as an absolute time. + */ + basic_waitable_timer(const executor_type& ex, const time_point& expiry_time) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().expires_at(impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + } + + /// Constructor to set a particular expiry time as an absolute time. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param context An execution context which provides the I/O executor that + * the timer will use, by default, to dispatch handlers for any asynchronous + * operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, expressed + * as an absolute time. + */ + template + explicit basic_waitable_timer(ExecutionContext& context, + const time_point& expiry_time, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().expires_at(impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + } + + /// Constructor to set a particular expiry time relative to now. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param ex The I/O executor that the timer will use, by default, to + * dispatch handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, relative to + * now. + */ + basic_waitable_timer(const executor_type& ex, const duration& expiry_time) + : impl_(0, ex) + { + asio::error_code ec; + impl_.get_service().expires_after( + impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_after"); + } + + /// Constructor to set a particular expiry time relative to now. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param context An execution context which provides the I/O executor that + * the timer will use, by default, to dispatch handlers for any asynchronous + * operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, relative to + * now. + */ + template + explicit basic_waitable_timer(ExecutionContext& context, + const duration& expiry_time, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(0, 0, context) + { + asio::error_code ec; + impl_.get_service().expires_after( + impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_after"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_waitable_timer from another. + /** + * This constructor moves a timer from one object to another. + * + * @param other The other basic_waitable_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_waitable_timer(const executor_type&) + * constructor. + */ + basic_waitable_timer(basic_waitable_timer&& other) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_waitable_timer from another. + /** + * This assignment operator moves a timer from one object to another. Cancels + * any outstanding asynchronous operations associated with the target object. + * + * @param other The other basic_waitable_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_waitable_timer(const executor_type&) + * constructor. + */ + basic_waitable_timer& operator=(basic_waitable_timer&& other) + { + impl_ = std::move(other.impl_); + return *this; + } + + // All timers have access to each other's implementations. + template + friend class basic_waitable_timer; + + /// Move-construct a basic_waitable_timer from another. + /** + * This constructor moves a timer from one object to another. + * + * @param other The other basic_waitable_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_waitable_timer(const executor_type&) + * constructor. + */ + template + basic_waitable_timer( + basic_waitable_timer&& other, + typename constraint< + is_convertible::value + >::type = 0) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_waitable_timer from another. + /** + * This assignment operator moves a timer from one object to another. Cancels + * any outstanding asynchronous operations associated with the target object. + * + * @param other The other basic_waitable_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_waitable_timer(const executor_type&) + * constructor. + */ + template + typename constraint< + is_convertible::value, + basic_waitable_timer& + >::type operator=(basic_waitable_timer&& other) + { + basic_waitable_timer tmp(std::move(other)); + impl_ = std::move(tmp.impl_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the timer. + /** + * This function destroys the timer, cancelling any outstanding asynchronous + * wait operations associated with the timer as if by calling @c cancel. + */ + ~basic_waitable_timer() + { + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + + /// Cancel any asynchronous operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel() + { + asio::error_code ec; + std::size_t s = impl_.get_service().cancel(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "cancel"); + return s; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use non-error_code overload.) Cancel any asynchronous + /// operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when cancel() is called, then the + * handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel(asio::error_code& ec) + { + return impl_.get_service().cancel(impl_.get_implementation(), ec); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Cancels one asynchronous operation that is waiting on the timer. + /** + * This function forces the completion of one pending asynchronous wait + * operation against the timer. Handlers are cancelled in FIFO order. The + * handler for the cancelled operation will be invoked with the + * asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. That is, + * either 0 or 1. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when cancel_one() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel_one() + { + asio::error_code ec; + std::size_t s = impl_.get_service().cancel_one( + impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "cancel_one"); + return s; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use non-error_code overload.) Cancels one asynchronous + /// operation that is waiting on the timer. + /** + * This function forces the completion of one pending asynchronous wait + * operation against the timer. Handlers are cancelled in FIFO order. The + * handler for the cancelled operation will be invoked with the + * asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. That is, + * either 0 or 1. + * + * @note If the timer has already expired when cancel_one() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t cancel_one(asio::error_code& ec) + { + return impl_.get_service().cancel_one(impl_.get_implementation(), ec); + } + + /// (Deprecated: Use expiry().) Get the timer's expiry time as an absolute + /// time. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + time_point expires_at() const + { + return impl_.get_service().expires_at(impl_.get_implementation()); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Get the timer's expiry time as an absolute time. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + time_point expiry() const + { + return impl_.get_service().expiry(impl_.get_implementation()); + } + + /// Set the timer's expiry time as an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when expires_at() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_at(const time_point& expiry_time) + { + asio::error_code ec; + std::size_t s = impl_.get_service().expires_at( + impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + return s; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use non-error_code overload.) Set the timer's expiry time as + /// an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when expires_at() is called, then + * the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_at(const time_point& expiry_time, + asio::error_code& ec) + { + return impl_.get_service().expires_at( + impl_.get_implementation(), expiry_time, ec); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Set the timer's expiry time relative to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when expires_after() is called, + * then the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_after(const duration& expiry_time) + { + asio::error_code ec; + std::size_t s = impl_.get_service().expires_after( + impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_after"); + return s; + } + +#if !defined(ASIO_NO_DEPRECATED) + /// (Deprecated: Use expiry().) Get the timer's expiry time relative to now. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + duration expires_from_now() const + { + return impl_.get_service().expires_from_now(impl_.get_implementation()); + } + + /// (Deprecated: Use expires_after().) Set the timer's expiry time relative + /// to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + * + * @note If the timer has already expired when expires_from_now() is called, + * then the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_from_now(const duration& expiry_time) + { + asio::error_code ec; + std::size_t s = impl_.get_service().expires_from_now( + impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_from_now"); + return s; + } + + /// (Deprecated: Use expires_after().) Set the timer's expiry time relative + /// to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + * + * @note If the timer has already expired when expires_from_now() is called, + * then the handlers for asynchronous wait operations will: + * + * @li have already been invoked; or + * + * @li have been queued for invocation in the near future. + * + * These handlers can no longer be cancelled, and therefore are passed an + * error code that indicates the successful completion of the wait operation. + */ + std::size_t expires_from_now(const duration& expiry_time, + asio::error_code& ec) + { + return impl_.get_service().expires_from_now( + impl_.get_implementation(), expiry_time, ec); + } +#endif // !defined(ASIO_NO_DEPRECATED) + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @throws asio::system_error Thrown on failure. + */ + void wait() + { + asio::error_code ec; + impl_.get_service().wait(impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "wait"); + } + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @param ec Set to indicate what error occurred, if any. + */ + void wait(asio::error_code& ec) + { + impl_.get_service().wait(impl_.get_implementation(), ec); + } + + /// Start an asynchronous wait on the timer. + /** + * This function may be used to initiate an asynchronous wait against the + * timer. It always returns immediately. + * + * For each call to async_wait(), the supplied handler will be called exactly + * once. The handler will be called when: + * + * @li The timer has expired. + * + * @li The timer was cancelled, in which case the handler is passed the error + * code asio::error::operation_aborted. + * + * @param handler The handler to be called when the timer expires. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + */ + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code)) + WaitHandler ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(WaitHandler, + void (asio::error_code)) + async_wait( + ASIO_MOVE_ARG(WaitHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return async_initiate( + initiate_async_wait(this), handler); + } + +private: + // Disallow copying and assignment. + basic_waitable_timer(const basic_waitable_timer&) ASIO_DELETED; + basic_waitable_timer& operator=( + const basic_waitable_timer&) ASIO_DELETED; + + class initiate_async_wait + { + public: + typedef Executor executor_type; + + explicit initiate_async_wait(basic_waitable_timer* self) + : self_(self) + { + } + + executor_type get_executor() const ASIO_NOEXCEPT + { + return self_->get_executor(); + } + + template + void operator()(ASIO_MOVE_ARG(WaitHandler) handler) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self_->impl_.get_service().async_wait( + self_->impl_.get_implementation(), + handler2.value, self_->impl_.get_executor()); + } + + private: + basic_waitable_timer* self_; + }; + + detail::io_object_impl< + detail::deadline_timer_service< + detail::chrono_time_traits >, + executor_type > impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_WAITABLE_TIMER_HPP diff --git a/third_party/asio/1.18.2/include/asio/bind_executor.hpp b/third_party/asio/1.18.2/include/asio/bind_executor.hpp new file mode 100644 index 000000000..276200324 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/bind_executor.hpp @@ -0,0 +1,575 @@ +// +// bind_executor.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BIND_EXECUTOR_HPP +#define ASIO_BIND_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/detail/variadic_templates.hpp" +#include "asio/associated_executor.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/async_result.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution_context.hpp" +#include "asio/is_executor.hpp" +#include "asio/uses_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper to automatically define nested typedef result_type. + +template +struct executor_binder_result_type +{ +protected: + typedef void result_type_or_void; +}; + +template +struct executor_binder_result_type::type> +{ + typedef typename T::result_type result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +template +struct executor_binder_result_type +{ + typedef R result_type; +protected: + typedef result_type result_type_or_void; +}; + +// Helper to automatically define nested typedef argument_type. + +template +struct executor_binder_argument_type {}; + +template +struct executor_binder_argument_type::type> +{ + typedef typename T::argument_type argument_type; +}; + +template +struct executor_binder_argument_type +{ + typedef A1 argument_type; +}; + +template +struct executor_binder_argument_type +{ + typedef A1 argument_type; +}; + +// Helper to automatically define nested typedefs first_argument_type and +// second_argument_type. + +template +struct executor_binder_argument_types {}; + +template +struct executor_binder_argument_types::type> +{ + typedef typename T::first_argument_type first_argument_type; + typedef typename T::second_argument_type second_argument_type; +}; + +template +struct executor_binder_argument_type +{ + typedef A1 first_argument_type; + typedef A2 second_argument_type; +}; + +template +struct executor_binder_argument_type +{ + typedef A1 first_argument_type; + typedef A2 second_argument_type; +}; + +// Helper to perform uses_executor construction of the target type, if +// required. + +template +class executor_binder_base; + +template +class executor_binder_base +{ +protected: + template + executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) u) + : executor_(ASIO_MOVE_CAST(E)(e)), + target_(executor_arg_t(), executor_, ASIO_MOVE_CAST(U)(u)) + { + } + + Executor executor_; + T target_; +}; + +template +class executor_binder_base +{ +protected: + template + executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) u) + : executor_(ASIO_MOVE_CAST(E)(e)), + target_(ASIO_MOVE_CAST(U)(u)) + { + } + + Executor executor_; + T target_; +}; + +// Helper to enable SFINAE on zero-argument operator() below. + +template +struct executor_binder_result_of0 +{ + typedef void type; +}; + +template +struct executor_binder_result_of0::type>::type> +{ + typedef typename result_of::type type; +}; + +} // namespace detail + +/// A call wrapper type to bind an executor of type @c Executor to an object of +/// type @c T. +template +class executor_binder +#if !defined(GENERATING_DOCUMENTATION) + : public detail::executor_binder_result_type, + public detail::executor_binder_argument_type, + public detail::executor_binder_argument_types, + private detail::executor_binder_base< + T, Executor, uses_executor::value> +#endif // !defined(GENERATING_DOCUMENTATION) +{ +public: + /// The type of the target object. + typedef T target_type; + + /// The type of the associated executor. + typedef Executor executor_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The return type if a function. + /** + * The type of @c result_type is based on the type @c T of the wrapper's + * target object: + * + * @li if @c T is a pointer to function type, @c result_type is a synonym for + * the return type of @c T; + * + * @li if @c T is a class type with a member type @c result_type, then @c + * result_type is a synonym for @c T::result_type; + * + * @li otherwise @c result_type is not defined. + */ + typedef see_below result_type; + + /// The type of the function's argument. + /** + * The type of @c argument_type is based on the type @c T of the wrapper's + * target object: + * + * @li if @c T is a pointer to a function type accepting a single argument, + * @c argument_type is a synonym for the return type of @c T; + * + * @li if @c T is a class type with a member type @c argument_type, then @c + * argument_type is a synonym for @c T::argument_type; + * + * @li otherwise @c argument_type is not defined. + */ + typedef see_below argument_type; + + /// The type of the function's first argument. + /** + * The type of @c first_argument_type is based on the type @c T of the + * wrapper's target object: + * + * @li if @c T is a pointer to a function type accepting two arguments, @c + * first_argument_type is a synonym for the return type of @c T; + * + * @li if @c T is a class type with a member type @c first_argument_type, + * then @c first_argument_type is a synonym for @c T::first_argument_type; + * + * @li otherwise @c first_argument_type is not defined. + */ + typedef see_below first_argument_type; + + /// The type of the function's second argument. + /** + * The type of @c second_argument_type is based on the type @c T of the + * wrapper's target object: + * + * @li if @c T is a pointer to a function type accepting two arguments, @c + * second_argument_type is a synonym for the return type of @c T; + * + * @li if @c T is a class type with a member type @c first_argument_type, + * then @c second_argument_type is a synonym for @c T::second_argument_type; + * + * @li otherwise @c second_argument_type is not defined. + */ + typedef see_below second_argument_type; +#endif // defined(GENERATING_DOCUMENTATION) + + /// Construct an executor wrapper for the specified object. + /** + * This constructor is only valid if the type @c T is constructible from type + * @c U. + */ + template + executor_binder(executor_arg_t, const executor_type& e, + ASIO_MOVE_ARG(U) u) + : base_type(e, ASIO_MOVE_CAST(U)(u)) + { + } + + /// Copy constructor. + executor_binder(const executor_binder& other) + : base_type(other.get_executor(), other.get()) + { + } + + /// Construct a copy, but specify a different executor. + executor_binder(executor_arg_t, const executor_type& e, + const executor_binder& other) + : base_type(e, other.get()) + { + } + + /// Construct a copy of a different executor wrapper type. + /** + * This constructor is only valid if the @c Executor type is constructible + * from type @c OtherExecutor, and the type @c T is constructible from type + * @c U. + */ + template + executor_binder(const executor_binder& other) + : base_type(other.get_executor(), other.get()) + { + } + + /// Construct a copy of a different executor wrapper type, but specify a + /// different executor. + /** + * This constructor is only valid if the type @c T is constructible from type + * @c U. + */ + template + executor_binder(executor_arg_t, const executor_type& e, + const executor_binder& other) + : base_type(e, other.get()) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Move constructor. + executor_binder(executor_binder&& other) + : base_type(ASIO_MOVE_CAST(executor_type)(other.get_executor()), + ASIO_MOVE_CAST(T)(other.get())) + { + } + + /// Move construct the target object, but specify a different executor. + executor_binder(executor_arg_t, const executor_type& e, + executor_binder&& other) + : base_type(e, ASIO_MOVE_CAST(T)(other.get())) + { + } + + /// Move construct from a different executor wrapper type. + template + executor_binder(executor_binder&& other) + : base_type(ASIO_MOVE_CAST(OtherExecutor)(other.get_executor()), + ASIO_MOVE_CAST(U)(other.get())) + { + } + + /// Move construct from a different executor wrapper type, but specify a + /// different executor. + template + executor_binder(executor_arg_t, const executor_type& e, + executor_binder&& other) + : base_type(e, ASIO_MOVE_CAST(U)(other.get())) + { + } + +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destructor. + ~executor_binder() + { + } + + /// Obtain a reference to the target object. + target_type& get() ASIO_NOEXCEPT + { + return this->target_; + } + + /// Obtain a reference to the target object. + const target_type& get() const ASIO_NOEXCEPT + { + return this->target_; + } + + /// Obtain the associated executor. + executor_type get_executor() const ASIO_NOEXCEPT + { + return this->executor_; + } + +#if defined(GENERATING_DOCUMENTATION) + + template auto operator()(Args&& ...); + template auto operator()(Args&& ...) const; + +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + + /// Forwarding function call operator. + template + typename result_of::type operator()( + ASIO_MOVE_ARG(Args)... args) + { + return this->target_(ASIO_MOVE_CAST(Args)(args)...); + } + + /// Forwarding function call operator. + template + typename result_of::type operator()( + ASIO_MOVE_ARG(Args)... args) const + { + return this->target_(ASIO_MOVE_CAST(Args)(args)...); + } + +#elif defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER) + + typename detail::executor_binder_result_of0::type operator()() + { + return this->target_(); + } + + typename detail::executor_binder_result_of0::type operator()() const + { + return this->target_(); + } + +#define ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF(n) \ + template \ + typename result_of::type operator()( \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + \ + template \ + typename result_of::type operator()( \ + ASIO_VARIADIC_MOVE_PARAMS(n)) const \ + { \ + return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF) +#undef ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF + +#else // defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER) + + typedef typename detail::executor_binder_result_type::result_type_or_void + result_type_or_void; + + result_type_or_void operator()() + { + return this->target_(); + } + + result_type_or_void operator()() const + { + return this->target_(); + } + +#define ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF(n) \ + template \ + result_type_or_void operator()( \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + \ + template \ + result_type_or_void operator()( \ + ASIO_VARIADIC_MOVE_PARAMS(n)) const \ + { \ + return this->target_(ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF) +#undef ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF + +#endif // defined(ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER) + +private: + typedef detail::executor_binder_base::value> base_type; +}; + +/// Associate an object of type @c T with an executor of type @c Executor. +template +inline executor_binder::type, Executor> +bind_executor(const Executor& ex, ASIO_MOVE_ARG(T) t, + typename constraint< + is_executor::value || execution::is_executor::value + >::type = 0) +{ + return executor_binder::type, Executor>( + executor_arg_t(), ex, ASIO_MOVE_CAST(T)(t)); +} + +/// Associate an object of type @c T with an execution context's executor. +template +inline executor_binder::type, + typename ExecutionContext::executor_type> +bind_executor(ExecutionContext& ctx, ASIO_MOVE_ARG(T) t, + typename constraint::value>::type = 0) +{ + return executor_binder::type, + typename ExecutionContext::executor_type>( + executor_arg_t(), ctx.get_executor(), ASIO_MOVE_CAST(T)(t)); +} + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct uses_executor, Executor> + : true_type {}; + +template +class async_result, Signature> +{ +public: + typedef executor_binder< + typename async_result::completion_handler_type, Executor> + completion_handler_type; + + typedef typename async_result::return_type return_type; + + explicit async_result(executor_binder& b) + : target_(b.get()) + { + } + + return_type get() + { + return target_.get(); + } + +private: + async_result(const async_result&) ASIO_DELETED; + async_result& operator=(const async_result&) ASIO_DELETED; + + async_result target_; +}; + +template +struct associated_allocator, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const executor_binder& b, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(b.get(), a); + } +}; + +template +struct associated_executor, Executor1> +{ + typedef Executor type; + + static type get(const executor_binder& b, + const Executor1& = Executor1()) ASIO_NOEXCEPT + { + return b.get_executor(); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BIND_EXECUTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/buffer.hpp b/third_party/asio/1.18.2/include/asio/buffer.hpp new file mode 100644 index 000000000..32848dc78 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/buffer.hpp @@ -0,0 +1,2496 @@ +// +// buffer.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFER_HPP +#define ASIO_BUFFER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/array_fwd.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/string_view.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/detail/type_traits.hpp" + +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1700) +# if defined(_HAS_ITERATOR_DEBUGGING) && (_HAS_ITERATOR_DEBUGGING != 0) +# if !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# define ASIO_ENABLE_BUFFER_DEBUGGING +# endif // !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# endif // defined(_HAS_ITERATOR_DEBUGGING) +#endif // defined(ASIO_MSVC) && (ASIO_MSVC >= 1700) + +#if defined(__GNUC__) +# if defined(_GLIBCXX_DEBUG) +# if !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# define ASIO_ENABLE_BUFFER_DEBUGGING +# endif // !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# endif // defined(_GLIBCXX_DEBUG) +#endif // defined(__GNUC__) + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) +# include "asio/detail/functional.hpp" +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + +#if defined(ASIO_HAS_BOOST_WORKAROUND) +# include +# if !defined(__clang__) +# if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) +# define ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND +# endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) +# elif BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) +# define ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND +# endif // BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) +#endif // defined(ASIO_HAS_BOOST_WORKAROUND) + +#if defined(ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND) +# include "asio/detail/type_traits.hpp" +#endif // defined(ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class mutable_buffer; +class const_buffer; + +/// Holds a buffer that can be modified. +/** + * The mutable_buffer class provides a safe representation of a buffer that can + * be modified. It does not own the underlying data, and so is cheap to copy or + * assign. + * + * @par Accessing Buffer Contents + * + * The contents of a buffer may be accessed using the @c data() and @c size() + * member functions: + * + * @code asio::mutable_buffer b1 = ...; + * std::size_t s1 = b1.size(); + * unsigned char* p1 = static_cast(b1.data()); + * @endcode + * + * The @c data() member function permits violations of type safety, so uses of + * it in application code should be carefully considered. + */ +class mutable_buffer +{ +public: + /// Construct an empty buffer. + mutable_buffer() ASIO_NOEXCEPT + : data_(0), + size_(0) + { + } + + /// Construct a buffer to represent a given memory range. + mutable_buffer(void* data, std::size_t size) ASIO_NOEXCEPT + : data_(data), + size_(size) + { + } + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + mutable_buffer(void* data, std::size_t size, + asio::detail::function debug_check) + : data_(data), + size_(size), + debug_check_(debug_check) + { + } + + const asio::detail::function& get_debug_check() const + { + return debug_check_; + } +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + + /// Get a pointer to the beginning of the memory range. + void* data() const ASIO_NOEXCEPT + { +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (size_ && debug_check_) + debug_check_(); +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + return data_; + } + + /// Get the size of the memory range. + std::size_t size() const ASIO_NOEXCEPT + { + return size_; + } + + /// Move the start of the buffer by the specified number of bytes. + mutable_buffer& operator+=(std::size_t n) ASIO_NOEXCEPT + { + std::size_t offset = n < size_ ? n : size_; + data_ = static_cast(data_) + offset; + size_ -= offset; + return *this; + } + +private: + void* data_; + std::size_t size_; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + asio::detail::function debug_check_; +#endif // ASIO_ENABLE_BUFFER_DEBUGGING +}; + +#if !defined(ASIO_NO_DEPRECATED) + +/// (Deprecated: Use mutable_buffer.) Adapts a single modifiable buffer so that +/// it meets the requirements of the MutableBufferSequence concept. +class mutable_buffers_1 + : public mutable_buffer +{ +public: + /// The type for each element in the list of buffers. + typedef mutable_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const mutable_buffer* const_iterator; + + /// Construct to represent a given memory range. + mutable_buffers_1(void* data, std::size_t size) ASIO_NOEXCEPT + : mutable_buffer(data, size) + { + } + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + mutable_buffers_1(void* data, std::size_t size, + asio::detail::function debug_check) + : mutable_buffer(data, size, debug_check) + { + } +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + + /// Construct to represent a single modifiable buffer. + explicit mutable_buffers_1(const mutable_buffer& b) ASIO_NOEXCEPT + : mutable_buffer(b) + { + } + + /// Get a random-access iterator to the first element. + const_iterator begin() const ASIO_NOEXCEPT + { + return this; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const ASIO_NOEXCEPT + { + return begin() + 1; + } +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Holds a buffer that cannot be modified. +/** + * The const_buffer class provides a safe representation of a buffer that cannot + * be modified. It does not own the underlying data, and so is cheap to copy or + * assign. + * + * @par Accessing Buffer Contents + * + * The contents of a buffer may be accessed using the @c data() and @c size() + * member functions: + * + * @code asio::const_buffer b1 = ...; + * std::size_t s1 = b1.size(); + * const unsigned char* p1 = static_cast(b1.data()); + * @endcode + * + * The @c data() member function permits violations of type safety, so uses of + * it in application code should be carefully considered. + */ +class const_buffer +{ +public: + /// Construct an empty buffer. + const_buffer() ASIO_NOEXCEPT + : data_(0), + size_(0) + { + } + + /// Construct a buffer to represent a given memory range. + const_buffer(const void* data, std::size_t size) ASIO_NOEXCEPT + : data_(data), + size_(size) + { + } + + /// Construct a non-modifiable buffer from a modifiable one. + const_buffer(const mutable_buffer& b) ASIO_NOEXCEPT + : data_(b.data()), + size_(b.size()) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , debug_check_(b.get_debug_check()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + { + } + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + const_buffer(const void* data, std::size_t size, + asio::detail::function debug_check) + : data_(data), + size_(size), + debug_check_(debug_check) + { + } + + const asio::detail::function& get_debug_check() const + { + return debug_check_; + } +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + + /// Get a pointer to the beginning of the memory range. + const void* data() const ASIO_NOEXCEPT + { +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (size_ && debug_check_) + debug_check_(); +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + return data_; + } + + /// Get the size of the memory range. + std::size_t size() const ASIO_NOEXCEPT + { + return size_; + } + + /// Move the start of the buffer by the specified number of bytes. + const_buffer& operator+=(std::size_t n) ASIO_NOEXCEPT + { + std::size_t offset = n < size_ ? n : size_; + data_ = static_cast(data_) + offset; + size_ -= offset; + return *this; + } + +private: + const void* data_; + std::size_t size_; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + asio::detail::function debug_check_; +#endif // ASIO_ENABLE_BUFFER_DEBUGGING +}; + +#if !defined(ASIO_NO_DEPRECATED) + +/// (Deprecated: Use const_buffer.) Adapts a single non-modifiable buffer so +/// that it meets the requirements of the ConstBufferSequence concept. +class const_buffers_1 + : public const_buffer +{ +public: + /// The type for each element in the list of buffers. + typedef const_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const const_buffer* const_iterator; + + /// Construct to represent a given memory range. + const_buffers_1(const void* data, std::size_t size) ASIO_NOEXCEPT + : const_buffer(data, size) + { + } + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + const_buffers_1(const void* data, std::size_t size, + asio::detail::function debug_check) + : const_buffer(data, size, debug_check) + { + } +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + + /// Construct to represent a single non-modifiable buffer. + explicit const_buffers_1(const const_buffer& b) ASIO_NOEXCEPT + : const_buffer(b) + { + } + + /// Get a random-access iterator to the first element. + const_iterator begin() const ASIO_NOEXCEPT + { + return this; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const ASIO_NOEXCEPT + { + return begin() + 1; + } +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +/// (Deprecated: Use the socket/descriptor wait() and async_wait() member +/// functions.) An implementation of both the ConstBufferSequence and +/// MutableBufferSequence concepts to represent a null buffer sequence. +class null_buffers +{ +public: + /// The type for each element in the list of buffers. + typedef mutable_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const mutable_buffer* const_iterator; + + /// Get a random-access iterator to the first element. + const_iterator begin() const ASIO_NOEXCEPT + { + return &buf_; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const ASIO_NOEXCEPT + { + return &buf_; + } + +private: + mutable_buffer buf_; +}; + +/** @defgroup buffer_sequence_begin asio::buffer_sequence_begin + * + * @brief The asio::buffer_sequence_begin function returns an iterator + * pointing to the first element in a buffer sequence. + */ +/*@{*/ + +/// Get an iterator to the first element in a buffer sequence. +template +inline const mutable_buffer* buffer_sequence_begin(const MutableBuffer& b, + typename constraint< + is_convertible::value + >::type = 0) ASIO_NOEXCEPT +{ + return static_cast(detail::addressof(b)); +} + +/// Get an iterator to the first element in a buffer sequence. +template +inline const const_buffer* buffer_sequence_begin(const ConstBuffer& b, + typename constraint< + is_convertible::value + >::type = 0) ASIO_NOEXCEPT +{ + return static_cast(detail::addressof(b)); +} + +#if defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +/// Get an iterator to the first element in a buffer sequence. +template +inline auto buffer_sequence_begin(C& c, + typename constraint< + !is_convertible::value + && !is_convertible::value + >::type = 0) ASIO_NOEXCEPT -> decltype(c.begin()) +{ + return c.begin(); +} + +/// Get an iterator to the first element in a buffer sequence. +template +inline auto buffer_sequence_begin(const C& c, + typename constraint< + !is_convertible::value + && !is_convertible::value + >::type = 0) ASIO_NOEXCEPT -> decltype(c.begin()) +{ + return c.begin(); +} + +#else // defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +template +inline typename C::iterator buffer_sequence_begin(C& c, + typename constraint< + !is_convertible::value + && !is_convertible::value + >::type = 0) ASIO_NOEXCEPT +{ + return c.begin(); +} + +template +inline typename C::const_iterator buffer_sequence_begin(const C& c, + typename constraint< + !is_convertible::value + && !is_convertible::value + >::type = 0) ASIO_NOEXCEPT +{ + return c.begin(); +} + +#endif // defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +/*@}*/ + +/** @defgroup buffer_sequence_end asio::buffer_sequence_end + * + * @brief The asio::buffer_sequence_end function returns an iterator + * pointing to one past the end element in a buffer sequence. + */ +/*@{*/ + +/// Get an iterator to one past the end element in a buffer sequence. +template +inline const mutable_buffer* buffer_sequence_end(const MutableBuffer& b, + typename constraint< + is_convertible::value + >::type = 0) ASIO_NOEXCEPT +{ + return static_cast(detail::addressof(b)) + 1; +} + +/// Get an iterator to one past the end element in a buffer sequence. +template +inline const const_buffer* buffer_sequence_end(const ConstBuffer& b, + typename constraint< + is_convertible::value + >::type = 0) ASIO_NOEXCEPT +{ + return static_cast(detail::addressof(b)) + 1; +} + +#if defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +/// Get an iterator to one past the end element in a buffer sequence. +template +inline auto buffer_sequence_end(C& c, + typename constraint< + !is_convertible::value + && !is_convertible::value + >::type = 0) ASIO_NOEXCEPT -> decltype(c.end()) +{ + return c.end(); +} + +/// Get an iterator to one past the end element in a buffer sequence. +template +inline auto buffer_sequence_end(const C& c, + typename constraint< + !is_convertible::value + && !is_convertible::value + >::type = 0) ASIO_NOEXCEPT -> decltype(c.end()) +{ + return c.end(); +} + +#else // defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +template +inline typename C::iterator buffer_sequence_end(C& c, + typename constraint< + !is_convertible::value + && !is_convertible::value + >::type = 0) ASIO_NOEXCEPT +{ + return c.end(); +} + +template +inline typename C::const_iterator buffer_sequence_end(const C& c, + typename constraint< + !is_convertible::value + && !is_convertible::value + >::type = 0) ASIO_NOEXCEPT +{ + return c.end(); +} + +#endif // defined(ASIO_HAS_DECLTYPE) || defined(GENERATING_DOCUMENTATION) + +/*@}*/ + +namespace detail { + +// Tag types used to select appropriately optimised overloads. +struct one_buffer {}; +struct multiple_buffers {}; + +// Helper trait to detect single buffers. +template +struct buffer_sequence_cardinality : + conditional< + is_same::value +#if !defined(ASIO_NO_DEPRECATED) + || is_same::value + || is_same::value +#endif // !defined(ASIO_NO_DEPRECATED) + || is_same::value, + one_buffer, multiple_buffers>::type {}; + +template +inline std::size_t buffer_size(one_buffer, + Iterator begin, Iterator) ASIO_NOEXCEPT +{ + return const_buffer(*begin).size(); +} + +template +inline std::size_t buffer_size(multiple_buffers, + Iterator begin, Iterator end) ASIO_NOEXCEPT +{ + std::size_t total_buffer_size = 0; + + Iterator iter = begin; + for (; iter != end; ++iter) + { + const_buffer b(*iter); + total_buffer_size += b.size(); + } + + return total_buffer_size; +} + +} // namespace detail + +/// Get the total number of bytes in a buffer sequence. +/** + * The @c buffer_size function determines the total size of all buffers in the + * buffer sequence, as if computed as follows: + * + * @code size_t total_size = 0; + * auto i = asio::buffer_sequence_begin(buffers); + * auto end = asio::buffer_sequence_end(buffers); + * for (; i != end; ++i) + * { + * const_buffer b(*i); + * total_size += b.size(); + * } + * return total_size; @endcode + * + * The @c BufferSequence template parameter may meet either of the @c + * ConstBufferSequence or @c MutableBufferSequence type requirements. + */ +template +inline std::size_t buffer_size(const BufferSequence& b) ASIO_NOEXCEPT +{ + return detail::buffer_size( + detail::buffer_sequence_cardinality(), + asio::buffer_sequence_begin(b), + asio::buffer_sequence_end(b)); +} + +#if !defined(ASIO_NO_DEPRECATED) + +/** @defgroup buffer_cast asio::buffer_cast + * + * @brief (Deprecated: Use the @c data() member function.) The + * asio::buffer_cast function is used to obtain a pointer to the + * underlying memory region associated with a buffer. + * + * @par Examples: + * + * To access the memory of a non-modifiable buffer, use: + * @code asio::const_buffer b1 = ...; + * const unsigned char* p1 = asio::buffer_cast(b1); + * @endcode + * + * To access the memory of a modifiable buffer, use: + * @code asio::mutable_buffer b2 = ...; + * unsigned char* p2 = asio::buffer_cast(b2); + * @endcode + * + * The asio::buffer_cast function permits violations of type safety, so + * uses of it in application code should be carefully considered. + */ +/*@{*/ + +/// Cast a non-modifiable buffer to a specified pointer to POD type. +template +inline PointerToPodType buffer_cast(const mutable_buffer& b) ASIO_NOEXCEPT +{ + return static_cast(b.data()); +} + +/// Cast a non-modifiable buffer to a specified pointer to POD type. +template +inline PointerToPodType buffer_cast(const const_buffer& b) ASIO_NOEXCEPT +{ + return static_cast(b.data()); +} + +/*@}*/ + +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Create a new modifiable buffer that is offset from the start of another. +/** + * @relates mutable_buffer + */ +inline mutable_buffer operator+(const mutable_buffer& b, + std::size_t n) ASIO_NOEXCEPT +{ + std::size_t offset = n < b.size() ? n : b.size(); + char* new_data = static_cast(b.data()) + offset; + std::size_t new_size = b.size() - offset; + return mutable_buffer(new_data, new_size +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that is offset from the start of another. +/** + * @relates mutable_buffer + */ +inline mutable_buffer operator+(std::size_t n, + const mutable_buffer& b) ASIO_NOEXCEPT +{ + return b + n; +} + +/// Create a new non-modifiable buffer that is offset from the start of another. +/** + * @relates const_buffer + */ +inline const_buffer operator+(const const_buffer& b, + std::size_t n) ASIO_NOEXCEPT +{ + std::size_t offset = n < b.size() ? n : b.size(); + const char* new_data = static_cast(b.data()) + offset; + std::size_t new_size = b.size() - offset; + return const_buffer(new_data, new_size +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that is offset from the start of another. +/** + * @relates const_buffer + */ +inline const_buffer operator+(std::size_t n, + const const_buffer& b) ASIO_NOEXCEPT +{ + return b + n; +} + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) +namespace detail { + +template +class buffer_debug_check +{ +public: + buffer_debug_check(Iterator iter) + : iter_(iter) + { + } + + ~buffer_debug_check() + { +#if defined(ASIO_MSVC) && (ASIO_MSVC == 1400) + // MSVC 8's string iterator checking may crash in a std::string::iterator + // object's destructor when the iterator points to an already-destroyed + // std::string object, unless the iterator is cleared first. + iter_ = Iterator(); +#endif // defined(ASIO_MSVC) && (ASIO_MSVC == 1400) + } + + void operator()() + { + (void)*iter_; + } + +private: + Iterator iter_; +}; + +} // namespace detail +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + +/** @defgroup buffer asio::buffer + * + * @brief The asio::buffer function is used to create a buffer object to + * represent raw memory, an array of POD elements, a vector of POD elements, + * or a std::string. + * + * A buffer object represents a contiguous region of memory as a 2-tuple + * consisting of a pointer and size in bytes. A tuple of the form {void*, + * size_t} specifies a mutable (modifiable) region of memory. Similarly, a + * tuple of the form {const void*, size_t} specifies a const + * (non-modifiable) region of memory. These two forms correspond to the classes + * mutable_buffer and const_buffer, respectively. To mirror C++'s conversion + * rules, a mutable_buffer is implicitly convertible to a const_buffer, and the + * opposite conversion is not permitted. + * + * The simplest use case involves reading or writing a single buffer of a + * specified size: + * + * @code sock.send(asio::buffer(data, size)); @endcode + * + * In the above example, the return value of asio::buffer meets the + * requirements of the ConstBufferSequence concept so that it may be directly + * passed to the socket's write function. A buffer created for modifiable + * memory also meets the requirements of the MutableBufferSequence concept. + * + * An individual buffer may be created from a builtin array, std::vector, + * std::array or boost::array of POD elements. This helps prevent buffer + * overruns by automatically determining the size of the buffer: + * + * @code char d1[128]; + * size_t bytes_transferred = sock.receive(asio::buffer(d1)); + * + * std::vector d2(128); + * bytes_transferred = sock.receive(asio::buffer(d2)); + * + * std::array d3; + * bytes_transferred = sock.receive(asio::buffer(d3)); + * + * boost::array d4; + * bytes_transferred = sock.receive(asio::buffer(d4)); @endcode + * + * In all three cases above, the buffers created are exactly 128 bytes long. + * Note that a vector is @e never automatically resized when creating or using + * a buffer. The buffer size is determined using the vector's size() + * member function, and not its capacity. + * + * @par Accessing Buffer Contents + * + * The contents of a buffer may be accessed using the @c data() and @c size() + * member functions: + * + * @code asio::mutable_buffer b1 = ...; + * std::size_t s1 = b1.size(); + * unsigned char* p1 = static_cast(b1.data()); + * + * asio::const_buffer b2 = ...; + * std::size_t s2 = b2.size(); + * const void* p2 = b2.data(); @endcode + * + * The @c data() member function permits violations of type safety, so + * uses of it in application code should be carefully considered. + * + * For convenience, a @ref buffer_size function is provided that works with + * both buffers and buffer sequences (that is, types meeting the + * ConstBufferSequence or MutableBufferSequence type requirements). In this + * case, the function returns the total size of all buffers in the sequence. + * + * @par Buffer Copying + * + * The @ref buffer_copy function may be used to copy raw bytes between + * individual buffers and buffer sequences. +* + * In particular, when used with the @ref buffer_size function, the @ref + * buffer_copy function can be used to linearise a sequence of buffers. For + * example: + * + * @code vector buffers = ...; + * + * vector data(asio::buffer_size(buffers)); + * asio::buffer_copy(asio::buffer(data), buffers); @endcode + * + * Note that @ref buffer_copy is implemented in terms of @c memcpy, and + * consequently it cannot be used to copy between overlapping memory regions. + * + * @par Buffer Invalidation + * + * A buffer object does not have any ownership of the memory it refers to. It + * is the responsibility of the application to ensure the memory region remains + * valid until it is no longer required for an I/O operation. When the memory + * is no longer available, the buffer is said to have been invalidated. + * + * For the asio::buffer overloads that accept an argument of type + * std::vector, the buffer objects returned are invalidated by any vector + * operation that also invalidates all references, pointers and iterators + * referring to the elements in the sequence (C++ Std, 23.2.4) + * + * For the asio::buffer overloads that accept an argument of type + * std::basic_string, the buffer objects returned are invalidated according to + * the rules defined for invalidation of references, pointers and iterators + * referring to elements of the sequence (C++ Std, 21.3). + * + * @par Buffer Arithmetic + * + * Buffer objects may be manipulated using simple arithmetic in a safe way + * which helps prevent buffer overruns. Consider an array initialised as + * follows: + * + * @code boost::array a = { 'a', 'b', 'c', 'd', 'e' }; @endcode + * + * A buffer object @c b1 created using: + * + * @code b1 = asio::buffer(a); @endcode + * + * represents the entire array, { 'a', 'b', 'c', 'd', 'e' }. An + * optional second argument to the asio::buffer function may be used to + * limit the size, in bytes, of the buffer: + * + * @code b2 = asio::buffer(a, 3); @endcode + * + * such that @c b2 represents the data { 'a', 'b', 'c' }. Even if the + * size argument exceeds the actual size of the array, the size of the buffer + * object created will be limited to the array size. + * + * An offset may be applied to an existing buffer to create a new one: + * + * @code b3 = b1 + 2; @endcode + * + * where @c b3 will set to represent { 'c', 'd', 'e' }. If the offset + * exceeds the size of the existing buffer, the newly created buffer will be + * empty. + * + * Both an offset and size may be specified to create a buffer that corresponds + * to a specific range of bytes within an existing buffer: + * + * @code b4 = asio::buffer(b1 + 1, 3); @endcode + * + * so that @c b4 will refer to the bytes { 'b', 'c', 'd' }. + * + * @par Buffers and Scatter-Gather I/O + * + * To read or write using multiple buffers (i.e. scatter-gather I/O), multiple + * buffer objects may be assigned into a container that supports the + * MutableBufferSequence (for read) or ConstBufferSequence (for write) concepts: + * + * @code + * char d1[128]; + * std::vector d2(128); + * boost::array d3; + * + * boost::array bufs1 = { + * asio::buffer(d1), + * asio::buffer(d2), + * asio::buffer(d3) }; + * bytes_transferred = sock.receive(bufs1); + * + * std::vector bufs2; + * bufs2.push_back(asio::buffer(d1)); + * bufs2.push_back(asio::buffer(d2)); + * bufs2.push_back(asio::buffer(d3)); + * bytes_transferred = sock.send(bufs2); @endcode + */ +/*@{*/ + +#if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) +# define ASIO_MUTABLE_BUFFER mutable_buffer +# define ASIO_CONST_BUFFER const_buffer +#else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) +# define ASIO_MUTABLE_BUFFER mutable_buffers_1 +# define ASIO_CONST_BUFFER const_buffers_1 +#endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) + +/// Create a new modifiable buffer from an existing buffer. +/** + * @returns mutable_buffer(b). + */ +inline ASIO_MUTABLE_BUFFER buffer( + const mutable_buffer& b) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(b); +} + +/// Create a new modifiable buffer from an existing buffer. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * b.data(), + * min(b.size(), max_size_in_bytes)); @endcode + */ +inline ASIO_MUTABLE_BUFFER buffer(const mutable_buffer& b, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER( + mutable_buffer(b.data(), + b.size() < max_size_in_bytes + ? b.size() : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + )); +} + +/// Create a new non-modifiable buffer from an existing buffer. +/** + * @returns const_buffer(b). + */ +inline ASIO_CONST_BUFFER buffer( + const const_buffer& b) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(b); +} + +/// Create a new non-modifiable buffer from an existing buffer. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * b.data(), + * min(b.size(), max_size_in_bytes)); @endcode + */ +inline ASIO_CONST_BUFFER buffer(const const_buffer& b, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(b.data(), + b.size() < max_size_in_bytes + ? b.size() : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that represents the given memory range. +/** + * @returns mutable_buffer(data, size_in_bytes). + */ +inline ASIO_MUTABLE_BUFFER buffer(void* data, + std::size_t size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data, size_in_bytes); +} + +/// Create a new non-modifiable buffer that represents the given memory range. +/** + * @returns const_buffer(data, size_in_bytes). + */ +inline ASIO_CONST_BUFFER buffer(const void* data, + std::size_t size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data, size_in_bytes); +} + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * static_cast(data), + * N * sizeof(PodType)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer(PodType (&data)[N]) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data, N * sizeof(PodType)); +} + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * static_cast(data), + * min(N * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer(PodType (&data)[N], + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data, + N * sizeof(PodType) < max_size_in_bytes + ? N * sizeof(PodType) : max_size_in_bytes); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * static_cast(data), + * N * sizeof(PodType)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + const PodType (&data)[N]) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data, N * sizeof(PodType)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * static_cast(data), + * min(N * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer(const PodType (&data)[N], + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data, + N * sizeof(PodType) < max_size_in_bytes + ? N * sizeof(PodType) : max_size_in_bytes); +} + +#if defined(ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND) + +// Borland C++ and Sun Studio think the overloads: +// +// unspecified buffer(boost::array& array ...); +// +// and +// +// unspecified buffer(boost::array& array ...); +// +// are ambiguous. This will be worked around by using a buffer_types traits +// class that contains typedefs for the appropriate buffer and container +// classes, based on whether PodType is const or non-const. + +namespace detail { + +template +struct buffer_types_base; + +template <> +struct buffer_types_base +{ + typedef mutable_buffer buffer_type; + typedef ASIO_MUTABLE_BUFFER container_type; +}; + +template <> +struct buffer_types_base +{ + typedef const_buffer buffer_type; + typedef ASIO_CONST_BUFFER container_type; +}; + +template +struct buffer_types + : public buffer_types_base::value> +{ +}; + +} // namespace detail + +template +inline typename detail::buffer_types::container_type +buffer(boost::array& data) ASIO_NOEXCEPT +{ + typedef typename asio::detail::buffer_types::buffer_type + buffer_type; + typedef typename asio::detail::buffer_types::container_type + container_type; + return container_type( + buffer_type(data.c_array(), data.size() * sizeof(PodType))); +} + +template +inline typename detail::buffer_types::container_type +buffer(boost::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + typedef typename asio::detail::buffer_types::buffer_type + buffer_type; + typedef typename asio::detail::buffer_types::container_type + container_type; + return container_type( + buffer_type(data.c_array(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes)); +} + +#else // defined(ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND) + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer( + boost::array& data) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER( + data.c_array(), data.size() * sizeof(PodType)); +} + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer(boost::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.c_array(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + boost::array& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), data.size() * sizeof(PodType)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer(boost::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +#endif // defined(ASIO_ENABLE_ARRAY_BUFFER_WORKAROUND) + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + const boost::array& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), data.size() * sizeof(PodType)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer(const boost::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +#if defined(ASIO_HAS_STD_ARRAY) || defined(GENERATING_DOCUMENTATION) + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer( + std::array& data) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.data(), data.size() * sizeof(PodType)); +} + +/// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_MUTABLE_BUFFER buffer(std::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + std::array& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), data.size() * sizeof(PodType)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer(std::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + const std::array& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), data.size() * sizeof(PodType)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer(const std::array& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes); +} + +#endif // defined(ASIO_HAS_STD_ARRAY) || defined(GENERATING_DOCUMENTATION) + +/// Create a new modifiable buffer that represents the given POD vector. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.size() ? &data[0] : 0, + * data.size() * sizeof(PodType)); @endcode + * + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline ASIO_MUTABLE_BUFFER buffer( + std::vector& data) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER( + data.size() ? &data[0] : 0, data.size() * sizeof(PodType) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that represents the given POD vector. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.size() ? &data[0] : 0, + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + * + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline ASIO_MUTABLE_BUFFER buffer(std::vector& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.size() ? &data[0] : 0, + * data.size() * sizeof(PodType)); @endcode + * + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline ASIO_CONST_BUFFER buffer( + const std::vector& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER( + data.size() ? &data[0] : 0, data.size() * sizeof(PodType) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::const_iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.size() ? &data[0] : 0, + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + * + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline ASIO_CONST_BUFFER buffer( + const std::vector& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::const_iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that represents the given string. +/** + * @returns mutable_buffer(data.size() ? &data[0] : 0, + * data.size() * sizeof(Elem)). + * + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +template +inline ASIO_MUTABLE_BUFFER buffer( + std::basic_string& data) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(Elem) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::basic_string::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that represents the given string. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.size() ? &data[0] : 0, + * min(data.size() * sizeof(Elem), max_size_in_bytes)); @endcode + * + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +template +inline ASIO_MUTABLE_BUFFER buffer( + std::basic_string& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_MUTABLE_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(Elem) < max_size_in_bytes + ? data.size() * sizeof(Elem) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::basic_string::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @returns const_buffer(data.data(), data.size() * sizeof(Elem)). + * + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +template +inline ASIO_CONST_BUFFER buffer( + const std::basic_string& data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), data.size() * sizeof(Elem) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::basic_string::const_iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @returns A const_buffer value equivalent to: + * @code const_buffer( + * data.data(), + * min(data.size() * sizeof(Elem), max_size_in_bytes)); @endcode + * + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +template +inline ASIO_CONST_BUFFER buffer( + const std::basic_string& data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.data(), + data.size() * sizeof(Elem) < max_size_in_bytes + ? data.size() * sizeof(Elem) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::basic_string::const_iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +#if defined(ASIO_HAS_STRING_VIEW) \ + || defined(GENERATING_DOCUMENTATION) + +/// Create a new modifiable buffer that represents the given string_view. +/** + * @returns mutable_buffer(data.size() ? &data[0] : 0, + * data.size() * sizeof(Elem)). + */ +template +inline ASIO_CONST_BUFFER buffer( + basic_string_view data) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(Elem) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename basic_string_view::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @returns A mutable_buffer value equivalent to: + * @code mutable_buffer( + * data.size() ? &data[0] : 0, + * min(data.size() * sizeof(Elem), max_size_in_bytes)); @endcode + */ +template +inline ASIO_CONST_BUFFER buffer( + basic_string_view data, + std::size_t max_size_in_bytes) ASIO_NOEXCEPT +{ + return ASIO_CONST_BUFFER(data.size() ? &data[0] : 0, + data.size() * sizeof(Elem) < max_size_in_bytes + ? data.size() * sizeof(Elem) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename basic_string_view::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +#endif // defined(ASIO_HAS_STRING_VIEW) + // || defined(GENERATING_DOCUMENTATION) + +/*@}*/ + +/// Adapt a basic_string to the DynamicBuffer requirements. +/** + * Requires that sizeof(Elem) == 1. + */ +template +class dynamic_string_buffer +{ +public: + /// The type used to represent a sequence of constant buffers that refers to + /// the underlying memory. + typedef ASIO_CONST_BUFFER const_buffers_type; + + /// The type used to represent a sequence of mutable buffers that refers to + /// the underlying memory. + typedef ASIO_MUTABLE_BUFFER mutable_buffers_type; + + /// Construct a dynamic buffer from a string. + /** + * @param s The string to be used as backing storage for the dynamic buffer. + * The object stores a reference to the string and the user is responsible + * for ensuring that the string object remains valid while the + * dynamic_string_buffer object, and copies of the object, are in use. + * + * @b DynamicBuffer_v1: Any existing data in the string is treated as the + * dynamic buffer's input sequence. + * + * @param maximum_size Specifies a maximum size for the buffer, in bytes. + */ + explicit dynamic_string_buffer(std::basic_string& s, + std::size_t maximum_size = + (std::numeric_limits::max)()) ASIO_NOEXCEPT + : string_(s), +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + size_((std::numeric_limits::max)()), +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + max_size_(maximum_size) + { + } + + /// @b DynamicBuffer_v2: Copy construct a dynamic buffer. + dynamic_string_buffer(const dynamic_string_buffer& other) ASIO_NOEXCEPT + : string_(other.string_), +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + size_(other.size_), +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + max_size_(other.max_size_) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move construct a dynamic buffer. + dynamic_string_buffer(dynamic_string_buffer&& other) ASIO_NOEXCEPT + : string_(other.string_), +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + size_(other.size_), +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + max_size_(other.max_size_) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// @b DynamicBuffer_v1: Get the size of the input sequence. + /// @b DynamicBuffer_v2: Get the current size of the underlying memory. + /** + * @returns @b DynamicBuffer_v1 The current size of the input sequence. + * @b DynamicBuffer_v2: The current size of the underlying string if less than + * max_size(). Otherwise returns max_size(). + */ + std::size_t size() const ASIO_NOEXCEPT + { +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + if (size_ != (std::numeric_limits::max)()) + return size_; +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + return (std::min)(string_.size(), max_size()); + } + + /// Get the maximum size of the dynamic buffer. + /** + * @returns The allowed maximum size of the underlying memory. + */ + std::size_t max_size() const ASIO_NOEXCEPT + { + return max_size_; + } + + /// Get the maximum size that the buffer may grow to without triggering + /// reallocation. + /** + * @returns The current capacity of the underlying string if less than + * max_size(). Otherwise returns max_size(). + */ + std::size_t capacity() const ASIO_NOEXCEPT + { + return (std::min)(string_.capacity(), max_size()); + } + +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + /// @b DynamicBuffer_v1: Get a list of buffers that represents the input + /// sequence. + /** + * @returns An object of type @c const_buffers_type that satisfies + * ConstBufferSequence requirements, representing the basic_string memory in + * the input sequence. + * + * @note The returned object is invalidated by any @c dynamic_string_buffer + * or @c basic_string member function that resizes or erases the string. + */ + const_buffers_type data() const ASIO_NOEXCEPT + { + return const_buffers_type(asio::buffer(string_, size_)); + } +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + + /// @b DynamicBuffer_v2: Get a sequence of buffers that represents the + /// underlying memory. + /** + * @param pos Position of the first byte to represent in the buffer sequence + * + * @param n The number of bytes to return in the buffer sequence. If the + * underlying memory is shorter, the buffer sequence represents as many bytes + * as are available. + * + * @returns An object of type @c mutable_buffers_type that satisfies + * MutableBufferSequence requirements, representing the basic_string memory. + * + * @note The returned object is invalidated by any @c dynamic_string_buffer + * or @c basic_string member function that resizes or erases the string. + */ + mutable_buffers_type data(std::size_t pos, std::size_t n) ASIO_NOEXCEPT + { + return mutable_buffers_type(asio::buffer( + asio::buffer(string_, max_size_) + pos, n)); + } + + /// @b DynamicBuffer_v2: Get a sequence of buffers that represents the + /// underlying memory. + /** + * @param pos Position of the first byte to represent in the buffer sequence + * + * @param n The number of bytes to return in the buffer sequence. If the + * underlying memory is shorter, the buffer sequence represents as many bytes + * as are available. + * + * @note The returned object is invalidated by any @c dynamic_string_buffer + * or @c basic_string member function that resizes or erases the string. + */ + const_buffers_type data(std::size_t pos, + std::size_t n) const ASIO_NOEXCEPT + { + return const_buffers_type(asio::buffer( + asio::buffer(string_, max_size_) + pos, n)); + } + +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + /// @b DynamicBuffer_v1: Get a list of buffers that represents the output + /// sequence, with the given size. + /** + * Ensures that the output sequence can accommodate @c n bytes, resizing the + * basic_string object as necessary. + * + * @returns An object of type @c mutable_buffers_type that satisfies + * MutableBufferSequence requirements, representing basic_string memory + * at the start of the output sequence of size @c n. + * + * @throws std::length_error If size() + n > max_size(). + * + * @note The returned object is invalidated by any @c dynamic_string_buffer + * or @c basic_string member function that modifies the input sequence or + * output sequence. + */ + mutable_buffers_type prepare(std::size_t n) + { + if (size() > max_size() || max_size() - size() < n) + { + std::length_error ex("dynamic_string_buffer too long"); + asio::detail::throw_exception(ex); + } + + if (size_ == (std::numeric_limits::max)()) + size_ = string_.size(); // Enable v1 behaviour. + + string_.resize(size_ + n); + + return asio::buffer(asio::buffer(string_) + size_, n); + } + + /// @b DynamicBuffer_v1: Move bytes from the output sequence to the input + /// sequence. + /** + * @param n The number of bytes to append from the start of the output + * sequence to the end of the input sequence. The remainder of the output + * sequence is discarded. + * + * Requires a preceding call prepare(x) where x >= n, and + * no intervening operations that modify the input or output sequence. + * + * @note If @c n is greater than the size of the output sequence, the entire + * output sequence is moved to the input sequence and no error is issued. + */ + void commit(std::size_t n) + { + size_ += (std::min)(n, string_.size() - size_); + string_.resize(size_); + } +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + + /// @b DynamicBuffer_v2: Grow the underlying memory by the specified number of + /// bytes. + /** + * Resizes the string to accommodate an additional @c n bytes at the end. + * + * @throws std::length_error If size() + n > max_size(). + */ + void grow(std::size_t n) + { + if (size() > max_size() || max_size() - size() < n) + { + std::length_error ex("dynamic_string_buffer too long"); + asio::detail::throw_exception(ex); + } + + string_.resize(size() + n); + } + + /// @b DynamicBuffer_v2: Shrink the underlying memory by the specified number + /// of bytes. + /** + * Erases @c n bytes from the end of the string by resizing the basic_string + * object. If @c n is greater than the current size of the string, the string + * is emptied. + */ + void shrink(std::size_t n) + { + string_.resize(n > size() ? 0 : size() - n); + } + + /// @b DynamicBuffer_v1: Remove characters from the input sequence. + /// @b DynamicBuffer_v2: Consume the specified number of bytes from the + /// beginning of the underlying memory. + /** + * @b DynamicBuffer_v1: Removes @c n characters from the beginning of the + * input sequence. @note If @c n is greater than the size of the input + * sequence, the entire input sequence is consumed and no error is issued. + * + * @b DynamicBuffer_v2: Erases @c n bytes from the beginning of the string. + * If @c n is greater than the current size of the string, the string is + * emptied. + */ + void consume(std::size_t n) + { +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + if (size_ != (std::numeric_limits::max)()) + { + std::size_t consume_length = (std::min)(n, size_); + string_.erase(0, consume_length); + size_ -= consume_length; + return; + } +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + string_.erase(0, n); + } + +private: + std::basic_string& string_; +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + std::size_t size_; +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + const std::size_t max_size_; +}; + +/// Adapt a vector to the DynamicBuffer requirements. +/** + * Requires that sizeof(Elem) == 1. + */ +template +class dynamic_vector_buffer +{ +public: + /// The type used to represent a sequence of constant buffers that refers to + /// the underlying memory. + typedef ASIO_CONST_BUFFER const_buffers_type; + + /// The type used to represent a sequence of mutable buffers that refers to + /// the underlying memory. + typedef ASIO_MUTABLE_BUFFER mutable_buffers_type; + + /// Construct a dynamic buffer from a vector. + /** + * @param v The vector to be used as backing storage for the dynamic buffer. + * The object stores a reference to the vector and the user is responsible + * for ensuring that the vector object remains valid while the + * dynamic_vector_buffer object, and copies of the object, are in use. + * + * @param maximum_size Specifies a maximum size for the buffer, in bytes. + */ + explicit dynamic_vector_buffer(std::vector& v, + std::size_t maximum_size = + (std::numeric_limits::max)()) ASIO_NOEXCEPT + : vector_(v), +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + size_((std::numeric_limits::max)()), +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + max_size_(maximum_size) + { + } + + /// @b DynamicBuffer_v2: Copy construct a dynamic buffer. + dynamic_vector_buffer(const dynamic_vector_buffer& other) ASIO_NOEXCEPT + : vector_(other.vector_), +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + size_(other.size_), +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + max_size_(other.max_size_) + { + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move construct a dynamic buffer. + dynamic_vector_buffer(dynamic_vector_buffer&& other) ASIO_NOEXCEPT + : vector_(other.vector_), +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + size_(other.size_), +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + max_size_(other.max_size_) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// @b DynamicBuffer_v1: Get the size of the input sequence. + /// @b DynamicBuffer_v2: Get the current size of the underlying memory. + /** + * @returns @b DynamicBuffer_v1 The current size of the input sequence. + * @b DynamicBuffer_v2: The current size of the underlying vector if less than + * max_size(). Otherwise returns max_size(). + */ + std::size_t size() const ASIO_NOEXCEPT + { +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + if (size_ != (std::numeric_limits::max)()) + return size_; +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + return (std::min)(vector_.size(), max_size()); + } + + /// Get the maximum size of the dynamic buffer. + /** + * @returns @b DynamicBuffer_v1: The allowed maximum of the sum of the sizes + * of the input sequence and output sequence. @b DynamicBuffer_v2: The allowed + * maximum size of the underlying memory. + */ + std::size_t max_size() const ASIO_NOEXCEPT + { + return max_size_; + } + + /// Get the maximum size that the buffer may grow to without triggering + /// reallocation. + /** + * @returns @b DynamicBuffer_v1: The current total capacity of the buffer, + * i.e. for both the input sequence and output sequence. @b DynamicBuffer_v2: + * The current capacity of the underlying vector if less than max_size(). + * Otherwise returns max_size(). + */ + std::size_t capacity() const ASIO_NOEXCEPT + { + return (std::min)(vector_.capacity(), max_size()); + } + +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + /// @b DynamicBuffer_v1: Get a list of buffers that represents the input + /// sequence. + /** + * @returns An object of type @c const_buffers_type that satisfies + * ConstBufferSequence requirements, representing the vector memory in the + * input sequence. + * + * @note The returned object is invalidated by any @c dynamic_vector_buffer + * or @c vector member function that modifies the input sequence or output + * sequence. + */ + const_buffers_type data() const ASIO_NOEXCEPT + { + return const_buffers_type(asio::buffer(vector_, size_)); + } +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + + /// @b DynamicBuffer_v2: Get a sequence of buffers that represents the + /// underlying memory. + /** + * @param pos Position of the first byte to represent in the buffer sequence + * + * @param n The number of bytes to return in the buffer sequence. If the + * underlying memory is shorter, the buffer sequence represents as many bytes + * as are available. + * + * @returns An object of type @c mutable_buffers_type that satisfies + * MutableBufferSequence requirements, representing the vector memory. + * + * @note The returned object is invalidated by any @c dynamic_vector_buffer + * or @c vector member function that resizes or erases the vector. + */ + mutable_buffers_type data(std::size_t pos, std::size_t n) ASIO_NOEXCEPT + { + return mutable_buffers_type(asio::buffer( + asio::buffer(vector_, max_size_) + pos, n)); + } + + /// @b DynamicBuffer_v2: Get a sequence of buffers that represents the + /// underlying memory. + /** + * @param pos Position of the first byte to represent in the buffer sequence + * + * @param n The number of bytes to return in the buffer sequence. If the + * underlying memory is shorter, the buffer sequence represents as many bytes + * as are available. + * + * @note The returned object is invalidated by any @c dynamic_vector_buffer + * or @c vector member function that resizes or erases the vector. + */ + const_buffers_type data(std::size_t pos, + std::size_t n) const ASIO_NOEXCEPT + { + return const_buffers_type(asio::buffer( + asio::buffer(vector_, max_size_) + pos, n)); + } + +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + /// @b DynamicBuffer_v1: Get a list of buffers that represents the output + /// sequence, with the given size. + /** + * Ensures that the output sequence can accommodate @c n bytes, resizing the + * vector object as necessary. + * + * @returns An object of type @c mutable_buffers_type that satisfies + * MutableBufferSequence requirements, representing vector memory at the + * start of the output sequence of size @c n. + * + * @throws std::length_error If size() + n > max_size(). + * + * @note The returned object is invalidated by any @c dynamic_vector_buffer + * or @c vector member function that modifies the input sequence or output + * sequence. + */ + mutable_buffers_type prepare(std::size_t n) + { + if (size () > max_size() || max_size() - size() < n) + { + std::length_error ex("dynamic_vector_buffer too long"); + asio::detail::throw_exception(ex); + } + + if (size_ == (std::numeric_limits::max)()) + size_ = vector_.size(); // Enable v1 behaviour. + + vector_.resize(size_ + n); + + return asio::buffer(asio::buffer(vector_) + size_, n); + } + + /// @b DynamicBuffer_v1: Move bytes from the output sequence to the input + /// sequence. + /** + * @param n The number of bytes to append from the start of the output + * sequence to the end of the input sequence. The remainder of the output + * sequence is discarded. + * + * Requires a preceding call prepare(x) where x >= n, and + * no intervening operations that modify the input or output sequence. + * + * @note If @c n is greater than the size of the output sequence, the entire + * output sequence is moved to the input sequence and no error is issued. + */ + void commit(std::size_t n) + { + size_ += (std::min)(n, vector_.size() - size_); + vector_.resize(size_); + } +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + + /// @b DynamicBuffer_v2: Grow the underlying memory by the specified number of + /// bytes. + /** + * Resizes the vector to accommodate an additional @c n bytes at the end. + * + * @throws std::length_error If size() + n > max_size(). + */ + void grow(std::size_t n) + { + if (size() > max_size() || max_size() - size() < n) + { + std::length_error ex("dynamic_vector_buffer too long"); + asio::detail::throw_exception(ex); + } + + vector_.resize(size() + n); + } + + /// @b DynamicBuffer_v2: Shrink the underlying memory by the specified number + /// of bytes. + /** + * Erases @c n bytes from the end of the vector by resizing the vector + * object. If @c n is greater than the current size of the vector, the vector + * is emptied. + */ + void shrink(std::size_t n) + { + vector_.resize(n > size() ? 0 : size() - n); + } + + /// @b DynamicBuffer_v1: Remove characters from the input sequence. + /// @b DynamicBuffer_v2: Consume the specified number of bytes from the + /// beginning of the underlying memory. + /** + * @b DynamicBuffer_v1: Removes @c n characters from the beginning of the + * input sequence. @note If @c n is greater than the size of the input + * sequence, the entire input sequence is consumed and no error is issued. + * + * @b DynamicBuffer_v2: Erases @c n bytes from the beginning of the vector. + * If @c n is greater than the current size of the vector, the vector is + * emptied. + */ + void consume(std::size_t n) + { +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + if (size_ != (std::numeric_limits::max)()) + { + std::size_t consume_length = (std::min)(n, size_); + vector_.erase(vector_.begin(), vector_.begin() + consume_length); + size_ -= consume_length; + return; + } +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + vector_.erase(vector_.begin(), vector_.begin() + (std::min)(size(), n)); + } + +private: + std::vector& vector_; +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + std::size_t size_; +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + const std::size_t max_size_; +}; + +/** @defgroup dynamic_buffer asio::dynamic_buffer + * + * @brief The asio::dynamic_buffer function is used to create a + * dynamically resized buffer from a @c std::basic_string or @c std::vector. + */ +/*@{*/ + +/// Create a new dynamic buffer that represents the given string. +/** + * @returns dynamic_string_buffer(data). + */ +template +inline dynamic_string_buffer dynamic_buffer( + std::basic_string& data) ASIO_NOEXCEPT +{ + return dynamic_string_buffer(data); +} + +/// Create a new dynamic buffer that represents the given string. +/** + * @returns dynamic_string_buffer(data, + * max_size). + */ +template +inline dynamic_string_buffer dynamic_buffer( + std::basic_string& data, + std::size_t max_size) ASIO_NOEXCEPT +{ + return dynamic_string_buffer(data, max_size); +} + +/// Create a new dynamic buffer that represents the given vector. +/** + * @returns dynamic_vector_buffer(data). + */ +template +inline dynamic_vector_buffer dynamic_buffer( + std::vector& data) ASIO_NOEXCEPT +{ + return dynamic_vector_buffer(data); +} + +/// Create a new dynamic buffer that represents the given vector. +/** + * @returns dynamic_vector_buffer(data, max_size). + */ +template +inline dynamic_vector_buffer dynamic_buffer( + std::vector& data, + std::size_t max_size) ASIO_NOEXCEPT +{ + return dynamic_vector_buffer(data, max_size); +} + +/*@}*/ + +/** @defgroup buffer_copy asio::buffer_copy + * + * @brief The asio::buffer_copy function is used to copy bytes from a + * source buffer (or buffer sequence) to a target buffer (or buffer sequence). + * + * The @c buffer_copy function is available in two forms: + * + * @li A 2-argument form: @c buffer_copy(target, source) + * + * @li A 3-argument form: @c buffer_copy(target, source, max_bytes_to_copy) + * + * Both forms return the number of bytes actually copied. The number of bytes + * copied is the lesser of: + * + * @li @c buffer_size(target) + * + * @li @c buffer_size(source) + * + * @li @c If specified, @c max_bytes_to_copy. + * + * This prevents buffer overflow, regardless of the buffer sizes used in the + * copy operation. + * + * Note that @ref buffer_copy is implemented in terms of @c memcpy, and + * consequently it cannot be used to copy between overlapping memory regions. + */ +/*@{*/ + +namespace detail { + +inline std::size_t buffer_copy_1(const mutable_buffer& target, + const const_buffer& source) +{ + using namespace std; // For memcpy. + std::size_t target_size = target.size(); + std::size_t source_size = source.size(); + std::size_t n = target_size < source_size ? target_size : source_size; + if (n > 0) + memcpy(target.data(), source.data(), n); + return n; +} + +template +inline std::size_t buffer_copy(one_buffer, one_buffer, + TargetIterator target_begin, TargetIterator, + SourceIterator source_begin, SourceIterator) ASIO_NOEXCEPT +{ + return (buffer_copy_1)(*target_begin, *source_begin); +} + +template +inline std::size_t buffer_copy(one_buffer, one_buffer, + TargetIterator target_begin, TargetIterator, + SourceIterator source_begin, SourceIterator, + std::size_t max_bytes_to_copy) ASIO_NOEXCEPT +{ + return (buffer_copy_1)(*target_begin, + asio::buffer(*source_begin, max_bytes_to_copy)); +} + +template +std::size_t buffer_copy(one_buffer, multiple_buffers, + TargetIterator target_begin, TargetIterator, + SourceIterator source_begin, SourceIterator source_end, + std::size_t max_bytes_to_copy + = (std::numeric_limits::max)()) ASIO_NOEXCEPT +{ + std::size_t total_bytes_copied = 0; + SourceIterator source_iter = source_begin; + + for (mutable_buffer target_buffer( + asio::buffer(*target_begin, max_bytes_to_copy)); + target_buffer.size() && source_iter != source_end; ++source_iter) + { + const_buffer source_buffer(*source_iter); + std::size_t bytes_copied = (buffer_copy_1)(target_buffer, source_buffer); + total_bytes_copied += bytes_copied; + target_buffer += bytes_copied; + } + + return total_bytes_copied; +} + +template +std::size_t buffer_copy(multiple_buffers, one_buffer, + TargetIterator target_begin, TargetIterator target_end, + SourceIterator source_begin, SourceIterator, + std::size_t max_bytes_to_copy + = (std::numeric_limits::max)()) ASIO_NOEXCEPT +{ + std::size_t total_bytes_copied = 0; + TargetIterator target_iter = target_begin; + + for (const_buffer source_buffer( + asio::buffer(*source_begin, max_bytes_to_copy)); + source_buffer.size() && target_iter != target_end; ++target_iter) + { + mutable_buffer target_buffer(*target_iter); + std::size_t bytes_copied = (buffer_copy_1)(target_buffer, source_buffer); + total_bytes_copied += bytes_copied; + source_buffer += bytes_copied; + } + + return total_bytes_copied; +} + +template +std::size_t buffer_copy(multiple_buffers, multiple_buffers, + TargetIterator target_begin, TargetIterator target_end, + SourceIterator source_begin, SourceIterator source_end) ASIO_NOEXCEPT +{ + std::size_t total_bytes_copied = 0; + + TargetIterator target_iter = target_begin; + std::size_t target_buffer_offset = 0; + + SourceIterator source_iter = source_begin; + std::size_t source_buffer_offset = 0; + + while (target_iter != target_end && source_iter != source_end) + { + mutable_buffer target_buffer = + mutable_buffer(*target_iter) + target_buffer_offset; + + const_buffer source_buffer = + const_buffer(*source_iter) + source_buffer_offset; + + std::size_t bytes_copied = (buffer_copy_1)(target_buffer, source_buffer); + total_bytes_copied += bytes_copied; + + if (bytes_copied == target_buffer.size()) + { + ++target_iter; + target_buffer_offset = 0; + } + else + target_buffer_offset += bytes_copied; + + if (bytes_copied == source_buffer.size()) + { + ++source_iter; + source_buffer_offset = 0; + } + else + source_buffer_offset += bytes_copied; + } + + return total_bytes_copied; +} + +template +std::size_t buffer_copy(multiple_buffers, multiple_buffers, + TargetIterator target_begin, TargetIterator target_end, + SourceIterator source_begin, SourceIterator source_end, + std::size_t max_bytes_to_copy) ASIO_NOEXCEPT +{ + std::size_t total_bytes_copied = 0; + + TargetIterator target_iter = target_begin; + std::size_t target_buffer_offset = 0; + + SourceIterator source_iter = source_begin; + std::size_t source_buffer_offset = 0; + + while (total_bytes_copied != max_bytes_to_copy + && target_iter != target_end && source_iter != source_end) + { + mutable_buffer target_buffer = + mutable_buffer(*target_iter) + target_buffer_offset; + + const_buffer source_buffer = + const_buffer(*source_iter) + source_buffer_offset; + + std::size_t bytes_copied = (buffer_copy_1)( + target_buffer, asio::buffer(source_buffer, + max_bytes_to_copy - total_bytes_copied)); + total_bytes_copied += bytes_copied; + + if (bytes_copied == target_buffer.size()) + { + ++target_iter; + target_buffer_offset = 0; + } + else + target_buffer_offset += bytes_copied; + + if (bytes_copied == source_buffer.size()) + { + ++source_iter; + source_buffer_offset = 0; + } + else + source_buffer_offset += bytes_copied; + } + + return total_bytes_copied; +} + +} // namespace detail + +/// Copies bytes from a source buffer sequence to a target buffer sequence. +/** + * @param target A modifiable buffer sequence representing the memory regions to + * which the bytes will be copied. + * + * @param source A non-modifiable buffer sequence representing the memory + * regions from which the bytes will be copied. + * + * @returns The number of bytes copied. + * + * @note The number of bytes copied is the lesser of: + * + * @li @c buffer_size(target) + * + * @li @c buffer_size(source) + * + * This function is implemented in terms of @c memcpy, and consequently it + * cannot be used to copy between overlapping memory regions. + */ +template +inline std::size_t buffer_copy(const MutableBufferSequence& target, + const ConstBufferSequence& source) ASIO_NOEXCEPT +{ + return detail::buffer_copy( + detail::buffer_sequence_cardinality(), + detail::buffer_sequence_cardinality(), + asio::buffer_sequence_begin(target), + asio::buffer_sequence_end(target), + asio::buffer_sequence_begin(source), + asio::buffer_sequence_end(source)); +} + +/// Copies a limited number of bytes from a source buffer sequence to a target +/// buffer sequence. +/** + * @param target A modifiable buffer sequence representing the memory regions to + * which the bytes will be copied. + * + * @param source A non-modifiable buffer sequence representing the memory + * regions from which the bytes will be copied. + * + * @param max_bytes_to_copy The maximum number of bytes to be copied. + * + * @returns The number of bytes copied. + * + * @note The number of bytes copied is the lesser of: + * + * @li @c buffer_size(target) + * + * @li @c buffer_size(source) + * + * @li @c max_bytes_to_copy + * + * This function is implemented in terms of @c memcpy, and consequently it + * cannot be used to copy between overlapping memory regions. + */ +template +inline std::size_t buffer_copy(const MutableBufferSequence& target, + const ConstBufferSequence& source, + std::size_t max_bytes_to_copy) ASIO_NOEXCEPT +{ + return detail::buffer_copy( + detail::buffer_sequence_cardinality(), + detail::buffer_sequence_cardinality(), + asio::buffer_sequence_begin(target), + asio::buffer_sequence_end(target), + asio::buffer_sequence_begin(source), + asio::buffer_sequence_end(source), max_bytes_to_copy); +} + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" +#include "asio/detail/is_buffer_sequence.hpp" +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Trait to determine whether a type satisfies the MutableBufferSequence +/// requirements. +template +struct is_mutable_buffer_sequence +#if defined(GENERATING_DOCUMENTATION) + : integral_constant +#else // defined(GENERATING_DOCUMENTATION) + : asio::detail::is_buffer_sequence +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +/// Trait to determine whether a type satisfies the ConstBufferSequence +/// requirements. +template +struct is_const_buffer_sequence +#if defined(GENERATING_DOCUMENTATION) + : integral_constant +#else // defined(GENERATING_DOCUMENTATION) + : asio::detail::is_buffer_sequence +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +#if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) +/// Trait to determine whether a type satisfies the DynamicBuffer_v1 +/// requirements. +template +struct is_dynamic_buffer_v1 +#if defined(GENERATING_DOCUMENTATION) + : integral_constant +#else // defined(GENERATING_DOCUMENTATION) + : asio::detail::is_dynamic_buffer_v1 +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; +#endif // !defined(ASIO_NO_DYNAMIC_BUFFER_V1) + +/// Trait to determine whether a type satisfies the DynamicBuffer_v2 +/// requirements. +template +struct is_dynamic_buffer_v2 +#if defined(GENERATING_DOCUMENTATION) + : integral_constant +#else // defined(GENERATING_DOCUMENTATION) + : asio::detail::is_dynamic_buffer_v2 +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +/// Trait to determine whether a type satisfies the DynamicBuffer requirements. +/** + * If @c ASIO_NO_DYNAMIC_BUFFER_V1 is not defined, determines whether the + * type satisfies the DynamicBuffer_v1 requirements. Otherwise, if @c + * ASIO_NO_DYNAMIC_BUFFER_V1 is defined, determines whether the type + * satisfies the DynamicBuffer_v2 requirements. + */ +template +struct is_dynamic_buffer +#if defined(GENERATING_DOCUMENTATION) + : integral_constant +#elif defined(ASIO_NO_DYNAMIC_BUFFER_V1) + : asio::is_dynamic_buffer_v2 +#else // defined(ASIO_NO_DYNAMIC_BUFFER_V1) + : asio::is_dynamic_buffer_v1 +#endif // defined(ASIO_NO_DYNAMIC_BUFFER_V1) +{ +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFER_HPP diff --git a/third_party/asio/1.18.2/include/asio/buffered_read_stream.hpp b/third_party/asio/1.18.2/include/asio/buffered_read_stream.hpp new file mode 100644 index 000000000..7ed91dc2e --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/buffered_read_stream.hpp @@ -0,0 +1,253 @@ +// +// buffered_read_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_READ_STREAM_HPP +#define ASIO_BUFFERED_READ_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffer.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_resize_guard.hpp" +#include "asio/detail/buffered_stream_storage.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Adds buffering to the read-related operations of a stream. +/** + * The buffered_read_stream class template can be used to add buffering to the + * synchronous and asynchronous read operations of a stream. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class buffered_read_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type of the executor associated with the object. + typedef typename lowest_layer_type::executor_type executor_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The default buffer size. + static const std::size_t default_buffer_size = implementation_defined; +#else + ASIO_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); +#endif + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_read_stream(Arg& a) + : next_layer_(a), + storage_(default_buffer_size) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + buffered_read_stream(Arg& a, std::size_t buffer_size) + : next_layer_(a), + storage_(buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get a const reference to the lowest layer. + const lowest_layer_type& lowest_layer() const + { + return next_layer_.lowest_layer(); + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return next_layer_.lowest_layer().get_executor(); + } + + /// Close the stream. + void close() + { + next_layer_.close(); + } + + /// Close the stream. + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + next_layer_.close(ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + return next_layer_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred. + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return next_layer_.write_some(buffers, ec); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return next_layer_.async_write_some(buffers, + ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation. Throws an exception on failure. + std::size_t fill(); + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation, or 0 if an error occurred. + std::size_t fill(asio::error_code& ec); + + /// Start an asynchronous fill. + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code, + std::size_t)) ReadHandler + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_fill( + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)); + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const MutableBufferSequence& buffers); + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred. + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec); + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)); + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const MutableBufferSequence& buffers); + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred. + template + std::size_t peek(const MutableBufferSequence& buffers, + asio::error_code& ec); + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return storage_.size(); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail(asio::error_code& ec) + { + ec = asio::error_code(); + return storage_.size(); + } + +private: + /// Copy data out of the internal buffer to the specified target buffer. + /// Returns the number of bytes copied. + template + std::size_t copy(const MutableBufferSequence& buffers) + { + std::size_t bytes_copied = asio::buffer_copy( + buffers, storage_.data(), storage_.size()); + storage_.consume(bytes_copied); + return bytes_copied; + } + + /// Copy data from the internal buffer to the specified target buffer, without + /// removing the data from the internal buffer. Returns the number of bytes + /// copied. + template + std::size_t peek_copy(const MutableBufferSequence& buffers) + { + return asio::buffer_copy(buffers, storage_.data(), storage_.size()); + } + + /// The next layer. + Stream next_layer_; + + // The data in the buffer. + detail::buffered_stream_storage storage_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/buffered_read_stream.hpp" + +#endif // ASIO_BUFFERED_READ_STREAM_HPP diff --git a/third_party/asio/1.18.2/include/asio/buffered_read_stream_fwd.hpp b/third_party/asio/1.18.2/include/asio/buffered_read_stream_fwd.hpp new file mode 100644 index 000000000..f189b27aa --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/buffered_read_stream_fwd.hpp @@ -0,0 +1,25 @@ +// +// buffered_read_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_READ_STREAM_FWD_HPP +#define ASIO_BUFFERED_READ_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace asio { + +template +class buffered_read_stream; + +} // namespace asio + +#endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP diff --git a/third_party/asio/1.18.2/include/asio/buffered_stream.hpp b/third_party/asio/1.18.2/include/asio/buffered_stream.hpp new file mode 100644 index 000000000..5d71bf661 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/buffered_stream.hpp @@ -0,0 +1,279 @@ +// +// buffered_stream.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_STREAM_HPP +#define ASIO_BUFFERED_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/async_result.hpp" +#include "asio/buffered_read_stream.hpp" +#include "asio/buffered_write_stream.hpp" +#include "asio/buffered_stream_fwd.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Adds buffering to the read- and write-related operations of a stream. +/** + * The buffered_stream class template can be used to add buffering to the + * synchronous and asynchronous read and write operations of a stream. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class buffered_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type of the executor associated with the object. + typedef typename lowest_layer_type::executor_type executor_type; + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_stream(Arg& a) + : inner_stream_impl_(a), + stream_impl_(inner_stream_impl_) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_stream(Arg& a, std::size_t read_buffer_size, + std::size_t write_buffer_size) + : inner_stream_impl_(a, write_buffer_size), + stream_impl_(inner_stream_impl_, read_buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return stream_impl_.next_layer().next_layer(); + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return stream_impl_.lowest_layer(); + } + + /// Get a const reference to the lowest layer. + const lowest_layer_type& lowest_layer() const + { + return stream_impl_.lowest_layer(); + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return stream_impl_.lowest_layer().get_executor(); + } + + /// Close the stream. + void close() + { + stream_impl_.close(); + } + + /// Close the stream. + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + stream_impl_.close(ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation. Throws an + /// exception on failure. + std::size_t flush() + { + return stream_impl_.next_layer().flush(); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation, or 0 if an + /// error occurred. + std::size_t flush(asio::error_code& ec) + { + return stream_impl_.next_layer().flush(ec); + } + + /// Start an asynchronous flush. + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code, + std::size_t)) WriteHandler + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_flush( + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return stream_impl_.next_layer().async_flush( + ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + return stream_impl_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred. + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return stream_impl_.write_some(buffers, ec); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return stream_impl_.async_write_some(buffers, + ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation. Throws an exception on failure. + std::size_t fill() + { + return stream_impl_.fill(); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation, or 0 if an error occurred. + std::size_t fill(asio::error_code& ec) + { + return stream_impl_.fill(ec); + } + + /// Start an asynchronous fill. + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code, + std::size_t)) ReadHandler + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_fill( + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return stream_impl_.async_fill(ASIO_MOVE_CAST(ReadHandler)(handler)); + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + return stream_impl_.read_some(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred. + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return stream_impl_.read_some(buffers, ec); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return stream_impl_.async_read_some(buffers, + ASIO_MOVE_CAST(ReadHandler)(handler)); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const MutableBufferSequence& buffers) + { + return stream_impl_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred. + template + std::size_t peek(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return stream_impl_.peek(buffers, ec); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return stream_impl_.in_avail(); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail(asio::error_code& ec) + { + return stream_impl_.in_avail(ec); + } + +private: + // The buffered write stream. + typedef buffered_write_stream write_stream_type; + write_stream_type inner_stream_impl_; + + // The buffered read stream. + typedef buffered_read_stream read_stream_type; + read_stream_type stream_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_STREAM_HPP diff --git a/third_party/asio/1.18.2/include/asio/buffered_stream_fwd.hpp b/third_party/asio/1.18.2/include/asio/buffered_stream_fwd.hpp new file mode 100644 index 000000000..d970cbba7 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/buffered_stream_fwd.hpp @@ -0,0 +1,25 @@ +// +// buffered_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_STREAM_FWD_HPP +#define ASIO_BUFFERED_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace asio { + +template +class buffered_stream; + +} // namespace asio + +#endif // ASIO_BUFFERED_STREAM_FWD_HPP diff --git a/third_party/asio/1.18.2/include/asio/buffered_write_stream.hpp b/third_party/asio/1.18.2/include/asio/buffered_write_stream.hpp new file mode 100644 index 000000000..526cd6029 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/buffered_write_stream.hpp @@ -0,0 +1,245 @@ +// +// buffered_write_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_WRITE_STREAM_HPP +#define ASIO_BUFFERED_WRITE_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/buffered_write_stream_fwd.hpp" +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffered_stream_storage.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" +#include "asio/write.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Adds buffering to the write-related operations of a stream. +/** + * The buffered_write_stream class template can be used to add buffering to the + * synchronous and asynchronous write operations of a stream. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class buffered_write_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type of the executor associated with the object. + typedef typename lowest_layer_type::executor_type executor_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The default buffer size. + static const std::size_t default_buffer_size = implementation_defined; +#else + ASIO_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); +#endif + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_write_stream(Arg& a) + : next_layer_(a), + storage_(default_buffer_size) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + buffered_write_stream(Arg& a, std::size_t buffer_size) + : next_layer_(a), + storage_(buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get a const reference to the lowest layer. + const lowest_layer_type& lowest_layer() const + { + return next_layer_.lowest_layer(); + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return next_layer_.lowest_layer().get_executor(); + } + + /// Close the stream. + void close() + { + next_layer_.close(); + } + + /// Close the stream. + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + next_layer_.close(ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation. Throws an + /// exception on failure. + std::size_t flush(); + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation, or 0 if an + /// error occurred. + std::size_t flush(asio::error_code& ec); + + /// Start an asynchronous flush. + template < + ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code, + std::size_t)) WriteHandler + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_flush( + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)); + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const ConstBufferSequence& buffers); + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec); + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + ASIO_INITFN_AUTO_RESULT_TYPE(WriteHandler, + void (asio::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + ASIO_MOVE_ARG(WriteHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)); + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + return next_layer_.read_some(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred. + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return next_layer_.read_some(buffers, ec); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler, + void (asio::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ASIO_MOVE_ARG(ReadHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return next_layer_.async_read_some(buffers, + ASIO_MOVE_CAST(ReadHandler)(handler)); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const MutableBufferSequence& buffers) + { + return next_layer_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred. + template + std::size_t peek(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return next_layer_.peek(buffers, ec); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return next_layer_.in_avail(); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail(asio::error_code& ec) + { + return next_layer_.in_avail(ec); + } + +private: + /// Copy data into the internal buffer from the specified source buffer. + /// Returns the number of bytes copied. + template + std::size_t copy(const ConstBufferSequence& buffers); + + /// The next layer. + Stream next_layer_; + + // The data in the buffer. + detail::buffered_stream_storage storage_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/buffered_write_stream.hpp" + +#endif // ASIO_BUFFERED_WRITE_STREAM_HPP diff --git a/third_party/asio/1.18.2/include/asio/buffered_write_stream_fwd.hpp b/third_party/asio/1.18.2/include/asio/buffered_write_stream_fwd.hpp new file mode 100644 index 000000000..8999d9e4c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/buffered_write_stream_fwd.hpp @@ -0,0 +1,25 @@ +// +// buffered_write_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_WRITE_STREAM_FWD_HPP +#define ASIO_BUFFERED_WRITE_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace asio { + +template +class buffered_write_stream; + +} // namespace asio + +#endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP diff --git a/third_party/asio/1.18.2/include/asio/buffers_iterator.hpp b/third_party/asio/1.18.2/include/asio/buffers_iterator.hpp new file mode 100644 index 000000000..76b734a64 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/buffers_iterator.hpp @@ -0,0 +1,521 @@ +// +// buffers_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERS_ITERATOR_HPP +#define ASIO_BUFFERS_ITERATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/buffer.hpp" +#include "asio/detail/assert.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail +{ + template + struct buffers_iterator_types_helper; + + template <> + struct buffers_iterator_types_helper + { + typedef const_buffer buffer_type; + template + struct byte_type + { + typedef typename add_const::type type; + }; + }; + + template <> + struct buffers_iterator_types_helper + { + typedef mutable_buffer buffer_type; + template + struct byte_type + { + typedef ByteType type; + }; + }; + + template + struct buffers_iterator_types + { + enum + { + is_mutable = is_convertible< + typename BufferSequence::value_type, + mutable_buffer>::value + }; + typedef buffers_iterator_types_helper helper; + typedef typename helper::buffer_type buffer_type; + typedef typename helper::template byte_type::type byte_type; + typedef typename BufferSequence::const_iterator const_iterator; + }; + + template + struct buffers_iterator_types + { + typedef mutable_buffer buffer_type; + typedef ByteType byte_type; + typedef const mutable_buffer* const_iterator; + }; + + template + struct buffers_iterator_types + { + typedef const_buffer buffer_type; + typedef typename add_const::type byte_type; + typedef const const_buffer* const_iterator; + }; + +#if !defined(ASIO_NO_DEPRECATED) + + template + struct buffers_iterator_types + { + typedef mutable_buffer buffer_type; + typedef ByteType byte_type; + typedef const mutable_buffer* const_iterator; + }; + + template + struct buffers_iterator_types + { + typedef const_buffer buffer_type; + typedef typename add_const::type byte_type; + typedef const const_buffer* const_iterator; + }; + +#endif // !defined(ASIO_NO_DEPRECATED) +} + +/// A random access iterator over the bytes in a buffer sequence. +template +class buffers_iterator +{ +private: + typedef typename detail::buffers_iterator_types< + BufferSequence, ByteType>::buffer_type buffer_type; + + typedef typename detail::buffers_iterator_types::const_iterator buffer_sequence_iterator_type; + +public: + /// The type used for the distance between two iterators. + typedef std::ptrdiff_t difference_type; + + /// The type of the value pointed to by the iterator. + typedef ByteType value_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The type of the result of applying operator->() to the iterator. + /** + * If the buffer sequence stores buffer objects that are convertible to + * mutable_buffer, this is a pointer to a non-const ByteType. Otherwise, a + * pointer to a const ByteType. + */ + typedef const_or_non_const_ByteType* pointer; +#else // defined(GENERATING_DOCUMENTATION) + typedef typename detail::buffers_iterator_types< + BufferSequence, ByteType>::byte_type* pointer; +#endif // defined(GENERATING_DOCUMENTATION) + +#if defined(GENERATING_DOCUMENTATION) + /// The type of the result of applying operator*() to the iterator. + /** + * If the buffer sequence stores buffer objects that are convertible to + * mutable_buffer, this is a reference to a non-const ByteType. Otherwise, a + * reference to a const ByteType. + */ + typedef const_or_non_const_ByteType& reference; +#else // defined(GENERATING_DOCUMENTATION) + typedef typename detail::buffers_iterator_types< + BufferSequence, ByteType>::byte_type& reference; +#endif // defined(GENERATING_DOCUMENTATION) + + /// The iterator category. + typedef std::random_access_iterator_tag iterator_category; + + /// Default constructor. Creates an iterator in an undefined state. + buffers_iterator() + : current_buffer_(), + current_buffer_position_(0), + begin_(), + current_(), + end_(), + position_(0) + { + } + + /// Construct an iterator representing the beginning of the buffers' data. + static buffers_iterator begin(const BufferSequence& buffers) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) + __attribute__ ((__noinline__)) +#endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) + { + buffers_iterator new_iter; + new_iter.begin_ = asio::buffer_sequence_begin(buffers); + new_iter.current_ = asio::buffer_sequence_begin(buffers); + new_iter.end_ = asio::buffer_sequence_end(buffers); + while (new_iter.current_ != new_iter.end_) + { + new_iter.current_buffer_ = *new_iter.current_; + if (new_iter.current_buffer_.size() > 0) + break; + ++new_iter.current_; + } + return new_iter; + } + + /// Construct an iterator representing the end of the buffers' data. + static buffers_iterator end(const BufferSequence& buffers) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) + __attribute__ ((__noinline__)) +#endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) + { + buffers_iterator new_iter; + new_iter.begin_ = asio::buffer_sequence_begin(buffers); + new_iter.current_ = asio::buffer_sequence_begin(buffers); + new_iter.end_ = asio::buffer_sequence_end(buffers); + while (new_iter.current_ != new_iter.end_) + { + buffer_type buffer = *new_iter.current_; + new_iter.position_ += buffer.size(); + ++new_iter.current_; + } + return new_iter; + } + + /// Dereference an iterator. + reference operator*() const + { + return dereference(); + } + + /// Dereference an iterator. + pointer operator->() const + { + return &dereference(); + } + + /// Access an individual element. + reference operator[](std::ptrdiff_t difference) const + { + buffers_iterator tmp(*this); + tmp.advance(difference); + return *tmp; + } + + /// Increment operator (prefix). + buffers_iterator& operator++() + { + increment(); + return *this; + } + + /// Increment operator (postfix). + buffers_iterator operator++(int) + { + buffers_iterator tmp(*this); + ++*this; + return tmp; + } + + /// Decrement operator (prefix). + buffers_iterator& operator--() + { + decrement(); + return *this; + } + + /// Decrement operator (postfix). + buffers_iterator operator--(int) + { + buffers_iterator tmp(*this); + --*this; + return tmp; + } + + /// Addition operator. + buffers_iterator& operator+=(std::ptrdiff_t difference) + { + advance(difference); + return *this; + } + + /// Subtraction operator. + buffers_iterator& operator-=(std::ptrdiff_t difference) + { + advance(-difference); + return *this; + } + + /// Addition operator. + friend buffers_iterator operator+(const buffers_iterator& iter, + std::ptrdiff_t difference) + { + buffers_iterator tmp(iter); + tmp.advance(difference); + return tmp; + } + + /// Addition operator. + friend buffers_iterator operator+(std::ptrdiff_t difference, + const buffers_iterator& iter) + { + buffers_iterator tmp(iter); + tmp.advance(difference); + return tmp; + } + + /// Subtraction operator. + friend buffers_iterator operator-(const buffers_iterator& iter, + std::ptrdiff_t difference) + { + buffers_iterator tmp(iter); + tmp.advance(-difference); + return tmp; + } + + /// Subtraction operator. + friend std::ptrdiff_t operator-(const buffers_iterator& a, + const buffers_iterator& b) + { + return b.distance_to(a); + } + + /// Test two iterators for equality. + friend bool operator==(const buffers_iterator& a, const buffers_iterator& b) + { + return a.equal(b); + } + + /// Test two iterators for inequality. + friend bool operator!=(const buffers_iterator& a, const buffers_iterator& b) + { + return !a.equal(b); + } + + /// Compare two iterators. + friend bool operator<(const buffers_iterator& a, const buffers_iterator& b) + { + return a.distance_to(b) > 0; + } + + /// Compare two iterators. + friend bool operator<=(const buffers_iterator& a, const buffers_iterator& b) + { + return !(b < a); + } + + /// Compare two iterators. + friend bool operator>(const buffers_iterator& a, const buffers_iterator& b) + { + return b < a; + } + + /// Compare two iterators. + friend bool operator>=(const buffers_iterator& a, const buffers_iterator& b) + { + return !(a < b); + } + +private: + // Dereference the iterator. + reference dereference() const + { + return static_cast( + current_buffer_.data())[current_buffer_position_]; + } + + // Compare two iterators for equality. + bool equal(const buffers_iterator& other) const + { + return position_ == other.position_; + } + + // Increment the iterator. + void increment() + { + ASIO_ASSERT(current_ != end_ && "iterator out of bounds"); + ++position_; + + // Check if the increment can be satisfied by the current buffer. + ++current_buffer_position_; + if (current_buffer_position_ != current_buffer_.size()) + return; + + // Find the next non-empty buffer. + ++current_; + current_buffer_position_ = 0; + while (current_ != end_) + { + current_buffer_ = *current_; + if (current_buffer_.size() > 0) + return; + ++current_; + } + } + + // Decrement the iterator. + void decrement() + { + ASIO_ASSERT(position_ > 0 && "iterator out of bounds"); + --position_; + + // Check if the decrement can be satisfied by the current buffer. + if (current_buffer_position_ != 0) + { + --current_buffer_position_; + return; + } + + // Find the previous non-empty buffer. + buffer_sequence_iterator_type iter = current_; + while (iter != begin_) + { + --iter; + buffer_type buffer = *iter; + std::size_t buffer_size = buffer.size(); + if (buffer_size > 0) + { + current_ = iter; + current_buffer_ = buffer; + current_buffer_position_ = buffer_size - 1; + return; + } + } + } + + // Advance the iterator by the specified distance. + void advance(std::ptrdiff_t n) + { + if (n > 0) + { + ASIO_ASSERT(current_ != end_ && "iterator out of bounds"); + for (;;) + { + std::ptrdiff_t current_buffer_balance + = current_buffer_.size() - current_buffer_position_; + + // Check if the advance can be satisfied by the current buffer. + if (current_buffer_balance > n) + { + position_ += n; + current_buffer_position_ += n; + return; + } + + // Update position. + n -= current_buffer_balance; + position_ += current_buffer_balance; + + // Move to next buffer. If it is empty then it will be skipped on the + // next iteration of this loop. + if (++current_ == end_) + { + ASIO_ASSERT(n == 0 && "iterator out of bounds"); + current_buffer_ = buffer_type(); + current_buffer_position_ = 0; + return; + } + current_buffer_ = *current_; + current_buffer_position_ = 0; + } + } + else if (n < 0) + { + std::size_t abs_n = -n; + ASIO_ASSERT(position_ >= abs_n && "iterator out of bounds"); + for (;;) + { + // Check if the advance can be satisfied by the current buffer. + if (current_buffer_position_ >= abs_n) + { + position_ -= abs_n; + current_buffer_position_ -= abs_n; + return; + } + + // Update position. + abs_n -= current_buffer_position_; + position_ -= current_buffer_position_; + + // Check if we've reached the beginning of the buffers. + if (current_ == begin_) + { + ASIO_ASSERT(abs_n == 0 && "iterator out of bounds"); + current_buffer_position_ = 0; + return; + } + + // Find the previous non-empty buffer. + buffer_sequence_iterator_type iter = current_; + while (iter != begin_) + { + --iter; + buffer_type buffer = *iter; + std::size_t buffer_size = buffer.size(); + if (buffer_size > 0) + { + current_ = iter; + current_buffer_ = buffer; + current_buffer_position_ = buffer_size; + break; + } + } + } + } + } + + // Determine the distance between two iterators. + std::ptrdiff_t distance_to(const buffers_iterator& other) const + { + return other.position_ - position_; + } + + buffer_type current_buffer_; + std::size_t current_buffer_position_; + buffer_sequence_iterator_type begin_; + buffer_sequence_iterator_type current_; + buffer_sequence_iterator_type end_; + std::size_t position_; +}; + +/// Construct an iterator representing the beginning of the buffers' data. +template +inline buffers_iterator buffers_begin( + const BufferSequence& buffers) +{ + return buffers_iterator::begin(buffers); +} + +/// Construct an iterator representing the end of the buffers' data. +template +inline buffers_iterator buffers_end( + const BufferSequence& buffers) +{ + return buffers_iterator::end(buffers); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERS_ITERATOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/co_spawn.hpp b/third_party/asio/1.18.2/include/asio/co_spawn.hpp new file mode 100644 index 000000000..7d64834fb --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/co_spawn.hpp @@ -0,0 +1,471 @@ +// +// co_spawn.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_CO_SPAWN_HPP +#define ASIO_CO_SPAWN_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) + +#include "asio/awaitable.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution_context.hpp" +#include "asio/is_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct awaitable_signature; + +template +struct awaitable_signature> +{ + typedef void type(std::exception_ptr, T); +}; + +template +struct awaitable_signature> +{ + typedef void type(std::exception_ptr); +}; + +} // namespace detail + +/// Spawn a new coroutined-based thread of execution. +/** + * @param ex The executor that will be used to schedule the new thread of + * execution. + * + * @param a The asio::awaitable object that is the result of calling the + * coroutine's entry point function. + * + * @param token The completion token that will handle the notification that + * the thread of execution has completed. The function signature of the + * completion handler must be: + * @code void handler(std::exception_ptr, T); @endcode + * + * @par Example + * @code + * asio::awaitable echo(tcp::socket socket) + * { + * std::size_t bytes_transferred = 0; + * + * try + * { + * char data[1024]; + * for (;;) + * { + * std::size_t n = co_await socket.async_read_some( + * asio::buffer(data), asio::use_awaitable); + * + * co_await asio::async_write(socket, + * asio::buffer(data, n), asio::use_awaitable); + * + * bytes_transferred += n; + * } + * } + * catch (const std::exception&) + * { + * } + * + * co_return bytes_transferred; + * } + * + * // ... + * + * asio::co_spawn(my_executor, + * echo(std::move(my_tcp_socket)), + * [](std::exception_ptr e, std::size_t n) + * { + * std::cout << "transferred " << n << "\n"; + * }); + * @endcode + */ +template +inline ASIO_INITFN_AUTO_RESULT_TYPE( + CompletionToken, void(std::exception_ptr, T)) +co_spawn(const Executor& ex, awaitable a, + CompletionToken&& token + ASIO_DEFAULT_COMPLETION_TOKEN(Executor), + typename constraint< + (is_executor::value || execution::is_executor::value) + && is_convertible::value + >::type = 0); + +/// Spawn a new coroutined-based thread of execution. +/** + * @param ex The executor that will be used to schedule the new thread of + * execution. + * + * @param a The asio::awaitable object that is the result of calling the + * coroutine's entry point function. + * + * @param token The completion token that will handle the notification that + * the thread of execution has completed. The function signature of the + * completion handler must be: + * @code void handler(std::exception_ptr); @endcode + * + * @par Example + * @code + * asio::awaitable echo(tcp::socket socket) + * { + * try + * { + * char data[1024]; + * for (;;) + * { + * std::size_t n = co_await socket.async_read_some( + * asio::buffer(data), asio::use_awaitable); + * + * co_await asio::async_write(socket, + * asio::buffer(data, n), asio::use_awaitable); + * } + * } + * catch (const std::exception& e) + * { + * std::cerr << "Exception: " << e.what() << "\n"; + * } + * } + * + * // ... + * + * asio::co_spawn(my_executor, + * echo(std::move(my_tcp_socket)), + * asio::detached); + * @endcode + */ +template +inline ASIO_INITFN_AUTO_RESULT_TYPE( + CompletionToken, void(std::exception_ptr)) +co_spawn(const Executor& ex, awaitable a, + CompletionToken&& token + ASIO_DEFAULT_COMPLETION_TOKEN(Executor), + typename constraint< + (is_executor::value || execution::is_executor::value) + && is_convertible::value + >::type = 0); + +/// Spawn a new coroutined-based thread of execution. +/** + * @param ctx An execution context that will provide the executor to be used to + * schedule the new thread of execution. + * + * @param a The asio::awaitable object that is the result of calling the + * coroutine's entry point function. + * + * @param token The completion token that will handle the notification that + * the thread of execution has completed. The function signature of the + * completion handler must be: + * @code void handler(std::exception_ptr); @endcode + * + * @par Example + * @code + * asio::awaitable echo(tcp::socket socket) + * { + * std::size_t bytes_transferred = 0; + * + * try + * { + * char data[1024]; + * for (;;) + * { + * std::size_t n = co_await socket.async_read_some( + * asio::buffer(data), asio::use_awaitable); + * + * co_await asio::async_write(socket, + * asio::buffer(data, n), asio::use_awaitable); + * + * bytes_transferred += n; + * } + * } + * catch (const std::exception&) + * { + * } + * + * co_return bytes_transferred; + * } + * + * // ... + * + * asio::co_spawn(my_io_context, + * echo(std::move(my_tcp_socket)), + * [](std::exception_ptr e, std::size_t n) + * { + * std::cout << "transferred " << n << "\n"; + * }); + * @endcode + */ +template +inline ASIO_INITFN_AUTO_RESULT_TYPE( + CompletionToken, void(std::exception_ptr, T)) +co_spawn(ExecutionContext& ctx, awaitable a, + CompletionToken&& token + ASIO_DEFAULT_COMPLETION_TOKEN( + typename ExecutionContext::executor_type), + typename constraint< + is_convertible::value + && is_convertible::value + >::type = 0); + +/// Spawn a new coroutined-based thread of execution. +/** + * @param ctx An execution context that will provide the executor to be used to + * schedule the new thread of execution. + * + * @param a The asio::awaitable object that is the result of calling the + * coroutine's entry point function. + * + * @param token The completion token that will handle the notification that + * the thread of execution has completed. The function signature of the + * completion handler must be: + * @code void handler(std::exception_ptr); @endcode + * + * @par Example + * @code + * asio::awaitable echo(tcp::socket socket) + * { + * try + * { + * char data[1024]; + * for (;;) + * { + * std::size_t n = co_await socket.async_read_some( + * asio::buffer(data), asio::use_awaitable); + * + * co_await asio::async_write(socket, + * asio::buffer(data, n), asio::use_awaitable); + * } + * } + * catch (const std::exception& e) + * { + * std::cerr << "Exception: " << e.what() << "\n"; + * } + * } + * + * // ... + * + * asio::co_spawn(my_io_context, + * echo(std::move(my_tcp_socket)), + * asio::detached); + * @endcode + */ +template +inline ASIO_INITFN_AUTO_RESULT_TYPE( + CompletionToken, void(std::exception_ptr)) +co_spawn(ExecutionContext& ctx, awaitable a, + CompletionToken&& token + ASIO_DEFAULT_COMPLETION_TOKEN( + typename ExecutionContext::executor_type), + typename constraint< + is_convertible::value + && is_convertible::value + >::type = 0); + +/// Spawn a new coroutined-based thread of execution. +/** + * @param ex The executor that will be used to schedule the new thread of + * execution. + * + * @param f A nullary function object with a return type of the form + * @c asio::awaitable that will be used as the coroutine's entry + * point. + * + * @param token The completion token that will handle the notification that the + * thread of execution has completed. If @c R is @c void, the function + * signature of the completion handler must be: + * + * @code void handler(std::exception_ptr); @endcode + * Otherwise, the function signature of the completion handler must be: + * @code void handler(std::exception_ptr, R); @endcode + * + * + * @par Example + * @code + * asio::awaitable echo(tcp::socket socket) + * { + * std::size_t bytes_transferred = 0; + * + * try + * { + * char data[1024]; + * for (;;) + * { + * std::size_t n = co_await socket.async_read_some( + * asio::buffer(data), asio::use_awaitable); + * + * co_await asio::async_write(socket, + * asio::buffer(data, n), asio::use_awaitable); + * + * bytes_transferred += n; + * } + * } + * catch (const std::exception&) + * { + * } + * + * co_return bytes_transferred; + * } + * + * // ... + * + * asio::co_spawn(my_executor, + * [socket = std::move(my_tcp_socket)]() mutable + * -> asio::awaitable + * { + * try + * { + * char data[1024]; + * for (;;) + * { + * std::size_t n = co_await socket.async_read_some( + * asio::buffer(data), asio::use_awaitable); + * + * co_await asio::async_write(socket, + * asio::buffer(data, n), asio::use_awaitable); + * } + * } + * catch (const std::exception& e) + * { + * std::cerr << "Exception: " << e.what() << "\n"; + * } + * }, asio::detached); + * @endcode + */ +template ::type>::type) CompletionToken + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)> +ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + typename detail::awaitable_signature::type>::type) +co_spawn(const Executor& ex, F&& f, + CompletionToken&& token + ASIO_DEFAULT_COMPLETION_TOKEN(Executor), + typename constraint< + is_executor::value || execution::is_executor::value + >::type = 0); + +/// Spawn a new coroutined-based thread of execution. +/** + * @param ctx An execution context that will provide the executor to be used to + * schedule the new thread of execution. + * + * @param f A nullary function object with a return type of the form + * @c asio::awaitable that will be used as the coroutine's entry + * point. + * + * @param token The completion token that will handle the notification that the + * thread of execution has completed. If @c R is @c void, the function + * signature of the completion handler must be: + * + * @code void handler(std::exception_ptr); @endcode + * Otherwise, the function signature of the completion handler must be: + * @code void handler(std::exception_ptr, R); @endcode + * + * + * @par Example + * @code + * asio::awaitable echo(tcp::socket socket) + * { + * std::size_t bytes_transferred = 0; + * + * try + * { + * char data[1024]; + * for (;;) + * { + * std::size_t n = co_await socket.async_read_some( + * asio::buffer(data), asio::use_awaitable); + * + * co_await asio::async_write(socket, + * asio::buffer(data, n), asio::use_awaitable); + * + * bytes_transferred += n; + * } + * } + * catch (const std::exception&) + * { + * } + * + * co_return bytes_transferred; + * } + * + * // ... + * + * asio::co_spawn(my_io_context, + * [socket = std::move(my_tcp_socket)]() mutable + * -> asio::awaitable + * { + * try + * { + * char data[1024]; + * for (;;) + * { + * std::size_t n = co_await socket.async_read_some( + * asio::buffer(data), asio::use_awaitable); + * + * co_await asio::async_write(socket, + * asio::buffer(data, n), asio::use_awaitable); + * } + * } + * catch (const std::exception& e) + * { + * std::cerr << "Exception: " << e.what() << "\n"; + * } + * }, asio::detached); + * @endcode + */ +template ::type>::type) CompletionToken + ASIO_DEFAULT_COMPLETION_TOKEN_TYPE( + typename ExecutionContext::executor_type)> +ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + typename detail::awaitable_signature::type>::type) +co_spawn(ExecutionContext& ctx, F&& f, + CompletionToken&& token + ASIO_DEFAULT_COMPLETION_TOKEN( + typename ExecutionContext::executor_type), + typename constraint< + is_convertible::value + >::type = 0); + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/co_spawn.hpp" + +#endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_CO_SPAWN_HPP diff --git a/third_party/asio/1.18.2/include/asio/completion_condition.hpp b/third_party/asio/1.18.2/include/asio/completion_condition.hpp new file mode 100644 index 000000000..cf8c7c2bc --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/completion_condition.hpp @@ -0,0 +1,218 @@ +// +// completion_condition.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_COMPLETION_CONDITION_HPP +#define ASIO_COMPLETION_CONDITION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail { + +// The default maximum number of bytes to transfer in a single operation. +enum default_max_transfer_size_t { default_max_transfer_size = 65536 }; + +// Adapt result of old-style completion conditions (which had a bool result +// where true indicated that the operation was complete). +inline std::size_t adapt_completion_condition_result(bool result) +{ + return result ? 0 : default_max_transfer_size; +} + +// Adapt result of current completion conditions (which have a size_t result +// where 0 means the operation is complete, and otherwise the result is the +// maximum number of bytes to transfer on the next underlying operation). +inline std::size_t adapt_completion_condition_result(std::size_t result) +{ + return result; +} + +class transfer_all_t +{ +public: + typedef std::size_t result_type; + + template + std::size_t operator()(const Error& err, std::size_t) + { + return !!err ? 0 : default_max_transfer_size; + } +}; + +class transfer_at_least_t +{ +public: + typedef std::size_t result_type; + + explicit transfer_at_least_t(std::size_t minimum) + : minimum_(minimum) + { + } + + template + std::size_t operator()(const Error& err, std::size_t bytes_transferred) + { + return (!!err || bytes_transferred >= minimum_) + ? 0 : default_max_transfer_size; + } + +private: + std::size_t minimum_; +}; + +class transfer_exactly_t +{ +public: + typedef std::size_t result_type; + + explicit transfer_exactly_t(std::size_t size) + : size_(size) + { + } + + template + std::size_t operator()(const Error& err, std::size_t bytes_transferred) + { + return (!!err || bytes_transferred >= size_) ? 0 : + (size_ - bytes_transferred < default_max_transfer_size + ? size_ - bytes_transferred : std::size_t(default_max_transfer_size)); + } + +private: + std::size_t size_; +}; + +} // namespace detail + +/** + * @defgroup completion_condition Completion Condition Function Objects + * + * Function objects used for determining when a read or write operation should + * complete. + */ +/*@{*/ + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until all of the data has been transferred, +/// or until an error occurs. +/** + * This function is used to create an object, of unspecified type, that meets + * CompletionCondition requirements. + * + * @par Example + * Reading until a buffer is full: + * @code + * boost::array buf; + * asio::error_code ec; + * std::size_t n = asio::read( + * sock, asio::buffer(buf), + * asio::transfer_all(), ec); + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * // n == 128 + * } + * @endcode + */ +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_all(); +#else +inline detail::transfer_all_t transfer_all() +{ + return detail::transfer_all_t(); +} +#endif + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until a minimum number of bytes has been +/// transferred, or until an error occurs. +/** + * This function is used to create an object, of unspecified type, that meets + * CompletionCondition requirements. + * + * @par Example + * Reading until a buffer is full or contains at least 64 bytes: + * @code + * boost::array buf; + * asio::error_code ec; + * std::size_t n = asio::read( + * sock, asio::buffer(buf), + * asio::transfer_at_least(64), ec); + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * // n >= 64 && n <= 128 + * } + * @endcode + */ +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_at_least(std::size_t minimum); +#else +inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum) +{ + return detail::transfer_at_least_t(minimum); +} +#endif + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until an exact number of bytes has been +/// transferred, or until an error occurs. +/** + * This function is used to create an object, of unspecified type, that meets + * CompletionCondition requirements. + * + * @par Example + * Reading until a buffer is full or contains exactly 64 bytes: + * @code + * boost::array buf; + * asio::error_code ec; + * std::size_t n = asio::read( + * sock, asio::buffer(buf), + * asio::transfer_exactly(64), ec); + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * // n == 64 + * } + * @endcode + */ +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_exactly(std::size_t size); +#else +inline detail::transfer_exactly_t transfer_exactly(std::size_t size) +{ + return detail::transfer_exactly_t(size); +} +#endif + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_COMPLETION_CONDITION_HPP diff --git a/third_party/asio/1.18.2/include/asio/compose.hpp b/third_party/asio/1.18.2/include/asio/compose.hpp new file mode 100644 index 000000000..3f6f1b889 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/compose.hpp @@ -0,0 +1,136 @@ +// +// compose.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_COMPOSE_HPP +#define ASIO_COMPOSE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) \ + || defined(GENERATING_DOCUMENTATION) + +/// Launch an asynchronous operation with a stateful implementation. +/** + * The async_compose function simplifies the implementation of composed + * asynchronous operations automatically wrapping a stateful function object + * with a conforming intermediate completion handler. + * + * @param implementation A function object that contains the implementation of + * the composed asynchronous operation. The first argument to the function + * object is a non-const reference to the enclosing intermediate completion + * handler. The remaining arguments are any arguments that originate from the + * completion handlers of any asynchronous operations performed by the + * implementation. + + * @param token The completion token. + * + * @param io_objects_or_executors Zero or more I/O objects or I/O executors for + * which outstanding work must be maintained. + * + * @par Example: + * + * @code struct async_echo_implementation + * { + * tcp::socket& socket_; + * asio::mutable_buffer buffer_; + * enum { starting, reading, writing } state_; + * + * template + * void operator()(Self& self, + * asio::error_code error = {}, + * std::size_t n = 0) + * { + * switch (state_) + * { + * case starting: + * state_ = reading; + * socket_.async_read_some( + * buffer_, std::move(self)); + * break; + * case reading: + * if (error) + * { + * self.complete(error, 0); + * } + * else + * { + * state_ = writing; + * asio::async_write(socket_, buffer_, + * asio::transfer_exactly(n), + * std::move(self)); + * } + * break; + * case writing: + * self.complete(error, n); + * break; + * } + * } + * }; + * + * template + * auto async_echo(tcp::socket& socket, + * asio::mutable_buffer buffer, + * CompletionToken&& token) -> + * typename asio::async_result< + * typename std::decay::type, + * void(asio::error_code, std::size_t)>::return_type + * { + * return asio::async_compose( + * async_echo_implementation{socket, buffer, + * async_echo_implementation::starting}, + * token, socket); + * } @endcode + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, Signature) +async_compose(ASIO_MOVE_ARG(Implementation) implementation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, + ASIO_MOVE_ARG(IoObjectsOrExecutors)... io_objects_or_executors); + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + +template +ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, Signature) +async_compose(ASIO_MOVE_ARG(Implementation) implementation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token); + +#define ASIO_PRIVATE_ASYNC_COMPOSE_DEF(n) \ + template \ + ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, Signature) \ + async_compose(ASIO_MOVE_ARG(Implementation) implementation, \ + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, \ + ASIO_VARIADIC_MOVE_PARAMS(n)); + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_ASYNC_COMPOSE_DEF) +#undef ASIO_PRIVATE_ASYNC_COMPOSE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/compose.hpp" + +#endif // ASIO_COMPOSE_HPP diff --git a/third_party/asio/1.18.2/include/asio/connect.hpp b/third_party/asio/1.18.2/include/asio/connect.hpp new file mode 100644 index 000000000..88cb21587 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/connect.hpp @@ -0,0 +1,1076 @@ +// +// connect.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_CONNECT_HPP +#define ASIO_CONNECT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/basic_socket.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +namespace detail +{ + char (&has_iterator_helper(...))[2]; + + template + char has_iterator_helper(T*, typename T::iterator* = 0); + + template + struct has_iterator_typedef + { + enum { value = (sizeof((has_iterator_helper)((T*)(0))) == 1) }; + }; +} // namespace detail + +/// Type trait used to determine whether a type is an endpoint sequence that can +/// be used with with @c connect and @c async_connect. +template +struct is_endpoint_sequence +{ +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true if the type may be used as an endpoint sequence. + static const bool value; +#else + enum + { + value = detail::has_iterator_typedef::value + }; +#endif +}; + +/** + * @defgroup connect asio::connect + * + * @brief The @c connect function is a composed operation that establishes a + * socket connection by trying each endpoint in a sequence. + */ +/*@{*/ + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @returns The successfully connected endpoint. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @par Example + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(my_context); + * asio::connect(s, r.resolve(q)); @endcode + */ +template +typename Protocol::endpoint connect(basic_socket& s, + const EndpointSequence& endpoints, + typename constraint::value>::type = 0); + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, the successfully connected endpoint. Otherwise, a + * default-constructed endpoint. + * + * @par Example + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(my_context); + * asio::error_code ec; + * asio::connect(s, r.resolve(q), ec); + * if (ec) + * { + * // An error occurred. + * } @endcode + */ +template +typename Protocol::endpoint connect(basic_socket& s, + const EndpointSequence& endpoints, asio::error_code& ec, + typename constraint::value>::type = 0); + +#if !defined(ASIO_NO_DEPRECATED) +/// (Deprecated: Use range overload.) Establishes a socket connection by trying +/// each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +Iterator connect(basic_socket& s, Iterator begin, + typename constraint::value>::type = 0); + +/// (Deprecated: Use range overload.) Establishes a socket connection by trying +/// each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +Iterator connect(basic_socket& s, + Iterator begin, asio::error_code& ec, + typename constraint::value>::type = 0); +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @returns An iterator denoting the successfully connected endpoint. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @par Example + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::resolver::results_type e = r.resolve(q); + * tcp::socket s(my_context); + * asio::connect(s, e.begin(), e.end()); @endcode + */ +template +Iterator connect(basic_socket& s, + Iterator begin, Iterator end); + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @par Example + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::resolver::results_type e = r.resolve(q); + * tcp::socket s(my_context); + * asio::error_code ec; + * asio::connect(s, e.begin(), e.end(), ec); + * if (ec) + * { + * // An error occurred. + * } @endcode + */ +template +Iterator connect(basic_socket& s, + Iterator begin, Iterator end, asio::error_code& ec); + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @returns The successfully connected endpoint. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(my_context); + * tcp::endpoint e = asio::connect(s, + * r.resolve(q), my_connect_condition()); + * std::cout << "Connected to: " << e << std::endl; @endcode + */ +template +typename Protocol::endpoint connect(basic_socket& s, + const EndpointSequence& endpoints, ConnectCondition connect_condition, + typename constraint::value>::type = 0); + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, the successfully connected endpoint. Otherwise, a + * default-constructed endpoint. + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(my_context); + * asio::error_code ec; + * tcp::endpoint e = asio::connect(s, + * r.resolve(q), my_connect_condition(), ec); + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * std::cout << "Connected to: " << e << std::endl; + * } @endcode + */ +template +typename Protocol::endpoint connect(basic_socket& s, + const EndpointSequence& endpoints, ConnectCondition connect_condition, + asio::error_code& ec, + typename constraint::value>::type = 0); + +#if !defined(ASIO_NO_DEPRECATED) +/// (Deprecated: Use range overload.) Establishes a socket connection by trying +/// each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +Iterator connect(basic_socket& s, + Iterator begin, ConnectCondition connect_condition, + typename constraint::value>::type = 0); + +/// (Deprecated: Use range overload.) Establishes a socket connection by trying +/// each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +Iterator connect(basic_socket& s, Iterator begin, + ConnectCondition connect_condition, asio::error_code& ec, + typename constraint::value>::type = 0); +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @returns An iterator denoting the successfully connected endpoint. + * + * @throws asio::system_error Thrown on failure. If the sequence is + * empty, the associated @c error_code is asio::error::not_found. + * Otherwise, contains the error from the last connection attempt. + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::resolver::results_type e = r.resolve(q); + * tcp::socket s(my_context); + * tcp::resolver::results_type::iterator i = asio::connect( + * s, e.begin(), e.end(), my_connect_condition()); + * std::cout << "Connected to: " << i->endpoint() << std::endl; @endcode + */ +template +Iterator connect(basic_socket& s, Iterator begin, + Iterator end, ConnectCondition connect_condition); + +/// Establishes a socket connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c connect member + * function, once for each endpoint in the sequence, until a connection is + * successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param ec Set to indicate what error occurred, if any. If the sequence is + * empty, set to asio::error::not_found. Otherwise, contains the error + * from the last connection attempt. + * + * @returns On success, an iterator denoting the successfully connected + * endpoint. Otherwise, the end iterator. + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::resolver::results_type e = r.resolve(q); + * tcp::socket s(my_context); + * asio::error_code ec; + * tcp::resolver::results_type::iterator i = asio::connect( + * s, e.begin(), e.end(), my_connect_condition()); + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * std::cout << "Connected to: " << i->endpoint() << std::endl; + * } @endcode + */ +template +Iterator connect(basic_socket& s, + Iterator begin, Iterator end, ConnectCondition connect_condition, + asio::error_code& ec); + +/*@}*/ + +/** + * @defgroup async_connect asio::async_connect + * + * @brief The @c async_connect function is a composed asynchronous operation + * that establishes a socket connection by trying each endpoint in a sequence. + */ +/*@{*/ + +/// Asynchronously establishes a socket connection by trying each endpoint in a +/// sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, the successfully connected endpoint. + * // Otherwise, a default-constructed endpoint. + * const typename Protocol::endpoint& endpoint + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(my_context); + * + * // ... + * + * r.async_resolve(q, resolve_handler); + * + * // ... + * + * void resolve_handler( + * const asio::error_code& ec, + * tcp::resolver::results_type results) + * { + * if (!ec) + * { + * asio::async_connect(s, results, connect_handler); + * } + * } + * + * // ... + * + * void connect_handler( + * const asio::error_code& ec, + * const tcp::endpoint& endpoint) + * { + * // ... + * } @endcode + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(RangeConnectHandler, + void (asio::error_code, typename Protocol::endpoint)) +async_connect(basic_socket& s, + const EndpointSequence& endpoints, + ASIO_MOVE_ARG(RangeConnectHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(Executor), + typename constraint::value>::type = 0); + +#if !defined(ASIO_NO_DEPRECATED) +/// (Deprecated: Use range overload.) Asynchronously establishes a socket +/// connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, an iterator denoting the successfully + * // connected endpoint. Otherwise, the end iterator. + * Iterator iterator + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, Iterator begin, + ASIO_MOVE_ARG(IteratorConnectHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(Executor), + typename constraint::value>::type = 0); +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Asynchronously establishes a socket connection by trying each endpoint in a +/// sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, an iterator denoting the successfully + * // connected endpoint. Otherwise, the end iterator. + * Iterator iterator + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code std::vector endpoints = ...; + * tcp::socket s(my_context); + * asio::async_connect(s, + * endpoints.begin(), endpoints.end(), + * connect_handler); + * + * // ... + * + * void connect_handler( + * const asio::error_code& ec, + * std::vector::iterator i) + * { + * // ... + * } @endcode + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, Iterator begin, Iterator end, + ASIO_MOVE_ARG(IteratorConnectHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(Executor)); + +/// Asynchronously establishes a socket connection by trying each endpoint in a +/// sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param endpoints A sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, an iterator denoting the successfully + * // connected endpoint. Otherwise, the end iterator. + * Iterator iterator + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(my_context); + * + * // ... + * + * r.async_resolve(q, resolve_handler); + * + * // ... + * + * void resolve_handler( + * const asio::error_code& ec, + * tcp::resolver::results_type results) + * { + * if (!ec) + * { + * asio::async_connect(s, results, + * my_connect_condition(), + * connect_handler); + * } + * } + * + * // ... + * + * void connect_handler( + * const asio::error_code& ec, + * const tcp::endpoint& endpoint) + * { + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * std::cout << "Connected to: " << endpoint << std::endl; + * } + * } @endcode + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(RangeConnectHandler, + void (asio::error_code, typename Protocol::endpoint)) +async_connect(basic_socket& s, + const EndpointSequence& endpoints, ConnectCondition connect_condition, + ASIO_MOVE_ARG(RangeConnectHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(Executor), + typename constraint::value>::type = 0); + +#if !defined(ASIO_NO_DEPRECATED) +/// (Deprecated: Use range overload.) Asynchronously establishes a socket +/// connection by trying each endpoint in a sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, an iterator denoting the successfully + * // connected endpoint. Otherwise, the end iterator. + * Iterator iterator + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @note This overload assumes that a default constructed object of type @c + * Iterator represents the end of the sequence. This is a valid assumption for + * iterator types such as @c asio::ip::tcp::resolver::iterator. + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, Iterator begin, + ConnectCondition connect_condition, + ASIO_MOVE_ARG(IteratorConnectHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(Executor), + typename constraint::value>::type = 0); +#endif // !defined(ASIO_NO_DEPRECATED) + +/// Asynchronously establishes a socket connection by trying each endpoint in a +/// sequence. +/** + * This function attempts to connect a socket to one of a sequence of + * endpoints. It does this by repeated calls to the socket's @c async_connect + * member function, once for each endpoint in the sequence, until a connection + * is successfully established. + * + * @param s The socket to be connected. If the socket is already open, it will + * be closed. + * + * @param begin An iterator pointing to the start of a sequence of endpoints. + * + * @param end An iterator pointing to the end of a sequence of endpoints. + * + * @param connect_condition A function object that is called prior to each + * connection attempt. The signature of the function object must be: + * @code bool connect_condition( + * const asio::error_code& ec, + * const typename Protocol::endpoint& next); @endcode + * The @c ec parameter contains the result from the most recent connect + * operation. Before the first connection attempt, @c ec is always set to + * indicate success. The @c next parameter is the next endpoint to be tried. + * The function object should return true if the next endpoint should be tried, + * and false if it should be skipped. + * + * @param handler The handler to be called when the connect operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * // Result of operation. if the sequence is empty, set to + * // asio::error::not_found. Otherwise, contains the + * // error from the last connection attempt. + * const asio::error_code& error, + * + * // On success, an iterator denoting the successfully + * // connected endpoint. Otherwise, the end iterator. + * Iterator iterator + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * The following connect condition function object can be used to output + * information about the individual connection attempts: + * @code struct my_connect_condition + * { + * bool operator()( + * const asio::error_code& ec, + * const::tcp::endpoint& next) + * { + * if (ec) std::cout << "Error: " << ec.message() << std::endl; + * std::cout << "Trying: " << next << std::endl; + * return true; + * } + * }; @endcode + * It would be used with the asio::connect function as follows: + * @code tcp::resolver r(my_context); + * tcp::resolver::query q("host", "service"); + * tcp::socket s(my_context); + * + * // ... + * + * r.async_resolve(q, resolve_handler); + * + * // ... + * + * void resolve_handler( + * const asio::error_code& ec, + * tcp::resolver::iterator i) + * { + * if (!ec) + * { + * tcp::resolver::iterator end; + * asio::async_connect(s, i, end, + * my_connect_condition(), + * connect_handler); + * } + * } + * + * // ... + * + * void connect_handler( + * const asio::error_code& ec, + * tcp::resolver::iterator i) + * { + * if (ec) + * { + * // An error occurred. + * } + * else + * { + * std::cout << "Connected to: " << i->endpoint() << std::endl; + * } + * } @endcode + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(IteratorConnectHandler, + void (asio::error_code, Iterator)) +async_connect(basic_socket& s, Iterator begin, + Iterator end, ConnectCondition connect_condition, + ASIO_MOVE_ARG(IteratorConnectHandler) handler + ASIO_DEFAULT_COMPLETION_TOKEN(Executor)); + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/connect.hpp" + +#endif diff --git a/third_party/asio/1.18.2/include/asio/coroutine.hpp b/third_party/asio/1.18.2/include/asio/coroutine.hpp new file mode 100644 index 000000000..bb0bdba84 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/coroutine.hpp @@ -0,0 +1,328 @@ +// +// coroutine.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_COROUTINE_HPP +#define ASIO_COROUTINE_HPP + +namespace asio { +namespace detail { + +class coroutine_ref; + +} // namespace detail + +/// Provides support for implementing stackless coroutines. +/** + * The @c coroutine class may be used to implement stackless coroutines. The + * class itself is used to store the current state of the coroutine. + * + * Coroutines are copy-constructible and assignable, and the space overhead is + * a single int. They can be used as a base class: + * + * @code class session : coroutine + * { + * ... + * }; @endcode + * + * or as a data member: + * + * @code class session + * { + * ... + * coroutine coro_; + * }; @endcode + * + * or even bound in as a function argument using lambdas or @c bind(). The + * important thing is that as the application maintains a copy of the object + * for as long as the coroutine must be kept alive. + * + * @par Pseudo-keywords + * + * A coroutine is used in conjunction with certain "pseudo-keywords", which + * are implemented as macros. These macros are defined by a header file: + * + * @code #include @endcode + * + * and may conversely be undefined as follows: + * + * @code #include @endcode + * + * reenter + * + * The @c reenter macro is used to define the body of a coroutine. It takes a + * single argument: a pointer or reference to a coroutine object. For example, + * if the base class is a coroutine object you may write: + * + * @code reenter (this) + * { + * ... coroutine body ... + * } @endcode + * + * and if a data member or other variable you can write: + * + * @code reenter (coro_) + * { + * ... coroutine body ... + * } @endcode + * + * When @c reenter is executed at runtime, control jumps to the location of the + * last @c yield or @c fork. + * + * The coroutine body may also be a single statement, such as: + * + * @code reenter (this) for (;;) + * { + * ... + * } @endcode + * + * @b Limitation: The @c reenter macro is implemented using a switch. This + * means that you must take care when using local variables within the + * coroutine body. The local variable is not allowed in a position where + * reentering the coroutine could bypass the variable definition. + * + * yield statement + * + * This form of the @c yield keyword is often used with asynchronous operations: + * + * @code yield socket_->async_read_some(buffer(*buffer_), *this); @endcode + * + * This divides into four logical steps: + * + * @li @c yield saves the current state of the coroutine. + * @li The statement initiates the asynchronous operation. + * @li The resume point is defined immediately following the statement. + * @li Control is transferred to the end of the coroutine body. + * + * When the asynchronous operation completes, the function object is invoked + * and @c reenter causes control to transfer to the resume point. It is + * important to remember to carry the coroutine state forward with the + * asynchronous operation. In the above snippet, the current class is a + * function object object with a coroutine object as base class or data member. + * + * The statement may also be a compound statement, and this permits us to + * define local variables with limited scope: + * + * @code yield + * { + * mutable_buffers_1 b = buffer(*buffer_); + * socket_->async_read_some(b, *this); + * } @endcode + * + * yield return expression ; + * + * This form of @c yield is often used in generators or coroutine-based parsers. + * For example, the function object: + * + * @code struct interleave : coroutine + * { + * istream& is1; + * istream& is2; + * char operator()(char c) + * { + * reenter (this) for (;;) + * { + * yield return is1.get(); + * yield return is2.get(); + * } + * } + * }; @endcode + * + * defines a trivial coroutine that interleaves the characters from two input + * streams. + * + * This type of @c yield divides into three logical steps: + * + * @li @c yield saves the current state of the coroutine. + * @li The resume point is defined immediately following the semicolon. + * @li The value of the expression is returned from the function. + * + * yield ; + * + * This form of @c yield is equivalent to the following steps: + * + * @li @c yield saves the current state of the coroutine. + * @li The resume point is defined immediately following the semicolon. + * @li Control is transferred to the end of the coroutine body. + * + * This form might be applied when coroutines are used for cooperative + * threading and scheduling is explicitly managed. For example: + * + * @code struct task : coroutine + * { + * ... + * void operator()() + * { + * reenter (this) + * { + * while (... not finished ...) + * { + * ... do something ... + * yield; + * ... do some more ... + * yield; + * } + * } + * } + * ... + * }; + * ... + * task t1, t2; + * for (;;) + * { + * t1(); + * t2(); + * } @endcode + * + * yield break ; + * + * The final form of @c yield is used to explicitly terminate the coroutine. + * This form is comprised of two steps: + * + * @li @c yield sets the coroutine state to indicate termination. + * @li Control is transferred to the end of the coroutine body. + * + * Once terminated, calls to is_complete() return true and the coroutine cannot + * be reentered. + * + * Note that a coroutine may also be implicitly terminated if the coroutine + * body is exited without a yield, e.g. by return, throw or by running to the + * end of the body. + * + * fork statement + * + * The @c fork pseudo-keyword is used when "forking" a coroutine, i.e. splitting + * it into two (or more) copies. One use of @c fork is in a server, where a new + * coroutine is created to handle each client connection: + * + * @code reenter (this) + * { + * do + * { + * socket_.reset(new tcp::socket(my_context_)); + * yield acceptor->async_accept(*socket_, *this); + * fork server(*this)(); + * } while (is_parent()); + * ... client-specific handling follows ... + * } @endcode + * + * The logical steps involved in a @c fork are: + * + * @li @c fork saves the current state of the coroutine. + * @li The statement creates a copy of the coroutine and either executes it + * immediately or schedules it for later execution. + * @li The resume point is defined immediately following the semicolon. + * @li For the "parent", control immediately continues from the next line. + * + * The functions is_parent() and is_child() can be used to differentiate + * between parent and child. You would use these functions to alter subsequent + * control flow. + * + * Note that @c fork doesn't do the actual forking by itself. It is the + * application's responsibility to create a clone of the coroutine and call it. + * The clone can be called immediately, as above, or scheduled for delayed + * execution using something like asio::post(). + * + * @par Alternate macro names + * + * If preferred, an application can use macro names that follow a more typical + * naming convention, rather than the pseudo-keywords. These are: + * + * @li @c ASIO_CORO_REENTER instead of @c reenter + * @li @c ASIO_CORO_YIELD instead of @c yield + * @li @c ASIO_CORO_FORK instead of @c fork + */ +class coroutine +{ +public: + /// Constructs a coroutine in its initial state. + coroutine() : value_(0) {} + + /// Returns true if the coroutine is the child of a fork. + bool is_child() const { return value_ < 0; } + + /// Returns true if the coroutine is the parent of a fork. + bool is_parent() const { return !is_child(); } + + /// Returns true if the coroutine has reached its terminal state. + bool is_complete() const { return value_ == -1; } + +private: + friend class detail::coroutine_ref; + int value_; +}; + + +namespace detail { + +class coroutine_ref +{ +public: + coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {} + coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {} + ~coroutine_ref() { if (!modified_) value_ = -1; } + operator int() const { return value_; } + int& operator=(int v) { modified_ = true; return value_ = v; } +private: + void operator=(const coroutine_ref&); + int& value_; + bool modified_; +}; + +} // namespace detail +} // namespace asio + +#define ASIO_CORO_REENTER(c) \ + switch (::asio::detail::coroutine_ref _coro_value = c) \ + case -1: if (_coro_value) \ + { \ + goto terminate_coroutine; \ + terminate_coroutine: \ + _coro_value = -1; \ + goto bail_out_of_coroutine; \ + bail_out_of_coroutine: \ + break; \ + } \ + else /* fall-through */ case 0: + +#define ASIO_CORO_YIELD_IMPL(n) \ + for (_coro_value = (n);;) \ + if (_coro_value == 0) \ + { \ + case (n): ; \ + break; \ + } \ + else \ + switch (_coro_value ? 0 : 1) \ + for (;;) \ + /* fall-through */ case -1: if (_coro_value) \ + goto terminate_coroutine; \ + else for (;;) \ + /* fall-through */ case 1: if (_coro_value) \ + goto bail_out_of_coroutine; \ + else /* fall-through */ case 0: + +#define ASIO_CORO_FORK_IMPL(n) \ + for (_coro_value = -(n);; _coro_value = (n)) \ + if (_coro_value == (n)) \ + { \ + case -(n): ; \ + break; \ + } \ + else + +#if defined(_MSC_VER) +# define ASIO_CORO_YIELD ASIO_CORO_YIELD_IMPL(__COUNTER__ + 1) +# define ASIO_CORO_FORK ASIO_CORO_FORK_IMPL(__COUNTER__ + 1) +#else // defined(_MSC_VER) +# define ASIO_CORO_YIELD ASIO_CORO_YIELD_IMPL(__LINE__) +# define ASIO_CORO_FORK ASIO_CORO_FORK_IMPL(__LINE__) +#endif // defined(_MSC_VER) + +#endif // ASIO_COROUTINE_HPP diff --git a/third_party/asio/1.18.2/include/asio/deadline_timer.hpp b/third_party/asio/1.18.2/include/asio/deadline_timer.hpp new file mode 100644 index 000000000..73872c8d3 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/deadline_timer.hpp @@ -0,0 +1,38 @@ +// +// deadline_timer.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DEADLINE_TIMER_HPP +#define ASIO_DEADLINE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) \ + || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. +#include "asio/basic_deadline_timer.hpp" + +#include + +namespace asio { + +/// Typedef for the typical usage of timer. Uses a UTC clock. +typedef basic_deadline_timer deadline_timer; + +} // namespace asio + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + // || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_DEADLINE_TIMER_HPP diff --git a/third_party/asio/1.18.2/include/asio/defer.hpp b/third_party/asio/1.18.2/include/asio/defer.hpp new file mode 100644 index 000000000..b0829e838 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/defer.hpp @@ -0,0 +1,130 @@ +// +// defer.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DEFER_HPP +#define ASIO_DEFER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution_context.hpp" +#include "asio/execution/executor.hpp" +#include "asio/is_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Submits a completion token or function object for execution. +/** + * This function submits an object for execution using the object's associated + * executor. The function object is queued for execution, and is never called + * from the current thread prior to returning from defer(). + * + * The use of @c defer(), rather than @ref post(), indicates the caller's + * preference that the executor defer the queueing of the function object. This + * may allow the executor to optimise queueing for cases when the function + * object represents a continuation of the current call context. + * + * This function has the following effects: + * + * @li Constructs a function object handler of type @c Handler, initialized + * with handler(forward(token)). + * + * @li Constructs an object @c result of type async_result, + * initializing the object as result(handler). + * + * @li Obtains the handler's associated executor object @c ex by performing + * get_associated_executor(handler). + * + * @li Obtains the handler's associated allocator object @c alloc by performing + * get_associated_allocator(handler). + * + * @li Performs ex.defer(std::move(handler), alloc). + * + * @li Returns result.get(). + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void()) defer( + ASIO_MOVE_ARG(CompletionToken) token); + +/// Submits a completion token or function object for execution. +/** + * This function submits an object for execution using the specified executor. + * The function object is queued for execution, and is never called from the + * current thread prior to returning from defer(). + * + * The use of @c defer(), rather than @ref post(), indicates the caller's + * preference that the executor defer the queueing of the function object. This + * may allow the executor to optimise queueing for cases when the function + * object represents a continuation of the current call context. + * + * This function has the following effects: + * + * @li Constructs a function object handler of type @c Handler, initialized + * with handler(forward(token)). + * + * @li Constructs an object @c result of type async_result, + * initializing the object as result(handler). + * + * @li Obtains the handler's associated executor object @c ex1 by performing + * get_associated_executor(handler). + * + * @li Creates a work object @c w by performing make_work(ex1). + * + * @li Obtains the handler's associated allocator object @c alloc by performing + * get_associated_allocator(handler). + * + * @li Constructs a function object @c f with a function call operator that + * performs ex1.dispatch(std::move(handler), alloc) followed by + * w.reset(). + * + * @li Performs Executor(ex).defer(std::move(f), alloc). + * + * @li Returns result.get(). + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void()) defer( + const Executor& ex, + ASIO_MOVE_ARG(CompletionToken) token + ASIO_DEFAULT_COMPLETION_TOKEN(Executor), + typename constraint< + execution::is_executor::value || is_executor::value + >::type = 0); + +/// Submits a completion token or function object for execution. +/** + * @returns defer(ctx.get_executor(), forward(token)). + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void()) defer( + ExecutionContext& ctx, + ASIO_MOVE_ARG(CompletionToken) token + ASIO_DEFAULT_COMPLETION_TOKEN( + typename ExecutionContext::executor_type), + typename constraint::value>::type = 0); + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/defer.hpp" + +#endif // ASIO_DEFER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detached.hpp b/third_party/asio/1.18.2/include/asio/detached.hpp new file mode 100644 index 000000000..e99875e36 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detached.hpp @@ -0,0 +1,112 @@ +// +// detached.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETACHED_HPP +#define ASIO_DETACHED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Class used to specify that an asynchronous operation is detached. +/** + + * The detached_t class is used to indicate that an asynchronous operation is + * detached. That is, there is no completion handler waiting for the + * operation's result. A detached_t object may be passed as a handler to an + * asynchronous operation, typically using the special value + * @c asio::detached. For example: + + * @code my_socket.async_send(my_buffer, asio::detached); + * @endcode + */ +class detached_t +{ +public: + /// Constructor. + ASIO_CONSTEXPR detached_t() + { + } + + /// Adapts an executor to add the @c detached_t completion token as the + /// default. + template + struct executor_with_default : InnerExecutor + { + /// Specify @c detached_t as the default completion token type. + typedef detached_t default_completion_token_type; + + /// Construct the adapted executor from the inner executor type. + executor_with_default(const InnerExecutor& ex) ASIO_NOEXCEPT + : InnerExecutor(ex) + { + } + + /// Convert the specified executor to the inner executor type, then use + /// that to construct the adapted executor. + template + executor_with_default(const OtherExecutor& ex, + typename constraint< + is_convertible::value + >::type = 0) ASIO_NOEXCEPT + : InnerExecutor(ex) + { + } + }; + + /// Type alias to adapt an I/O object to use @c detached_t as its + /// default completion token type. +#if defined(ASIO_HAS_ALIAS_TEMPLATES) \ + || defined(GENERATING_DOCUMENTATION) + template + using as_default_on_t = typename T::template rebind_executor< + executor_with_default >::other; +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + + /// Function helper to adapt an I/O object to use @c detached_t as its + /// default completion token type. + template + static typename decay::type::template rebind_executor< + executor_with_default::type::executor_type> + >::other + as_default_on(ASIO_MOVE_ARG(T) object) + { + return typename decay::type::template rebind_executor< + executor_with_default::type::executor_type> + >::other(ASIO_MOVE_CAST(T)(object)); + } +}; + +/// A special value, similar to std::nothrow. +/** + * See the documentation for asio::detached_t for a usage example. + */ +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr detached_t detached; +#elif defined(ASIO_MSVC) +__declspec(selectany) detached_t detached; +#endif + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/detached.hpp" + +#endif // ASIO_DETACHED_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/array.hpp b/third_party/asio/1.18.2/include/asio/detail/array.hpp new file mode 100644 index 000000000..ef230c321 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/array.hpp @@ -0,0 +1,38 @@ +// +// detail/array.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_ARRAY_HPP +#define ASIO_DETAIL_ARRAY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_ARRAY) +# include +#else // defined(ASIO_HAS_STD_ARRAY) +# include +#endif // defined(ASIO_HAS_STD_ARRAY) + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_STD_ARRAY) +using std::array; +#else // defined(ASIO_HAS_STD_ARRAY) +using boost::array; +#endif // defined(ASIO_HAS_STD_ARRAY) + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_ARRAY_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/array_fwd.hpp b/third_party/asio/1.18.2/include/asio/detail/array_fwd.hpp new file mode 100644 index 000000000..fb81dba5c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/array_fwd.hpp @@ -0,0 +1,34 @@ +// +// detail/array_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_ARRAY_FWD_HPP +#define ASIO_DETAIL_ARRAY_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +namespace boost { + +template +class array; + +} // namespace boost + +// Standard library components can't be forward declared, so we'll have to +// include the array header. Fortunately, it's fairly lightweight and doesn't +// add significantly to the compile time. +#if defined(ASIO_HAS_STD_ARRAY) +# include +#endif // defined(ASIO_HAS_STD_ARRAY) + +#endif // ASIO_DETAIL_ARRAY_FWD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/assert.hpp b/third_party/asio/1.18.2/include/asio/detail/assert.hpp new file mode 100644 index 000000000..8eaf59530 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/assert.hpp @@ -0,0 +1,32 @@ +// +// detail/assert.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_ASSERT_HPP +#define ASIO_DETAIL_ASSERT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_ASSERT) +# include +#else // defined(ASIO_HAS_BOOST_ASSERT) +# include +#endif // defined(ASIO_HAS_BOOST_ASSERT) + +#if defined(ASIO_HAS_BOOST_ASSERT) +# define ASIO_ASSERT(expr) BOOST_ASSERT(expr) +#else // defined(ASIO_HAS_BOOST_ASSERT) +# define ASIO_ASSERT(expr) assert(expr) +#endif // defined(ASIO_HAS_BOOST_ASSERT) + +#endif // ASIO_DETAIL_ASSERT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/atomic_count.hpp b/third_party/asio/1.18.2/include/asio/detail/atomic_count.hpp new file mode 100644 index 000000000..8409298a9 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/atomic_count.hpp @@ -0,0 +1,64 @@ +// +// detail/atomic_count.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_ATOMIC_COUNT_HPP +#define ASIO_DETAIL_ATOMIC_COUNT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +// Nothing to include. +#elif defined(ASIO_HAS_STD_ATOMIC) +# include +#else // defined(ASIO_HAS_STD_ATOMIC) +# include +#endif // defined(ASIO_HAS_STD_ATOMIC) + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) +typedef long atomic_count; +inline void increment(atomic_count& a, long b) { a += b; } +inline void ref_count_up(atomic_count& a) { ++a; } +inline bool ref_count_down(atomic_count& a) { return --a == 0; } +#elif defined(ASIO_HAS_STD_ATOMIC) +typedef std::atomic atomic_count; +inline void increment(atomic_count& a, long b) { a += b; } + +inline void ref_count_up(atomic_count& a) +{ + a.fetch_add(1, std::memory_order_relaxed); +} + +inline bool ref_count_down(atomic_count& a) +{ + if (a.fetch_sub(1, std::memory_order_release) == 1) + { + std::atomic_thread_fence(std::memory_order_acquire); + return true; + } + return false; +} +#else // defined(ASIO_HAS_STD_ATOMIC) +typedef boost::detail::atomic_count atomic_count; +inline void increment(atomic_count& a, long b) { while (b > 0) ++a, --b; } +inline void ref_count_up(atomic_count& a) { ++a; } +inline bool ref_count_down(atomic_count& a) { return --a == 0; } +#endif // defined(ASIO_HAS_STD_ATOMIC) + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_ATOMIC_COUNT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/base_from_completion_cond.hpp b/third_party/asio/1.18.2/include/asio/detail/base_from_completion_cond.hpp new file mode 100644 index 000000000..0139d2612 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/base_from_completion_cond.hpp @@ -0,0 +1,69 @@ +// +// detail/base_from_completion_cond.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP +#define ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/completion_condition.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class base_from_completion_cond +{ +protected: + explicit base_from_completion_cond(CompletionCondition& completion_condition) + : completion_condition_( + ASIO_MOVE_CAST(CompletionCondition)(completion_condition)) + { + } + + std::size_t check_for_completion( + const asio::error_code& ec, + std::size_t total_transferred) + { + return detail::adapt_completion_condition_result( + completion_condition_(ec, total_transferred)); + } + +private: + CompletionCondition completion_condition_; +}; + +template <> +class base_from_completion_cond +{ +protected: + explicit base_from_completion_cond(transfer_all_t) + { + } + + static std::size_t check_for_completion( + const asio::error_code& ec, + std::size_t total_transferred) + { + return transfer_all_t()(ec, total_transferred); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/bind_handler.hpp b/third_party/asio/1.18.2/include/asio/detail/bind_handler.hpp new file mode 100644 index 000000000..715105096 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/bind_handler.hpp @@ -0,0 +1,938 @@ +// +// detail/bind_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BIND_HANDLER_HPP +#define ASIO_DETAIL_BIND_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class binder1 +{ +public: + template + binder1(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1) + : handler_(ASIO_MOVE_CAST(T)(handler)), + arg1_(arg1) + { + } + + binder1(Handler& handler, const Arg1& arg1) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1) + { + } + +#if defined(ASIO_HAS_MOVE) + binder1(const binder1& other) + : handler_(other.handler_), + arg1_(other.arg1_) + { + } + + binder1(binder1&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(static_cast(arg1_)); + } + + void operator()() const + { + handler_(arg1_); + } + +//private: + Handler handler_; + Arg1 arg1_; +}; + +template +inline asio_handler_allocate_is_deprecated +asio_handler_allocate(std::size_t size, + binder1* this_handler) +{ +#if defined(ASIO_NO_DEPRECATED) + asio_handler_alloc_helpers::allocate(size, this_handler->handler_); + return asio_handler_allocate_is_no_longer_used(); +#else // defined(ASIO_NO_DEPRECATED) + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_deallocate_is_deprecated +asio_handler_deallocate(void* pointer, std::size_t size, + binder1* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_deallocate_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline bool asio_handler_is_continuation( + binder1* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(Function& function, + binder1* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(const Function& function, + binder1* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline binder1::type, Arg1> bind_handler( + ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1) +{ + return binder1::type, Arg1>(0, + ASIO_MOVE_CAST(Handler)(handler), arg1); +} + +template +class binder2 +{ +public: + template + binder2(int, ASIO_MOVE_ARG(T) handler, + const Arg1& arg1, const Arg2& arg2) + : handler_(ASIO_MOVE_CAST(T)(handler)), + arg1_(arg1), + arg2_(arg2) + { + } + + binder2(Handler& handler, const Arg1& arg1, const Arg2& arg2) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1), + arg2_(arg2) + { + } + +#if defined(ASIO_HAS_MOVE) + binder2(const binder2& other) + : handler_(other.handler_), + arg1_(other.arg1_), + arg2_(other.arg2_) + { + } + + binder2(binder2&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), + arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(static_cast(arg1_), + static_cast(arg2_)); + } + + void operator()() const + { + handler_(arg1_, arg2_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; +}; + +template +inline asio_handler_allocate_is_deprecated +asio_handler_allocate(std::size_t size, + binder2* this_handler) +{ +#if defined(ASIO_NO_DEPRECATED) + asio_handler_alloc_helpers::allocate(size, this_handler->handler_); + return asio_handler_allocate_is_no_longer_used(); +#else // defined(ASIO_NO_DEPRECATED) + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_deallocate_is_deprecated +asio_handler_deallocate(void* pointer, std::size_t size, + binder2* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_deallocate_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline bool asio_handler_is_continuation( + binder2* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(Function& function, + binder2* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(const Function& function, + binder2* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline binder2::type, Arg1, Arg2> bind_handler( + ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, const Arg2& arg2) +{ + return binder2::type, Arg1, Arg2>(0, + ASIO_MOVE_CAST(Handler)(handler), arg1, arg2); +} + +template +class binder3 +{ +public: + template + binder3(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3) + : handler_(ASIO_MOVE_CAST(T)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3) + { + } + + binder3(Handler& handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3) + { + } + +#if defined(ASIO_HAS_MOVE) + binder3(const binder3& other) + : handler_(other.handler_), + arg1_(other.arg1_), + arg2_(other.arg2_), + arg3_(other.arg3_) + { + } + + binder3(binder3&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), + arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)), + arg3_(ASIO_MOVE_CAST(Arg3)(other.arg3_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(static_cast(arg1_), + static_cast(arg2_), static_cast(arg3_)); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; +}; + +template +inline asio_handler_allocate_is_deprecated +asio_handler_allocate(std::size_t size, + binder3* this_handler) +{ +#if defined(ASIO_NO_DEPRECATED) + asio_handler_alloc_helpers::allocate(size, this_handler->handler_); + return asio_handler_allocate_is_no_longer_used(); +#else // defined(ASIO_NO_DEPRECATED) + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_deallocate_is_deprecated +asio_handler_deallocate(void* pointer, std::size_t size, + binder3* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_deallocate_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline bool asio_handler_is_continuation( + binder3* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(Function& function, + binder3* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(const Function& function, + binder3* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline binder3::type, Arg1, Arg2, Arg3> bind_handler( + ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3) +{ + return binder3::type, Arg1, Arg2, Arg3>(0, + ASIO_MOVE_CAST(Handler)(handler), arg1, arg2, arg3); +} + +template +class binder4 +{ +public: + template + binder4(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) + : handler_(ASIO_MOVE_CAST(T)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4) + { + } + + binder4(Handler& handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4) + { + } + +#if defined(ASIO_HAS_MOVE) + binder4(const binder4& other) + : handler_(other.handler_), + arg1_(other.arg1_), + arg2_(other.arg2_), + arg3_(other.arg3_), + arg4_(other.arg4_) + { + } + + binder4(binder4&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), + arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)), + arg3_(ASIO_MOVE_CAST(Arg3)(other.arg3_)), + arg4_(ASIO_MOVE_CAST(Arg4)(other.arg4_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(static_cast(arg1_), + static_cast(arg2_), static_cast(arg3_), + static_cast(arg4_)); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; +}; + +template +inline asio_handler_allocate_is_deprecated +asio_handler_allocate(std::size_t size, + binder4* this_handler) +{ +#if defined(ASIO_NO_DEPRECATED) + asio_handler_alloc_helpers::allocate(size, this_handler->handler_); + return asio_handler_allocate_is_no_longer_used(); +#else // defined(ASIO_NO_DEPRECATED) + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_deallocate_is_deprecated +asio_handler_deallocate(void* pointer, std::size_t size, + binder4* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_deallocate_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline bool asio_handler_is_continuation( + binder4* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(Function& function, + binder4* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(const Function& function, + binder4* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline binder4::type, Arg1, Arg2, Arg3, Arg4> +bind_handler(ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) +{ + return binder4::type, Arg1, Arg2, Arg3, Arg4>(0, + ASIO_MOVE_CAST(Handler)(handler), arg1, arg2, arg3, arg4); +} + +template +class binder5 +{ +public: + template + binder5(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) + : handler_(ASIO_MOVE_CAST(T)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4), + arg5_(arg5) + { + } + + binder5(Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4), + arg5_(arg5) + { + } + +#if defined(ASIO_HAS_MOVE) + binder5(const binder5& other) + : handler_(other.handler_), + arg1_(other.arg1_), + arg2_(other.arg2_), + arg3_(other.arg3_), + arg4_(other.arg4_), + arg5_(other.arg5_) + { + } + + binder5(binder5&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), + arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)), + arg3_(ASIO_MOVE_CAST(Arg3)(other.arg3_)), + arg4_(ASIO_MOVE_CAST(Arg4)(other.arg4_)), + arg5_(ASIO_MOVE_CAST(Arg5)(other.arg5_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(static_cast(arg1_), + static_cast(arg2_), static_cast(arg3_), + static_cast(arg4_), static_cast(arg5_)); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; + Arg5 arg5_; +}; + +template +inline asio_handler_allocate_is_deprecated +asio_handler_allocate(std::size_t size, + binder5* this_handler) +{ +#if defined(ASIO_NO_DEPRECATED) + asio_handler_alloc_helpers::allocate(size, this_handler->handler_); + return asio_handler_allocate_is_no_longer_used(); +#else // defined(ASIO_NO_DEPRECATED) + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_deallocate_is_deprecated +asio_handler_deallocate(void* pointer, std::size_t size, + binder5* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_deallocate_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline bool asio_handler_is_continuation( + binder5* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(Function& function, + binder5* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(const Function& function, + binder5* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline binder5::type, Arg1, Arg2, Arg3, Arg4, Arg5> +bind_handler(ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, + const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) +{ + return binder5::type, Arg1, Arg2, Arg3, Arg4, Arg5>(0, + ASIO_MOVE_CAST(Handler)(handler), arg1, arg2, arg3, arg4, arg5); +} + +#if defined(ASIO_HAS_MOVE) + +template +class move_binder1 +{ +public: + move_binder1(int, ASIO_MOVE_ARG(Handler) handler, + ASIO_MOVE_ARG(Arg1) arg1) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(ASIO_MOVE_CAST(Arg1)(arg1)) + { + } + + move_binder1(move_binder1&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)) + { + } + + void operator()() + { + handler_(ASIO_MOVE_CAST(Arg1)(arg1_)); + } + +//private: + Handler handler_; + Arg1 arg1_; +}; + +template +inline asio_handler_allocate_is_deprecated +asio_handler_allocate(std::size_t size, + move_binder1* this_handler) +{ +#if defined(ASIO_NO_DEPRECATED) + asio_handler_alloc_helpers::allocate(size, this_handler->handler_); + return asio_handler_allocate_is_no_longer_used(); +#else // defined(ASIO_NO_DEPRECATED) + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_deallocate_is_deprecated +asio_handler_deallocate(void* pointer, std::size_t size, + move_binder1* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_deallocate_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline bool asio_handler_is_continuation( + move_binder1* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(ASIO_MOVE_ARG(Function) function, + move_binder1* this_handler) +{ + asio_handler_invoke_helpers::invoke( + ASIO_MOVE_CAST(Function)(function), this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +class move_binder2 +{ +public: + move_binder2(int, ASIO_MOVE_ARG(Handler) handler, + const Arg1& arg1, ASIO_MOVE_ARG(Arg2) arg2) + : handler_(ASIO_MOVE_CAST(Handler)(handler)), + arg1_(arg1), + arg2_(ASIO_MOVE_CAST(Arg2)(arg2)) + { + } + + move_binder2(move_binder2&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), + arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)) + { + } + + void operator()() + { + handler_(static_cast(arg1_), + ASIO_MOVE_CAST(Arg2)(arg2_)); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; +}; + +template +inline asio_handler_allocate_is_deprecated +asio_handler_allocate(std::size_t size, + move_binder2* this_handler) +{ +#if defined(ASIO_NO_DEPRECATED) + asio_handler_alloc_helpers::allocate(size, this_handler->handler_); + return asio_handler_allocate_is_no_longer_used(); +#else // defined(ASIO_NO_DEPRECATED) + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_deallocate_is_deprecated +asio_handler_deallocate(void* pointer, std::size_t size, + move_binder2* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_deallocate_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline bool asio_handler_is_continuation( + move_binder2* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->handler_); +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(ASIO_MOVE_ARG(Function) function, + move_binder2* this_handler) +{ + asio_handler_invoke_helpers::invoke( + ASIO_MOVE_CAST(Function)(function), this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +#endif // defined(ASIO_HAS_MOVE) + +} // namespace detail + +template +struct associated_allocator, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::binder1& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_allocator, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::binder2& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor, Executor> + : detail::associated_executor_forwarding_base +{ + typedef typename associated_executor::type type; + + static type get(const detail::binder1& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +template +struct associated_executor, Executor> + : detail::associated_executor_forwarding_base +{ + typedef typename associated_executor::type type; + + static type get(const detail::binder2& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#if defined(ASIO_HAS_MOVE) + +template +struct associated_allocator, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::move_binder1& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_allocator< + detail::move_binder2, Allocator> +{ + typedef typename associated_allocator::type type; + + static type get(const detail::move_binder2& h, + const Allocator& a = Allocator()) ASIO_NOEXCEPT + { + return associated_allocator::get(h.handler_, a); + } +}; + +template +struct associated_executor, Executor> + : detail::associated_executor_forwarding_base +{ + typedef typename associated_executor::type type; + + static type get(const detail::move_binder1& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +template +struct associated_executor, Executor> + : detail::associated_executor_forwarding_base +{ + typedef typename associated_executor::type type; + + static type get(const detail::move_binder2& h, + const Executor& ex = Executor()) ASIO_NOEXCEPT + { + return associated_executor::get(h.handler_, ex); + } +}; + +#endif // defined(ASIO_HAS_MOVE) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BIND_HANDLER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/blocking_executor_op.hpp b/third_party/asio/1.18.2/include/asio/detail/blocking_executor_op.hpp new file mode 100644 index 000000000..d58df20d7 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/blocking_executor_op.hpp @@ -0,0 +1,107 @@ +// +// detail/blocking_executor_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BLOCKING_EXECUTOR_OP_HPP +#define ASIO_DETAIL_BLOCKING_EXECUTOR_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/event.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/scheduler_operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class blocking_executor_op_base : public Operation +{ +public: + blocking_executor_op_base(typename Operation::func_type complete_func) + : Operation(complete_func), + is_complete_(false) + { + } + + void wait() + { + asio::detail::mutex::scoped_lock lock(mutex_); + while (!is_complete_) + event_.wait(lock); + } + +protected: + struct do_complete_cleanup + { + ~do_complete_cleanup() + { + asio::detail::mutex::scoped_lock lock(op_->mutex_); + op_->is_complete_ = true; + op_->event_.unlock_and_signal_one_for_destruction(lock); + } + + blocking_executor_op_base* op_; + }; + +private: + asio::detail::mutex mutex_; + asio::detail::event event_; + bool is_complete_; +}; + +template +class blocking_executor_op : public blocking_executor_op_base +{ +public: + blocking_executor_op(Handler& h) + : blocking_executor_op_base(&blocking_executor_op::do_complete), + handler_(h) + { + } + + static void do_complete(void* owner, Operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + blocking_executor_op* o(static_cast(base)); + + typename blocking_executor_op_base::do_complete_cleanup + on_exit = { o }; + (void)on_exit; + + ASIO_HANDLER_COMPLETION((*o)); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN(()); + asio_handler_invoke_helpers::invoke(o->handler_, o->handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler& handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BLOCKING_EXECUTOR_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/buffer_resize_guard.hpp b/third_party/asio/1.18.2/include/asio/detail/buffer_resize_guard.hpp new file mode 100644 index 000000000..00f4aa34c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/buffer_resize_guard.hpp @@ -0,0 +1,66 @@ +// +// detail/buffer_resize_guard.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP +#define ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/limits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper class to manage buffer resizing in an exception safe way. +template +class buffer_resize_guard +{ +public: + // Constructor. + buffer_resize_guard(Buffer& buffer) + : buffer_(buffer), + old_size_(buffer.size()) + { + } + + // Destructor rolls back the buffer resize unless commit was called. + ~buffer_resize_guard() + { + if (old_size_ != (std::numeric_limits::max)()) + { + buffer_.resize(old_size_); + } + } + + // Commit the resize transaction. + void commit() + { + old_size_ = (std::numeric_limits::max)(); + } + +private: + // The buffer being managed. + Buffer& buffer_; + + // The size of the buffer at the time the guard was constructed. + size_t old_size_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/buffer_sequence_adapter.hpp b/third_party/asio/1.18.2/include/asio/detail/buffer_sequence_adapter.hpp new file mode 100644 index 000000000..89b05ce37 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/buffer_sequence_adapter.hpp @@ -0,0 +1,650 @@ +// +// detail/buffer_sequence_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP +#define ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/buffer.hpp" +#include "asio/detail/array_fwd.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class buffer_sequence_adapter_base +{ +#if defined(ASIO_WINDOWS_RUNTIME) +public: + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 1 }; + +protected: + typedef Windows::Storage::Streams::IBuffer^ native_buffer_type; + + ASIO_DECL static void init_native_buffer( + native_buffer_type& buf, + const asio::mutable_buffer& buffer); + + ASIO_DECL static void init_native_buffer( + native_buffer_type& buf, + const asio::const_buffer& buffer); +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +public: + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; + +protected: + typedef WSABUF native_buffer_type; + + static void init_native_buffer(WSABUF& buf, + const asio::mutable_buffer& buffer) + { + buf.buf = static_cast(buffer.data()); + buf.len = static_cast(buffer.size()); + } + + static void init_native_buffer(WSABUF& buf, + const asio::const_buffer& buffer) + { + buf.buf = const_cast(static_cast(buffer.data())); + buf.len = static_cast(buffer.size()); + } +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +public: + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; + +protected: + typedef iovec native_buffer_type; + + static void init_iov_base(void*& base, void* addr) + { + base = addr; + } + + template + static void init_iov_base(T& base, void* addr) + { + base = static_cast(addr); + } + + static void init_native_buffer(iovec& iov, + const asio::mutable_buffer& buffer) + { + init_iov_base(iov.iov_base, buffer.data()); + iov.iov_len = buffer.size(); + } + + static void init_native_buffer(iovec& iov, + const asio::const_buffer& buffer) + { + init_iov_base(iov.iov_base, const_cast(buffer.data())); + iov.iov_len = buffer.size(); + } +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +}; + +// Helper class to translate buffers into the native buffer representation. +template +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + enum { is_single_buffer = false }; + + explicit buffer_sequence_adapter(const Buffers& buffer_sequence) + : count_(0), total_buffer_size_(0) + { + buffer_sequence_adapter::init( + asio::buffer_sequence_begin(buffer_sequence), + asio::buffer_sequence_end(buffer_sequence)); + } + + native_buffer_type* buffers() + { + return buffers_; + } + + std::size_t count() const + { + return count_; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const Buffers& buffer_sequence) + { + return buffer_sequence_adapter::all_empty( + asio::buffer_sequence_begin(buffer_sequence), + asio::buffer_sequence_end(buffer_sequence)); + } + + static void validate(const Buffers& buffer_sequence) + { + buffer_sequence_adapter::validate( + asio::buffer_sequence_begin(buffer_sequence), + asio::buffer_sequence_end(buffer_sequence)); + } + + static Buffer first(const Buffers& buffer_sequence) + { + return buffer_sequence_adapter::first( + asio::buffer_sequence_begin(buffer_sequence), + asio::buffer_sequence_end(buffer_sequence)); + } + + enum { linearisation_storage_size = 8192 }; + + static Buffer linearise(const Buffers& buffer_sequence, + const asio::mutable_buffer& storage) + { + return buffer_sequence_adapter::linearise( + asio::buffer_sequence_begin(buffer_sequence), + asio::buffer_sequence_end(buffer_sequence), storage); + } + +private: + template + void init(Iterator begin, Iterator end) + { + Iterator iter = begin; + for (; iter != end && count_ < max_buffers; ++iter, ++count_) + { + Buffer buffer(*iter); + init_native_buffer(buffers_[count_], buffer); + total_buffer_size_ += buffer.size(); + } + } + + template + static bool all_empty(Iterator begin, Iterator end) + { + Iterator iter = begin; + std::size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + if (Buffer(*iter).size() > 0) + return false; + return true; + } + + template + static void validate(Iterator begin, Iterator end) + { + Iterator iter = begin; + for (; iter != end; ++iter) + { + Buffer buffer(*iter); + buffer.data(); + } + } + + template + static Buffer first(Iterator begin, Iterator end) + { + Iterator iter = begin; + for (; iter != end; ++iter) + { + Buffer buffer(*iter); + if (buffer.size() != 0) + return buffer; + } + return Buffer(); + } + + template + static Buffer linearise(Iterator begin, Iterator end, + const asio::mutable_buffer& storage) + { + asio::mutable_buffer unused_storage = storage; + Iterator iter = begin; + while (iter != end && unused_storage.size() != 0) + { + Buffer buffer(*iter); + ++iter; + if (buffer.size() == 0) + continue; + if (unused_storage.size() == storage.size()) + { + if (iter == end) + return buffer; + if (buffer.size() >= unused_storage.size()) + return buffer; + } + unused_storage += asio::buffer_copy(unused_storage, buffer); + } + return Buffer(storage.data(), storage.size() - unused_storage.size()); + } + + native_buffer_type buffers_[max_buffers]; + std::size_t count_; + std::size_t total_buffer_size_; +}; + +template +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + enum { is_single_buffer = true }; + + explicit buffer_sequence_adapter( + const asio::mutable_buffer& buffer_sequence) + { + init_native_buffer(buffer_, Buffer(buffer_sequence)); + total_buffer_size_ = buffer_sequence.size(); + } + + native_buffer_type* buffers() + { + return &buffer_; + } + + std::size_t count() const + { + return 1; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const asio::mutable_buffer& buffer_sequence) + { + return buffer_sequence.size() == 0; + } + + static void validate(const asio::mutable_buffer& buffer_sequence) + { + buffer_sequence.data(); + } + + static Buffer first(const asio::mutable_buffer& buffer_sequence) + { + return Buffer(buffer_sequence); + } + + enum { linearisation_storage_size = 1 }; + + static Buffer linearise(const asio::mutable_buffer& buffer_sequence, + const Buffer&) + { + return Buffer(buffer_sequence); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +template +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + enum { is_single_buffer = true }; + + explicit buffer_sequence_adapter( + const asio::const_buffer& buffer_sequence) + { + init_native_buffer(buffer_, Buffer(buffer_sequence)); + total_buffer_size_ = buffer_sequence.size(); + } + + native_buffer_type* buffers() + { + return &buffer_; + } + + std::size_t count() const + { + return 1; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const asio::const_buffer& buffer_sequence) + { + return buffer_sequence.size() == 0; + } + + static void validate(const asio::const_buffer& buffer_sequence) + { + buffer_sequence.data(); + } + + static Buffer first(const asio::const_buffer& buffer_sequence) + { + return Buffer(buffer_sequence); + } + + enum { linearisation_storage_size = 1 }; + + static Buffer linearise(const asio::const_buffer& buffer_sequence, + const Buffer&) + { + return Buffer(buffer_sequence); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +#if !defined(ASIO_NO_DEPRECATED) + +template +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + enum { is_single_buffer = true }; + + explicit buffer_sequence_adapter( + const asio::mutable_buffers_1& buffer_sequence) + { + init_native_buffer(buffer_, Buffer(buffer_sequence)); + total_buffer_size_ = buffer_sequence.size(); + } + + native_buffer_type* buffers() + { + return &buffer_; + } + + std::size_t count() const + { + return 1; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const asio::mutable_buffers_1& buffer_sequence) + { + return buffer_sequence.size() == 0; + } + + static void validate(const asio::mutable_buffers_1& buffer_sequence) + { + buffer_sequence.data(); + } + + static Buffer first(const asio::mutable_buffers_1& buffer_sequence) + { + return Buffer(buffer_sequence); + } + + enum { linearisation_storage_size = 1 }; + + static Buffer linearise(const asio::mutable_buffers_1& buffer_sequence, + const Buffer&) + { + return Buffer(buffer_sequence); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +template +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + enum { is_single_buffer = true }; + + explicit buffer_sequence_adapter( + const asio::const_buffers_1& buffer_sequence) + { + init_native_buffer(buffer_, Buffer(buffer_sequence)); + total_buffer_size_ = buffer_sequence.size(); + } + + native_buffer_type* buffers() + { + return &buffer_; + } + + std::size_t count() const + { + return 1; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const asio::const_buffers_1& buffer_sequence) + { + return buffer_sequence.size() == 0; + } + + static void validate(const asio::const_buffers_1& buffer_sequence) + { + buffer_sequence.data(); + } + + static Buffer first(const asio::const_buffers_1& buffer_sequence) + { + return Buffer(buffer_sequence); + } + + enum { linearisation_storage_size = 1 }; + + static Buffer linearise(const asio::const_buffers_1& buffer_sequence, + const Buffer&) + { + return Buffer(buffer_sequence); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +template +class buffer_sequence_adapter > + : buffer_sequence_adapter_base +{ +public: + enum { is_single_buffer = false }; + + explicit buffer_sequence_adapter( + const boost::array& buffer_sequence) + { + init_native_buffer(buffers_[0], Buffer(buffer_sequence[0])); + init_native_buffer(buffers_[1], Buffer(buffer_sequence[1])); + total_buffer_size_ = buffer_sequence[0].size() + buffer_sequence[1].size(); + } + + native_buffer_type* buffers() + { + return buffers_; + } + + std::size_t count() const + { + return 2; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const boost::array& buffer_sequence) + { + return buffer_sequence[0].size() == 0 && buffer_sequence[1].size() == 0; + } + + static void validate(const boost::array& buffer_sequence) + { + buffer_sequence[0].data(); + buffer_sequence[1].data(); + } + + static Buffer first(const boost::array& buffer_sequence) + { + return Buffer(buffer_sequence[0].size() != 0 + ? buffer_sequence[0] : buffer_sequence[1]); + } + + enum { linearisation_storage_size = 8192 }; + + static Buffer linearise(const boost::array& buffer_sequence, + const asio::mutable_buffer& storage) + { + if (buffer_sequence[0].size() == 0) + return Buffer(buffer_sequence[1]); + if (buffer_sequence[1].size() == 0) + return Buffer(buffer_sequence[0]); + return Buffer(storage.data(), + asio::buffer_copy(storage, buffer_sequence)); + } + +private: + native_buffer_type buffers_[2]; + std::size_t total_buffer_size_; +}; + +#if defined(ASIO_HAS_STD_ARRAY) + +template +class buffer_sequence_adapter > + : buffer_sequence_adapter_base +{ +public: + enum { is_single_buffer = false }; + + explicit buffer_sequence_adapter( + const std::array& buffer_sequence) + { + init_native_buffer(buffers_[0], Buffer(buffer_sequence[0])); + init_native_buffer(buffers_[1], Buffer(buffer_sequence[1])); + total_buffer_size_ = buffer_sequence[0].size() + buffer_sequence[1].size(); + } + + native_buffer_type* buffers() + { + return buffers_; + } + + std::size_t count() const + { + return 2; + } + + std::size_t total_size() const + { + return total_buffer_size_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const std::array& buffer_sequence) + { + return buffer_sequence[0].size() == 0 && buffer_sequence[1].size() == 0; + } + + static void validate(const std::array& buffer_sequence) + { + buffer_sequence[0].data(); + buffer_sequence[1].data(); + } + + static Buffer first(const std::array& buffer_sequence) + { + return Buffer(buffer_sequence[0].size() != 0 + ? buffer_sequence[0] : buffer_sequence[1]); + } + + enum { linearisation_storage_size = 8192 }; + + static Buffer linearise(const std::array& buffer_sequence, + const asio::mutable_buffer& storage) + { + if (buffer_sequence[0].size() == 0) + return Buffer(buffer_sequence[1]); + if (buffer_sequence[1].size() == 0) + return Buffer(buffer_sequence[0]); + return Buffer(storage.data(), + asio::buffer_copy(storage, buffer_sequence)); + } + +private: + native_buffer_type buffers_[2]; + std::size_t total_buffer_size_; +}; + +#endif // defined(ASIO_HAS_STD_ARRAY) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/buffer_sequence_adapter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/buffered_stream_storage.hpp b/third_party/asio/1.18.2/include/asio/detail/buffered_stream_storage.hpp new file mode 100644 index 000000000..04cef8250 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/buffered_stream_storage.hpp @@ -0,0 +1,126 @@ +// +// detail/buffered_stream_storage.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP +#define ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/buffer.hpp" +#include "asio/detail/assert.hpp" +#include +#include +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class buffered_stream_storage +{ +public: + // The type of the bytes stored in the buffer. + typedef unsigned char byte_type; + + // The type used for offsets into the buffer. + typedef std::size_t size_type; + + // Constructor. + explicit buffered_stream_storage(std::size_t buffer_capacity) + : begin_offset_(0), + end_offset_(0), + buffer_(buffer_capacity) + { + } + + /// Clear the buffer. + void clear() + { + begin_offset_ = 0; + end_offset_ = 0; + } + + // Return a pointer to the beginning of the unread data. + mutable_buffer data() + { + return asio::buffer(buffer_) + begin_offset_; + } + + // Return a pointer to the beginning of the unread data. + const_buffer data() const + { + return asio::buffer(buffer_) + begin_offset_; + } + + // Is there no unread data in the buffer. + bool empty() const + { + return begin_offset_ == end_offset_; + } + + // Return the amount of unread data the is in the buffer. + size_type size() const + { + return end_offset_ - begin_offset_; + } + + // Resize the buffer to the specified length. + void resize(size_type length) + { + ASIO_ASSERT(length <= capacity()); + if (begin_offset_ + length <= capacity()) + { + end_offset_ = begin_offset_ + length; + } + else + { + using namespace std; // For memmove. + memmove(&buffer_[0], &buffer_[0] + begin_offset_, size()); + end_offset_ = length; + begin_offset_ = 0; + } + } + + // Return the maximum size for data in the buffer. + size_type capacity() const + { + return buffer_.size(); + } + + // Consume multiple bytes from the beginning of the buffer. + void consume(size_type count) + { + ASIO_ASSERT(begin_offset_ + count <= end_offset_); + begin_offset_ += count; + if (empty()) + clear(); + } + +private: + // The offset to the beginning of the unread data. + size_type begin_offset_; + + // The offset to the end of the unread data. + size_type end_offset_; + + // The data in the buffer. + std::vector buffer_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/bulk_executor_op.hpp b/third_party/asio/1.18.2/include/asio/detail/bulk_executor_op.hpp new file mode 100644 index 000000000..8f6921b9f --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/bulk_executor_op.hpp @@ -0,0 +1,88 @@ +// +// detail/bulk_executor_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BULK_EXECUTOR_OP_HPP +#define ASIO_DETAIL_BULK_EXECUTOR_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/scheduler_operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class bulk_executor_op : public Operation +{ +public: + ASIO_DEFINE_HANDLER_ALLOCATOR_PTR(bulk_executor_op); + + template + bulk_executor_op(ASIO_MOVE_ARG(H) h, + const Alloc& allocator, std::size_t i) + : Operation(&bulk_executor_op::do_complete), + handler_(ASIO_MOVE_CAST(H)(h)), + allocator_(allocator), + index_(i) + { + } + + static void do_complete(void* owner, Operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + bulk_executor_op* o(static_cast(base)); + Alloc allocator(o->allocator_); + ptr p = { detail::addressof(allocator), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 handler(o->handler_, o->index_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN(()); + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + Alloc allocator_; + std::size_t index_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BULK_EXECUTOR_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/call_stack.hpp b/third_party/asio/1.18.2/include/asio/detail/call_stack.hpp new file mode 100644 index 000000000..40f762d44 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/call_stack.hpp @@ -0,0 +1,125 @@ +// +// detail/call_stack.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CALL_STACK_HPP +#define ASIO_DETAIL_CALL_STACK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/tss_ptr.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper class to determine whether or not the current thread is inside an +// invocation of io_context::run() for a specified io_context object. +template +class call_stack +{ +public: + // Context class automatically pushes the key/value pair on to the stack. + class context + : private noncopyable + { + public: + // Push the key on to the stack. + explicit context(Key* k) + : key_(k), + next_(call_stack::top_) + { + value_ = reinterpret_cast(this); + call_stack::top_ = this; + } + + // Push the key/value pair on to the stack. + context(Key* k, Value& v) + : key_(k), + value_(&v), + next_(call_stack::top_) + { + call_stack::top_ = this; + } + + // Pop the key/value pair from the stack. + ~context() + { + call_stack::top_ = next_; + } + + // Find the next context with the same key. + Value* next_by_key() const + { + context* elem = next_; + while (elem) + { + if (elem->key_ == key_) + return elem->value_; + elem = elem->next_; + } + return 0; + } + + private: + friend class call_stack; + + // The key associated with the context. + Key* key_; + + // The value associated with the context. + Value* value_; + + // The next element in the stack. + context* next_; + }; + + friend class context; + + // Determine whether the specified owner is on the stack. Returns address of + // key if present, 0 otherwise. + static Value* contains(Key* k) + { + context* elem = top_; + while (elem) + { + if (elem->key_ == k) + return elem->value_; + elem = elem->next_; + } + return 0; + } + + // Obtain the value at the top of the stack. + static Value* top() + { + context* elem = top_; + return elem ? elem->value_ : 0; + } + +private: + // The top of the stack of calls for the current thread. + static tss_ptr top_; +}; + +template +tss_ptr::context> +call_stack::top_; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CALL_STACK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/chrono.hpp b/third_party/asio/1.18.2/include/asio/detail/chrono.hpp new file mode 100644 index 000000000..0abae1cd9 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/chrono.hpp @@ -0,0 +1,66 @@ +// +// detail/chrono.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CHRONO_HPP +#define ASIO_DETAIL_CHRONO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_CHRONO) +# include +#elif defined(ASIO_HAS_BOOST_CHRONO) +# include +#endif // defined(ASIO_HAS_BOOST_CHRONO) + +namespace asio { +namespace chrono { + +#if defined(ASIO_HAS_STD_CHRONO) +using std::chrono::duration; +using std::chrono::time_point; +using std::chrono::duration_cast; +using std::chrono::nanoseconds; +using std::chrono::microseconds; +using std::chrono::milliseconds; +using std::chrono::seconds; +using std::chrono::minutes; +using std::chrono::hours; +using std::chrono::time_point_cast; +#if defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) +typedef std::chrono::monotonic_clock steady_clock; +#else // defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) +using std::chrono::steady_clock; +#endif // defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) +using std::chrono::system_clock; +using std::chrono::high_resolution_clock; +#elif defined(ASIO_HAS_BOOST_CHRONO) +using boost::chrono::duration; +using boost::chrono::time_point; +using boost::chrono::duration_cast; +using boost::chrono::nanoseconds; +using boost::chrono::microseconds; +using boost::chrono::milliseconds; +using boost::chrono::seconds; +using boost::chrono::minutes; +using boost::chrono::hours; +using boost::chrono::time_point_cast; +using boost::chrono::system_clock; +using boost::chrono::steady_clock; +using boost::chrono::high_resolution_clock; +#endif // defined(ASIO_HAS_BOOST_CHRONO) + +} // namespace chrono +} // namespace asio + +#endif // ASIO_DETAIL_CHRONO_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/chrono_time_traits.hpp b/third_party/asio/1.18.2/include/asio/detail/chrono_time_traits.hpp new file mode 100644 index 000000000..17b9645cc --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/chrono_time_traits.hpp @@ -0,0 +1,190 @@ +// +// detail/chrono_time_traits.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CHRONO_TIME_TRAITS_HPP +#define ASIO_DETAIL_CHRONO_TIME_TRAITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/cstdint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper template to compute the greatest common divisor. +template +struct gcd { enum { value = gcd::value }; }; + +template +struct gcd { enum { value = v1 }; }; + +// Adapts std::chrono clocks for use with a deadline timer. +template +struct chrono_time_traits +{ + // The clock type. + typedef Clock clock_type; + + // The duration type of the clock. + typedef typename clock_type::duration duration_type; + + // The time point type of the clock. + typedef typename clock_type::time_point time_type; + + // The period of the clock. + typedef typename duration_type::period period_type; + + // Get the current time. + static time_type now() + { + return clock_type::now(); + } + + // Add a duration to a time. + static time_type add(const time_type& t, const duration_type& d) + { + const time_type epoch; + if (t >= epoch) + { + if ((time_type::max)() - t < d) + return (time_type::max)(); + } + else // t < epoch + { + if (-(t - (time_type::min)()) > d) + return (time_type::min)(); + } + + return t + d; + } + + // Subtract one time from another. + static duration_type subtract(const time_type& t1, const time_type& t2) + { + const time_type epoch; + if (t1 >= epoch) + { + if (t2 >= epoch) + { + return t1 - t2; + } + else if (t2 == (time_type::min)()) + { + return (duration_type::max)(); + } + else if ((time_type::max)() - t1 < epoch - t2) + { + return (duration_type::max)(); + } + else + { + return t1 - t2; + } + } + else // t1 < epoch + { + if (t2 < epoch) + { + return t1 - t2; + } + else if (t1 == (time_type::min)()) + { + return (duration_type::min)(); + } + else if ((time_type::max)() - t2 < epoch - t1) + { + return (duration_type::min)(); + } + else + { + return -(t2 - t1); + } + } + } + + // Test whether one time is less than another. + static bool less_than(const time_type& t1, const time_type& t2) + { + return t1 < t2; + } + + // Implement just enough of the posix_time::time_duration interface to supply + // what the timer_queue requires. + class posix_time_duration + { + public: + explicit posix_time_duration(const duration_type& d) + : d_(d) + { + } + + int64_t ticks() const + { + return d_.count(); + } + + int64_t total_seconds() const + { + return duration_cast<1, 1>(); + } + + int64_t total_milliseconds() const + { + return duration_cast<1, 1000>(); + } + + int64_t total_microseconds() const + { + return duration_cast<1, 1000000>(); + } + + private: + template + int64_t duration_cast() const + { + const int64_t num1 = period_type::num / gcd::value; + const int64_t num2 = Num / gcd::value; + + const int64_t den1 = period_type::den / gcd::value; + const int64_t den2 = Den / gcd::value; + + const int64_t num = num1 * den2; + const int64_t den = num2 * den1; + + if (num == 1 && den == 1) + return ticks(); + else if (num != 1 && den == 1) + return ticks() * num; + else if (num == 1 && period_type::den != 1) + return ticks() / den; + else + return ticks() * num / den; + } + + duration_type d_; + }; + + // Convert to POSIX duration type. + static posix_time_duration to_posix_duration(const duration_type& d) + { + return posix_time_duration(WaitTraits::to_wait_duration(d)); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CHRONO_TIME_TRAITS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/completion_handler.hpp b/third_party/asio/1.18.2/include/asio/detail/completion_handler.hpp new file mode 100644 index 000000000..5c24fa42a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/completion_handler.hpp @@ -0,0 +1,88 @@ +// +// detail/completion_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_COMPLETION_HANDLER_HPP +#define ASIO_DETAIL_COMPLETION_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class completion_handler : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(completion_handler); + + completion_handler(Handler& h, const IoExecutor& io_ex) + : operation(&completion_handler::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(h)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + completion_handler* h(static_cast(base)); + ptr p = { asio::detail::addressof(h->handler_), h, h }; + + ASIO_HANDLER_COMPLETION((*h)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + h->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + Handler handler(ASIO_MOVE_CAST(Handler)(h->handler_)); + p.h = asio::detail::addressof(handler); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN(()); + w.complete(handler, handler); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_COMPLETION_HANDLER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/concurrency_hint.hpp b/third_party/asio/1.18.2/include/asio/detail/concurrency_hint.hpp new file mode 100644 index 000000000..60ab2a8cd --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/concurrency_hint.hpp @@ -0,0 +1,94 @@ +// +// detail/concurrency_hint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONCURRENCY_HINT_HPP +#define ASIO_DETAIL_CONCURRENCY_HINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" + +// The concurrency hint ID and mask are used to identify when a "well-known" +// concurrency hint value has been passed to the io_context. +#define ASIO_CONCURRENCY_HINT_ID 0xA5100000u +#define ASIO_CONCURRENCY_HINT_ID_MASK 0xFFFF0000u + +// If set, this bit indicates that the scheduler should perform locking. +#define ASIO_CONCURRENCY_HINT_LOCKING_SCHEDULER 0x1u + +// If set, this bit indicates that the reactor should perform locking when +// managing descriptor registrations. +#define ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_REGISTRATION 0x2u + +// If set, this bit indicates that the reactor should perform locking for I/O. +#define ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_IO 0x4u + +// Helper macro to determine if we have a special concurrency hint. +#define ASIO_CONCURRENCY_HINT_IS_SPECIAL(hint) \ + ((static_cast(hint) \ + & ASIO_CONCURRENCY_HINT_ID_MASK) \ + == ASIO_CONCURRENCY_HINT_ID) + +// Helper macro to determine if locking is enabled for a given facility. +#define ASIO_CONCURRENCY_HINT_IS_LOCKING(facility, hint) \ + (((static_cast(hint) \ + & (ASIO_CONCURRENCY_HINT_ID_MASK \ + | ASIO_CONCURRENCY_HINT_LOCKING_ ## facility)) \ + ^ ASIO_CONCURRENCY_HINT_ID) != 0) + +// This special concurrency hint disables locking in both the scheduler and +// reactor I/O. This hint has the following restrictions: +// +// - Care must be taken to ensure that all operations on the io_context and any +// of its associated I/O objects (such as sockets and timers) occur in only +// one thread at a time. +// +// - Asynchronous resolve operations fail with operation_not_supported. +// +// - If a signal_set is used with the io_context, signal_set objects cannot be +// used with any other io_context in the program. +#define ASIO_CONCURRENCY_HINT_UNSAFE \ + static_cast(ASIO_CONCURRENCY_HINT_ID) + +// This special concurrency hint disables locking in the reactor I/O. This hint +// has the following restrictions: +// +// - Care must be taken to ensure that run functions on the io_context, and all +// operations on the io_context's associated I/O objects (such as sockets and +// timers), occur in only one thread at a time. +#define ASIO_CONCURRENCY_HINT_UNSAFE_IO \ + static_cast(ASIO_CONCURRENCY_HINT_ID \ + | ASIO_CONCURRENCY_HINT_LOCKING_SCHEDULER \ + | ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_REGISTRATION) + +// The special concurrency hint provides full thread safety. +#define ASIO_CONCURRENCY_HINT_SAFE \ + static_cast(ASIO_CONCURRENCY_HINT_ID \ + | ASIO_CONCURRENCY_HINT_LOCKING_SCHEDULER \ + | ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_REGISTRATION \ + | ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_IO) + +// This #define may be overridden at compile time to specify a program-wide +// default concurrency hint, used by the zero-argument io_context constructor. +#if !defined(ASIO_CONCURRENCY_HINT_DEFAULT) +# define ASIO_CONCURRENCY_HINT_DEFAULT -1 +#endif // !defined(ASIO_CONCURRENCY_HINT_DEFAULT) + +// This #define may be overridden at compile time to specify a program-wide +// concurrency hint, used by the one-argument io_context constructor when +// passed a value of 1. +#if !defined(ASIO_CONCURRENCY_HINT_1) +# define ASIO_CONCURRENCY_HINT_1 1 +#endif // !defined(ASIO_CONCURRENCY_HINT_DEFAULT) + +#endif // ASIO_DETAIL_CONCURRENCY_HINT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/conditionally_enabled_event.hpp b/third_party/asio/1.18.2/include/asio/detail/conditionally_enabled_event.hpp new file mode 100644 index 000000000..bb5c524b0 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/conditionally_enabled_event.hpp @@ -0,0 +1,120 @@ +// +// detail/conditionally_enabled_event.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONDITIONALLY_ENABLED_EVENT_HPP +#define ASIO_DETAIL_CONDITIONALLY_ENABLED_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/conditionally_enabled_mutex.hpp" +#include "asio/detail/event.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/null_event.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Mutex adapter used to conditionally enable or disable locking. +class conditionally_enabled_event + : private noncopyable +{ +public: + // Constructor. + conditionally_enabled_event() + { + } + + // Destructor. + ~conditionally_enabled_event() + { + } + + // Signal the event. (Retained for backward compatibility.) + void signal(conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.signal(lock); + } + + // Signal all waiters. + void signal_all(conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.signal_all(lock); + } + + // Unlock the mutex and signal one waiter. + void unlock_and_signal_one( + conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.unlock_and_signal_one(lock); + } + + // Unlock the mutex and signal one waiter who may destroy us. + void unlock_and_signal_one_for_destruction( + conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.unlock_and_signal_one(lock); + } + + // If there's a waiter, unlock the mutex and signal it. + bool maybe_unlock_and_signal_one( + conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + return event_.maybe_unlock_and_signal_one(lock); + else + return false; + } + + // Reset the event. + void clear(conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.clear(lock); + } + + // Wait for the event to become signalled. + void wait(conditionally_enabled_mutex::scoped_lock& lock) + { + if (lock.mutex_.enabled_) + event_.wait(lock); + else + null_event().wait(lock); + } + + // Timed wait for the event to become signalled. + bool wait_for_usec( + conditionally_enabled_mutex::scoped_lock& lock, long usec) + { + if (lock.mutex_.enabled_) + return event_.wait_for_usec(lock, usec); + else + return null_event().wait_for_usec(lock, usec); + } + +private: + asio::detail::event event_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONDITIONALLY_ENABLED_EVENT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/conditionally_enabled_mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/conditionally_enabled_mutex.hpp new file mode 100644 index 000000000..4f354ea5b --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/conditionally_enabled_mutex.hpp @@ -0,0 +1,149 @@ +// +// detail/conditionally_enabled_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONDITIONALLY_ENABLED_MUTEX_HPP +#define ASIO_DETAIL_CONDITIONALLY_ENABLED_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Mutex adapter used to conditionally enable or disable locking. +class conditionally_enabled_mutex + : private noncopyable +{ +public: + // Helper class to lock and unlock a mutex automatically. + class scoped_lock + : private noncopyable + { + public: + // Tag type used to distinguish constructors. + enum adopt_lock_t { adopt_lock }; + + // Constructor adopts a lock that is already held. + scoped_lock(conditionally_enabled_mutex& m, adopt_lock_t) + : mutex_(m), + locked_(m.enabled_) + { + } + + // Constructor acquires the lock. + explicit scoped_lock(conditionally_enabled_mutex& m) + : mutex_(m) + { + if (m.enabled_) + { + mutex_.mutex_.lock(); + locked_ = true; + } + else + locked_ = false; + } + + // Destructor releases the lock. + ~scoped_lock() + { + if (locked_) + mutex_.mutex_.unlock(); + } + + // Explicitly acquire the lock. + void lock() + { + if (mutex_.enabled_ && !locked_) + { + mutex_.mutex_.lock(); + locked_ = true; + } + } + + // Explicitly release the lock. + void unlock() + { + if (locked_) + { + mutex_.unlock(); + locked_ = false; + } + } + + // Test whether the lock is held. + bool locked() const + { + return locked_; + } + + // Get the underlying mutex. + asio::detail::mutex& mutex() + { + return mutex_.mutex_; + } + + private: + friend class conditionally_enabled_event; + conditionally_enabled_mutex& mutex_; + bool locked_; + }; + + // Constructor. + explicit conditionally_enabled_mutex(bool enabled) + : enabled_(enabled) + { + } + + // Destructor. + ~conditionally_enabled_mutex() + { + } + + // Determine whether locking is enabled. + bool enabled() const + { + return enabled_; + } + + // Lock the mutex. + void lock() + { + if (enabled_) + mutex_.lock(); + } + + // Unlock the mutex. + void unlock() + { + if (enabled_) + mutex_.unlock(); + } + +private: + friend class scoped_lock; + friend class conditionally_enabled_event; + asio::detail::mutex mutex_; + const bool enabled_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONDITIONALLY_ENABLED_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/config.hpp b/third_party/asio/1.18.2/include/asio/detail/config.hpp new file mode 100644 index 000000000..a09cd17e0 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/config.hpp @@ -0,0 +1,1865 @@ +// +// detail/config.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONFIG_HPP +#define ASIO_DETAIL_CONFIG_HPP + +// boostify: non-boost code starts here +#if !defined(ASIO_STANDALONE) +# if !defined(ASIO_ENABLE_BOOST) +# if (__cplusplus >= 201103) +# define ASIO_STANDALONE 1 +# elif defined(_MSC_VER) && defined(_MSVC_LANG) +# if (_MSC_VER >= 1900) && (_MSVC_LANG >= 201103) +# define ASIO_STANDALONE 1 +# endif // (_MSC_VER >= 1900) && (_MSVC_LANG >= 201103) +# endif // defined(_MSC_VER) && defined(_MSVC_LANG) +# endif // !defined(ASIO_ENABLE_BOOST) +#endif // !defined(ASIO_STANDALONE) + +// boostify: non-boost code ends here +#if defined(ASIO_STANDALONE) +# define ASIO_DISABLE_BOOST_ARRAY 1 +# define ASIO_DISABLE_BOOST_ASSERT 1 +# define ASIO_DISABLE_BOOST_BIND 1 +# define ASIO_DISABLE_BOOST_CHRONO 1 +# define ASIO_DISABLE_BOOST_DATE_TIME 1 +# define ASIO_DISABLE_BOOST_LIMITS 1 +# define ASIO_DISABLE_BOOST_REGEX 1 +# define ASIO_DISABLE_BOOST_STATIC_CONSTANT 1 +# define ASIO_DISABLE_BOOST_THROW_EXCEPTION 1 +# define ASIO_DISABLE_BOOST_WORKAROUND 1 +#else // defined(ASIO_STANDALONE) +# include +# include +# define ASIO_HAS_BOOST_CONFIG 1 +#endif // defined(ASIO_STANDALONE) + +// Default to a header-only implementation. The user must specifically request +// separate compilation by defining either ASIO_SEPARATE_COMPILATION or +// ASIO_DYN_LINK (as a DLL/shared library implies separate compilation). +#if !defined(ASIO_HEADER_ONLY) +# if !defined(ASIO_SEPARATE_COMPILATION) +# if !defined(ASIO_DYN_LINK) +# define ASIO_HEADER_ONLY 1 +# endif // !defined(ASIO_DYN_LINK) +# endif // !defined(ASIO_SEPARATE_COMPILATION) +#endif // !defined(ASIO_HEADER_ONLY) + +#if defined(ASIO_HEADER_ONLY) +# define ASIO_DECL inline +#else // defined(ASIO_HEADER_ONLY) +# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CODEGEARC__) +// We need to import/export our code only if the user has specifically asked +// for it by defining ASIO_DYN_LINK. +# if defined(ASIO_DYN_LINK) +// Export if this is our own source, otherwise import. +# if defined(ASIO_SOURCE) +# define ASIO_DECL __declspec(dllexport) +# else // defined(ASIO_SOURCE) +# define ASIO_DECL __declspec(dllimport) +# endif // defined(ASIO_SOURCE) +# endif // defined(ASIO_DYN_LINK) +# endif // defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CODEGEARC__) +#endif // defined(ASIO_HEADER_ONLY) + +// If ASIO_DECL isn't defined yet define it now. +#if !defined(ASIO_DECL) +# define ASIO_DECL +#endif // !defined(ASIO_DECL) + +// Helper macro for documentation. +#define ASIO_UNSPECIFIED(e) e + +// Microsoft Visual C++ detection. +#if !defined(ASIO_MSVC) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_MSVC) +# define ASIO_MSVC BOOST_MSVC +# elif defined(_MSC_VER) && (defined(__INTELLISENSE__) \ + || (!defined(__MWERKS__) && !defined(__EDG_VERSION__))) +# define ASIO_MSVC _MSC_VER +# endif // defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_MSVC) +#endif // !defined(ASIO_MSVC) + +// Clang / libc++ detection. +#if defined(__clang__) +# if (__cplusplus >= 201103) +# if __has_include(<__config>) +# include <__config> +# if defined(_LIBCPP_VERSION) +# define ASIO_HAS_CLANG_LIBCXX 1 +# endif // defined(_LIBCPP_VERSION) +# endif // __has_include(<__config>) +# endif // (__cplusplus >= 201103) +#endif // defined(__clang__) + +// Android platform detection. +#if defined(__ANDROID__) +# include +#endif // defined(__ANDROID__) + +// Support move construction and assignment on compilers known to allow it. +#if !defined(ASIO_HAS_MOVE) +# if !defined(ASIO_DISABLE_MOVE) +# if defined(__clang__) +# if __has_feature(__cxx_rvalue_references__) +# define ASIO_HAS_MOVE 1 +# endif // __has_feature(__cxx_rvalue_references__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_MOVE 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_MOVE 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# if defined(__INTEL_CXX11_MODE__) +# if defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1500) +# define ASIO_HAS_MOVE 1 +# endif // defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1500) +# if defined(__ICL) && (__ICL >= 1500) +# define ASIO_HAS_MOVE 1 +# endif // defined(__ICL) && (__ICL >= 1500) +# endif // defined(__INTEL_CXX11_MODE__) +# endif // !defined(ASIO_DISABLE_MOVE) +#endif // !defined(ASIO_HAS_MOVE) + +// If ASIO_MOVE_CAST isn't defined, and move support is available, define +// * ASIO_MOVE_ARG, +// * ASIO_NONDEDUCED_MOVE_ARG, and +// * ASIO_MOVE_CAST +// to take advantage of rvalue references and perfect forwarding. +#if defined(ASIO_HAS_MOVE) && !defined(ASIO_MOVE_CAST) +# define ASIO_MOVE_ARG(type) type&& +# define ASIO_MOVE_ARG2(type1, type2) type1, type2&& +# define ASIO_NONDEDUCED_MOVE_ARG(type) type& +# define ASIO_MOVE_CAST(type) static_cast +# define ASIO_MOVE_CAST2(type1, type2) static_cast +# define ASIO_MOVE_OR_LVALUE(type) static_cast +# define ASIO_MOVE_OR_LVALUE_TYPE(type) type +#endif // defined(ASIO_HAS_MOVE) && !defined(ASIO_MOVE_CAST) + +// If ASIO_MOVE_CAST still isn't defined, default to a C++03-compatible +// implementation. Note that older g++ and MSVC versions don't like it when you +// pass a non-member function through a const reference, so for most compilers +// we'll play it safe and stick with the old approach of passing the handler by +// value. +#if !defined(ASIO_MOVE_CAST) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ > 4) +# define ASIO_MOVE_ARG(type) const type& +# else // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ > 4) +# define ASIO_MOVE_ARG(type) type +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ > 4) +# elif defined(ASIO_MSVC) +# if (_MSC_VER >= 1400) +# define ASIO_MOVE_ARG(type) const type& +# else // (_MSC_VER >= 1400) +# define ASIO_MOVE_ARG(type) type +# endif // (_MSC_VER >= 1400) +# else +# define ASIO_MOVE_ARG(type) type +# endif +# define ASIO_NONDEDUCED_MOVE_ARG(type) const type& +# define ASIO_MOVE_CAST(type) static_cast +# define ASIO_MOVE_CAST2(type1, type2) static_cast +# define ASIO_MOVE_OR_LVALUE(type) +# define ASIO_MOVE_OR_LVALUE_TYPE(type) type& +#endif // !defined(ASIO_MOVE_CAST) + +// Support variadic templates on compilers known to allow it. +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) +# if !defined(ASIO_DISABLE_VARIADIC_TEMPLATES) +# if defined(__clang__) +# if __has_feature(__cxx_variadic_templates__) +# define ASIO_HAS_VARIADIC_TEMPLATES 1 +# endif // __has_feature(__cxx_variadic_templates__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_VARIADIC_TEMPLATES 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_HAS_VARIADIC_TEMPLATES 1 +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_VARIADIC_TEMPLATES) +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) +#if !defined(ASIO_ELLIPSIS) +# if defined(ASIO_HAS_VARIADIC_TEMPLATES) +# define ASIO_ELLIPSIS ... +# else // defined(ASIO_HAS_VARIADIC_TEMPLATES) +# define ASIO_ELLIPSIS +# endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +#endif // !defined(ASIO_ELLIPSIS) + +// Support deleted functions on compilers known to allow it. +#if !defined(ASIO_DELETED) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_DELETED = delete +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(__clang__) +# if __has_feature(__cxx_deleted_functions__) +# define ASIO_DELETED = delete +# endif // __has_feature(__cxx_deleted_functions__) +# endif // defined(__clang__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_DELETED = delete +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# if !defined(ASIO_DELETED) +# define ASIO_DELETED +# endif // !defined(ASIO_DELETED) +#endif // !defined(ASIO_DELETED) + +// Support constexpr on compilers known to allow it. +#if !defined(ASIO_HAS_CONSTEXPR) +# if !defined(ASIO_DISABLE_CONSTEXPR) +# if defined(__clang__) +# if __has_feature(__cxx_constexpr__) +# define ASIO_HAS_CONSTEXPR 1 +# endif // __has_feature(__cxx_constexpr__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_CONSTEXPR 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_HAS_CONSTEXPR 1 +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_CONSTEXPR) +#endif // !defined(ASIO_HAS_CONSTEXPR) +#if !defined(ASIO_CONSTEXPR) +# if defined(ASIO_HAS_CONSTEXPR) +# define ASIO_CONSTEXPR constexpr +# else // defined(ASIO_HAS_CONSTEXPR) +# define ASIO_CONSTEXPR +# endif // defined(ASIO_HAS_CONSTEXPR) +#endif // !defined(ASIO_CONSTEXPR) +#if !defined(ASIO_STATIC_CONSTEXPR) +# if defined(ASIO_HAS_CONSTEXPR) +# define ASIO_STATIC_CONSTEXPR(type, assignment) \ + static constexpr type assignment +# else // defined(ASIO_HAS_CONSTEXPR) +# define ASIO_STATIC_CONSTEXPR(type, assignment) \ + static const type assignment +# endif // defined(ASIO_HAS_CONSTEXPR) +#endif // !defined(ASIO_STATIC_CONSTEXPR) +#if !defined(ASIO_STATIC_CONSTEXPR_DEFAULT_INIT) +# if defined(ASIO_HAS_CONSTEXPR) +# if defined(__GNUC__) +# if (__GNUC__ >= 8) +# define ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(type, name) \ + static constexpr const type name{} +# else // (__GNUC__ >= 8) +# define ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(type, name) \ + static const type name +# endif // (__GNUC__ >= 8) +# elif defined(ASIO_MSVC) +# define ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(type, name) \ + static const type name +# else // defined(ASIO_MSVC) +# define ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(type, name) \ + static constexpr const type name{} +# endif // defined(ASIO_MSVC) +# else // defined(ASIO_HAS_CONSTEXPR) +# define ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(type, name) \ + static const type name +# endif // defined(ASIO_HAS_CONSTEXPR) +#endif // !defined(ASIO_STATIC_CONSTEXPR_DEFAULT_INIT) + +// Support noexcept on compilers known to allow it. +#if !defined(ASIO_HAS_NOEXCEPT) +# if !defined(ASIO_DISABLE_NOEXCEPT) +# if defined(ASIO_HAS_BOOST_CONFIG) && (BOOST_VERSION >= 105300) +# if !defined(BOOST_NO_NOEXCEPT) +# define ASIO_HAS_NOEXCEPT 1 +# endif // !defined(BOOST_NO_NOEXCEPT) +# define ASIO_NOEXCEPT BOOST_NOEXCEPT +# define ASIO_NOEXCEPT_OR_NOTHROW BOOST_NOEXCEPT_OR_NOTHROW +# define ASIO_NOEXCEPT_IF(c) BOOST_NOEXCEPT_IF(c) +# elif defined(__clang__) +# if __has_feature(__cxx_noexcept__) +# define ASIO_HAS_NOEXCEPT 1 +# endif // __has_feature(__cxx_noexcept__) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_NOEXCEPT 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# elif defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_HAS_NOEXCEPT 1 +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_NOEXCEPT) +# if !defined(ASIO_NOEXCEPT) +# endif // !defined(ASIO_NOEXCEPT) +# if !defined(ASIO_NOEXCEPT_OR_NOTHROW) +# endif // !defined(ASIO_NOEXCEPT_OR_NOTHROW) +#endif // !defined(ASIO_HAS_NOEXCEPT) +#if !defined(ASIO_NOEXCEPT) +# if defined(ASIO_HAS_NOEXCEPT) +# define ASIO_NOEXCEPT noexcept(true) +# else // defined(ASIO_HAS_NOEXCEPT) +# define ASIO_NOEXCEPT +# endif // defined(ASIO_HAS_NOEXCEPT) +#endif // !defined(ASIO_NOEXCEPT) +#if !defined(ASIO_NOEXCEPT_OR_NOTHROW) +# if defined(ASIO_HAS_NOEXCEPT) +# define ASIO_NOEXCEPT_OR_NOTHROW noexcept(true) +# else // defined(ASIO_HAS_NOEXCEPT) +# define ASIO_NOEXCEPT_OR_NOTHROW throw() +# endif // defined(ASIO_HAS_NOEXCEPT) +#endif // !defined(ASIO_NOEXCEPT_OR_NOTHROW) +#if !defined(ASIO_NOEXCEPT_IF) +# if defined(ASIO_HAS_NOEXCEPT) +# define ASIO_NOEXCEPT_IF(c) noexcept(c) +# else // defined(ASIO_HAS_NOEXCEPT) +# define ASIO_NOEXCEPT_IF(c) +# endif // defined(ASIO_HAS_NOEXCEPT) +#endif // !defined(ASIO_NOEXCEPT_IF) + +// Support automatic type deduction on compilers known to support it. +#if !defined(ASIO_HAS_DECLTYPE) +# if !defined(ASIO_DISABLE_DECLTYPE) +# if defined(__clang__) +# if __has_feature(__cxx_decltype__) +# define ASIO_HAS_DECLTYPE 1 +# endif // __has_feature(__cxx_decltype__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_DECLTYPE 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1800) +# define ASIO_HAS_DECLTYPE 1 +# endif // (_MSC_VER >= 1800) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_DECLTYPE) +#endif // !defined(ASIO_HAS_DECLTYPE) + +// Support alias templates on compilers known to allow it. +#if !defined(ASIO_HAS_ALIAS_TEMPLATES) +# if !defined(ASIO_DISABLE_ALIAS_TEMPLATES) +# if defined(__clang__) +# if __has_feature(__cxx_alias_templates__) +# define ASIO_HAS_ALIAS_TEMPLATES 1 +# endif // __has_feature(__cxx_alias_templates__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_ALIAS_TEMPLATES 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_HAS_ALIAS_TEMPLATES 1 +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_ALIAS_TEMPLATES) +#endif // !defined(ASIO_HAS_ALIAS_TEMPLATES) + +// Support return type deduction on compilers known to allow it. +#if !defined(ASIO_HAS_RETURN_TYPE_DEDUCTION) +# if !defined(ASIO_DISABLE_RETURN_TYPE_DEDUCTION) +# if defined(__clang__) +# if __has_feature(__cxx_return_type_deduction__) +# define ASIO_HAS_RETURN_TYPE_DEDUCTION 1 +# endif // __has_feature(__cxx_return_type_deduction__) +# elif (__cplusplus >= 201402) +# define ASIO_HAS_RETURN_TYPE_DEDUCTION 1 +# elif defined(__cpp_return_type_deduction) +# if (__cpp_return_type_deduction >= 201304) +# define ASIO_HAS_RETURN_TYPE_DEDUCTION 1 +# endif // (__cpp_return_type_deduction >= 201304) +# endif // defined(__cpp_return_type_deduction) +# endif // !defined(ASIO_DISABLE_RETURN_TYPE_DEDUCTION) +#endif // !defined(ASIO_HAS_RETURN_TYPE_DEDUCTION) + +// Support default function template arguments on compilers known to allow it. +#if !defined(ASIO_HAS_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS) +# if !defined(ASIO_DISABLE_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS) +# if (__cplusplus >= 201103) +# define ASIO_HAS_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS 1 +# elif defined(ASIO_MSVC) +# if (_MSC_VER >= 1900 && _MSVC_LANG >= 201103) +# define ASIO_HAS_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS 1 +# endif // (_MSC_VER >= 1900 && _MSVC_LANG >= 201103) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS) +#endif // !defined(ASIO_HAS_DEFAULT_FUNCTION_TEMPLATE_ARGUMENTS) + +// Support concepts on compilers known to allow them. +#if !defined(ASIO_HAS_CONCEPTS) +# if !defined(ASIO_DISABLE_CONCEPTS) +# if defined(__cpp_concepts) +# define ASIO_HAS_CONCEPTS 1 +# if (__cpp_concepts >= 201707) +# define ASIO_CONCEPT concept +# else // (__cpp_concepts >= 201707) +# define ASIO_CONCEPT concept bool +# endif // (__cpp_concepts >= 201707) +# endif // defined(__cpp_concepts) +# endif // !defined(ASIO_DISABLE_CONCEPTS) +#endif // !defined(ASIO_HAS_CONCEPTS) + +// Support template variables on compilers known to allow it. +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) +# if !defined(ASIO_DISABLE_VARIABLE_TEMPLATES) +# if defined(__clang__) +# if (__cplusplus >= 201402) +# if __has_feature(__cxx_variable_templates__) +# define ASIO_HAS_VARIABLE_TEMPLATES 1 +# endif // __has_feature(__cxx_variable_templates__) +# endif // (__cplusplus >= 201402) +# endif // defined(__clang__) +# if defined(__GNUC__) && !defined(__INTEL_COMPILER) +# if (__GNUC__ >= 6) +# if (__cplusplus >= 201402) +# define ASIO_HAS_VARIABLE_TEMPLATES 1 +# endif // (__cplusplus >= 201402) +# endif // (__GNUC__ >= 6) +# endif // defined(__GNUC__) && !defined(__INTEL_COMPILER) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1901) +# define ASIO_HAS_VARIABLE_TEMPLATES 1 +# endif // (_MSC_VER >= 1901) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_VARIABLE_TEMPLATES) +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +// Support SFINAEd template variables on compilers known to allow it. +#if !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +# if !defined(ASIO_DISABLE_SFINAE_VARIABLE_TEMPLATES) +# if defined(__clang__) +# if (__cplusplus >= 201703) +# if __has_feature(__cxx_variable_templates__) +# define ASIO_HAS_SFINAE_VARIABLE_TEMPLATES 1 +# endif // __has_feature(__cxx_variable_templates__) +# endif // (__cplusplus >= 201703) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 8) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 8) +# if (__cplusplus >= 201402) +# define ASIO_HAS_SFINAE_VARIABLE_TEMPLATES 1 +# endif // (__cplusplus >= 201402) +# endif // ((__GNUC__ == 8) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 8) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1901) +# define ASIO_HAS_SFINAE_VARIABLE_TEMPLATES 1 +# endif // (_MSC_VER >= 1901) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_SFINAE_VARIABLE_TEMPLATES) +#endif // !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +// Support SFINAE use of constant expressions on compilers known to allow it. +#if !defined(ASIO_HAS_CONSTANT_EXPRESSION_SFINAE) +# if !defined(ASIO_DISABLE_CONSTANT_EXPRESSION_SFINAE) +# if defined(__clang__) +# if (__cplusplus >= 201402) +# define ASIO_HAS_CONSTANT_EXPRESSION_SFINAE 1 +# endif // (__cplusplus >= 201402) +# endif // defined(__clang__) +# if defined(__GNUC__) && !defined(__INTEL_COMPILER) +# if (__GNUC__ >= 7) +# if (__cplusplus >= 201402) +# define ASIO_HAS_CONSTANT_EXPRESSION_SFINAE 1 +# endif // (__cplusplus >= 201402) +# endif // (__GNUC__ >= 7) +# endif // defined(__GNUC__) && !defined(__INTEL_COMPILER) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1901) +# define ASIO_HAS_CONSTANT_EXPRESSION_SFINAE 1 +# endif // (_MSC_VER >= 1901) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_CONSTANT_EXPRESSION_SFINAE) +#endif // !defined(ASIO_HAS_CONSTANT_EXPRESSION_SFINAE) + +// Enable workarounds for lack of working expression SFINAE. +#if !defined(ASIO_HAS_WORKING_EXPRESSION_SFINAE) +# if !defined(ASIO_DISABLE_WORKING_EXPRESSION_SFINAE) +# if !defined(ASIO_MSVC) && !defined(__INTEL_COMPILER) +# if (__cplusplus >= 201103) +# define ASIO_HAS_WORKING_EXPRESSION_SFINAE 1 +# endif // (__cplusplus >= 201103) +# endif // !defined(ASIO_MSVC) && !defined(__INTEL_COMPILER) +# endif // !defined(ASIO_DISABLE_WORKING_EXPRESSION_SFINAE) +#endif // !defined(ASIO_HAS_WORKING_EXPRESSION_SFINAE) + +// Support ref-qualified functions on compilers known to allow it. +#if !defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS) +# if !defined(ASIO_DISABLE_REF_QUALIFIED_FUNCTIONS) +# if defined(__clang__) +# if __has_feature(__cxx_reference_qualified_functions__) +# define ASIO_HAS_REF_QUALIFIED_FUNCTIONS 1 +# endif // __has_feature(__cxx_reference_qualified_functions__) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_REF_QUALIFIED_FUNCTIONS 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_HAS_REF_QUALIFIED_FUNCTIONS 1 +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_REF_QUALIFIED_FUNCTIONS) +#endif // !defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS) +#if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS) +# if !defined(ASIO_LVALUE_REF_QUAL) +# define ASIO_LVALUE_REF_QUAL & +# endif // !defined(ASIO_LVALUE_REF_QUAL) +# if !defined(ASIO_RVALUE_REF_QUAL) +# define ASIO_RVALUE_REF_QUAL && +# endif // !defined(ASIO_RVALUE_REF_QUAL) +#else // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS) +# if !defined(ASIO_LVALUE_REF_QUAL) +# define ASIO_LVALUE_REF_QUAL +# endif // !defined(ASIO_LVALUE_REF_QUAL) +# if !defined(ASIO_RVALUE_REF_QUAL) +# define ASIO_RVALUE_REF_QUAL +# endif // !defined(ASIO_RVALUE_REF_QUAL) +#endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS) + +// Standard library support for system errors. +#if !defined(ASIO_HAS_STD_SYSTEM_ERROR) +# if !defined(ASIO_DISABLE_STD_SYSTEM_ERROR) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_SYSTEM_ERROR 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_SYSTEM_ERROR 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_SYSTEM_ERROR 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_SYSTEM_ERROR 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_SYSTEM_ERROR) +#endif // !defined(ASIO_HAS_STD_SYSTEM_ERROR) + +// Compliant C++11 compilers put noexcept specifiers on error_category members. +#if !defined(ASIO_ERROR_CATEGORY_NOEXCEPT) +# if defined(ASIO_HAS_BOOST_CONFIG) && (BOOST_VERSION >= 105300) +# define ASIO_ERROR_CATEGORY_NOEXCEPT BOOST_NOEXCEPT +# elif defined(__clang__) +# if __has_feature(__cxx_noexcept__) +# define ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true) +# endif // __has_feature(__cxx_noexcept__) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true) +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# elif defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true) +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# if !defined(ASIO_ERROR_CATEGORY_NOEXCEPT) +# define ASIO_ERROR_CATEGORY_NOEXCEPT +# endif // !defined(ASIO_ERROR_CATEGORY_NOEXCEPT) +#endif // !defined(ASIO_ERROR_CATEGORY_NOEXCEPT) + +// Standard library support for arrays. +#if !defined(ASIO_HAS_STD_ARRAY) +# if !defined(ASIO_DISABLE_STD_ARRAY) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_ARRAY 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_ARRAY 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_ARRAY 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1600) +# define ASIO_HAS_STD_ARRAY 1 +# endif // (_MSC_VER >= 1600) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_ARRAY) +#endif // !defined(ASIO_HAS_STD_ARRAY) + +// Standard library support for shared_ptr and weak_ptr. +#if !defined(ASIO_HAS_STD_SHARED_PTR) +# if !defined(ASIO_DISABLE_STD_SHARED_PTR) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_SHARED_PTR 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_SHARED_PTR 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_SHARED_PTR 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1600) +# define ASIO_HAS_STD_SHARED_PTR 1 +# endif // (_MSC_VER >= 1600) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_SHARED_PTR) +#endif // !defined(ASIO_HAS_STD_SHARED_PTR) + +// Standard library support for allocator_arg_t. +#if !defined(ASIO_HAS_STD_ALLOCATOR_ARG) +# if !defined(ASIO_DISABLE_STD_ALLOCATOR_ARG) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_ALLOCATOR_ARG 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_ALLOCATOR_ARG 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_ALLOCATOR_ARG 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1600) +# define ASIO_HAS_STD_ALLOCATOR_ARG 1 +# endif // (_MSC_VER >= 1600) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_ALLOCATOR_ARG) +#endif // !defined(ASIO_HAS_STD_ALLOCATOR_ARG) + +// Standard library support for atomic operations. +#if !defined(ASIO_HAS_STD_ATOMIC) +# if !defined(ASIO_DISABLE_STD_ATOMIC) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_ATOMIC 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_ATOMIC 1 +# endif // __has_include() +# elif defined(__apple_build_version__) && defined(_LIBCPP_VERSION) +# if (__clang_major__ >= 10) +# if __has_include() +# define ASIO_HAS_STD_ATOMIC 1 +# endif // __has_include() +# endif // (__clang_major__ >= 10) +# endif // defined(__apple_build_version__) && defined(_LIBCPP_VERSION) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_ATOMIC 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_ATOMIC 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_ATOMIC) +#endif // !defined(ASIO_HAS_STD_ATOMIC) + +// Standard library support for chrono. Some standard libraries (such as the +// libstdc++ shipped with gcc 4.6) provide monotonic_clock as per early C++0x +// drafts, rather than the eventually standardised name of steady_clock. +#if !defined(ASIO_HAS_STD_CHRONO) +# if !defined(ASIO_DISABLE_STD_CHRONO) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_CHRONO 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_CHRONO 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_CHRONO 1 +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 6)) +# define ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK 1 +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 6)) +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_CHRONO 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_CHRONO) +#endif // !defined(ASIO_HAS_STD_CHRONO) + +// Boost support for chrono. +#if !defined(ASIO_HAS_BOOST_CHRONO) +# if !defined(ASIO_DISABLE_BOOST_CHRONO) +# if defined(ASIO_HAS_BOOST_CONFIG) && (BOOST_VERSION >= 104700) +# define ASIO_HAS_BOOST_CHRONO 1 +# endif // defined(ASIO_HAS_BOOST_CONFIG) && (BOOST_VERSION >= 104700) +# endif // !defined(ASIO_DISABLE_BOOST_CHRONO) +#endif // !defined(ASIO_HAS_BOOST_CHRONO) + +// Some form of chrono library is available. +#if !defined(ASIO_HAS_CHRONO) +# if defined(ASIO_HAS_STD_CHRONO) \ + || defined(ASIO_HAS_BOOST_CHRONO) +# define ASIO_HAS_CHRONO 1 +# endif // defined(ASIO_HAS_STD_CHRONO) + // || defined(ASIO_HAS_BOOST_CHRONO) +#endif // !defined(ASIO_HAS_CHRONO) + +// Boost support for the DateTime library. +#if !defined(ASIO_HAS_BOOST_DATE_TIME) +# if !defined(ASIO_DISABLE_BOOST_DATE_TIME) +# define ASIO_HAS_BOOST_DATE_TIME 1 +# endif // !defined(ASIO_DISABLE_BOOST_DATE_TIME) +#endif // !defined(ASIO_HAS_BOOST_DATE_TIME) + +// Standard library support for addressof. +#if !defined(ASIO_HAS_STD_ADDRESSOF) +# if !defined(ASIO_DISABLE_STD_ADDRESSOF) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_ADDRESSOF 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_ADDRESSOF 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_ADDRESSOF 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_ADDRESSOF 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_ADDRESSOF) +#endif // !defined(ASIO_HAS_STD_ADDRESSOF) + +// Standard library support for the function class. +#if !defined(ASIO_HAS_STD_FUNCTION) +# if !defined(ASIO_DISABLE_STD_FUNCTION) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_FUNCTION 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_FUNCTION 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_FUNCTION 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_FUNCTION 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_FUNCTION) +#endif // !defined(ASIO_HAS_STD_FUNCTION) + +// Standard library support for type traits. +#if !defined(ASIO_HAS_STD_TYPE_TRAITS) +# if !defined(ASIO_DISABLE_STD_TYPE_TRAITS) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_TYPE_TRAITS 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_TYPE_TRAITS 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_TYPE_TRAITS 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_TYPE_TRAITS 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_TYPE_TRAITS) +#endif // !defined(ASIO_HAS_STD_TYPE_TRAITS) + +// Standard library support for the nullptr_t type. +#if !defined(ASIO_HAS_NULLPTR) +# if !defined(ASIO_DISABLE_NULLPTR) +# if defined(__clang__) +# if __has_feature(__cxx_nullptr__) +# define ASIO_HAS_NULLPTR 1 +# endif // __has_feature(__cxx_nullptr__) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_NULLPTR 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_NULLPTR 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_NULLPTR) +#endif // !defined(ASIO_HAS_NULLPTR) + +// Standard library support for the C++11 allocator additions. +#if !defined(ASIO_HAS_CXX11_ALLOCATORS) +# if !defined(ASIO_DISABLE_CXX11_ALLOCATORS) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_CXX11_ALLOCATORS 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_CXX11_ALLOCATORS 1 +# endif // (__cplusplus >= 201103) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_CXX11_ALLOCATORS 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1800) +# define ASIO_HAS_CXX11_ALLOCATORS 1 +# endif // (_MSC_VER >= 1800) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_CXX11_ALLOCATORS) +#endif // !defined(ASIO_HAS_CXX11_ALLOCATORS) + +// Standard library support for the cstdint header. +#if !defined(ASIO_HAS_CSTDINT) +# if !defined(ASIO_DISABLE_CSTDINT) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_CSTDINT 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_CSTDINT 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_CSTDINT 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_CSTDINT 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_CSTDINT) +#endif // !defined(ASIO_HAS_CSTDINT) + +// Standard library support for the thread class. +#if !defined(ASIO_HAS_STD_THREAD) +# if !defined(ASIO_DISABLE_STD_THREAD) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_THREAD 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_THREAD 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_THREAD 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_THREAD 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_THREAD) +#endif // !defined(ASIO_HAS_STD_THREAD) + +// Standard library support for the mutex and condition variable classes. +#if !defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +# if !defined(ASIO_DISABLE_STD_MUTEX_AND_CONDVAR) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_MUTEX_AND_CONDVAR 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_MUTEX_AND_CONDVAR 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_MUTEX_AND_CONDVAR 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_MUTEX_AND_CONDVAR 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_MUTEX_AND_CONDVAR) +#endif // !defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +// Standard library support for the call_once function. +#if !defined(ASIO_HAS_STD_CALL_ONCE) +# if !defined(ASIO_DISABLE_STD_CALL_ONCE) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_CALL_ONCE 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_CALL_ONCE 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_CALL_ONCE 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_CALL_ONCE 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_CALL_ONCE) +#endif // !defined(ASIO_HAS_STD_CALL_ONCE) + +// Standard library support for futures. +#if !defined(ASIO_HAS_STD_FUTURE) +# if !defined(ASIO_DISABLE_STD_FUTURE) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_FUTURE 1 +# elif (__cplusplus >= 201103) +# if __has_include() +# define ASIO_HAS_STD_FUTURE 1 +# endif // __has_include() +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_FUTURE 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_FUTURE 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_FUTURE) +#endif // !defined(ASIO_HAS_STD_FUTURE) + +// Standard library support for std::string_view. +#if !defined(ASIO_HAS_STD_STRING_VIEW) +# if !defined(ASIO_DISABLE_STD_STRING_VIEW) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# if (__cplusplus >= 201402) +# if __has_include() +# define ASIO_HAS_STD_STRING_VIEW 1 +# endif // __has_include() +# endif // (__cplusplus >= 201402) +# else // defined(ASIO_HAS_CLANG_LIBCXX) +# if (__cplusplus >= 201703) +# if __has_include() +# define ASIO_HAS_STD_STRING_VIEW 1 +# endif // __has_include() +# endif // (__cplusplus >= 201703) +# endif // defined(ASIO_HAS_CLANG_LIBCXX) +# elif defined(__GNUC__) +# if (__GNUC__ >= 7) +# if (__cplusplus >= 201703) +# define ASIO_HAS_STD_STRING_VIEW 1 +# endif // (__cplusplus >= 201703) +# endif // (__GNUC__ >= 7) +# elif defined(ASIO_MSVC) +# if (_MSC_VER >= 1910 && _MSVC_LANG >= 201703) +# define ASIO_HAS_STD_STRING_VIEW 1 +# endif // (_MSC_VER >= 1910 && _MSVC_LANG >= 201703) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_STRING_VIEW) +#endif // !defined(ASIO_HAS_STD_STRING_VIEW) + +// Standard library support for std::experimental::string_view. +#if !defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +# if !defined(ASIO_DISABLE_STD_EXPERIMENTAL_STRING_VIEW) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# if (_LIBCPP_VERSION < 7000) +# if (__cplusplus >= 201402) +# if __has_include() +# define ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW 1 +# endif // __has_include() +# endif // (__cplusplus >= 201402) +# endif // (_LIBCPP_VERSION < 7000) +# else // defined(ASIO_HAS_CLANG_LIBCXX) +# if (__cplusplus >= 201402) +# if __has_include() +# define ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW 1 +# endif // __has_include() +# endif // (__cplusplus >= 201402) +# endif // // defined(ASIO_HAS_CLANG_LIBCXX) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4) +# if (__cplusplus >= 201402) +# define ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW 1 +# endif // (__cplusplus >= 201402) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# endif // !defined(ASIO_DISABLE_STD_EXPERIMENTAL_STRING_VIEW) +#endif // !defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) + +// Standard library has a string_view that we can use. +#if !defined(ASIO_HAS_STRING_VIEW) +# if !defined(ASIO_DISABLE_STRING_VIEW) +# if defined(ASIO_HAS_STD_STRING_VIEW) +# define ASIO_HAS_STRING_VIEW 1 +# elif defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +# define ASIO_HAS_STRING_VIEW 1 +# endif // defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +# endif // !defined(ASIO_DISABLE_STRING_VIEW) +#endif // !defined(ASIO_HAS_STRING_VIEW) + +// Standard library support for iostream move construction and assignment. +#if !defined(ASIO_HAS_STD_IOSTREAM_MOVE) +# if !defined(ASIO_DISABLE_STD_IOSTREAM_MOVE) +# if defined(__GNUC__) +# if (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_IOSTREAM_MOVE 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_IOSTREAM_MOVE 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_IOSTREAM_MOVE) +#endif // !defined(ASIO_HAS_STD_IOSTREAM_MOVE) + +// Standard library has invoke_result (which supersedes result_of). +#if !defined(ASIO_HAS_STD_INVOKE_RESULT) +# if !defined(ASIO_DISABLE_STD_INVOKE_RESULT) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1911 && _MSVC_LANG >= 201703) +# define ASIO_HAS_STD_INVOKE_RESULT 1 +# endif // (_MSC_VER >= 1911 && _MSVC_LANG >= 201703) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_INVOKE_RESULT) +#endif // !defined(ASIO_HAS_STD_INVOKE_RESULT) + +// Standard library support for std::exception_ptr and std::current_exception. +#if !defined(ASIO_HAS_STD_EXCEPTION_PTR) +# if !defined(ASIO_DISABLE_STD_EXCEPTION_PTR) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_EXCEPTION_PTR 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_EXCEPTION_PTR 1 +# endif // (__cplusplus >= 201103) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_EXCEPTION_PTR 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1800) +# define ASIO_HAS_STD_EXCEPTION_PTR 1 +# endif // (_MSC_VER >= 1800) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_EXCEPTION_PTR) +#endif // !defined(ASIO_HAS_STD_EXCEPTION_PTR) + +// Standard library support for std::nested_exception. +#if !defined(ASIO_HAS_STD_NESTED_EXCEPTION) +# if !defined(ASIO_DISABLE_STD_NESTED_EXCEPTION) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_NESTED_EXCEPTION 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_NESTED_EXCEPTION 1 +# endif // (__cplusplus >= 201103) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_NESTED_EXCEPTION 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1900) +# define ASIO_HAS_STD_NESTED_EXCEPTION 1 +# endif // (_MSC_VER >= 1900) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_NESTED_EXCEPTION) +#endif // !defined(ASIO_HAS_STD_NESTED_EXCEPTION) + +// Standard library support for std::any. +#if !defined(ASIO_HAS_STD_ANY) +# if !defined(ASIO_DISABLE_STD_ANY) +# if defined(__clang__) +# if (__cplusplus >= 201703) +# if __has_include() +# define ASIO_HAS_STD_ANY 1 +# endif // __has_include() +# endif // (__cplusplus >= 201703) +# elif defined(__GNUC__) +# if (__GNUC__ >= 7) +# if (__cplusplus >= 201703) +# define ASIO_HAS_STD_ANY 1 +# endif // (__cplusplus >= 201703) +# endif // (__GNUC__ >= 7) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1910) && (_MSVC_LANG >= 201703) +# define ASIO_HAS_STD_ANY 1 +# endif // (_MSC_VER >= 1910) && (_MSVC_LANG >= 201703) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_ANY) +#endif // !defined(ASIO_HAS_STD_ANY) + +// Standard library support for std::source_location. +#if !defined(ASIO_HAS_STD_SOURCE_LOCATION) +# if !defined(ASIO_DISABLE_STD_SOURCE_LOCATION) +// ... +# endif // !defined(ASIO_DISABLE_STD_SOURCE_LOCATION) +#endif // !defined(ASIO_HAS_STD_SOURCE_LOCATION) + +// Standard library support for std::experimental::source_location. +#if !defined(ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION) +# if !defined(ASIO_DISABLE_STD_EXPERIMENTAL_SOURCE_LOCATION) +# if defined(__GNUC__) +# if (__cplusplus >= 201709) +# if __has_include() +# define ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION 1 +# endif // __has_include() +# endif // (__cplusplus >= 201709) +# endif // defined(__GNUC__) +# endif // !defined(ASIO_DISABLE_STD_EXPERIMENTAL_SOURCE_LOCATION) +#endif // !defined(ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION) + +// Standard library has a source_location that we can use. +#if !defined(ASIO_HAS_SOURCE_LOCATION) +# if !defined(ASIO_DISABLE_SOURCE_LOCATION) +# if defined(ASIO_HAS_STD_SOURCE_LOCATION) +# define ASIO_HAS_SOURCE_LOCATION 1 +# elif defined(ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION) +# define ASIO_HAS_SOURCE_LOCATION 1 +# endif // defined(ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION) +# endif // !defined(ASIO_DISABLE_SOURCE_LOCATION) +#endif // !defined(ASIO_HAS_SOURCE_LOCATION) + +// Windows App target. Windows but with a limited API. +#if !defined(ASIO_WINDOWS_APP) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0603) +# include +# if (WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) \ + || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE)) \ + && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define ASIO_WINDOWS_APP 1 +# endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + // && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0603) +#endif // !defined(ASIO_WINDOWS_APP) + +// Legacy WinRT target. Windows App is preferred. +#if !defined(ASIO_WINDOWS_RUNTIME) +# if !defined(ASIO_WINDOWS_APP) +# if defined(__cplusplus_winrt) +# include +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) \ + && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define ASIO_WINDOWS_RUNTIME 1 +# endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + // && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# endif // defined(__cplusplus_winrt) +# endif // !defined(ASIO_WINDOWS_APP) +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +// Windows target. Excludes WinRT but includes Windows App targets. +#if !defined(ASIO_WINDOWS) +# if !defined(ASIO_WINDOWS_RUNTIME) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_WINDOWS) +# define ASIO_WINDOWS 1 +# elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# define ASIO_WINDOWS 1 +# elif defined(ASIO_WINDOWS_APP) +# define ASIO_WINDOWS 1 +# endif // defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_WINDOWS) +# endif // !defined(ASIO_WINDOWS_RUNTIME) +#endif // !defined(ASIO_WINDOWS) + +// Windows: target OS version. +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) || (defined(__BORLANDC__) && !defined(__clang__)) +# pragma message( \ + "Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:\n"\ + "- add -D_WIN32_WINNT=0x0601 to the compiler command line; or\n"\ + "- add _WIN32_WINNT=0x0601 to your project's Preprocessor Definitions.\n"\ + "Assuming _WIN32_WINNT=0x0601 (i.e. Windows 7 target).") +# else // defined(_MSC_VER) || (defined(__BORLANDC__) && !defined(__clang__)) +# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. +# warning For example, add -D_WIN32_WINNT=0x0601 to the compiler command line. +# warning Assuming _WIN32_WINNT=0x0601 (i.e. Windows 7 target). +# endif // defined(_MSC_VER) || (defined(__BORLANDC__) && !defined(__clang__)) +# define _WIN32_WINNT 0x0601 +# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) +# if defined(_WIN32) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(_WIN32) && !defined(WIN32) +# endif // defined(_MSC_VER) +# if defined(__BORLANDC__) +# if defined(__WIN32__) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(__WIN32__) && !defined(WIN32) +# endif // defined(__BORLANDC__) +# if defined(__CYGWIN__) +# if !defined(__USE_W32_SOCKETS) +# error You must add -D__USE_W32_SOCKETS to your compiler options. +# endif // !defined(__USE_W32_SOCKETS) +# endif // defined(__CYGWIN__) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +// Windows: minimise header inclusion. +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif // !defined(WIN32_LEAN_AND_MEAN) +# endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +// Windows: suppress definition of "min" and "max" macros. +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if !defined(ASIO_NO_NOMINMAX) +# if !defined(NOMINMAX) +# define NOMINMAX 1 +# endif // !defined(NOMINMAX) +# endif // !defined(ASIO_NO_NOMINMAX) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +// Windows: IO Completion Ports. +#if !defined(ASIO_HAS_IOCP) +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +# if !defined(UNDER_CE) && !defined(ASIO_WINDOWS_APP) +# if !defined(ASIO_DISABLE_IOCP) +# define ASIO_HAS_IOCP 1 +# endif // !defined(ASIO_DISABLE_IOCP) +# endif // !defined(UNDER_CE) && !defined(ASIO_WINDOWS_APP) +# endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +#endif // !defined(ASIO_HAS_IOCP) + +// On POSIX (and POSIX-like) platforms we need to include unistd.h in order to +// get access to the various platform feature macros, e.g. to be able to test +// for threads support. +#if !defined(ASIO_HAS_UNISTD_H) +# if !defined(ASIO_HAS_BOOST_CONFIG) +# if defined(unix) \ + || defined(__unix) \ + || defined(_XOPEN_SOURCE) \ + || defined(_POSIX_SOURCE) \ + || (defined(__MACH__) && defined(__APPLE__)) \ + || defined(__FreeBSD__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) \ + || defined(__linux__) \ + || defined(__HAIKU__) +# define ASIO_HAS_UNISTD_H 1 +# endif +# endif // !defined(ASIO_HAS_BOOST_CONFIG) +#endif // !defined(ASIO_HAS_UNISTD_H) +#if defined(ASIO_HAS_UNISTD_H) +# include +#endif // defined(ASIO_HAS_UNISTD_H) + +// Linux: epoll, eventfd and timerfd. +#if defined(__linux__) +# include +# if !defined(ASIO_HAS_EPOLL) +# if !defined(ASIO_DISABLE_EPOLL) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45) +# define ASIO_HAS_EPOLL 1 +# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45) +# endif // !defined(ASIO_DISABLE_EPOLL) +# endif // !defined(ASIO_HAS_EPOLL) +# if !defined(ASIO_HAS_EVENTFD) +# if !defined(ASIO_DISABLE_EVENTFD) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +# define ASIO_HAS_EVENTFD 1 +# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +# endif // !defined(ASIO_DISABLE_EVENTFD) +# endif // !defined(ASIO_HAS_EVENTFD) +# if !defined(ASIO_HAS_TIMERFD) +# if defined(ASIO_HAS_EPOLL) +# if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) +# define ASIO_HAS_TIMERFD 1 +# endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) +# endif // defined(ASIO_HAS_EPOLL) +# endif // !defined(ASIO_HAS_TIMERFD) +#endif // defined(__linux__) + +// Mac OS X, FreeBSD, NetBSD, OpenBSD: kqueue. +#if (defined(__MACH__) && defined(__APPLE__)) \ + || defined(__FreeBSD__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) +# if !defined(ASIO_HAS_KQUEUE) +# if !defined(ASIO_DISABLE_KQUEUE) +# define ASIO_HAS_KQUEUE 1 +# endif // !defined(ASIO_DISABLE_KQUEUE) +# endif // !defined(ASIO_HAS_KQUEUE) +#endif // (defined(__MACH__) && defined(__APPLE__)) + // || defined(__FreeBSD__) + // || defined(__NetBSD__) + // || defined(__OpenBSD__) + +// Solaris: /dev/poll. +#if defined(__sun) +# if !defined(ASIO_HAS_DEV_POLL) +# if !defined(ASIO_DISABLE_DEV_POLL) +# define ASIO_HAS_DEV_POLL 1 +# endif // !defined(ASIO_DISABLE_DEV_POLL) +# endif // !defined(ASIO_HAS_DEV_POLL) +#endif // defined(__sun) + +// Serial ports. +#if !defined(ASIO_HAS_SERIAL_PORT) +# if defined(ASIO_HAS_IOCP) \ + || !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) +# if !defined(__SYMBIAN32__) +# if !defined(ASIO_DISABLE_SERIAL_PORT) +# define ASIO_HAS_SERIAL_PORT 1 +# endif // !defined(ASIO_DISABLE_SERIAL_PORT) +# endif // !defined(__SYMBIAN32__) +# endif // defined(ASIO_HAS_IOCP) + // || !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +#endif // !defined(ASIO_HAS_SERIAL_PORT) + +// Windows: stream handles. +#if !defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) +# if !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_STREAM_HANDLE 1 +# endif // defined(ASIO_HAS_IOCP) +# endif // !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) +#endif // !defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + +// Windows: random access handles. +#if !defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) +# if !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE 1 +# endif // defined(ASIO_HAS_IOCP) +# endif // !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) +#endif // !defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + +// Windows: object handles. +#if !defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) +# if !defined(ASIO_DISABLE_WINDOWS_OBJECT_HANDLE) +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if !defined(UNDER_CE) && !defined(ASIO_WINDOWS_APP) +# define ASIO_HAS_WINDOWS_OBJECT_HANDLE 1 +# endif // !defined(UNDER_CE) && !defined(ASIO_WINDOWS_APP) +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# endif // !defined(ASIO_DISABLE_WINDOWS_OBJECT_HANDLE) +#endif // !defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + +// Windows: OVERLAPPED wrapper. +#if !defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) +# if !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_OVERLAPPED_PTR 1 +# endif // defined(ASIO_HAS_IOCP) +# endif // !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR) +#endif // !defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) + +// POSIX: stream-oriented file descriptors. +#if !defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) +# if !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) +# if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) +# define ASIO_HAS_POSIX_STREAM_DESCRIPTOR 1 +# endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +# endif // !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) +#endif // !defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + +// UNIX domain sockets. +#if !defined(ASIO_HAS_LOCAL_SOCKETS) +# if !defined(ASIO_DISABLE_LOCAL_SOCKETS) +# if !defined(ASIO_WINDOWS_RUNTIME) +# define ASIO_HAS_LOCAL_SOCKETS 1 +# endif // !defined(ASIO_WINDOWS_RUNTIME) +# endif // !defined(ASIO_DISABLE_LOCAL_SOCKETS) +#endif // !defined(ASIO_HAS_LOCAL_SOCKETS) + +// Can use sigaction() instead of signal(). +#if !defined(ASIO_HAS_SIGACTION) +# if !defined(ASIO_DISABLE_SIGACTION) +# if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) +# define ASIO_HAS_SIGACTION 1 +# endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +# endif // !defined(ASIO_DISABLE_SIGACTION) +#endif // !defined(ASIO_HAS_SIGACTION) + +// Can use signal(). +#if !defined(ASIO_HAS_SIGNAL) +# if !defined(ASIO_DISABLE_SIGNAL) +# if !defined(UNDER_CE) +# define ASIO_HAS_SIGNAL 1 +# endif // !defined(UNDER_CE) +# endif // !defined(ASIO_DISABLE_SIGNAL) +#endif // !defined(ASIO_HAS_SIGNAL) + +// Can use getaddrinfo() and getnameinfo(). +#if !defined(ASIO_HAS_GETADDRINFO) +# if !defined(ASIO_DISABLE_GETADDRINFO) +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) +# define ASIO_HAS_GETADDRINFO 1 +# elif defined(UNDER_CE) +# define ASIO_HAS_GETADDRINFO 1 +# endif // defined(UNDER_CE) +# elif defined(__MACH__) && defined(__APPLE__) +# if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +# if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) +# define ASIO_HAS_GETADDRINFO 1 +# endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) +# else // defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +# define ASIO_HAS_GETADDRINFO 1 +# endif // defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +# else // defined(__MACH__) && defined(__APPLE__) +# define ASIO_HAS_GETADDRINFO 1 +# endif // defined(__MACH__) && defined(__APPLE__) +# endif // !defined(ASIO_DISABLE_GETADDRINFO) +#endif // !defined(ASIO_HAS_GETADDRINFO) + +// Whether standard iostreams are disabled. +#if !defined(ASIO_NO_IOSTREAM) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_NO_IOSTREAM) +# define ASIO_NO_IOSTREAM 1 +# endif // !defined(BOOST_NO_IOSTREAM) +#endif // !defined(ASIO_NO_IOSTREAM) + +// Whether exception handling is disabled. +#if !defined(ASIO_NO_EXCEPTIONS) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_NO_EXCEPTIONS) +# define ASIO_NO_EXCEPTIONS 1 +# endif // !defined(BOOST_NO_EXCEPTIONS) +#endif // !defined(ASIO_NO_EXCEPTIONS) + +// Whether the typeid operator is supported. +#if !defined(ASIO_NO_TYPEID) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_NO_TYPEID) +# define ASIO_NO_TYPEID 1 +# endif // !defined(BOOST_NO_TYPEID) +#endif // !defined(ASIO_NO_TYPEID) + +// Threads. +#if !defined(ASIO_HAS_THREADS) +# if !defined(ASIO_DISABLE_THREADS) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_HAS_THREADS) +# define ASIO_HAS_THREADS 1 +# elif defined(__GNUC__) && !defined(__MINGW32__) \ + && !defined(linux) && !defined(__linux) && !defined(__linux__) +# define ASIO_HAS_THREADS 1 +# elif defined(_MT) || defined(__MT__) +# define ASIO_HAS_THREADS 1 +# elif defined(_REENTRANT) +# define ASIO_HAS_THREADS 1 +# elif defined(__APPLE__) +# define ASIO_HAS_THREADS 1 +# elif defined(__HAIKU__) +# define ASIO_HAS_THREADS 1 +# elif defined(_POSIX_THREADS) && (_POSIX_THREADS + 0 >= 0) +# define ASIO_HAS_THREADS 1 +# elif defined(_PTHREADS) +# define ASIO_HAS_THREADS 1 +# endif // defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_HAS_THREADS) +# endif // !defined(ASIO_DISABLE_THREADS) +#endif // !defined(ASIO_HAS_THREADS) + +// POSIX threads. +#if !defined(ASIO_HAS_PTHREADS) +# if defined(ASIO_HAS_THREADS) +# if defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_HAS_PTHREADS) +# define ASIO_HAS_PTHREADS 1 +# elif defined(_POSIX_THREADS) && (_POSIX_THREADS + 0 >= 0) +# define ASIO_HAS_PTHREADS 1 +# elif defined(__HAIKU__) +# define ASIO_HAS_PTHREADS 1 +# endif // defined(ASIO_HAS_BOOST_CONFIG) && defined(BOOST_HAS_PTHREADS) +# endif // defined(ASIO_HAS_THREADS) +#endif // !defined(ASIO_HAS_PTHREADS) + +// Helper to prevent macro expansion. +#define ASIO_PREVENT_MACRO_SUBSTITUTION + +// Helper to define in-class constants. +#if !defined(ASIO_STATIC_CONSTANT) +# if !defined(ASIO_DISABLE_BOOST_STATIC_CONSTANT) +# define ASIO_STATIC_CONSTANT(type, assignment) \ + BOOST_STATIC_CONSTANT(type, assignment) +# else // !defined(ASIO_DISABLE_BOOST_STATIC_CONSTANT) +# define ASIO_STATIC_CONSTANT(type, assignment) \ + static const type assignment +# endif // !defined(ASIO_DISABLE_BOOST_STATIC_CONSTANT) +#endif // !defined(ASIO_STATIC_CONSTANT) + +// Boost array library. +#if !defined(ASIO_HAS_BOOST_ARRAY) +# if !defined(ASIO_DISABLE_BOOST_ARRAY) +# define ASIO_HAS_BOOST_ARRAY 1 +# endif // !defined(ASIO_DISABLE_BOOST_ARRAY) +#endif // !defined(ASIO_HAS_BOOST_ARRAY) + +// Boost assert macro. +#if !defined(ASIO_HAS_BOOST_ASSERT) +# if !defined(ASIO_DISABLE_BOOST_ASSERT) +# define ASIO_HAS_BOOST_ASSERT 1 +# endif // !defined(ASIO_DISABLE_BOOST_ASSERT) +#endif // !defined(ASIO_HAS_BOOST_ASSERT) + +// Boost limits header. +#if !defined(ASIO_HAS_BOOST_LIMITS) +# if !defined(ASIO_DISABLE_BOOST_LIMITS) +# define ASIO_HAS_BOOST_LIMITS 1 +# endif // !defined(ASIO_DISABLE_BOOST_LIMITS) +#endif // !defined(ASIO_HAS_BOOST_LIMITS) + +// Boost throw_exception function. +#if !defined(ASIO_HAS_BOOST_THROW_EXCEPTION) +# if !defined(ASIO_DISABLE_BOOST_THROW_EXCEPTION) +# define ASIO_HAS_BOOST_THROW_EXCEPTION 1 +# endif // !defined(ASIO_DISABLE_BOOST_THROW_EXCEPTION) +#endif // !defined(ASIO_HAS_BOOST_THROW_EXCEPTION) + +// Boost regex library. +#if !defined(ASIO_HAS_BOOST_REGEX) +# if !defined(ASIO_DISABLE_BOOST_REGEX) +# define ASIO_HAS_BOOST_REGEX 1 +# endif // !defined(ASIO_DISABLE_BOOST_REGEX) +#endif // !defined(ASIO_HAS_BOOST_REGEX) + +// Boost bind function. +#if !defined(ASIO_HAS_BOOST_BIND) +# if !defined(ASIO_DISABLE_BOOST_BIND) +# define ASIO_HAS_BOOST_BIND 1 +# endif // !defined(ASIO_DISABLE_BOOST_BIND) +#endif // !defined(ASIO_HAS_BOOST_BIND) + +// Boost's BOOST_WORKAROUND macro. +#if !defined(ASIO_HAS_BOOST_WORKAROUND) +# if !defined(ASIO_DISABLE_BOOST_WORKAROUND) +# define ASIO_HAS_BOOST_WORKAROUND 1 +# endif // !defined(ASIO_DISABLE_BOOST_WORKAROUND) +#endif // !defined(ASIO_HAS_BOOST_WORKAROUND) + +// Microsoft Visual C++'s secure C runtime library. +#if !defined(ASIO_HAS_SECURE_RTL) +# if !defined(ASIO_DISABLE_SECURE_RTL) +# if defined(ASIO_MSVC) \ + && (ASIO_MSVC >= 1400) \ + && !defined(UNDER_CE) +# define ASIO_HAS_SECURE_RTL 1 +# endif // defined(ASIO_MSVC) + // && (ASIO_MSVC >= 1400) + // && !defined(UNDER_CE) +# endif // !defined(ASIO_DISABLE_SECURE_RTL) +#endif // !defined(ASIO_HAS_SECURE_RTL) + +// Handler hooking. Disabled for ancient Borland C++ and gcc compilers. +#if !defined(ASIO_HAS_HANDLER_HOOKS) +# if !defined(ASIO_DISABLE_HANDLER_HOOKS) +# if defined(__GNUC__) +# if (__GNUC__ >= 3) +# define ASIO_HAS_HANDLER_HOOKS 1 +# endif // (__GNUC__ >= 3) +# elif !defined(__BORLANDC__) || defined(__clang__) +# define ASIO_HAS_HANDLER_HOOKS 1 +# endif // !defined(__BORLANDC__) || defined(__clang__) +# endif // !defined(ASIO_DISABLE_HANDLER_HOOKS) +#endif // !defined(ASIO_HAS_HANDLER_HOOKS) + +// Support for the __thread keyword extension. +#if !defined(ASIO_DISABLE_THREAD_KEYWORD_EXTENSION) +# if defined(__linux__) +# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +# if ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3) +# if !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !(defined(__clang__) && defined(__ANDROID__)) +# define ASIO_HAS_THREAD_KEYWORD_EXTENSION 1 +# define ASIO_THREAD_KEYWORD __thread +# elif defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1100) +# define ASIO_HAS_THREAD_KEYWORD_EXTENSION 1 +# endif // defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1100) + // && !(defined(__clang__) && defined(__ANDROID__)) +# endif // ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3) +# endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +# endif // defined(__linux__) +# if defined(ASIO_MSVC) && defined(ASIO_WINDOWS_RUNTIME) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_THREAD_KEYWORD_EXTENSION 1 +# define ASIO_THREAD_KEYWORD __declspec(thread) +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) && defined(ASIO_WINDOWS_RUNTIME) +#endif // !defined(ASIO_DISABLE_THREAD_KEYWORD_EXTENSION) +#if !defined(ASIO_THREAD_KEYWORD) +# define ASIO_THREAD_KEYWORD __thread +#endif // !defined(ASIO_THREAD_KEYWORD) + +// Support for POSIX ssize_t typedef. +#if !defined(ASIO_DISABLE_SSIZE_T) +# if defined(__linux__) \ + || (defined(__MACH__) && defined(__APPLE__)) +# define ASIO_HAS_SSIZE_T 1 +# endif // defined(__linux__) + // || (defined(__MACH__) && defined(__APPLE__)) +#endif // !defined(ASIO_DISABLE_SSIZE_T) + +// Helper macros to manage transition away from error_code return values. +#if defined(ASIO_NO_DEPRECATED) +# define ASIO_SYNC_OP_VOID void +# define ASIO_SYNC_OP_VOID_RETURN(e) return +#else // defined(ASIO_NO_DEPRECATED) +# define ASIO_SYNC_OP_VOID asio::error_code +# define ASIO_SYNC_OP_VOID_RETURN(e) return e +#endif // defined(ASIO_NO_DEPRECATED) + +// Newer gcc, clang need special treatment to suppress unused typedef warnings. +#if defined(__clang__) +# if defined(__apple_build_version__) +# if (__clang_major__ >= 7) +# define ASIO_UNUSED_TYPEDEF __attribute__((__unused__)) +# endif // (__clang_major__ >= 7) +# elif ((__clang_major__ == 3) && (__clang_minor__ >= 6)) \ + || (__clang_major__ > 3) +# define ASIO_UNUSED_TYPEDEF __attribute__((__unused__)) +# endif // ((__clang_major__ == 3) && (__clang_minor__ >= 6)) + // || (__clang_major__ > 3) +#elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# define ASIO_UNUSED_TYPEDEF __attribute__((__unused__)) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +#endif // defined(__GNUC__) +#if !defined(ASIO_UNUSED_TYPEDEF) +# define ASIO_UNUSED_TYPEDEF +#endif // !defined(ASIO_UNUSED_TYPEDEF) + +// Some versions of gcc generate spurious warnings about unused variables. +#if defined(__GNUC__) +# if (__GNUC__ >= 4) +# define ASIO_UNUSED_VARIABLE __attribute__((__unused__)) +# endif // (__GNUC__ >= 4) +#endif // defined(__GNUC__) +#if !defined(ASIO_UNUSED_VARIABLE) +# define ASIO_UNUSED_VARIABLE +#endif // !defined(ASIO_UNUSED_VARIABLE) + +// Support the co_await keyword on compilers known to allow it. +#if !defined(ASIO_HAS_CO_AWAIT) +# if !defined(ASIO_DISABLE_CO_AWAIT) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1928) && (_MSVC_LANG >= 201705) +# define ASIO_HAS_CO_AWAIT 1 +# elif (_MSC_FULL_VER >= 190023506) +# if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) +# define ASIO_HAS_CO_AWAIT 1 +# endif // defined(_RESUMABLE_FUNCTIONS_SUPPORTED) +# endif // (_MSC_FULL_VER >= 190023506) +# endif // defined(ASIO_MSVC) +# if defined(__clang__) +# if (__cplusplus >= 201703) && (__cpp_coroutines >= 201703) +# if __has_include() +# define ASIO_HAS_CO_AWAIT 1 +# endif // __has_include() +# endif // (__cplusplus >= 201703) && (__cpp_coroutines >= 201703) +# elif defined(__GNUC__) +# if (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902) +# if __has_include() +# define ASIO_HAS_CO_AWAIT 1 +# endif // __has_include() +# endif // (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902) +# endif // defined(__GNUC__) +# endif // !defined(ASIO_DISABLE_CO_AWAIT) +#endif // !defined(ASIO_HAS_CO_AWAIT) + +// Standard library support for coroutines. +#if !defined(ASIO_HAS_STD_COROUTINE) +# if !defined(ASIO_DISABLE_STD_COROUTINE) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1928) && (_MSVC_LANG >= 201705) +# define ASIO_HAS_STD_COROUTINE 1 +# endif // (_MSC_VER >= 1928) && (_MSVC_LANG >= 201705) +# endif // defined(ASIO_MSVC) +# if defined(__GNUC__) +# if (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902) +# if __has_include() +# define ASIO_HAS_STD_COROUTINE 1 +# endif // __has_include() +# endif // (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902) +# endif // defined(__GNUC__) +# endif // !defined(ASIO_DISABLE_STD_COROUTINE) +#endif // !defined(ASIO_HAS_STD_COROUTINE) + +// Compiler support for the the [[nodiscard]] attribute. +#if !defined(ASIO_NODISCARD) +# if defined(__has_cpp_attribute) +# if __has_cpp_attribute(nodiscard) +# if (__cplusplus >= 201703) +# define ASIO_NODISCARD [[nodiscard]] +# endif // (__cplusplus >= 201703) +# endif // __has_cpp_attribute(nodiscard) +# endif // defined(__has_cpp_attribute) +#endif // !defined(ASIO_NODISCARD) +#if !defined(ASIO_NODISCARD) +# define ASIO_NODISCARD +#endif // !defined(ASIO_NODISCARD) + +// Kernel support for MSG_NOSIGNAL. +#if !defined(ASIO_HAS_MSG_NOSIGNAL) +# if defined(__linux__) +# define ASIO_HAS_MSG_NOSIGNAL 1 +# elif defined(_POSIX_VERSION) +# if (_POSIX_VERSION >= 200809L) +# define ASIO_HAS_MSG_NOSIGNAL 1 +# endif // _POSIX_VERSION >= 200809L +# endif // defined(_POSIX_VERSION) +#endif // !defined(ASIO_HAS_MSG_NOSIGNAL) + +// Standard library support for std::hash. +#if !defined(ASIO_HAS_STD_HASH) +# if !defined(ASIO_DISABLE_STD_HASH) +# if defined(__clang__) +# if defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_HASH 1 +# elif (__cplusplus >= 201103) +# define ASIO_HAS_STD_HASH 1 +# endif // (__cplusplus >= 201103) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_HAS_STD_HASH 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1700) +# define ASIO_HAS_STD_HASH 1 +# endif // (_MSC_VER >= 1700) +# endif // defined(ASIO_MSVC) +# endif // !defined(ASIO_DISABLE_STD_HASH) +#endif // !defined(ASIO_HAS_STD_HASH) + +#endif // ASIO_DETAIL_CONFIG_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/consuming_buffers.hpp b/third_party/asio/1.18.2/include/asio/detail/consuming_buffers.hpp new file mode 100644 index 000000000..0d99093a4 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/consuming_buffers.hpp @@ -0,0 +1,414 @@ +// +// detail/consuming_buffers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONSUMING_BUFFERS_HPP +#define ASIO_DETAIL_CONSUMING_BUFFERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/buffer.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/limits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper template to determine the maximum number of prepared buffers. +template +struct prepared_buffers_max +{ + enum { value = buffer_sequence_adapter_base::max_buffers }; +}; + +template +struct prepared_buffers_max > +{ + enum { value = N }; +}; + +#if defined(ASIO_HAS_STD_ARRAY) + +template +struct prepared_buffers_max > +{ + enum { value = N }; +}; + +#endif // defined(ASIO_HAS_STD_ARRAY) + +// A buffer sequence used to represent a subsequence of the buffers. +template +struct prepared_buffers +{ + typedef Buffer value_type; + typedef const Buffer* const_iterator; + + enum { max_buffers = MaxBuffers < 16 ? MaxBuffers : 16 }; + + prepared_buffers() : count(0) {} + const_iterator begin() const { return elems; } + const_iterator end() const { return elems + count; } + + Buffer elems[max_buffers]; + std::size_t count; +}; + +// A proxy for a sub-range in a list of buffers. +template +class consuming_buffers +{ +public: + typedef prepared_buffers::value> + prepared_buffers_type; + + // Construct to represent the entire list of buffers. + explicit consuming_buffers(const Buffers& buffers) + : buffers_(buffers), + total_consumed_(0), + next_elem_(0), + next_elem_offset_(0) + { + using asio::buffer_size; + total_size_ = buffer_size(buffers); + } + + // Determine if we are at the end of the buffers. + bool empty() const + { + return total_consumed_ >= total_size_; + } + + // Get the buffer for a single transfer, with a size. + prepared_buffers_type prepare(std::size_t max_size) + { + prepared_buffers_type result; + + Buffer_Iterator next = asio::buffer_sequence_begin(buffers_); + Buffer_Iterator end = asio::buffer_sequence_end(buffers_); + + std::advance(next, next_elem_); + std::size_t elem_offset = next_elem_offset_; + while (next != end && max_size > 0 && (result.count) < result.max_buffers) + { + Buffer next_buf = Buffer(*next) + elem_offset; + result.elems[result.count] = asio::buffer(next_buf, max_size); + max_size -= result.elems[result.count].size(); + elem_offset = 0; + if (result.elems[result.count].size() > 0) + ++result.count; + ++next; + } + + return result; + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + total_consumed_ += size; + + Buffer_Iterator next = asio::buffer_sequence_begin(buffers_); + Buffer_Iterator end = asio::buffer_sequence_end(buffers_); + + std::advance(next, next_elem_); + while (next != end && size > 0) + { + Buffer next_buf = Buffer(*next) + next_elem_offset_; + if (size < next_buf.size()) + { + next_elem_offset_ += size; + size = 0; + } + else + { + size -= next_buf.size(); + next_elem_offset_ = 0; + ++next_elem_; + ++next; + } + } + } + + // Get the total number of bytes consumed from the buffers. + std::size_t total_consumed() const + { + return total_consumed_; + } + +private: + Buffers buffers_; + std::size_t total_size_; + std::size_t total_consumed_; + std::size_t next_elem_; + std::size_t next_elem_offset_; +}; + +// Base class of all consuming_buffers specialisations for single buffers. +template +class consuming_single_buffer +{ +public: + // Construct to represent the entire list of buffers. + template + explicit consuming_single_buffer(const Buffer1& buffer) + : buffer_(buffer), + total_consumed_(0) + { + } + + // Determine if we are at the end of the buffers. + bool empty() const + { + return total_consumed_ >= buffer_.size(); + } + + // Get the buffer for a single transfer, with a size. + Buffer prepare(std::size_t max_size) + { + return asio::buffer(buffer_ + total_consumed_, max_size); + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + total_consumed_ += size; + } + + // Get the total number of bytes consumed from the buffers. + std::size_t total_consumed() const + { + return total_consumed_; + } + +private: + Buffer buffer_; + std::size_t total_consumed_; +}; + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const mutable_buffer& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const mutable_buffer& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const const_buffer& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +#if !defined(ASIO_NO_DEPRECATED) + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const mutable_buffers_1& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const mutable_buffers_1& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +template <> +class consuming_buffers + : public consuming_single_buffer +{ +public: + explicit consuming_buffers(const const_buffers_1& buffer) + : consuming_single_buffer(buffer) + { + } +}; + +#endif // !defined(ASIO_NO_DEPRECATED) + +template +class consuming_buffers, + typename boost::array::const_iterator> +{ +public: + // Construct to represent the entire list of buffers. + explicit consuming_buffers(const boost::array& buffers) + : buffers_(buffers), + total_consumed_(0) + { + } + + // Determine if we are at the end of the buffers. + bool empty() const + { + return total_consumed_ >= + Buffer(buffers_[0]).size() + Buffer(buffers_[1]).size(); + } + + // Get the buffer for a single transfer, with a size. + boost::array prepare(std::size_t max_size) + { + boost::array result = {{ + Buffer(buffers_[0]), Buffer(buffers_[1]) }}; + std::size_t buffer0_size = result[0].size(); + result[0] = asio::buffer(result[0] + total_consumed_, max_size); + result[1] = asio::buffer( + result[1] + (total_consumed_ < buffer0_size + ? 0 : total_consumed_ - buffer0_size), + max_size - result[0].size()); + return result; + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + total_consumed_ += size; + } + + // Get the total number of bytes consumed from the buffers. + std::size_t total_consumed() const + { + return total_consumed_; + } + +private: + boost::array buffers_; + std::size_t total_consumed_; +}; + +#if defined(ASIO_HAS_STD_ARRAY) + +template +class consuming_buffers, + typename std::array::const_iterator> +{ +public: + // Construct to represent the entire list of buffers. + explicit consuming_buffers(const std::array& buffers) + : buffers_(buffers), + total_consumed_(0) + { + } + + // Determine if we are at the end of the buffers. + bool empty() const + { + return total_consumed_ >= + Buffer(buffers_[0]).size() + Buffer(buffers_[1]).size(); + } + + // Get the buffer for a single transfer, with a size. + std::array prepare(std::size_t max_size) + { + std::array result = {{ + Buffer(buffers_[0]), Buffer(buffers_[1]) }}; + std::size_t buffer0_size = result[0].size(); + result[0] = asio::buffer(result[0] + total_consumed_, max_size); + result[1] = asio::buffer( + result[1] + (total_consumed_ < buffer0_size + ? 0 : total_consumed_ - buffer0_size), + max_size - result[0].size()); + return result; + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + total_consumed_ += size; + } + + // Get the total number of bytes consumed from the buffers. + std::size_t total_consumed() const + { + return total_consumed_; + } + +private: + std::array buffers_; + std::size_t total_consumed_; +}; + +#endif // defined(ASIO_HAS_STD_ARRAY) + +// Specialisation for null_buffers to ensure that the null_buffers type is +// always passed through to the underlying read or write operation. +template +class consuming_buffers + : public asio::null_buffers +{ +public: + consuming_buffers(const null_buffers&) + { + // No-op. + } + + bool empty() + { + return false; + } + + null_buffers prepare(std::size_t) + { + return null_buffers(); + } + + void consume(std::size_t) + { + // No-op. + } + + std::size_t total_consumed() const + { + return 0; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONSUMING_BUFFERS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/cstddef.hpp b/third_party/asio/1.18.2/include/asio/detail/cstddef.hpp new file mode 100644 index 000000000..e9fd2d23b --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/cstddef.hpp @@ -0,0 +1,31 @@ +// +// detail/cstddef.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CSTDDEF_HPP +#define ASIO_DETAIL_CSTDDEF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +namespace asio { + +#if defined(ASIO_HAS_NULLPTR) +using std::nullptr_t; +#else // defined(ASIO_HAS_NULLPTR) +struct nullptr_t {}; +#endif // defined(ASIO_HAS_NULLPTR) + +} // namespace asio + +#endif // ASIO_DETAIL_CSTDDEF_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/cstdint.hpp b/third_party/asio/1.18.2/include/asio/detail/cstdint.hpp new file mode 100644 index 000000000..d29929b71 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/cstdint.hpp @@ -0,0 +1,60 @@ +// +// detail/cstdint.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CSTDINT_HPP +#define ASIO_DETAIL_CSTDINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_CSTDINT) +# include +#else // defined(ASIO_HAS_CSTDINT) +# include +#endif // defined(ASIO_HAS_CSTDINT) + +namespace asio { + +#if defined(ASIO_HAS_CSTDINT) +using std::int16_t; +using std::int_least16_t; +using std::uint16_t; +using std::uint_least16_t; +using std::int32_t; +using std::int_least32_t; +using std::uint32_t; +using std::uint_least32_t; +using std::int64_t; +using std::int_least64_t; +using std::uint64_t; +using std::uint_least64_t; +using std::uintmax_t; +#else // defined(ASIO_HAS_CSTDINT) +using boost::int16_t; +using boost::int_least16_t; +using boost::uint16_t; +using boost::uint_least16_t; +using boost::int32_t; +using boost::int_least32_t; +using boost::uint32_t; +using boost::uint_least32_t; +using boost::int64_t; +using boost::int_least64_t; +using boost::uint64_t; +using boost::uint_least64_t; +using boost::uintmax_t; +#endif // defined(ASIO_HAS_CSTDINT) + +} // namespace asio + +#endif // ASIO_DETAIL_CSTDINT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/date_time_fwd.hpp b/third_party/asio/1.18.2/include/asio/detail/date_time_fwd.hpp new file mode 100644 index 000000000..1b210228d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/date_time_fwd.hpp @@ -0,0 +1,34 @@ +// +// detail/date_time_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DATE_TIME_FWD_HPP +#define ASIO_DETAIL_DATE_TIME_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +namespace boost { +namespace date_time { + +template +class base_time; + +} // namespace date_time +namespace posix_time { + +class ptime; + +} // namespace posix_time +} // namespace boost + +#endif // ASIO_DETAIL_DATE_TIME_FWD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/deadline_timer_service.hpp b/third_party/asio/1.18.2/include/asio/detail/deadline_timer_service.hpp new file mode 100644 index 000000000..262651ea5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/deadline_timer_service.hpp @@ -0,0 +1,295 @@ +// +// detail/deadline_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP +#define ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue.hpp" +#include "asio/detail/timer_queue_ptime.hpp" +#include "asio/detail/timer_scheduler.hpp" +#include "asio/detail/wait_handler.hpp" +#include "asio/detail/wait_op.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include +# include +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class deadline_timer_service + : public execution_context_service_base > +{ +public: + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // The implementation type of the timer. This type is dependent on the + // underlying implementation of the timer service. + struct implementation_type + : private asio::detail::noncopyable + { + time_type expiry; + bool might_have_pending_waits; + typename timer_queue::per_timer_data timer_data; + }; + + // Constructor. + deadline_timer_service(execution_context& context) + : execution_context_service_base< + deadline_timer_service >(context), + scheduler_(asio::use_service(context)) + { + scheduler_.init_task(); + scheduler_.add_timer_queue(timer_queue_); + } + + // Destructor. + ~deadline_timer_service() + { + scheduler_.remove_timer_queue(timer_queue_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + } + + // Construct a new timer implementation. + void construct(implementation_type& impl) + { + impl.expiry = time_type(); + impl.might_have_pending_waits = false; + } + + // Destroy a timer implementation. + void destroy(implementation_type& impl) + { + asio::error_code ec; + cancel(impl, ec); + } + + // Move-construct a new timer implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + scheduler_.move_timer(timer_queue_, impl.timer_data, other_impl.timer_data); + + impl.expiry = other_impl.expiry; + other_impl.expiry = time_type(); + + impl.might_have_pending_waits = other_impl.might_have_pending_waits; + other_impl.might_have_pending_waits = false; + } + + // Move-assign from another timer implementation. + void move_assign(implementation_type& impl, + deadline_timer_service& other_service, + implementation_type& other_impl) + { + if (this != &other_service) + if (impl.might_have_pending_waits) + scheduler_.cancel_timer(timer_queue_, impl.timer_data); + + other_service.scheduler_.move_timer(other_service.timer_queue_, + impl.timer_data, other_impl.timer_data); + + impl.expiry = other_impl.expiry; + other_impl.expiry = time_type(); + + impl.might_have_pending_waits = other_impl.might_have_pending_waits; + other_impl.might_have_pending_waits = false; + } + + // Move-construct a new timer implementation. + void converting_move_construct(implementation_type& impl, + deadline_timer_service&, implementation_type& other_impl) + { + move_construct(impl, other_impl); + } + + // Move-assign from another timer implementation. + void converting_move_assign(implementation_type& impl, + deadline_timer_service& other_service, + implementation_type& other_impl) + { + move_assign(impl, other_service, other_impl); + } + + // Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(implementation_type& impl, asio::error_code& ec) + { + if (!impl.might_have_pending_waits) + { + ec = asio::error_code(); + return 0; + } + + ASIO_HANDLER_OPERATION((scheduler_.context(), + "deadline_timer", &impl, 0, "cancel")); + + std::size_t count = scheduler_.cancel_timer(timer_queue_, impl.timer_data); + impl.might_have_pending_waits = false; + ec = asio::error_code(); + return count; + } + + // Cancels one asynchronous wait operation associated with the timer. + std::size_t cancel_one(implementation_type& impl, + asio::error_code& ec) + { + if (!impl.might_have_pending_waits) + { + ec = asio::error_code(); + return 0; + } + + ASIO_HANDLER_OPERATION((scheduler_.context(), + "deadline_timer", &impl, 0, "cancel_one")); + + std::size_t count = scheduler_.cancel_timer( + timer_queue_, impl.timer_data, 1); + if (count == 0) + impl.might_have_pending_waits = false; + ec = asio::error_code(); + return count; + } + + // Get the expiry time for the timer as an absolute time. + time_type expiry(const implementation_type& impl) const + { + return impl.expiry; + } + + // Get the expiry time for the timer as an absolute time. + time_type expires_at(const implementation_type& impl) const + { + return impl.expiry; + } + + // Get the expiry time for the timer relative to now. + duration_type expires_from_now(const implementation_type& impl) const + { + return Time_Traits::subtract(this->expiry(impl), Time_Traits::now()); + } + + // Set the expiry time for the timer as an absolute time. + std::size_t expires_at(implementation_type& impl, + const time_type& expiry_time, asio::error_code& ec) + { + std::size_t count = cancel(impl, ec); + impl.expiry = expiry_time; + ec = asio::error_code(); + return count; + } + + // Set the expiry time for the timer relative to now. + std::size_t expires_after(implementation_type& impl, + const duration_type& expiry_time, asio::error_code& ec) + { + return expires_at(impl, + Time_Traits::add(Time_Traits::now(), expiry_time), ec); + } + + // Set the expiry time for the timer relative to now. + std::size_t expires_from_now(implementation_type& impl, + const duration_type& expiry_time, asio::error_code& ec) + { + return expires_at(impl, + Time_Traits::add(Time_Traits::now(), expiry_time), ec); + } + + // Perform a blocking wait on the timer. + void wait(implementation_type& impl, asio::error_code& ec) + { + time_type now = Time_Traits::now(); + ec = asio::error_code(); + while (Time_Traits::less_than(now, impl.expiry) && !ec) + { + this->do_wait(Time_Traits::to_posix_duration( + Time_Traits::subtract(impl.expiry, now)), ec); + now = Time_Traits::now(); + } + } + + // Start an asynchronous wait on the timer. + template + void async_wait(implementation_type& impl, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef wait_handler op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler, io_ex); + + impl.might_have_pending_waits = true; + + ASIO_HANDLER_CREATION((scheduler_.context(), + *p.p, "deadline_timer", &impl, 0, "async_wait")); + + scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p); + p.v = p.p = 0; + } + +private: + // Helper function to wait given a duration type. The duration type should + // either be of type boost::posix_time::time_duration, or implement the + // required subset of its interface. + template + void do_wait(const Duration& timeout, asio::error_code& ec) + { +#if defined(ASIO_WINDOWS_RUNTIME) + std::this_thread::sleep_for( + std::chrono::seconds(timeout.total_seconds()) + + std::chrono::microseconds(timeout.total_microseconds())); + ec = asio::error_code(); +#else // defined(ASIO_WINDOWS_RUNTIME) + ::timeval tv; + tv.tv_sec = timeout.total_seconds(); + tv.tv_usec = timeout.total_microseconds() % 1000000; + socket_ops::select(0, 0, 0, 0, &tv, ec); +#endif // defined(ASIO_WINDOWS_RUNTIME) + } + + // The queue of timers. + timer_queue timer_queue_; + + // The object that schedules and executes timers. Usually a reactor. + timer_scheduler& scheduler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/dependent_type.hpp b/third_party/asio/1.18.2/include/asio/detail/dependent_type.hpp new file mode 100644 index 000000000..bcdfff3c1 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/dependent_type.hpp @@ -0,0 +1,36 @@ +// +// detail/dependent_type.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DEPENDENT_TYPE_HPP +#define ASIO_DETAIL_DEPENDENT_TYPE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct dependent_type +{ + typedef T type; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_DEPENDENT_TYPE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/descriptor_ops.hpp b/third_party/asio/1.18.2/include/asio/detail/descriptor_ops.hpp new file mode 100644 index 000000000..384aa8d1d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/descriptor_ops.hpp @@ -0,0 +1,139 @@ +// +// detail/descriptor_ops.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DESCRIPTOR_OPS_HPP +#define ASIO_DETAIL_DESCRIPTOR_OPS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + +#include +#include "asio/error.hpp" +#include "asio/error_code.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace descriptor_ops { + +// Descriptor state bits. +enum +{ + // The user wants a non-blocking descriptor. + user_set_non_blocking = 1, + + // The descriptor has been set non-blocking. + internal_non_blocking = 2, + + // Helper "state" used to determine whether the descriptor is non-blocking. + non_blocking = user_set_non_blocking | internal_non_blocking, + + // The descriptor may have been dup()-ed. + possible_dup = 4 +}; + +typedef unsigned char state_type; + +inline void get_last_error( + asio::error_code& ec, bool is_error_condition) +{ + if (!is_error_condition) + { + ec.assign(0, ec.category()); + } + else + { + ec = asio::error_code(errno, + asio::error::get_system_category()); + } +} + +ASIO_DECL int open(const char* path, int flags, + asio::error_code& ec); + +ASIO_DECL int close(int d, state_type& state, + asio::error_code& ec); + +ASIO_DECL bool set_user_non_blocking(int d, + state_type& state, bool value, asio::error_code& ec); + +ASIO_DECL bool set_internal_non_blocking(int d, + state_type& state, bool value, asio::error_code& ec); + +typedef iovec buf; + +ASIO_DECL std::size_t sync_read(int d, state_type state, buf* bufs, + std::size_t count, bool all_empty, asio::error_code& ec); + +ASIO_DECL std::size_t sync_read1(int d, state_type state, void* data, + std::size_t size, asio::error_code& ec); + +ASIO_DECL bool non_blocking_read(int d, buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred); + +ASIO_DECL bool non_blocking_read1(int d, void* data, std::size_t size, + asio::error_code& ec, std::size_t& bytes_transferred); + +ASIO_DECL std::size_t sync_write(int d, state_type state, + const buf* bufs, std::size_t count, bool all_empty, + asio::error_code& ec); + +ASIO_DECL std::size_t sync_write1(int d, state_type state, + const void* data, std::size_t size, asio::error_code& ec); + +ASIO_DECL bool non_blocking_write(int d, + const buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred); + +ASIO_DECL bool non_blocking_write1(int d, + const void* data, std::size_t size, + asio::error_code& ec, std::size_t& bytes_transferred); + +ASIO_DECL int ioctl(int d, state_type& state, long cmd, + ioctl_arg_type* arg, asio::error_code& ec); + +ASIO_DECL int fcntl(int d, int cmd, asio::error_code& ec); + +ASIO_DECL int fcntl(int d, int cmd, + long arg, asio::error_code& ec); + +ASIO_DECL int poll_read(int d, + state_type state, asio::error_code& ec); + +ASIO_DECL int poll_write(int d, + state_type state, asio::error_code& ec); + +ASIO_DECL int poll_error(int d, + state_type state, asio::error_code& ec); + +} // namespace descriptor_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/descriptor_ops.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_DESCRIPTOR_OPS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/descriptor_read_op.hpp b/third_party/asio/1.18.2/include/asio/detail/descriptor_read_op.hpp new file mode 100644 index 000000000..edc8d3402 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/descriptor_read_op.hpp @@ -0,0 +1,148 @@ +// +// detail/descriptor_read_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP +#define ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class descriptor_read_op_base : public reactor_op +{ +public: + descriptor_read_op_base(const asio::error_code& success_ec, + int descriptor, const MutableBufferSequence& buffers, + func_type complete_func) + : reactor_op(success_ec, + &descriptor_read_op_base::do_perform, complete_func), + descriptor_(descriptor), + buffers_(buffers) + { + } + + static status do_perform(reactor_op* base) + { + descriptor_read_op_base* o(static_cast(base)); + + typedef buffer_sequence_adapter bufs_type; + + status result; + if (bufs_type::is_single_buffer) + { + result = descriptor_ops::non_blocking_read1(o->descriptor_, + bufs_type::first(o->buffers_).data(), + bufs_type::first(o->buffers_).size(), + o->ec_, o->bytes_transferred_) ? done : not_done; + } + else + { + bufs_type bufs(o->buffers_); + result = descriptor_ops::non_blocking_read(o->descriptor_, + bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_) + ? done : not_done; + } + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_read", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + int descriptor_; + MutableBufferSequence buffers_; +}; + +template +class descriptor_read_op + : public descriptor_read_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(descriptor_read_op); + + descriptor_read_op(const asio::error_code& success_ec, + int descriptor, const MutableBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + : descriptor_read_op_base(success_ec, + descriptor, buffers, &descriptor_read_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + descriptor_read_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/descriptor_write_op.hpp b/third_party/asio/1.18.2/include/asio/detail/descriptor_write_op.hpp new file mode 100644 index 000000000..c2bc8675f --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/descriptor_write_op.hpp @@ -0,0 +1,148 @@ +// +// detail/descriptor_write_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP +#define ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class descriptor_write_op_base : public reactor_op +{ +public: + descriptor_write_op_base(const asio::error_code& success_ec, + int descriptor, const ConstBufferSequence& buffers, + func_type complete_func) + : reactor_op(success_ec, + &descriptor_write_op_base::do_perform, complete_func), + descriptor_(descriptor), + buffers_(buffers) + { + } + + static status do_perform(reactor_op* base) + { + descriptor_write_op_base* o(static_cast(base)); + + typedef buffer_sequence_adapter bufs_type; + + status result; + if (bufs_type::is_single_buffer) + { + result = descriptor_ops::non_blocking_write1(o->descriptor_, + bufs_type::first(o->buffers_).data(), + bufs_type::first(o->buffers_).size(), + o->ec_, o->bytes_transferred_) ? done : not_done; + } + else + { + bufs_type bufs(o->buffers_); + result = descriptor_ops::non_blocking_write(o->descriptor_, + bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_) + ? done : not_done; + } + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_write", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + int descriptor_; + ConstBufferSequence buffers_; +}; + +template +class descriptor_write_op + : public descriptor_write_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(descriptor_write_op); + + descriptor_write_op(const asio::error_code& success_ec, + int descriptor, const ConstBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + : descriptor_write_op_base(success_ec, + descriptor, buffers, &descriptor_write_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + descriptor_write_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/dev_poll_reactor.hpp b/third_party/asio/1.18.2/include/asio/detail/dev_poll_reactor.hpp new file mode 100644 index 000000000..f9e2f5a9b --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/dev_poll_reactor.hpp @@ -0,0 +1,218 @@ +// +// detail/dev_poll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DEV_POLL_REACTOR_HPP +#define ASIO_DETAIL_DEV_POLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_DEV_POLL) + +#include +#include +#include +#include "asio/detail/hash_map.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class dev_poll_reactor + : public execution_context_service_base +{ +public: + enum op_types { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + + // Per-descriptor data. + struct per_descriptor_data + { + }; + + // Constructor. + ASIO_DECL dev_poll_reactor(asio::execution_context& ctx); + + // Destructor. + ASIO_DECL ~dev_poll_reactor(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Recreate internal descriptors following a fork. + ASIO_DECL void notify_fork( + asio::execution_context::fork_event fork_ev); + + // Initialise the task. + ASIO_DECL void init_task(); + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + ASIO_DECL int register_descriptor(socket_type, per_descriptor_data&); + + // Register a descriptor with an associated single operation. Returns 0 on + // success, system error code on failure. + ASIO_DECL int register_internal_descriptor( + int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op); + + // Move descriptor registration from one descriptor_data object to another. + ASIO_DECL void move_descriptor(socket_type descriptor, + per_descriptor_data& target_descriptor_data, + per_descriptor_data& source_descriptor_data); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op, bool is_continuation) + { + scheduler_.post_immediate_completion(op, is_continuation); + } + + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + ASIO_DECL void start_op(int op_type, socket_type descriptor, + per_descriptor_data&, reactor_op* op, + bool is_continuation, bool allow_speculative); + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data&); + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. The reactor resources associated with + // the descriptor must be released by calling cleanup_descriptor_data. + ASIO_DECL void deregister_descriptor(socket_type descriptor, + per_descriptor_data&, bool closing); + + // Remove the descriptor's registration from the reactor. The reactor + // resources associated with the descriptor must be released by calling + // cleanup_descriptor_data. + ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data&); + + // Perform any post-deregistration cleanup tasks associated with the + // descriptor data. + ASIO_DECL void cleanup_descriptor_data(per_descriptor_data&); + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& queue); + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source); + + // Run /dev/poll once until interrupted or events are ready to be dispatched. + ASIO_DECL void run(long usec, op_queue& ops); + + // Interrupt the select loop. + ASIO_DECL void interrupt(); + +private: + // Create the /dev/poll file descriptor. Throws an exception if the descriptor + // cannot be created. + ASIO_DECL static int do_dev_poll_create(); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Get the timeout value for the /dev/poll DP_POLL operation. The timeout + // value is returned as a number of milliseconds. A return value of -1 + // indicates that the poll should block indefinitely. + ASIO_DECL int get_timeout(int msec); + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the dev_poll_reactor's mutex. + ASIO_DECL void cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec); + + // Add a pending event entry for the given descriptor. + ASIO_DECL ::pollfd& add_pending_event_change(int descriptor); + + // The scheduler implementation used to post completions. + scheduler& scheduler_; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The /dev/poll file descriptor. + int dev_poll_fd_; + + // Vector of /dev/poll events waiting to be written to the descriptor. + std::vector< ::pollfd> pending_event_changes_; + + // Hash map to associate a descriptor with a pending event change index. + hash_map pending_event_change_index_; + + // The interrupter is used to break a blocking DP_POLL operation. + select_interrupter interrupter_; + + // The queues of read, write and except operations. + reactor_op_queue op_queue_[max_ops]; + + // The timer queues. + timer_queue_set timer_queues_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/dev_poll_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/dev_poll_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_DEV_POLL) + +#endif // ASIO_DETAIL_DEV_POLL_REACTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/epoll_reactor.hpp b/third_party/asio/1.18.2/include/asio/detail/epoll_reactor.hpp new file mode 100644 index 000000000..af8ffe4e1 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/epoll_reactor.hpp @@ -0,0 +1,266 @@ +// +// detail/epoll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EPOLL_REACTOR_HPP +#define ASIO_DETAIL_EPOLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_EPOLL) + +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/conditionally_enabled_mutex.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/object_pool.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/execution_context.hpp" + +#if defined(ASIO_HAS_TIMERFD) +# include +#endif // defined(ASIO_HAS_TIMERFD) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class epoll_reactor + : public execution_context_service_base +{ +private: + // The mutex type used by this reactor. + typedef conditionally_enabled_mutex mutex; + +public: + enum op_types { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + + // Per-descriptor queues. + class descriptor_state : operation + { + friend class epoll_reactor; + friend class object_pool_access; + + descriptor_state* next_; + descriptor_state* prev_; + + mutex mutex_; + epoll_reactor* reactor_; + int descriptor_; + uint32_t registered_events_; + op_queue op_queue_[max_ops]; + bool try_speculative_[max_ops]; + bool shutdown_; + + ASIO_DECL descriptor_state(bool locking); + void set_ready_events(uint32_t events) { task_result_ = events; } + void add_ready_events(uint32_t events) { task_result_ |= events; } + ASIO_DECL operation* perform_io(uint32_t events); + ASIO_DECL static void do_complete( + void* owner, operation* base, + const asio::error_code& ec, std::size_t bytes_transferred); + }; + + // Per-descriptor data. + typedef descriptor_state* per_descriptor_data; + + // Constructor. + ASIO_DECL epoll_reactor(asio::execution_context& ctx); + + // Destructor. + ASIO_DECL ~epoll_reactor(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Recreate internal descriptors following a fork. + ASIO_DECL void notify_fork( + asio::execution_context::fork_event fork_ev); + + // Initialise the task. + ASIO_DECL void init_task(); + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + ASIO_DECL int register_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data); + + // Register a descriptor with an associated single operation. Returns 0 on + // success, system error code on failure. + ASIO_DECL int register_internal_descriptor( + int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op); + + // Move descriptor registration from one descriptor_data object to another. + ASIO_DECL void move_descriptor(socket_type descriptor, + per_descriptor_data& target_descriptor_data, + per_descriptor_data& source_descriptor_data); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op, bool is_continuation) + { + scheduler_.post_immediate_completion(op, is_continuation); + } + + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + ASIO_DECL void start_op(int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op, + bool is_continuation, bool allow_speculative); + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + ASIO_DECL void cancel_ops(socket_type descriptor, + per_descriptor_data& descriptor_data); + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. The reactor resources associated with + // the descriptor must be released by calling cleanup_descriptor_data. + ASIO_DECL void deregister_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data, bool closing); + + // Remove the descriptor's registration from the reactor. The reactor + // resources associated with the descriptor must be released by calling + // cleanup_descriptor_data. + ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data& descriptor_data); + + // Perform any post-deregistration cleanup tasks associated with the + // descriptor data. + ASIO_DECL void cleanup_descriptor_data( + per_descriptor_data& descriptor_data); + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& timer_queue); + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& timer_queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source); + + // Run epoll once until interrupted or events are ready to be dispatched. + ASIO_DECL void run(long usec, op_queue& ops); + + // Interrupt the select loop. + ASIO_DECL void interrupt(); + +private: + // The hint to pass to epoll_create to size its data structures. + enum { epoll_size = 20000 }; + + // Create the epoll file descriptor. Throws an exception if the descriptor + // cannot be created. + ASIO_DECL static int do_epoll_create(); + + // Create the timerfd file descriptor. Does not throw. + ASIO_DECL static int do_timerfd_create(); + + // Allocate a new descriptor state object. + ASIO_DECL descriptor_state* allocate_descriptor_state(); + + // Free an existing descriptor state object. + ASIO_DECL void free_descriptor_state(descriptor_state* s); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Called to recalculate and update the timeout. + ASIO_DECL void update_timeout(); + + // Get the timeout value for the epoll_wait call. The timeout value is + // returned as a number of milliseconds. A return value of -1 indicates + // that epoll_wait should block indefinitely. + ASIO_DECL int get_timeout(int msec); + +#if defined(ASIO_HAS_TIMERFD) + // Get the timeout value for the timer descriptor. The return value is the + // flag argument to be used when calling timerfd_settime. + ASIO_DECL int get_timeout(itimerspec& ts); +#endif // defined(ASIO_HAS_TIMERFD) + + // The scheduler implementation used to post completions. + scheduler& scheduler_; + + // Mutex to protect access to internal data. + mutex mutex_; + + // The interrupter is used to break a blocking epoll_wait call. + select_interrupter interrupter_; + + // The epoll file descriptor. + int epoll_fd_; + + // The timer file descriptor. + int timer_fd_; + + // The timer queues. + timer_queue_set timer_queues_; + + // Whether the service has been shut down. + bool shutdown_; + + // Mutex to protect access to the registered descriptors. + mutex registered_descriptors_mutex_; + + // Keep track of all registered descriptors. + object_pool registered_descriptors_; + + // Helper class to do post-perform_io cleanup. + struct perform_io_cleanup_on_block_exit; + friend struct perform_io_cleanup_on_block_exit; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/epoll_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/epoll_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_EPOLL) + +#endif // ASIO_DETAIL_EPOLL_REACTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/event.hpp b/third_party/asio/1.18.2/include/asio/detail/event.hpp new file mode 100644 index 000000000..d2b850af1 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/event.hpp @@ -0,0 +1,48 @@ +// +// detail/event.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EVENT_HPP +#define ASIO_DETAIL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_event.hpp" +#elif defined(ASIO_WINDOWS) +# include "asio/detail/win_event.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_event.hpp" +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +# include "asio/detail/std_event.hpp" +#else +# error Only Windows, POSIX and std::condition_variable are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) +typedef null_event event; +#elif defined(ASIO_WINDOWS) +typedef win_event event; +#elif defined(ASIO_HAS_PTHREADS) +typedef posix_event event; +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +typedef std_event event; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_EVENT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/eventfd_select_interrupter.hpp b/third_party/asio/1.18.2/include/asio/detail/eventfd_select_interrupter.hpp new file mode 100644 index 000000000..964e90c74 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/eventfd_select_interrupter.hpp @@ -0,0 +1,83 @@ +// +// detail/eventfd_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_EVENTFD) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class eventfd_select_interrupter +{ +public: + // Constructor. + ASIO_DECL eventfd_select_interrupter(); + + // Destructor. + ASIO_DECL ~eventfd_select_interrupter(); + + // Recreate the interrupter's descriptors. Used after a fork. + ASIO_DECL void recreate(); + + // Interrupt the select call. + ASIO_DECL void interrupt(); + + // Reset the select interrupter. Returns true if the reset was successful. + ASIO_DECL bool reset(); + + // Get the read descriptor to be passed to select. + int read_descriptor() const + { + return read_descriptor_; + } + +private: + // Open the descriptors. Throws on error. + ASIO_DECL void open_descriptors(); + + // Close the descriptors. + ASIO_DECL void close_descriptors(); + + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // 64bit value will be written on the other end of the connection and this + // descriptor will become readable. + int read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // 64bit non-zero value may be written to this to wake up the select which is + // waiting for the other end to become readable. This descriptor will only + // differ from the read descriptor when a pipe is used. + int write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/eventfd_select_interrupter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_EVENTFD) + +#endif // ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/executor_function.hpp b/third_party/asio/1.18.2/include/asio/detail/executor_function.hpp new file mode 100644 index 000000000..6b24f3aa1 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/executor_function.hpp @@ -0,0 +1,204 @@ +// +// detail/executor_function.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EXECUTOR_FUNCTION_HPP +#define ASIO_DETAIL_EXECUTOR_FUNCTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_MOVE) + +// Lightweight, move-only function object wrapper. +class executor_function +{ +public: + template + explicit executor_function(F f, const Alloc& a) + { + // Allocate and construct an object to wrap the function. + typedef impl impl_type; + typename impl_type::ptr p = { + detail::addressof(a), impl_type::ptr::allocate(a), 0 }; + impl_ = new (p.v) impl_type(ASIO_MOVE_CAST(F)(f), a); + p.v = 0; + } + + executor_function(executor_function&& other) ASIO_NOEXCEPT + : impl_(other.impl_) + { + other.impl_ = 0; + } + + ~executor_function() + { + if (impl_) + impl_->complete_(impl_, false); + } + + void operator()() + { + if (impl_) + { + impl_base* i = impl_; + impl_ = 0; + i->complete_(i, true); + } + } + +private: + // Base class for polymorphic function implementations. + struct impl_base + { + void (*complete_)(impl_base*, bool); + }; + + // Polymorphic function implementation. + template + struct impl : impl_base + { + ASIO_DEFINE_TAGGED_HANDLER_ALLOCATOR_PTR( + thread_info_base::executor_function_tag, impl); + + template + impl(ASIO_MOVE_ARG(F) f, const Alloc& a) + : function_(ASIO_MOVE_CAST(F)(f)), + allocator_(a) + { + complete_ = &executor_function::complete; + } + + Function function_; + Alloc allocator_; + }; + + // Helper to complete function invocation. + template + static void complete(impl_base* base, bool call) + { + // Take ownership of the function object. + impl* i(static_cast*>(base)); + Alloc allocator(i->allocator_); + typename impl::ptr p = { + detail::addressof(allocator), i, i }; + + // Make a copy of the function so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the function may be the true owner of the memory + // associated with the function. Consequently, a local copy of the function + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Function function(ASIO_MOVE_CAST(Function)(i->function_)); + p.reset(); + + // Make the upcall if required. + if (call) + { + asio_handler_invoke_helpers::invoke(function, function); + } + } + + impl_base* impl_; +}; + +#else // defined(ASIO_HAS_MOVE) + +// Not so lightweight, copyable function object wrapper. +class executor_function +{ +public: + template + explicit executor_function(const F& f, const Alloc&) + : impl_(new impl::type>(f)) + { + } + + void operator()() + { + impl_->complete_(impl_.get()); + } + +private: + // Base class for polymorphic function implementations. + struct impl_base + { + void (*complete_)(impl_base*); + }; + + // Polymorphic function implementation. + template + struct impl : impl_base + { + impl(const F& f) + : function_(f) + { + complete_ = &executor_function::complete; + } + + F function_; + }; + + // Helper to complete function invocation. + template + static void complete(impl_base* i) + { + static_cast*>(i)->function_(); + } + + shared_ptr impl_; +}; + +#endif // defined(ASIO_HAS_MOVE) + +// Lightweight, non-owning, copyable function object wrapper. +class executor_function_view +{ +public: + template + explicit executor_function_view(F& f) ASIO_NOEXCEPT + : complete_(&executor_function_view::complete), + function_(&f) + { + } + + void operator()() + { + complete_(function_); + } + +private: + // Helper to complete function invocation. + template + static void complete(void* f) + { + (*static_cast(f))(); + } + + void (*complete_)(void*); + void* function_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EXECUTOR_FUNCTION_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/executor_op.hpp b/third_party/asio/1.18.2/include/asio/detail/executor_op.hpp new file mode 100644 index 000000000..f97618c37 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/executor_op.hpp @@ -0,0 +1,84 @@ +// +// detail/executor_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EXECUTOR_OP_HPP +#define ASIO_DETAIL_EXECUTOR_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/scheduler_operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class executor_op : public Operation +{ +public: + ASIO_DEFINE_HANDLER_ALLOCATOR_PTR(executor_op); + + template + executor_op(ASIO_MOVE_ARG(H) h, const Alloc& allocator) + : Operation(&executor_op::do_complete), + handler_(ASIO_MOVE_CAST(H)(h)), + allocator_(allocator) + { + } + + static void do_complete(void* owner, Operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + executor_op* o(static_cast(base)); + Alloc allocator(o->allocator_); + ptr p = { detail::addressof(allocator), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + Handler handler(ASIO_MOVE_CAST(Handler)(o->handler_)); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN(()); + asio_handler_invoke_helpers::invoke(handler, handler); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + Alloc allocator_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EXECUTOR_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/fd_set_adapter.hpp b/third_party/asio/1.18.2/include/asio/detail/fd_set_adapter.hpp new file mode 100644 index 000000000..ade20ca56 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/fd_set_adapter.hpp @@ -0,0 +1,39 @@ +// +// detail/fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/posix_fd_set_adapter.hpp" +#include "asio/detail/win_fd_set_adapter.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef win_fd_set_adapter fd_set_adapter; +#else +typedef posix_fd_set_adapter fd_set_adapter; +#endif + +} // namespace detail +} // namespace asio + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/fenced_block.hpp b/third_party/asio/1.18.2/include/asio/detail/fenced_block.hpp new file mode 100644 index 000000000..08e47d671 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/fenced_block.hpp @@ -0,0 +1,80 @@ +// +// detail/fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_FENCED_BLOCK_HPP +#define ASIO_DETAIL_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) \ + || defined(ASIO_DISABLE_FENCED_BLOCK) +# include "asio/detail/null_fenced_block.hpp" +#elif defined(ASIO_HAS_STD_ATOMIC) +# include "asio/detail/std_fenced_block.hpp" +#elif defined(__MACH__) && defined(__APPLE__) +# include "asio/detail/macos_fenced_block.hpp" +#elif defined(__sun) +# include "asio/detail/solaris_fenced_block.hpp" +#elif defined(__GNUC__) && defined(__arm__) \ + && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) +# include "asio/detail/gcc_arm_fenced_block.hpp" +#elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) +# include "asio/detail/gcc_hppa_fenced_block.hpp" +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +# include "asio/detail/gcc_x86_fenced_block.hpp" +#elif defined(__GNUC__) \ + && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ + && !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) +# include "asio/detail/gcc_sync_fenced_block.hpp" +#elif defined(ASIO_WINDOWS) && !defined(UNDER_CE) +# include "asio/detail/win_fenced_block.hpp" +#else +# include "asio/detail/null_fenced_block.hpp" +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) \ + || defined(ASIO_DISABLE_FENCED_BLOCK) +typedef null_fenced_block fenced_block; +#elif defined(ASIO_HAS_STD_ATOMIC) +typedef std_fenced_block fenced_block; +#elif defined(__MACH__) && defined(__APPLE__) +typedef macos_fenced_block fenced_block; +#elif defined(__sun) +typedef solaris_fenced_block fenced_block; +#elif defined(__GNUC__) && defined(__arm__) \ + && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) +typedef gcc_arm_fenced_block fenced_block; +#elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) +typedef gcc_hppa_fenced_block fenced_block; +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +typedef gcc_x86_fenced_block fenced_block; +#elif defined(__GNUC__) \ + && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ + && !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) +typedef gcc_sync_fenced_block fenced_block; +#elif defined(ASIO_WINDOWS) && !defined(UNDER_CE) +typedef win_fenced_block fenced_block; +#else +typedef null_fenced_block fenced_block; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_FENCED_BLOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/functional.hpp b/third_party/asio/1.18.2/include/asio/detail/functional.hpp new file mode 100644 index 000000000..c4cfc393c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/functional.hpp @@ -0,0 +1,38 @@ +// +// detail/functional.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_FUNCTIONAL_HPP +#define ASIO_DETAIL_FUNCTIONAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include + +#if !defined(ASIO_HAS_STD_FUNCTION) +# include +#endif // !defined(ASIO_HAS_STD_FUNCTION) + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_STD_FUNCTION) +using std::function; +#else // defined(ASIO_HAS_STD_FUNCTION) +using boost::function; +#endif // defined(ASIO_HAS_STD_FUNCTION) + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_FUNCTIONAL_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/future.hpp b/third_party/asio/1.18.2/include/asio/detail/future.hpp new file mode 100644 index 000000000..9fe828e78 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/future.hpp @@ -0,0 +1,33 @@ +// +// detail/future.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_FUTURE_HPP +#define ASIO_DETAIL_FUTURE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#if defined(ASIO_HAS_STD_FUTURE) +# include +// Even though the future header is available, libstdc++ may not implement the +// std::future class itself. However, we need to have already included the +// future header to reliably test for _GLIBCXX_HAS_GTHREADS. +# if defined(__GNUC__) && !defined(ASIO_HAS_CLANG_LIBCXX) +# if defined(_GLIBCXX_HAS_GTHREADS) +# define ASIO_HAS_STD_FUTURE_CLASS 1 +# endif // defined(_GLIBCXX_HAS_GTHREADS) +# else // defined(__GNUC__) && !defined(ASIO_HAS_CLANG_LIBCXX) +# define ASIO_HAS_STD_FUTURE_CLASS 1 +# endif // defined(__GNUC__) && !defined(ASIO_HAS_CLANG_LIBCXX) +#endif // defined(ASIO_HAS_STD_FUTURE) + +#endif // ASIO_DETAIL_FUTURE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/gcc_arm_fenced_block.hpp b/third_party/asio/1.18.2/include/asio/detail/gcc_arm_fenced_block.hpp new file mode 100644 index 000000000..c879c22d6 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/gcc_arm_fenced_block.hpp @@ -0,0 +1,91 @@ +// +// detail/gcc_arm_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) && defined(__arm__) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_arm_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit gcc_arm_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit gcc_arm_fenced_block(full_t) + { + barrier(); + } + + // Destructor. + ~gcc_arm_fenced_block() + { + barrier(); + } + +private: + static void barrier() + { +#if defined(__ARM_ARCH_4__) \ + || defined(__ARM_ARCH_4T__) \ + || defined(__ARM_ARCH_5__) \ + || defined(__ARM_ARCH_5E__) \ + || defined(__ARM_ARCH_5T__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) \ + || defined(__ARM_ARCH_6__) \ + || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6T2__) +# if defined(__thumb__) + // This is just a placeholder and almost certainly not sufficient. + __asm__ __volatile__ ("" : : : "memory"); +# else // defined(__thumb__) + int a = 0, b = 0; + __asm__ __volatile__ ("swp %0, %1, [%2]" + : "=&r"(a) : "r"(1), "r"(&b) : "memory", "cc"); +# endif // defined(__thumb__) +#else + // ARMv7 and later. + __asm__ __volatile__ ("dmb" : : : "memory"); +#endif + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) && defined(__arm__) + +#endif // ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/gcc_hppa_fenced_block.hpp b/third_party/asio/1.18.2/include/asio/detail/gcc_hppa_fenced_block.hpp new file mode 100644 index 000000000..38caa4b01 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/gcc_hppa_fenced_block.hpp @@ -0,0 +1,68 @@ +// +// detail/gcc_hppa_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_hppa_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit gcc_hppa_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit gcc_hppa_fenced_block(full_t) + { + barrier(); + } + + // Destructor. + ~gcc_hppa_fenced_block() + { + barrier(); + } + +private: + static void barrier() + { + // This is just a placeholder and almost certainly not sufficient. + __asm__ __volatile__ ("" : : : "memory"); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) + +#endif // ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/gcc_sync_fenced_block.hpp b/third_party/asio/1.18.2/include/asio/detail/gcc_sync_fenced_block.hpp new file mode 100644 index 000000000..68fdcf722 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/gcc_sync_fenced_block.hpp @@ -0,0 +1,65 @@ +// +// detail/gcc_sync_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) \ + && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ + && !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_sync_fenced_block + : private noncopyable +{ +public: + enum half_or_full_t { half, full }; + + // Constructor. + explicit gcc_sync_fenced_block(half_or_full_t) + : value_(0) + { + __sync_lock_test_and_set(&value_, 1); + } + + // Destructor. + ~gcc_sync_fenced_block() + { + __sync_lock_release(&value_); + } + +private: + int value_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) + // && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) + // && !defined(__INTEL_COMPILER) && !defined(__ICL) + // && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) + +#endif // ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/gcc_x86_fenced_block.hpp b/third_party/asio/1.18.2/include/asio/detail/gcc_x86_fenced_block.hpp new file mode 100644 index 000000000..c21b2bad5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/gcc_x86_fenced_block.hpp @@ -0,0 +1,99 @@ +// +// detail/gcc_x86_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_x86_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit gcc_x86_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit gcc_x86_fenced_block(full_t) + { + lbarrier(); + } + + // Destructor. + ~gcc_x86_fenced_block() + { + sbarrier(); + } + +private: + static int barrier() + { + int r = 0, m = 1; + __asm__ __volatile__ ( + "xchgl %0, %1" : + "=r"(r), "=m"(m) : + "0"(1), "m"(m) : + "memory", "cc"); + return r; + } + + static void lbarrier() + { +#if defined(__SSE2__) +# if (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) + __builtin_ia32_lfence(); +# else // (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) + __asm__ __volatile__ ("lfence" ::: "memory"); +# endif // (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) +#else // defined(__SSE2__) + barrier(); +#endif // defined(__SSE2__) + } + + static void sbarrier() + { +#if defined(__SSE2__) +# if (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) + __builtin_ia32_sfence(); +# else // (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) + __asm__ __volatile__ ("sfence" ::: "memory"); +# endif // (__GNUC__ >= 4) && !defined(__INTEL_COMPILER) && !defined(__ICL) +#else // defined(__SSE2__) + barrier(); +#endif // defined(__SSE2__) + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +#endif // ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/global.hpp b/third_party/asio/1.18.2/include/asio/detail/global.hpp new file mode 100644 index 000000000..685cf90f2 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/global.hpp @@ -0,0 +1,52 @@ +// +// detail/global.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GLOBAL_HPP +#define ASIO_DETAIL_GLOBAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_global.hpp" +#elif defined(ASIO_WINDOWS) +# include "asio/detail/win_global.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_global.hpp" +#elif defined(ASIO_HAS_STD_CALL_ONCE) +# include "asio/detail/std_global.hpp" +#else +# error Only Windows, POSIX and std::call_once are supported! +#endif + +namespace asio { +namespace detail { + +template +inline T& global() +{ +#if !defined(ASIO_HAS_THREADS) + return null_global(); +#elif defined(ASIO_WINDOWS) + return win_global(); +#elif defined(ASIO_HAS_PTHREADS) + return posix_global(); +#elif defined(ASIO_HAS_STD_CALL_ONCE) + return std_global(); +#endif +} + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_GLOBAL_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/handler_alloc_helpers.hpp b/third_party/asio/1.18.2/include/asio/detail/handler_alloc_helpers.hpp new file mode 100644 index 000000000..3c562fc48 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/handler_alloc_helpers.hpp @@ -0,0 +1,284 @@ +// +// detail/handler_alloc_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/recycling_allocator.hpp" +#include "asio/detail/thread_info_base.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/handler_alloc_hook.hpp" + +#include "asio/detail/push_options.hpp" + +// Calls to asio_handler_allocate and asio_handler_deallocate must be made from +// a namespace that does not contain any overloads of these functions. The +// asio_handler_alloc_helpers namespace is defined here for that purpose. +namespace asio_handler_alloc_helpers { + +#if defined(ASIO_NO_DEPRECATED) +template +inline void error_if_hooks_are_defined(Handler& h) +{ + using asio::asio_handler_allocate; + // If you get an error here it is because some of your handlers still + // overload asio_handler_allocate, but this hook is no longer used. + (void)static_cast( + asio_handler_allocate(static_cast(0), + asio::detail::addressof(h))); + + using asio::asio_handler_deallocate; + // If you get an error here it is because some of your handlers still + // overload asio_handler_deallocate, but this hook is no longer used. + (void)static_cast( + asio_handler_deallocate(static_cast(0), + static_cast(0), asio::detail::addressof(h))); +} +#endif // defined(ASIO_NO_DEPRECATED) + +template +inline void* allocate(std::size_t s, Handler& h) +{ +#if !defined(ASIO_HAS_HANDLER_HOOKS) + return ::operator new(s); +#elif defined(ASIO_NO_DEPRECATED) + // The asio_handler_allocate hook is no longer used to obtain memory. + (void)&error_if_hooks_are_defined; + (void)h; +#if !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) + return asio::detail::thread_info_base::allocate( + asio::detail::thread_context::top_of_thread_call_stack(), s); +#else // !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) + return ::operator new(size); +#endif // !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) +#else + using asio::asio_handler_allocate; + return asio_handler_allocate(s, asio::detail::addressof(h)); +#endif +} + +template +inline void deallocate(void* p, std::size_t s, Handler& h) +{ +#if !defined(ASIO_HAS_HANDLER_HOOKS) + ::operator delete(p); +#elif defined(ASIO_NO_DEPRECATED) + // The asio_handler_allocate hook is no longer used to obtain memory. + (void)&error_if_hooks_are_defined; + (void)h; +#if !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) + asio::detail::thread_info_base::deallocate( + asio::detail::thread_context::top_of_thread_call_stack(), p, s); +#else // !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) + (void)s; + ::operator delete(p); +#endif // !defined(ASIO_DISABLE_SMALL_BLOCK_RECYCLING) +#else + using asio::asio_handler_deallocate; + asio_handler_deallocate(p, s, asio::detail::addressof(h)); +#endif +} + +} // namespace asio_handler_alloc_helpers + +namespace asio { +namespace detail { + +template +class hook_allocator +{ +public: + typedef T value_type; + + template + struct rebind + { + typedef hook_allocator other; + }; + + explicit hook_allocator(Handler& h) + : handler_(h) + { + } + + template + hook_allocator(const hook_allocator& a) + : handler_(a.handler_) + { + } + + T* allocate(std::size_t n) + { + return static_cast( + asio_handler_alloc_helpers::allocate(sizeof(T) * n, handler_)); + } + + void deallocate(T* p, std::size_t n) + { + asio_handler_alloc_helpers::deallocate(p, sizeof(T) * n, handler_); + } + +//private: + Handler& handler_; +}; + +template +class hook_allocator +{ +public: + typedef void value_type; + + template + struct rebind + { + typedef hook_allocator other; + }; + + explicit hook_allocator(Handler& h) + : handler_(h) + { + } + + template + hook_allocator(const hook_allocator& a) + : handler_(a.handler_) + { + } + +//private: + Handler& handler_; +}; + +template +struct get_hook_allocator +{ + typedef Allocator type; + + static type get(Handler&, const Allocator& a) + { + return a; + } +}; + +template +struct get_hook_allocator > +{ + typedef hook_allocator type; + + static type get(Handler& handler, const std::allocator&) + { + return type(handler); + } +}; + +} // namespace detail +} // namespace asio + +#define ASIO_DEFINE_HANDLER_PTR(op) \ + struct ptr \ + { \ + Handler* h; \ + op* v; \ + op* p; \ + ~ptr() \ + { \ + reset(); \ + } \ + static op* allocate(Handler& handler) \ + { \ + typedef typename ::asio::associated_allocator< \ + Handler>::type associated_allocator_type; \ + typedef typename ::asio::detail::get_hook_allocator< \ + Handler, associated_allocator_type>::type hook_allocator_type; \ + ASIO_REBIND_ALLOC(hook_allocator_type, op) a( \ + ::asio::detail::get_hook_allocator< \ + Handler, associated_allocator_type>::get( \ + handler, ::asio::get_associated_allocator(handler))); \ + return a.allocate(1); \ + } \ + void reset() \ + { \ + if (p) \ + { \ + p->~op(); \ + p = 0; \ + } \ + if (v) \ + { \ + typedef typename ::asio::associated_allocator< \ + Handler>::type associated_allocator_type; \ + typedef typename ::asio::detail::get_hook_allocator< \ + Handler, associated_allocator_type>::type hook_allocator_type; \ + ASIO_REBIND_ALLOC(hook_allocator_type, op) a( \ + ::asio::detail::get_hook_allocator< \ + Handler, associated_allocator_type>::get( \ + *h, ::asio::get_associated_allocator(*h))); \ + a.deallocate(static_cast(v), 1); \ + v = 0; \ + } \ + } \ + } \ + /**/ + +#define ASIO_DEFINE_TAGGED_HANDLER_ALLOCATOR_PTR(purpose, op) \ + struct ptr \ + { \ + const Alloc* a; \ + void* v; \ + op* p; \ + ~ptr() \ + { \ + reset(); \ + } \ + static op* allocate(const Alloc& a) \ + { \ + typedef typename ::asio::detail::get_recycling_allocator< \ + Alloc, purpose>::type recycling_allocator_type; \ + ASIO_REBIND_ALLOC(recycling_allocator_type, op) a1( \ + ::asio::detail::get_recycling_allocator< \ + Alloc, purpose>::get(a)); \ + return a1.allocate(1); \ + } \ + void reset() \ + { \ + if (p) \ + { \ + p->~op(); \ + p = 0; \ + } \ + if (v) \ + { \ + typedef typename ::asio::detail::get_recycling_allocator< \ + Alloc, purpose>::type recycling_allocator_type; \ + ASIO_REBIND_ALLOC(recycling_allocator_type, op) a1( \ + ::asio::detail::get_recycling_allocator< \ + Alloc, purpose>::get(*a)); \ + a1.deallocate(static_cast(v), 1); \ + v = 0; \ + } \ + } \ + } \ + /**/ + +#define ASIO_DEFINE_HANDLER_ALLOCATOR_PTR(op) \ + ASIO_DEFINE_TAGGED_HANDLER_ALLOCATOR_PTR( \ + ::asio::detail::thread_info_base::default_tag, op ) \ + /**/ + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/handler_cont_helpers.hpp b/third_party/asio/1.18.2/include/asio/detail/handler_cont_helpers.hpp new file mode 100644 index 000000000..5d47465bd --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/handler_cont_helpers.hpp @@ -0,0 +1,45 @@ +// +// detail/handler_cont_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_CONT_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_CONT_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/memory.hpp" +#include "asio/handler_continuation_hook.hpp" + +#include "asio/detail/push_options.hpp" + +// Calls to asio_handler_is_continuation must be made from a namespace that +// does not contain overloads of this function. This namespace is defined here +// for that purpose. +namespace asio_handler_cont_helpers { + +template +inline bool is_continuation(Context& context) +{ +#if !defined(ASIO_HAS_HANDLER_HOOKS) + return false; +#else + using asio::asio_handler_is_continuation; + return asio_handler_is_continuation( + asio::detail::addressof(context)); +#endif +} + +} // namespace asio_handler_cont_helpers + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_CONT_HELPERS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/handler_invoke_helpers.hpp b/third_party/asio/1.18.2/include/asio/detail/handler_invoke_helpers.hpp new file mode 100644 index 000000000..86b8bf2b4 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/handler_invoke_helpers.hpp @@ -0,0 +1,80 @@ +// +// detail/handler_invoke_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/memory.hpp" +#include "asio/handler_invoke_hook.hpp" + +#include "asio/detail/push_options.hpp" + +// Calls to asio_handler_invoke must be made from a namespace that does not +// contain overloads of this function. The asio_handler_invoke_helpers +// namespace is defined here for that purpose. +namespace asio_handler_invoke_helpers { + +#if defined(ASIO_NO_DEPRECATED) +template +inline void error_if_hook_is_defined(Function& function, Context& context) +{ + using asio::asio_handler_invoke; + // If you get an error here it is because some of your handlers still + // overload asio_handler_invoke, but this hook is no longer used. + (void)static_cast( + asio_handler_invoke(function, asio::detail::addressof(context))); +} +#endif // defined(ASIO_NO_DEPRECATED) + +template +inline void invoke(Function& function, Context& context) +{ +#if !defined(ASIO_HAS_HANDLER_HOOKS) + Function tmp(function); + tmp(); +#elif defined(ASIO_NO_DEPRECATED) + // The asio_handler_invoke hook is no longer used to invoke the function. + (void)&error_if_hook_is_defined; + (void)context; + function(); +#else + using asio::asio_handler_invoke; + asio_handler_invoke(function, asio::detail::addressof(context)); +#endif +} + +template +inline void invoke(const Function& function, Context& context) +{ +#if !defined(ASIO_HAS_HANDLER_HOOKS) + Function tmp(function); + tmp(); +#elif defined(ASIO_NO_DEPRECATED) + // The asio_handler_invoke hook is no longer used to invoke the function. + (void)&error_if_hook_is_defined; + (void)context; + Function tmp(function); + tmp(); +#else + using asio::asio_handler_invoke; + asio_handler_invoke(function, asio::detail::addressof(context)); +#endif +} + +} // namespace asio_handler_invoke_helpers + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/handler_tracking.hpp b/third_party/asio/1.18.2/include/asio/detail/handler_tracking.hpp new file mode 100644 index 000000000..14195cd43 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/handler_tracking.hpp @@ -0,0 +1,264 @@ +// +// detail/handler_tracking.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_TRACKING_HPP +#define ASIO_DETAIL_HANDLER_TRACKING_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +namespace asio { + +class execution_context; + +} // namespace asio + +#if defined(ASIO_CUSTOM_HANDLER_TRACKING) +# include ASIO_CUSTOM_HANDLER_TRACKING +#elif defined(ASIO_ENABLE_HANDLER_TRACKING) +# include "asio/error_code.hpp" +# include "asio/detail/cstdint.hpp" +# include "asio/detail/static_mutex.hpp" +# include "asio/detail/tss_ptr.hpp" +#endif // defined(ASIO_ENABLE_HANDLER_TRACKING) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_CUSTOM_HANDLER_TRACKING) + +// The user-specified header must define the following macros: +// - ASIO_INHERIT_TRACKED_HANDLER +// - ASIO_ALSO_INHERIT_TRACKED_HANDLER +// - ASIO_HANDLER_TRACKING_INIT +// - ASIO_HANDLER_CREATION(args) +// - ASIO_HANDLER_COMPLETION(args) +// - ASIO_HANDLER_INVOCATION_BEGIN(args) +// - ASIO_HANDLER_INVOCATION_END +// - ASIO_HANDLER_OPERATION(args) +// - ASIO_HANDLER_REACTOR_REGISTRATION(args) +// - ASIO_HANDLER_REACTOR_DEREGISTRATION(args) +// - ASIO_HANDLER_REACTOR_READ_EVENT +// - ASIO_HANDLER_REACTOR_WRITE_EVENT +// - ASIO_HANDLER_REACTOR_ERROR_EVENT +// - ASIO_HANDLER_REACTOR_EVENTS(args) +// - ASIO_HANDLER_REACTOR_OPERATION(args) + +# if !defined(ASIO_ENABLE_HANDLER_TRACKING) +# define ASIO_ENABLE_HANDLER_TRACKING 1 +# endif /// !defined(ASIO_ENABLE_HANDLER_TRACKING) + +#elif defined(ASIO_ENABLE_HANDLER_TRACKING) + +class handler_tracking +{ +public: + class completion; + + // Base class for objects containing tracked handlers. + class tracked_handler + { + private: + // Only the handler_tracking class will have access to the id. + friend class handler_tracking; + friend class completion; + uint64_t id_; + + protected: + // Constructor initialises with no id. + tracked_handler() : id_(0) {} + + // Prevent deletion through this type. + ~tracked_handler() {} + }; + + // Initialise the tracking system. + ASIO_DECL static void init(); + + class location + { + public: + // Constructor adds a location to the stack. + ASIO_DECL explicit location(const char* file, + int line, const char* func); + + // Destructor removes a location from the stack. + ASIO_DECL ~location(); + + private: + // Disallow copying and assignment. + location(const location&) ASIO_DELETED; + location& operator=(const location&) ASIO_DELETED; + + friend class handler_tracking; + const char* file_; + int line_; + const char* func_; + location* next_; + }; + + // Record the creation of a tracked handler. + ASIO_DECL static void creation( + execution_context& context, tracked_handler& h, + const char* object_type, void* object, + uintmax_t native_handle, const char* op_name); + + class completion + { + public: + // Constructor records that handler is to be invoked with no arguments. + ASIO_DECL explicit completion(const tracked_handler& h); + + // Destructor records only when an exception is thrown from the handler, or + // if the memory is being freed without the handler having been invoked. + ASIO_DECL ~completion(); + + // Records that handler is to be invoked with no arguments. + ASIO_DECL void invocation_begin(); + + // Records that handler is to be invoked with one arguments. + ASIO_DECL void invocation_begin(const asio::error_code& ec); + + // Constructor records that handler is to be invoked with two arguments. + ASIO_DECL void invocation_begin( + const asio::error_code& ec, std::size_t bytes_transferred); + + // Constructor records that handler is to be invoked with two arguments. + ASIO_DECL void invocation_begin( + const asio::error_code& ec, int signal_number); + + // Constructor records that handler is to be invoked with two arguments. + ASIO_DECL void invocation_begin( + const asio::error_code& ec, const char* arg); + + // Record that handler invocation has ended. + ASIO_DECL void invocation_end(); + + private: + friend class handler_tracking; + uint64_t id_; + bool invoked_; + completion* next_; + }; + + // Record an operation that is not directly associated with a handler. + ASIO_DECL static void operation(execution_context& context, + const char* object_type, void* object, + uintmax_t native_handle, const char* op_name); + + // Record that a descriptor has been registered with the reactor. + ASIO_DECL static void reactor_registration(execution_context& context, + uintmax_t native_handle, uintmax_t registration); + + // Record that a descriptor has been deregistered from the reactor. + ASIO_DECL static void reactor_deregistration(execution_context& context, + uintmax_t native_handle, uintmax_t registration); + + // Record a reactor-based operation that is associated with a handler. + ASIO_DECL static void reactor_events(execution_context& context, + uintmax_t registration, unsigned events); + + // Record a reactor-based operation that is associated with a handler. + ASIO_DECL static void reactor_operation( + const tracked_handler& h, const char* op_name, + const asio::error_code& ec); + + // Record a reactor-based operation that is associated with a handler. + ASIO_DECL static void reactor_operation( + const tracked_handler& h, const char* op_name, + const asio::error_code& ec, std::size_t bytes_transferred); + + // Write a line of output. + ASIO_DECL static void write_line(const char* format, ...); + +private: + struct tracking_state; + ASIO_DECL static tracking_state* get_state(); +}; + +# define ASIO_INHERIT_TRACKED_HANDLER \ + : public asio::detail::handler_tracking::tracked_handler + +# define ASIO_ALSO_INHERIT_TRACKED_HANDLER \ + , public asio::detail::handler_tracking::tracked_handler + +# define ASIO_HANDLER_TRACKING_INIT \ + asio::detail::handler_tracking::init() + +# define ASIO_HANDLER_LOCATION(args) \ + asio::detail::handler_tracking::location tracked_location args + +# define ASIO_HANDLER_CREATION(args) \ + asio::detail::handler_tracking::creation args + +# define ASIO_HANDLER_COMPLETION(args) \ + asio::detail::handler_tracking::completion tracked_completion args + +# define ASIO_HANDLER_INVOCATION_BEGIN(args) \ + tracked_completion.invocation_begin args + +# define ASIO_HANDLER_INVOCATION_END \ + tracked_completion.invocation_end() + +# define ASIO_HANDLER_OPERATION(args) \ + asio::detail::handler_tracking::operation args + +# define ASIO_HANDLER_REACTOR_REGISTRATION(args) \ + asio::detail::handler_tracking::reactor_registration args + +# define ASIO_HANDLER_REACTOR_DEREGISTRATION(args) \ + asio::detail::handler_tracking::reactor_deregistration args + +# define ASIO_HANDLER_REACTOR_READ_EVENT 1 +# define ASIO_HANDLER_REACTOR_WRITE_EVENT 2 +# define ASIO_HANDLER_REACTOR_ERROR_EVENT 4 + +# define ASIO_HANDLER_REACTOR_EVENTS(args) \ + asio::detail::handler_tracking::reactor_events args + +# define ASIO_HANDLER_REACTOR_OPERATION(args) \ + asio::detail::handler_tracking::reactor_operation args + +#else // defined(ASIO_ENABLE_HANDLER_TRACKING) + +# define ASIO_INHERIT_TRACKED_HANDLER +# define ASIO_ALSO_INHERIT_TRACKED_HANDLER +# define ASIO_HANDLER_TRACKING_INIT (void)0 +# define ASIO_HANDLER_LOCATION(loc) (void)0 +# define ASIO_HANDLER_CREATION(args) (void)0 +# define ASIO_HANDLER_COMPLETION(args) (void)0 +# define ASIO_HANDLER_INVOCATION_BEGIN(args) (void)0 +# define ASIO_HANDLER_INVOCATION_END (void)0 +# define ASIO_HANDLER_OPERATION(args) (void)0 +# define ASIO_HANDLER_REACTOR_REGISTRATION(args) (void)0 +# define ASIO_HANDLER_REACTOR_DEREGISTRATION(args) (void)0 +# define ASIO_HANDLER_REACTOR_READ_EVENT 0 +# define ASIO_HANDLER_REACTOR_WRITE_EVENT 0 +# define ASIO_HANDLER_REACTOR_ERROR_EVENT 0 +# define ASIO_HANDLER_REACTOR_EVENTS(args) (void)0 +# define ASIO_HANDLER_REACTOR_OPERATION(args) (void)0 + +#endif // defined(ASIO_ENABLE_HANDLER_TRACKING) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/handler_tracking.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_HANDLER_TRACKING_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/handler_type_requirements.hpp b/third_party/asio/1.18.2/include/asio/detail/handler_type_requirements.hpp new file mode 100644 index 000000000..06de6c9cf --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/handler_type_requirements.hpp @@ -0,0 +1,556 @@ +// +// detail/handler_type_requirements.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_HPP +#define ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +// Older versions of gcc have difficulty compiling the sizeof expressions where +// we test the handler type requirements. We'll disable checking of handler type +// requirements for those compilers, but otherwise enable it by default. +#if !defined(ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) +# if !defined(__GNUC__) || (__GNUC__ >= 4) +# define ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS 1 +# endif // !defined(__GNUC__) || (__GNUC__ >= 4) +#endif // !defined(ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) + +// With C++0x we can use a combination of enhanced SFINAE and static_assert to +// generate better template error messages. As this technique is not yet widely +// portable, we'll only enable it for tested compilers. +#if !defined(ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(ASIO_MSVC) +# if (_MSC_VER >= 1600) +# define ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // (_MSC_VER >= 1600) +# endif // defined(ASIO_MSVC) +# if defined(__clang__) +# if __has_feature(__cxx_static_assert__) +# define ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // __has_feature(cxx_static_assert) +# endif // defined(__clang__) +#endif // !defined(ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) + +#if defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) +# include "asio/async_result.hpp" +#endif // defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +namespace asio { +namespace detail { + +#if defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +# if defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +template +auto zero_arg_copyable_handler_test(Handler h, void*) + -> decltype( + sizeof(Handler(static_cast(h))), + ((h)()), + char(0)); + +template +char (&zero_arg_copyable_handler_test(Handler, ...))[2]; + +template +auto one_arg_handler_test(Handler h, Arg1* a1) + -> decltype( + sizeof(Handler(ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1)), + char(0)); + +template +char (&one_arg_handler_test(Handler h, ...))[2]; + +template +auto two_arg_handler_test(Handler h, Arg1* a1, Arg2* a2) + -> decltype( + sizeof(Handler(ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, *a2)), + char(0)); + +template +char (&two_arg_handler_test(Handler, ...))[2]; + +template +auto two_arg_move_handler_test(Handler h, Arg1* a1, Arg2* a2) + -> decltype( + sizeof(Handler(ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, ASIO_MOVE_CAST(Arg2)(*a2))), + char(0)); + +template +char (&two_arg_move_handler_test(Handler, ...))[2]; + +# define ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) \ + static_assert(expr, msg); + +# else // defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +# define ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) + +# endif // defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +template T& lvref(); +template T& lvref(T); +template const T& clvref(); +template const T& clvref(T); +#if defined(ASIO_HAS_MOVE) +template T rvref(); +template T rvref(T); +#else // defined(ASIO_HAS_MOVE) +template const T& rvref(); +template const T& rvref(T); +#endif // defined(ASIO_HAS_MOVE) +template char argbyv(T); + +template +struct handler_type_requirements +{ +}; + +#define ASIO_LEGACY_COMPLETION_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void()) asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::zero_arg_copyable_handler_test( \ + asio::detail::clvref< \ + asio_true_handler_type>(), 0)) == 1, \ + "CompletionHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::clvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()(), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_READ_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, std::size_t)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "ReadHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_WRITE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, std::size_t)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "WriteHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_ACCEPT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::one_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0))) == 1, \ + "AcceptHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_MOVE_ACCEPT_HANDLER_CHECK( \ + handler_type, handler, socket_type) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, socket_type)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_move_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "MoveAcceptHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::rvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_CONNECT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::one_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0))) == 1, \ + "ConnectHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_RANGE_CONNECT_HANDLER_CHECK( \ + handler_type, handler, endpoint_type) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, endpoint_type)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "RangeConnectHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_ITERATOR_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, iter_type)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "IteratorConnectHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_RESOLVE_HANDLER_CHECK( \ + handler_type, handler, range_type) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, range_type)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "ResolveHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_WAIT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::one_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0))) == 1, \ + "WaitHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_SIGNAL_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, int)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "SignalHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::one_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0))) == 1, \ + "HandshakeHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code, std::size_t)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::two_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0), \ + static_cast(0))) == 1, \ + "BufferedHandshakeHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref(), \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#define ASIO_SHUTDOWN_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef ASIO_HANDLER_TYPE(handler_type, \ + void(asio::error_code)) \ + asio_true_handler_type; \ + \ + ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(asio::detail::one_arg_handler_test( \ + asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast(0))) == 1, \ + "ShutdownHandler type requirements not met") \ + \ + typedef asio::detail::handler_type_requirements< \ + sizeof( \ + asio::detail::argbyv( \ + asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + asio::detail::lvref< \ + asio_true_handler_type>()( \ + asio::detail::lvref()), \ + char(0))> ASIO_UNUSED_TYPEDEF + +#else // !defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +#define ASIO_LEGACY_COMPLETION_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_READ_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_WRITE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_ACCEPT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_MOVE_ACCEPT_HANDLER_CHECK( \ + handler_type, handler, socket_type) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_CONNECT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_RANGE_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_ITERATOR_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_RESOLVE_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_WAIT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_SIGNAL_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#define ASIO_SHUTDOWN_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int ASIO_UNUSED_TYPEDEF + +#endif // !defined(ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/handler_work.hpp b/third_party/asio/1.18.2/include/asio/detail/handler_work.hpp new file mode 100644 index 000000000..b9cf4f44b --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/handler_work.hpp @@ -0,0 +1,514 @@ +// +// detail/handler_work.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_WORK_HPP +#define ASIO_DETAIL_HANDLER_WORK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_executor.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/allocator.hpp" +#include "asio/execution/blocking.hpp" +#include "asio/execution/execute.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/outstanding_work.hpp" +#include "asio/executor_work_guard.hpp" +#include "asio/prefer.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class executor; +class io_context; + +#if !defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT) + +class any_io_executor; + +#endif // !defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT) + +namespace execution { + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template class any_executor; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template class any_executor; + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +} // namespace execution +namespace detail { + +template +class handler_work_base +{ +public: + explicit handler_work_base(int, int, const Executor& ex) ASIO_NOEXCEPT + : executor_(asio::prefer(ex, execution::outstanding_work.tracked)) + { + } + + template + handler_work_base(const Executor& ex, + const OtherExecutor&) ASIO_NOEXCEPT + : executor_(asio::prefer(ex, execution::outstanding_work.tracked)) + { + } + + handler_work_base(const handler_work_base& other) ASIO_NOEXCEPT + : executor_(other.executor_) + { + } + +#if defined(ASIO_HAS_MOVE) + handler_work_base(handler_work_base&& other) ASIO_NOEXCEPT + : executor_(ASIO_MOVE_CAST(executor_type)(other.executor_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + bool owns_work() const ASIO_NOEXCEPT + { + return true; + } + + template + void dispatch(Function& function, Handler& handler) + { + execution::execute( + asio::prefer(executor_, + execution::blocking.possibly, + execution::allocator((get_associated_allocator)(handler))), + ASIO_MOVE_CAST(Function)(function)); + } + +private: + typedef typename decay< + typename prefer_result::type + >::type executor_type; + + executor_type executor_; +}; + +template +class handler_work_base::value + && (!is_same::value + || !is_same::value) + >::type> +{ +public: + explicit handler_work_base(int, int, const Executor& ex) ASIO_NOEXCEPT + : executor_(ex), + owns_work_(true) + { + executor_.on_work_started(); + } + + handler_work_base(const Executor& ex, + const Executor& candidate) ASIO_NOEXCEPT + : executor_(ex), + owns_work_(ex != candidate) + { + if (owns_work_) + executor_.on_work_started(); + } + + template + handler_work_base(const Executor& ex, + const OtherExecutor&) ASIO_NOEXCEPT + : executor_(ex), + owns_work_(true) + { + executor_.on_work_started(); + } + + handler_work_base(const handler_work_base& other) ASIO_NOEXCEPT + : executor_(other.executor_), + owns_work_(other.owns_work_) + { + if (owns_work_) + executor_.on_work_started(); + } + +#if defined(ASIO_HAS_MOVE) + handler_work_base(handler_work_base&& other) ASIO_NOEXCEPT + : executor_(ASIO_MOVE_CAST(Executor)(other.executor_)), + owns_work_(other.owns_work_) + { + other.owns_work_ = false; + } +#endif // defined(ASIO_HAS_MOVE) + + ~handler_work_base() + { + if (owns_work_) + executor_.on_work_finished(); + } + + bool owns_work() const ASIO_NOEXCEPT + { + return owns_work_; + } + + template + void dispatch(Function& function, Handler& handler) + { + executor_.dispatch(ASIO_MOVE_CAST(Function)(function), + asio::get_associated_allocator(handler)); + } + +private: + Executor executor_; + bool owns_work_; +}; + +template +class handler_work_base::value + >::type> +{ +public: + explicit handler_work_base(int, int, const Executor&) + { + } + + bool owns_work() const ASIO_NOEXCEPT + { + return false; + } + + template + void dispatch(Function& function, Handler& handler) + { + // When using a native implementation, I/O completion handlers are + // already dispatched according to the execution context's executor's + // rules. We can call the function directly. + asio_handler_invoke_helpers::invoke(function, handler); + } +}; + +template +class handler_work_base +{ +public: + explicit handler_work_base(int, int, const Executor& ex) ASIO_NOEXCEPT +#if !defined(ASIO_NO_TYPEID) + : executor_( + ex.target_type() == typeid(typename IoContext::executor_type) + ? Executor() : ex) +#else // !defined(ASIO_NO_TYPEID) + : executor_(ex) +#endif // !defined(ASIO_NO_TYPEID) + { + if (executor_) + executor_.on_work_started(); + } + + handler_work_base(const Executor& ex, + const Executor& candidate) ASIO_NOEXCEPT + : executor_(ex != candidate ? ex : Executor()) + { + if (executor_) + executor_.on_work_started(); + } + + template + handler_work_base(const Executor& ex, + const OtherExecutor&) ASIO_NOEXCEPT + : executor_(ex) + { + executor_.on_work_started(); + } + + handler_work_base(const handler_work_base& other) ASIO_NOEXCEPT + : executor_(other.executor_) + { + if (executor_) + executor_.on_work_started(); + } + +#if defined(ASIO_HAS_MOVE) + handler_work_base(handler_work_base&& other) ASIO_NOEXCEPT + : executor_(ASIO_MOVE_CAST(Executor)(other.executor_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + ~handler_work_base() + { + if (executor_) + executor_.on_work_finished(); + } + + bool owns_work() const ASIO_NOEXCEPT + { + return !!executor_; + } + + template + void dispatch(Function& function, Handler& handler) + { + executor_.dispatch(ASIO_MOVE_CAST(Function)(function), + asio::get_associated_allocator(handler)); + } + +private: + Executor executor_; +}; + +template < +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + typename... SupportableProperties, +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + typename IoContext, typename PolymorphicExecutor> +class handler_work_base< +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + execution::any_executor, +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + execution::any_executor, +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + void, IoContext, PolymorphicExecutor> +{ +public: + typedef +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + execution::any_executor +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + execution::any_executor +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + executor_type; + + explicit handler_work_base(int, int, + const executor_type& ex) ASIO_NOEXCEPT +#if !defined(ASIO_NO_TYPEID) + : executor_( + ex.target_type() == typeid(typename IoContext::executor_type) + ? executor_type() + : asio::prefer(ex, execution::outstanding_work.tracked)) +#else // !defined(ASIO_NO_TYPEID) + : executor_(asio::prefer(ex, execution::outstanding_work.tracked)) +#endif // !defined(ASIO_NO_TYPEID) + { + } + + handler_work_base(const executor_type& ex, + const executor_type& candidate) ASIO_NOEXCEPT + : executor_(ex != candidate ? ex : executor_type()) + { + } + + template + handler_work_base(const executor_type& ex, + const OtherExecutor&) ASIO_NOEXCEPT + : executor_(asio::prefer(ex, execution::outstanding_work.tracked)) + { + } + + handler_work_base(const handler_work_base& other) ASIO_NOEXCEPT + : executor_(other.executor_) + { + } + +#if defined(ASIO_HAS_MOVE) + handler_work_base(handler_work_base&& other) ASIO_NOEXCEPT + : executor_(ASIO_MOVE_CAST(executor_type)(other.executor_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + bool owns_work() const ASIO_NOEXCEPT + { + return !!executor_; + } + + template + void dispatch(Function& function, Handler&) + { + execution::execute( + asio::prefer(executor_, execution::blocking.possibly), + ASIO_MOVE_CAST(Function)(function)); + } + +private: + executor_type executor_; +}; + +#if !defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT) + +template +class handler_work_base::value + >::type> +{ +public: + typedef Executor executor_type; + + explicit handler_work_base(int, int, + const executor_type& ex) ASIO_NOEXCEPT +#if !defined(ASIO_NO_TYPEID) + : executor_( + ex.target_type() == typeid(typename IoContext::executor_type) + ? executor_type() + : asio::prefer(ex, execution::outstanding_work.tracked)) +#else // !defined(ASIO_NO_TYPEID) + : executor_(asio::prefer(ex, execution::outstanding_work.tracked)) +#endif // !defined(ASIO_NO_TYPEID) + { + } + + handler_work_base(const executor_type& ex, + const executor_type& candidate) ASIO_NOEXCEPT + : executor_(ex != candidate ? ex : executor_type()) + { + } + + template + handler_work_base(const executor_type& ex, + const OtherExecutor&) ASIO_NOEXCEPT + : executor_(asio::prefer(ex, execution::outstanding_work.tracked)) + { + } + + handler_work_base(const handler_work_base& other) ASIO_NOEXCEPT + : executor_(other.executor_) + { + } + +#if defined(ASIO_HAS_MOVE) + handler_work_base(handler_work_base&& other) ASIO_NOEXCEPT + : executor_(ASIO_MOVE_CAST(executor_type)(other.executor_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + bool owns_work() const ASIO_NOEXCEPT + { + return !!executor_; + } + + template + void dispatch(Function& function, Handler&) + { + execution::execute( + asio::prefer(executor_, execution::blocking.possibly), + ASIO_MOVE_CAST(Function)(function)); + } + +private: + executor_type executor_; +}; + +#endif // !defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT) + +template +class handler_work : + handler_work_base, + handler_work_base::type, IoExecutor> +{ +public: + typedef handler_work_base base1_type; + typedef handler_work_base::type, IoExecutor> base2_type; + + handler_work(Handler& handler, const IoExecutor& io_ex) ASIO_NOEXCEPT + : base1_type(0, 0, io_ex), + base2_type(asio::get_associated_executor(handler, io_ex), io_ex) + { + } + + template + void complete(Function& function, Handler& handler) + { + if (!base1_type::owns_work() && !base2_type::owns_work()) + { + // When using a native implementation, I/O completion handlers are + // already dispatched according to the execution context's executor's + // rules. We can call the function directly. + asio_handler_invoke_helpers::invoke(function, handler); + } + else + { + base2_type::dispatch(function, handler); + } + } +}; + +template +class handler_work< + Handler, IoExecutor, + typename enable_if< + is_same< + typename associated_executor::asio_associated_executor_is_unspecialised, + void + >::value + >::type> : handler_work_base +{ +public: + typedef handler_work_base base1_type; + + handler_work(Handler&, const IoExecutor& io_ex) ASIO_NOEXCEPT + : base1_type(0, 0, io_ex) + { + } + + template + void complete(Function& function, Handler& handler) + { + if (!base1_type::owns_work()) + { + // When using a native implementation, I/O completion handlers are + // already dispatched according to the execution context's executor's + // rules. We can call the function directly. + asio_handler_invoke_helpers::invoke(function, handler); + } + else + { + base1_type::dispatch(function, handler); + } + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_WORK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/hash_map.hpp b/third_party/asio/1.18.2/include/asio/detail/hash_map.hpp new file mode 100644 index 000000000..78edec8bd --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/hash_map.hpp @@ -0,0 +1,331 @@ +// +// detail/hash_map.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HASH_MAP_HPP +#define ASIO_DETAIL_HASH_MAP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/noncopyable.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/socket_types.hpp" +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +inline std::size_t calculate_hash_value(int i) +{ + return static_cast(i); +} + +inline std::size_t calculate_hash_value(void* p) +{ + return reinterpret_cast(p) + + (reinterpret_cast(p) >> 3); +} + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +inline std::size_t calculate_hash_value(SOCKET s) +{ + return static_cast(s); +} +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +// Note: assumes K and V are POD types. +template +class hash_map + : private noncopyable +{ +public: + // The type of a value in the map. + typedef std::pair value_type; + + // The type of a non-const iterator over the hash map. + typedef typename std::list::iterator iterator; + + // The type of a const iterator over the hash map. + typedef typename std::list::const_iterator const_iterator; + + // Constructor. + hash_map() + : size_(0), + buckets_(0), + num_buckets_(0) + { + } + + // Destructor. + ~hash_map() + { + delete[] buckets_; + } + + // Get an iterator for the beginning of the map. + iterator begin() + { + return values_.begin(); + } + + // Get an iterator for the beginning of the map. + const_iterator begin() const + { + return values_.begin(); + } + + // Get an iterator for the end of the map. + iterator end() + { + return values_.end(); + } + + // Get an iterator for the end of the map. + const_iterator end() const + { + return values_.end(); + } + + // Check whether the map is empty. + bool empty() const + { + return values_.empty(); + } + + // Find an entry in the map. + iterator find(const K& k) + { + if (num_buckets_) + { + size_t bucket = calculate_hash_value(k) % num_buckets_; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + return values_.end(); + iterator end_it = buckets_[bucket].last; + ++end_it; + while (it != end_it) + { + if (it->first == k) + return it; + ++it; + } + } + return values_.end(); + } + + // Find an entry in the map. + const_iterator find(const K& k) const + { + if (num_buckets_) + { + size_t bucket = calculate_hash_value(k) % num_buckets_; + const_iterator it = buckets_[bucket].first; + if (it == values_.end()) + return it; + const_iterator end_it = buckets_[bucket].last; + ++end_it; + while (it != end_it) + { + if (it->first == k) + return it; + ++it; + } + } + return values_.end(); + } + + // Insert a new entry into the map. + std::pair insert(const value_type& v) + { + if (size_ + 1 >= num_buckets_) + rehash(hash_size(size_ + 1)); + size_t bucket = calculate_hash_value(v.first) % num_buckets_; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + { + buckets_[bucket].first = buckets_[bucket].last = + values_insert(values_.end(), v); + ++size_; + return std::pair(buckets_[bucket].last, true); + } + iterator end_it = buckets_[bucket].last; + ++end_it; + while (it != end_it) + { + if (it->first == v.first) + return std::pair(it, false); + ++it; + } + buckets_[bucket].last = values_insert(end_it, v); + ++size_; + return std::pair(buckets_[bucket].last, true); + } + + // Erase an entry from the map. + void erase(iterator it) + { + ASIO_ASSERT(it != values_.end()); + ASIO_ASSERT(num_buckets_ != 0); + + size_t bucket = calculate_hash_value(it->first) % num_buckets_; + bool is_first = (it == buckets_[bucket].first); + bool is_last = (it == buckets_[bucket].last); + if (is_first && is_last) + buckets_[bucket].first = buckets_[bucket].last = values_.end(); + else if (is_first) + ++buckets_[bucket].first; + else if (is_last) + --buckets_[bucket].last; + + values_erase(it); + --size_; + } + + // Erase a key from the map. + void erase(const K& k) + { + iterator it = find(k); + if (it != values_.end()) + erase(it); + } + + // Remove all entries from the map. + void clear() + { + // Clear the values. + values_.clear(); + size_ = 0; + + // Initialise all buckets to empty. + iterator end_it = values_.end(); + for (size_t i = 0; i < num_buckets_; ++i) + buckets_[i].first = buckets_[i].last = end_it; + } + +private: + // Calculate the hash size for the specified number of elements. + static std::size_t hash_size(std::size_t num_elems) + { + static std::size_t sizes[] = + { +#if defined(ASIO_HASH_MAP_BUCKETS) + ASIO_HASH_MAP_BUCKETS +#else // ASIO_HASH_MAP_BUCKETS + 3, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, + 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, + 12582917, 25165843 +#endif // ASIO_HASH_MAP_BUCKETS + }; + const std::size_t nth_size = sizeof(sizes) / sizeof(std::size_t) - 1; + for (std::size_t i = 0; i < nth_size; ++i) + if (num_elems < sizes[i]) + return sizes[i]; + return sizes[nth_size]; + } + + // Re-initialise the hash from the values already contained in the list. + void rehash(std::size_t num_buckets) + { + if (num_buckets == num_buckets_) + return; + ASIO_ASSERT(num_buckets != 0); + + iterator end_iter = values_.end(); + + // Update number of buckets and initialise all buckets to empty. + bucket_type* tmp = new bucket_type[num_buckets]; + delete[] buckets_; + buckets_ = tmp; + num_buckets_ = num_buckets; + for (std::size_t i = 0; i < num_buckets_; ++i) + buckets_[i].first = buckets_[i].last = end_iter; + + // Put all values back into the hash. + iterator iter = values_.begin(); + while (iter != end_iter) + { + std::size_t bucket = calculate_hash_value(iter->first) % num_buckets_; + if (buckets_[bucket].last == end_iter) + { + buckets_[bucket].first = buckets_[bucket].last = iter++; + } + else if (++buckets_[bucket].last == iter) + { + ++iter; + } + else + { + values_.splice(buckets_[bucket].last, values_, iter++); + --buckets_[bucket].last; + } + } + } + + // Insert an element into the values list by splicing from the spares list, + // if a spare is available, and otherwise by inserting a new element. + iterator values_insert(iterator it, const value_type& v) + { + if (spares_.empty()) + { + return values_.insert(it, v); + } + else + { + spares_.front() = v; + values_.splice(it, spares_, spares_.begin()); + return --it; + } + } + + // Erase an element from the values list by splicing it to the spares list. + void values_erase(iterator it) + { + *it = value_type(); + spares_.splice(spares_.begin(), values_, it); + } + + // The number of elements in the hash. + std::size_t size_; + + // The list of all values in the hash map. + std::list values_; + + // The list of spare nodes waiting to be recycled. Assumes that POD types only + // are stored in the hash map. + std::list spares_; + + // The type for a bucket in the hash table. + struct bucket_type + { + iterator first; + iterator last; + }; + + // The buckets in the hash. + bucket_type* buckets_; + + // The number of buckets in the hash. + std::size_t num_buckets_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HASH_MAP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/buffer_sequence_adapter.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/buffer_sequence_adapter.ipp new file mode 100644 index 000000000..d7c5e35ba --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/buffer_sequence_adapter.ipp @@ -0,0 +1,118 @@ +// +// detail/impl/buffer_sequence_adapter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_BUFFER_SEQUENCE_ADAPTER_IPP +#define ASIO_DETAIL_IMPL_BUFFER_SEQUENCE_ADAPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include +#include +#include +#include "asio/detail/buffer_sequence_adapter.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class winrt_buffer_impl : + public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< + Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>, + ABI::Windows::Storage::Streams::IBuffer, + Windows::Storage::Streams::IBufferByteAccess> +{ +public: + explicit winrt_buffer_impl(const asio::const_buffer& b) + { + bytes_ = const_cast(static_cast(b.data())); + length_ = b.size(); + capacity_ = b.size(); + } + + explicit winrt_buffer_impl(const asio::mutable_buffer& b) + { + bytes_ = static_cast(b.data()); + length_ = 0; + capacity_ = b.size(); + } + + ~winrt_buffer_impl() + { + } + + STDMETHODIMP Buffer(byte** value) + { + *value = bytes_; + return S_OK; + } + + STDMETHODIMP get_Capacity(UINT32* value) + { + *value = capacity_; + return S_OK; + } + + STDMETHODIMP get_Length(UINT32 *value) + { + *value = length_; + return S_OK; + } + + STDMETHODIMP put_Length(UINT32 value) + { + if (value > capacity_) + return E_INVALIDARG; + length_ = value; + return S_OK; + } + +private: + byte* bytes_; + UINT32 length_; + UINT32 capacity_; +}; + +void buffer_sequence_adapter_base::init_native_buffer( + buffer_sequence_adapter_base::native_buffer_type& buf, + const asio::mutable_buffer& buffer) +{ + std::memset(&buf, 0, sizeof(native_buffer_type)); + Microsoft::WRL::ComPtr insp + = Microsoft::WRL::Make(buffer); + buf = reinterpret_cast(insp.Get()); +} + +void buffer_sequence_adapter_base::init_native_buffer( + buffer_sequence_adapter_base::native_buffer_type& buf, + const asio::const_buffer& buffer) +{ + std::memset(&buf, 0, sizeof(native_buffer_type)); + Microsoft::WRL::ComPtr insp + = Microsoft::WRL::Make(buffer); + Platform::Object^ buf_obj = reinterpret_cast(insp.Get()); + buf = reinterpret_cast(insp.Get()); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_BUFFER_SEQUENCE_ADAPTER_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/descriptor_ops.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/descriptor_ops.ipp new file mode 100644 index 000000000..9abf204a8 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/descriptor_ops.ipp @@ -0,0 +1,608 @@ +// +// detail/impl/descriptor_ops.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP +#define ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/descriptor_ops.hpp" +#include "asio/error.hpp" + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace descriptor_ops { + +int open(const char* path, int flags, asio::error_code& ec) +{ + int result = ::open(path, flags); + get_last_error(ec, result < 0); + return result; +} + +int close(int d, state_type& state, asio::error_code& ec) +{ + int result = 0; + if (d != -1) + { + result = ::close(d); + get_last_error(ec, result < 0); + + if (result != 0 + && (ec == asio::error::would_block + || ec == asio::error::try_again)) + { + // According to UNIX Network Programming Vol. 1, it is possible for + // close() to fail with EWOULDBLOCK under certain circumstances. What + // isn't clear is the state of the descriptor after this error. The one + // current OS where this behaviour is seen, Windows, says that the socket + // remains open. Therefore we'll put the descriptor back into blocking + // mode and have another attempt at closing it. +#if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + int flags = ::fcntl(d, F_GETFL, 0); + if (flags >= 0) + ::fcntl(d, F_SETFL, flags & ~O_NONBLOCK); +#else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + ioctl_arg_type arg = 0; + ::ioctl(d, FIONBIO, &arg); +#endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + state &= ~non_blocking; + + result = ::close(d); + get_last_error(ec, result < 0); + } + } + + return result; +} + +bool set_user_non_blocking(int d, state_type& state, + bool value, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return false; + } + +#if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + int result = ::fcntl(d, F_GETFL, 0); + get_last_error(ec, result < 0); + if (result >= 0) + { + int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); + result = ::fcntl(d, F_SETFL, flag); + get_last_error(ec, result < 0); + } +#else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + ioctl_arg_type arg = (value ? 1 : 0); + int result = ::ioctl(d, FIONBIO, &arg); + get_last_error(ec, result < 0); +#endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + + if (result >= 0) + { + if (value) + state |= user_set_non_blocking; + else + { + // Clearing the user-set non-blocking mode always overrides any + // internally-set non-blocking flag. Any subsequent asynchronous + // operations will need to re-enable non-blocking I/O. + state &= ~(user_set_non_blocking | internal_non_blocking); + } + return true; + } + + return false; +} + +bool set_internal_non_blocking(int d, state_type& state, + bool value, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return false; + } + + if (!value && (state & user_set_non_blocking)) + { + // It does not make sense to clear the internal non-blocking flag if the + // user still wants non-blocking behaviour. Return an error and let the + // caller figure out whether to update the user-set non-blocking flag. + ec = asio::error::invalid_argument; + return false; + } + +#if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + int result = ::fcntl(d, F_GETFL, 0); + get_last_error(ec, result < 0); + if (result >= 0) + { + int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); + result = ::fcntl(d, F_SETFL, flag); + get_last_error(ec, result < 0); + } +#else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + ioctl_arg_type arg = (value ? 1 : 0); + int result = ::ioctl(d, FIONBIO, &arg); + get_last_error(ec, result < 0); +#endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + + if (result >= 0) + { + if (value) + state |= internal_non_blocking; + else + state &= ~internal_non_blocking; + return true; + } + + return false; +} + +std::size_t sync_read(int d, state_type state, buf* bufs, + std::size_t count, bool all_empty, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream is a no-op. + if (all_empty) + { + ec.assign(0, ec.category()); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = ::readv(d, bufs, static_cast(count)); + get_last_error(ec, bytes < 0); + + // Check if operation succeeded. + if (bytes > 0) + return bytes; + + // Check for EOF. + if (bytes == 0) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for descriptor to become ready. + if (descriptor_ops::poll_read(d, 0, ec) < 0) + return 0; + } +} + +std::size_t sync_read1(int d, state_type state, void* data, + std::size_t size, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream is a no-op. + if (size == 0) + { + ec.assign(0, ec.category()); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = ::read(d, data, size); + get_last_error(ec, bytes < 0); + + // Check if operation succeeded. + if (bytes > 0) + return bytes; + + // Check for EOF. + if (bytes == 0) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for descriptor to become ready. + if (descriptor_ops::poll_read(d, 0, ec) < 0) + return 0; + } +} + +bool non_blocking_read(int d, buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = ::readv(d, bufs, static_cast(count)); + get_last_error(ec, bytes < 0); + + // Check for end of stream. + if (bytes == 0) + { + ec = asio::error::eof; + return true; + } + + // Check if operation succeeded. + if (bytes > 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +bool non_blocking_read1(int d, void* data, std::size_t size, + asio::error_code& ec, std::size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = ::read(d, data, size); + get_last_error(ec, bytes < 0); + + // Check for end of stream. + if (bytes == 0) + { + ec = asio::error::eof; + return true; + } + + // Check if operation succeeded. + if (bytes > 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +std::size_t sync_write(int d, state_type state, const buf* bufs, + std::size_t count, bool all_empty, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes on a stream is a no-op. + if (all_empty) + { + ec.assign(0, ec.category()); + return 0; + } + + // Write some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = ::writev(d, bufs, static_cast(count)); + get_last_error(ec, bytes < 0); + + // Check if operation succeeded. + if (bytes > 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for descriptor to become ready. + if (descriptor_ops::poll_write(d, 0, ec) < 0) + return 0; + } +} + +std::size_t sync_write1(int d, state_type state, const void* data, + std::size_t size, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes on a stream is a no-op. + if (size == 0) + { + ec.assign(0, ec.category()); + return 0; + } + + // Write some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = ::write(d, data, size); + get_last_error(ec, bytes < 0); + + // Check if operation succeeded. + if (bytes > 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for descriptor to become ready. + if (descriptor_ops::poll_write(d, 0, ec) < 0) + return 0; + } +} + +bool non_blocking_write(int d, const buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + signed_size_type bytes = ::writev(d, bufs, static_cast(count)); + get_last_error(ec, bytes < 0); + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +bool non_blocking_write1(int d, const void* data, std::size_t size, + asio::error_code& ec, std::size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + signed_size_type bytes = ::write(d, data, size); + get_last_error(ec, bytes < 0); + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +int ioctl(int d, state_type& state, long cmd, + ioctl_arg_type* arg, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + int result = ::ioctl(d, cmd, arg); + get_last_error(ec, result < 0); + + if (result >= 0) + { + // When updating the non-blocking mode we always perform the ioctl syscall, + // even if the flags would otherwise indicate that the descriptor is + // already in the correct state. This ensures that the underlying + // descriptor is put into the state that has been requested by the user. If + // the ioctl syscall was successful then we need to update the flags to + // match. + if (cmd == static_cast(FIONBIO)) + { + if (*arg) + { + state |= user_set_non_blocking; + } + else + { + // Clearing the non-blocking mode always overrides any internally-set + // non-blocking flag. Any subsequent asynchronous operations will need + // to re-enable non-blocking I/O. + state &= ~(user_set_non_blocking | internal_non_blocking); + } + } + } + + return result; +} + +int fcntl(int d, int cmd, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + int result = ::fcntl(d, cmd); + get_last_error(ec, result < 0); + return result; +} + +int fcntl(int d, int cmd, long arg, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + int result = ::fcntl(d, cmd, arg); + get_last_error(ec, result < 0); + return result; +} + +int poll_read(int d, state_type state, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + pollfd fds; + fds.fd = d; + fds.events = POLLIN; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : -1; + int result = ::poll(&fds, 1, timeout); + get_last_error(ec, result < 0); + if (result == 0) + if (state & user_set_non_blocking) + ec = asio::error::would_block; + return result; +} + +int poll_write(int d, state_type state, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + pollfd fds; + fds.fd = d; + fds.events = POLLOUT; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : -1; + int result = ::poll(&fds, 1, timeout); + get_last_error(ec, result < 0); + if (result == 0) + if (state & user_set_non_blocking) + ec = asio::error::would_block; + return result; +} + +int poll_error(int d, state_type state, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + pollfd fds; + fds.fd = d; + fds.events = POLLPRI | POLLERR | POLLHUP; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : -1; + int result = ::poll(&fds, 1, timeout); + get_last_error(ec, result < 0); + if (result == 0) + if (state & user_set_non_blocking) + ec = asio::error::would_block; + return result; +} + +} // namespace descriptor_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/dev_poll_reactor.hpp b/third_party/asio/1.18.2/include/asio/detail/impl/dev_poll_reactor.hpp new file mode 100644 index 000000000..8fac4ddde --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/dev_poll_reactor.hpp @@ -0,0 +1,91 @@ +// +// detail/impl/dev_poll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP +#define ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_DEV_POLL) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void dev_poll_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +template +void dev_poll_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void dev_poll_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + scheduler_.post_immediate_completion(op, false); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + scheduler_.work_started(); + if (earliest) + interrupter_.interrupt(); +} + +template +std::size_t dev_poll_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + scheduler_.post_deferred_completions(ops); + return n; +} + +template +void dev_poll_reactor::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + queue.cancel_timer(target, ops); + queue.move_timer(target, source); + lock.unlock(); + scheduler_.post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_DEV_POLL) + +#endif // ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/dev_poll_reactor.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/dev_poll_reactor.ipp new file mode 100644 index 000000000..a60b68748 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/dev_poll_reactor.ipp @@ -0,0 +1,446 @@ +// +// detail/impl/dev_poll_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP +#define ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_DEV_POLL) + +#include "asio/detail/dev_poll_reactor.hpp" +#include "asio/detail/assert.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +dev_poll_reactor::dev_poll_reactor(asio::execution_context& ctx) + : asio::detail::execution_context_service_base(ctx), + scheduler_(use_service(ctx)), + mutex_(), + dev_poll_fd_(do_dev_poll_create()), + interrupter_(), + shutdown_(false) +{ + // Add the interrupter's descriptor to /dev/poll. + ::pollfd ev = { 0, 0, 0 }; + ev.fd = interrupter_.read_descriptor(); + ev.events = POLLIN | POLLERR; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); +} + +dev_poll_reactor::~dev_poll_reactor() +{ + shutdown(); + ::close(dev_poll_fd_); +} + +void dev_poll_reactor::shutdown() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue ops; + + for (int i = 0; i < max_ops; ++i) + op_queue_[i].get_all_operations(ops); + + timer_queues_.get_all_timers(ops); + + scheduler_.abandon_operations(ops); +} + +void dev_poll_reactor::notify_fork( + asio::execution_context::fork_event fork_ev) +{ + if (fork_ev == asio::execution_context::fork_child) + { + detail::mutex::scoped_lock lock(mutex_); + + if (dev_poll_fd_ != -1) + ::close(dev_poll_fd_); + dev_poll_fd_ = -1; + dev_poll_fd_ = do_dev_poll_create(); + + interrupter_.recreate(); + + // Add the interrupter's descriptor to /dev/poll. + ::pollfd ev = { 0, 0, 0 }; + ev.fd = interrupter_.read_descriptor(); + ev.events = POLLIN | POLLERR; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + + // Re-register all descriptors with /dev/poll. The changes will be written + // to the /dev/poll descriptor the next time the reactor is run. + for (int i = 0; i < max_ops; ++i) + { + reactor_op_queue::iterator iter = op_queue_[i].begin(); + reactor_op_queue::iterator end = op_queue_[i].end(); + for (; iter != end; ++iter) + { + ::pollfd& pending_ev = add_pending_event_change(iter->first); + pending_ev.events |= POLLERR | POLLHUP; + switch (i) + { + case read_op: pending_ev.events |= POLLIN; break; + case write_op: pending_ev.events |= POLLOUT; break; + case except_op: pending_ev.events |= POLLPRI; break; + default: break; + } + } + } + interrupter_.interrupt(); + } +} + +void dev_poll_reactor::init_task() +{ + scheduler_.init_task(); +} + +int dev_poll_reactor::register_descriptor(socket_type, per_descriptor_data&) +{ + return 0; +} + +int dev_poll_reactor::register_internal_descriptor(int op_type, + socket_type descriptor, per_descriptor_data&, reactor_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + op_queue_[op_type].enqueue_operation(descriptor, op); + ::pollfd& ev = add_pending_event_change(descriptor); + ev.events = POLLERR | POLLHUP; + switch (op_type) + { + case read_op: ev.events |= POLLIN; break; + case write_op: ev.events |= POLLOUT; break; + case except_op: ev.events |= POLLPRI; break; + default: break; + } + interrupter_.interrupt(); + + return 0; +} + +void dev_poll_reactor::move_descriptor(socket_type, + dev_poll_reactor::per_descriptor_data&, + dev_poll_reactor::per_descriptor_data&) +{ +} + +void dev_poll_reactor::start_op(int op_type, socket_type descriptor, + dev_poll_reactor::per_descriptor_data&, reactor_op* op, + bool is_continuation, bool allow_speculative) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + post_immediate_completion(op, is_continuation); + return; + } + + if (allow_speculative) + { + if (op_type != read_op || !op_queue_[except_op].has_operation(descriptor)) + { + if (!op_queue_[op_type].has_operation(descriptor)) + { + if (op->perform()) + { + lock.unlock(); + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + } + } + } + + bool first = op_queue_[op_type].enqueue_operation(descriptor, op); + scheduler_.work_started(); + if (first) + { + ::pollfd& ev = add_pending_event_change(descriptor); + ev.events = POLLERR | POLLHUP; + if (op_type == read_op + || op_queue_[read_op].has_operation(descriptor)) + ev.events |= POLLIN; + if (op_type == write_op + || op_queue_[write_op].has_operation(descriptor)) + ev.events |= POLLOUT; + if (op_type == except_op + || op_queue_[except_op].has_operation(descriptor)) + ev.events |= POLLPRI; + interrupter_.interrupt(); + } +} + +void dev_poll_reactor::cancel_ops(socket_type descriptor, + dev_poll_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void dev_poll_reactor::deregister_descriptor(socket_type descriptor, + dev_poll_reactor::per_descriptor_data&, bool) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from /dev/poll. + ::pollfd& ev = add_pending_event_change(descriptor); + ev.events = POLLREMOVE; + interrupter_.interrupt(); + + // Cancel any outstanding operations associated with the descriptor. + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void dev_poll_reactor::deregister_internal_descriptor( + socket_type descriptor, dev_poll_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from /dev/poll. Since this function is only called + // during a fork, we can apply the change immediately. + ::pollfd ev = { 0, 0, 0 }; + ev.fd = descriptor; + ev.events = POLLREMOVE; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + + // Destroy all operations associated with the descriptor. + op_queue ops; + asio::error_code ec; + for (int i = 0; i < max_ops; ++i) + op_queue_[i].cancel_operations(descriptor, ops, ec); +} + +void dev_poll_reactor::cleanup_descriptor_data( + dev_poll_reactor::per_descriptor_data&) +{ +} + +void dev_poll_reactor::run(long usec, op_queue& ops) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (usec == 0 && op_queue_[read_op].empty() && op_queue_[write_op].empty() + && op_queue_[except_op].empty() && timer_queues_.all_empty()) + return; + + // Write the pending event registration changes to the /dev/poll descriptor. + std::size_t events_size = sizeof(::pollfd) * pending_event_changes_.size(); + if (events_size > 0) + { + errno = 0; + int result = ::write(dev_poll_fd_, + &pending_event_changes_[0], events_size); + if (result != static_cast(events_size)) + { + asio::error_code ec = asio::error_code( + errno, asio::error::get_system_category()); + for (std::size_t i = 0; i < pending_event_changes_.size(); ++i) + { + int descriptor = pending_event_changes_[i].fd; + for (int j = 0; j < max_ops; ++j) + op_queue_[j].cancel_operations(descriptor, ops, ec); + } + } + pending_event_changes_.clear(); + pending_event_change_index_.clear(); + } + + // Calculate timeout. + int timeout; + if (usec == 0) + timeout = 0; + else + { + timeout = (usec < 0) ? -1 : ((usec - 1) / 1000 + 1); + timeout = get_timeout(timeout); + } + lock.unlock(); + + // Block on the /dev/poll descriptor. + ::pollfd events[128] = { { 0, 0, 0 } }; + ::dvpoll dp = { 0, 0, 0 }; + dp.dp_fds = events; + dp.dp_nfds = 128; + dp.dp_timeout = timeout; + int num_events = ::ioctl(dev_poll_fd_, DP_POLL, &dp); + + lock.lock(); + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].fd; + if (descriptor == interrupter_.read_descriptor()) + { + interrupter_.reset(); + } + else + { + bool more_reads = false; + bool more_writes = false; + bool more_except = false; + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + if (events[i].events & (POLLPRI | POLLERR | POLLHUP)) + more_except = + op_queue_[except_op].perform_operations(descriptor, ops); + else + more_except = op_queue_[except_op].has_operation(descriptor); + + if (events[i].events & (POLLIN | POLLERR | POLLHUP)) + more_reads = op_queue_[read_op].perform_operations(descriptor, ops); + else + more_reads = op_queue_[read_op].has_operation(descriptor); + + if (events[i].events & (POLLOUT | POLLERR | POLLHUP)) + more_writes = op_queue_[write_op].perform_operations(descriptor, ops); + else + more_writes = op_queue_[write_op].has_operation(descriptor); + + if ((events[i].events & (POLLERR | POLLHUP)) != 0 + && !more_except && !more_reads && !more_writes) + { + // If we have an event and no operations associated with the + // descriptor then we need to delete the descriptor from /dev/poll. + // The poll operation can produce POLLHUP or POLLERR events when there + // is no operation pending, so if we do not remove the descriptor we + // can end up in a tight polling loop. + ::pollfd ev = { 0, 0, 0 }; + ev.fd = descriptor; + ev.events = POLLREMOVE; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + } + else + { + ::pollfd ev = { 0, 0, 0 }; + ev.fd = descriptor; + ev.events = POLLERR | POLLHUP; + if (more_reads) + ev.events |= POLLIN; + if (more_writes) + ev.events |= POLLOUT; + if (more_except) + ev.events |= POLLPRI; + ev.revents = 0; + int result = ::write(dev_poll_fd_, &ev, sizeof(ev)); + if (result != sizeof(ev)) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + for (int j = 0; j < max_ops; ++j) + op_queue_[j].cancel_operations(descriptor, ops, ec); + } + } + } + } + timer_queues_.get_ready_timers(ops); +} + +void dev_poll_reactor::interrupt() +{ + interrupter_.interrupt(); +} + +int dev_poll_reactor::do_dev_poll_create() +{ + int fd = ::open("/dev/poll", O_RDWR); + if (fd == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "/dev/poll"); + } + return fd; +} + +void dev_poll_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void dev_poll_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +int dev_poll_reactor::get_timeout(int msec) +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + const int max_msec = 5 * 60 * 1000; + return timer_queues_.wait_duration_msec( + (msec < 0 || max_msec < msec) ? max_msec : msec); +} + +void dev_poll_reactor::cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec) +{ + bool need_interrupt = false; + op_queue ops; + for (int i = 0; i < max_ops; ++i) + need_interrupt = op_queue_[i].cancel_operations( + descriptor, ops, ec) || need_interrupt; + scheduler_.post_deferred_completions(ops); + if (need_interrupt) + interrupter_.interrupt(); +} + +::pollfd& dev_poll_reactor::add_pending_event_change(int descriptor) +{ + hash_map::iterator iter + = pending_event_change_index_.find(descriptor); + if (iter == pending_event_change_index_.end()) + { + std::size_t index = pending_event_changes_.size(); + pending_event_changes_.reserve(pending_event_changes_.size() + 1); + pending_event_change_index_.insert(std::make_pair(descriptor, index)); + pending_event_changes_.push_back(::pollfd()); + pending_event_changes_[index].fd = descriptor; + pending_event_changes_[index].revents = 0; + return pending_event_changes_[index]; + } + else + { + return pending_event_changes_[iter->second]; + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_DEV_POLL) + +#endif // ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/epoll_reactor.hpp b/third_party/asio/1.18.2/include/asio/detail/impl/epoll_reactor.hpp new file mode 100644 index 000000000..cce80e966 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/epoll_reactor.hpp @@ -0,0 +1,89 @@ +// +// detail/impl/epoll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP +#define ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if defined(ASIO_HAS_EPOLL) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void epoll_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +template +void epoll_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void epoll_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + scheduler_.post_immediate_completion(op, false); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + scheduler_.work_started(); + if (earliest) + update_timeout(); +} + +template +std::size_t epoll_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + scheduler_.post_deferred_completions(ops); + return n; +} + +template +void epoll_reactor::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source) +{ + mutex::scoped_lock lock(mutex_); + op_queue ops; + queue.cancel_timer(target, ops); + queue.move_timer(target, source); + lock.unlock(); + scheduler_.post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_EPOLL) + +#endif // ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/epoll_reactor.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/epoll_reactor.ipp new file mode 100644 index 000000000..f56c5193e --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/epoll_reactor.ipp @@ -0,0 +1,787 @@ +// +// detail/impl/epoll_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP +#define ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_EPOLL) + +#include +#include +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#if defined(ASIO_HAS_TIMERFD) +# include +#endif // defined(ASIO_HAS_TIMERFD) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +epoll_reactor::epoll_reactor(asio::execution_context& ctx) + : execution_context_service_base(ctx), + scheduler_(use_service(ctx)), + mutex_(ASIO_CONCURRENCY_HINT_IS_LOCKING( + REACTOR_REGISTRATION, scheduler_.concurrency_hint())), + interrupter_(), + epoll_fd_(do_epoll_create()), + timer_fd_(do_timerfd_create()), + shutdown_(false), + registered_descriptors_mutex_(mutex_.enabled()) +{ + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + interrupter_.interrupt(); + + // Add the timer descriptor to epoll. + if (timer_fd_ != -1) + { + ev.events = EPOLLIN | EPOLLERR; + ev.data.ptr = &timer_fd_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev); + } +} + +epoll_reactor::~epoll_reactor() +{ + if (epoll_fd_ != -1) + close(epoll_fd_); + if (timer_fd_ != -1) + close(timer_fd_); +} + +void epoll_reactor::shutdown() +{ + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue ops; + + while (descriptor_state* state = registered_descriptors_.first()) + { + for (int i = 0; i < max_ops; ++i) + ops.push(state->op_queue_[i]); + state->shutdown_ = true; + registered_descriptors_.free(state); + } + + timer_queues_.get_all_timers(ops); + + scheduler_.abandon_operations(ops); +} + +void epoll_reactor::notify_fork( + asio::execution_context::fork_event fork_ev) +{ + if (fork_ev == asio::execution_context::fork_child) + { + if (epoll_fd_ != -1) + ::close(epoll_fd_); + epoll_fd_ = -1; + epoll_fd_ = do_epoll_create(); + + if (timer_fd_ != -1) + ::close(timer_fd_); + timer_fd_ = -1; + timer_fd_ = do_timerfd_create(); + + interrupter_.recreate(); + + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + interrupter_.interrupt(); + + // Add the timer descriptor to epoll. + if (timer_fd_ != -1) + { + ev.events = EPOLLIN | EPOLLERR; + ev.data.ptr = &timer_fd_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev); + } + + update_timeout(); + + // Re-register all descriptors with epoll. + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + for (descriptor_state* state = registered_descriptors_.first(); + state != 0; state = state->next_) + { + ev.events = state->registered_events_; + ev.data.ptr = state; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, state->descriptor_, &ev); + if (result != 0) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "epoll re-registration"); + } + } + } +} + +void epoll_reactor::init_task() +{ + scheduler_.init_task(); +} + +int epoll_reactor::register_descriptor(socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + descriptor_data = allocate_descriptor_state(); + + ASIO_HANDLER_REACTOR_REGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + descriptor_data->reactor_ = this; + descriptor_data->descriptor_ = descriptor; + descriptor_data->shutdown_ = false; + for (int i = 0; i < max_ops; ++i) + descriptor_data->try_speculative_[i] = true; + } + + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLPRI | EPOLLET; + descriptor_data->registered_events_ = ev.events; + ev.data.ptr = descriptor_data; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + if (result != 0) + { + if (errno == EPERM) + { + // This file descriptor type is not supported by epoll. However, if it is + // a regular file then operations on it will not block. We will allow + // this descriptor to be used and fail later if an operation on it would + // otherwise require a trip through the reactor. + descriptor_data->registered_events_ = 0; + return 0; + } + return errno; + } + + return 0; +} + +int epoll_reactor::register_internal_descriptor( + int op_type, socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data, reactor_op* op) +{ + descriptor_data = allocate_descriptor_state(); + + ASIO_HANDLER_REACTOR_REGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + descriptor_data->reactor_ = this; + descriptor_data->descriptor_ = descriptor; + descriptor_data->shutdown_ = false; + descriptor_data->op_queue_[op_type].push(op); + for (int i = 0; i < max_ops; ++i) + descriptor_data->try_speculative_[i] = true; + } + + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLPRI | EPOLLET; + descriptor_data->registered_events_ = ev.events; + ev.data.ptr = descriptor_data; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + if (result != 0) + return errno; + + return 0; +} + +void epoll_reactor::move_descriptor(socket_type, + epoll_reactor::per_descriptor_data& target_descriptor_data, + epoll_reactor::per_descriptor_data& source_descriptor_data) +{ + target_descriptor_data = source_descriptor_data; + source_descriptor_data = 0; +} + +void epoll_reactor::start_op(int op_type, socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data, reactor_op* op, + bool is_continuation, bool allow_speculative) +{ + if (!descriptor_data) + { + op->ec_ = asio::error::bad_descriptor; + post_immediate_completion(op, is_continuation); + return; + } + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (descriptor_data->shutdown_) + { + post_immediate_completion(op, is_continuation); + return; + } + + if (descriptor_data->op_queue_[op_type].empty()) + { + if (allow_speculative + && (op_type != read_op + || descriptor_data->op_queue_[except_op].empty())) + { + if (descriptor_data->try_speculative_[op_type]) + { + if (reactor_op::status status = op->perform()) + { + if (status == reactor_op::done_and_exhausted) + if (descriptor_data->registered_events_ != 0) + descriptor_data->try_speculative_[op_type] = false; + descriptor_lock.unlock(); + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + } + + if (descriptor_data->registered_events_ == 0) + { + op->ec_ = asio::error::operation_not_supported; + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + if (op_type == write_op) + { + if ((descriptor_data->registered_events_ & EPOLLOUT) == 0) + { + epoll_event ev = { 0, { 0 } }; + ev.events = descriptor_data->registered_events_ | EPOLLOUT; + ev.data.ptr = descriptor_data; + if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev) == 0) + { + descriptor_data->registered_events_ |= ev.events; + } + else + { + op->ec_ = asio::error_code(errno, + asio::error::get_system_category()); + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + } + } + } + else if (descriptor_data->registered_events_ == 0) + { + op->ec_ = asio::error::operation_not_supported; + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + else + { + if (op_type == write_op) + { + descriptor_data->registered_events_ |= EPOLLOUT; + } + + epoll_event ev = { 0, { 0 } }; + ev.events = descriptor_data->registered_events_; + ev.data.ptr = descriptor_data; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + } + } + + descriptor_data->op_queue_[op_type].push(op); + scheduler_.work_started(); +} + +void epoll_reactor::cancel_ops(socket_type, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_lock.unlock(); + + scheduler_.post_deferred_completions(ops); +} + +void epoll_reactor::deregister_descriptor(socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data, bool closing) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (!descriptor_data->shutdown_) + { + if (closing) + { + // The descriptor will be automatically removed from the epoll set when + // it is closed. + } + else if (descriptor_data->registered_events_ != 0) + { + epoll_event ev = { 0, { 0 } }; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); + } + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + ASIO_HANDLER_REACTOR_DEREGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + scheduler_.post_deferred_completions(ops); + + // Leave descriptor_data set so that it will be freed by the subsequent + // call to cleanup_descriptor_data. + } + else + { + // We are shutting down, so prevent cleanup_descriptor_data from freeing + // the descriptor_data object and let the destructor free it instead. + descriptor_data = 0; + } +} + +void epoll_reactor::deregister_internal_descriptor(socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (!descriptor_data->shutdown_) + { + epoll_event ev = { 0, { 0 } }; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + ops.push(descriptor_data->op_queue_[i]); + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + ASIO_HANDLER_REACTOR_DEREGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + // Leave descriptor_data set so that it will be freed by the subsequent + // call to cleanup_descriptor_data. + } + else + { + // We are shutting down, so prevent cleanup_descriptor_data from freeing + // the descriptor_data object and let the destructor free it instead. + descriptor_data = 0; + } +} + +void epoll_reactor::cleanup_descriptor_data( + per_descriptor_data& descriptor_data) +{ + if (descriptor_data) + { + free_descriptor_state(descriptor_data); + descriptor_data = 0; + } +} + +void epoll_reactor::run(long usec, op_queue& ops) +{ + // This code relies on the fact that the scheduler queues the reactor task + // behind all descriptor operations generated by this function. This means, + // that by the time we reach this point, any previously returned descriptor + // operations have already been dequeued. Therefore it is now safe for us to + // reuse and return them for the scheduler to queue again. + + // Calculate timeout. Check the timer queues only if timerfd is not in use. + int timeout; + if (usec == 0) + timeout = 0; + else + { + timeout = (usec < 0) ? -1 : ((usec - 1) / 1000 + 1); + if (timer_fd_ == -1) + { + mutex::scoped_lock lock(mutex_); + timeout = get_timeout(timeout); + } + } + + // Block on the epoll descriptor. + epoll_event events[128]; + int num_events = epoll_wait(epoll_fd_, events, 128, timeout); + +#if defined(ASIO_ENABLE_HANDLER_TRACKING) + // Trace the waiting events. + for (int i = 0; i < num_events; ++i) + { + void* ptr = events[i].data.ptr; + if (ptr == &interrupter_) + { + // Ignore. + } +# if defined(ASIO_HAS_TIMERFD) + else if (ptr == &timer_fd_) + { + // Ignore. + } +# endif // defined(ASIO_HAS_TIMERFD) + else + { + unsigned event_mask = 0; + if ((events[i].events & EPOLLIN) != 0) + event_mask |= ASIO_HANDLER_REACTOR_READ_EVENT; + if ((events[i].events & EPOLLOUT)) + event_mask |= ASIO_HANDLER_REACTOR_WRITE_EVENT; + if ((events[i].events & (EPOLLERR | EPOLLHUP)) != 0) + event_mask |= ASIO_HANDLER_REACTOR_ERROR_EVENT; + ASIO_HANDLER_REACTOR_EVENTS((context(), + reinterpret_cast(ptr), event_mask)); + } + } +#endif // defined(ASIO_ENABLE_HANDLER_TRACKING) + +#if defined(ASIO_HAS_TIMERFD) + bool check_timers = (timer_fd_ == -1); +#else // defined(ASIO_HAS_TIMERFD) + bool check_timers = true; +#endif // defined(ASIO_HAS_TIMERFD) + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + void* ptr = events[i].data.ptr; + if (ptr == &interrupter_) + { + // No need to reset the interrupter since we're leaving the descriptor + // in a ready-to-read state and relying on edge-triggered notifications + // to make it so that we only get woken up when the descriptor's epoll + // registration is updated. + +#if defined(ASIO_HAS_TIMERFD) + if (timer_fd_ == -1) + check_timers = true; +#else // defined(ASIO_HAS_TIMERFD) + check_timers = true; +#endif // defined(ASIO_HAS_TIMERFD) + } +#if defined(ASIO_HAS_TIMERFD) + else if (ptr == &timer_fd_) + { + check_timers = true; + } +#endif // defined(ASIO_HAS_TIMERFD) + else + { + // The descriptor operation doesn't count as work in and of itself, so we + // don't call work_started() here. This still allows the scheduler to + // stop if the only remaining operations are descriptor operations. + descriptor_state* descriptor_data = static_cast(ptr); + if (!ops.is_enqueued(descriptor_data)) + { + descriptor_data->set_ready_events(events[i].events); + ops.push(descriptor_data); + } + else + { + descriptor_data->add_ready_events(events[i].events); + } + } + } + + if (check_timers) + { + mutex::scoped_lock common_lock(mutex_); + timer_queues_.get_ready_timers(ops); + +#if defined(ASIO_HAS_TIMERFD) + if (timer_fd_ != -1) + { + itimerspec new_timeout; + itimerspec old_timeout; + int flags = get_timeout(new_timeout); + timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); + } +#endif // defined(ASIO_HAS_TIMERFD) + } +} + +void epoll_reactor::interrupt() +{ + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, interrupter_.read_descriptor(), &ev); +} + +int epoll_reactor::do_epoll_create() +{ +#if defined(EPOLL_CLOEXEC) + int fd = epoll_create1(EPOLL_CLOEXEC); +#else // defined(EPOLL_CLOEXEC) + int fd = -1; + errno = EINVAL; +#endif // defined(EPOLL_CLOEXEC) + + if (fd == -1 && (errno == EINVAL || errno == ENOSYS)) + { + fd = epoll_create(epoll_size); + if (fd != -1) + ::fcntl(fd, F_SETFD, FD_CLOEXEC); + } + + if (fd == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "epoll"); + } + + return fd; +} + +int epoll_reactor::do_timerfd_create() +{ +#if defined(ASIO_HAS_TIMERFD) +# if defined(TFD_CLOEXEC) + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); +# else // defined(TFD_CLOEXEC) + int fd = -1; + errno = EINVAL; +# endif // defined(TFD_CLOEXEC) + + if (fd == -1 && errno == EINVAL) + { + fd = timerfd_create(CLOCK_MONOTONIC, 0); + if (fd != -1) + ::fcntl(fd, F_SETFD, FD_CLOEXEC); + } + + return fd; +#else // defined(ASIO_HAS_TIMERFD) + return -1; +#endif // defined(ASIO_HAS_TIMERFD) +} + +epoll_reactor::descriptor_state* epoll_reactor::allocate_descriptor_state() +{ + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + return registered_descriptors_.alloc(ASIO_CONCURRENCY_HINT_IS_LOCKING( + REACTOR_IO, scheduler_.concurrency_hint())); +} + +void epoll_reactor::free_descriptor_state(epoll_reactor::descriptor_state* s) +{ + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + registered_descriptors_.free(s); +} + +void epoll_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void epoll_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +void epoll_reactor::update_timeout() +{ +#if defined(ASIO_HAS_TIMERFD) + if (timer_fd_ != -1) + { + itimerspec new_timeout; + itimerspec old_timeout; + int flags = get_timeout(new_timeout); + timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); + return; + } +#endif // defined(ASIO_HAS_TIMERFD) + interrupt(); +} + +int epoll_reactor::get_timeout(int msec) +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + const int max_msec = 5 * 60 * 1000; + return timer_queues_.wait_duration_msec( + (msec < 0 || max_msec < msec) ? max_msec : msec); +} + +#if defined(ASIO_HAS_TIMERFD) +int epoll_reactor::get_timeout(itimerspec& ts) +{ + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + + long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); + ts.it_value.tv_sec = usec / 1000000; + ts.it_value.tv_nsec = usec ? (usec % 1000000) * 1000 : 1; + + return usec ? 0 : TFD_TIMER_ABSTIME; +} +#endif // defined(ASIO_HAS_TIMERFD) + +struct epoll_reactor::perform_io_cleanup_on_block_exit +{ + explicit perform_io_cleanup_on_block_exit(epoll_reactor* r) + : reactor_(r), first_op_(0) + { + } + + ~perform_io_cleanup_on_block_exit() + { + if (first_op_) + { + // Post the remaining completed operations for invocation. + if (!ops_.empty()) + reactor_->scheduler_.post_deferred_completions(ops_); + + // A user-initiated operation has completed, but there's no need to + // explicitly call work_finished() here. Instead, we'll take advantage of + // the fact that the scheduler will call work_finished() once we return. + } + else + { + // No user-initiated operations have completed, so we need to compensate + // for the work_finished() call that the scheduler will make once this + // operation returns. + reactor_->scheduler_.compensating_work_started(); + } + } + + epoll_reactor* reactor_; + op_queue ops_; + operation* first_op_; +}; + +epoll_reactor::descriptor_state::descriptor_state(bool locking) + : operation(&epoll_reactor::descriptor_state::do_complete), + mutex_(locking) +{ +} + +operation* epoll_reactor::descriptor_state::perform_io(uint32_t events) +{ + mutex_.lock(); + perform_io_cleanup_on_block_exit io_cleanup(reactor_); + mutex::scoped_lock descriptor_lock(mutex_, mutex::scoped_lock::adopt_lock); + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI }; + for (int j = max_ops - 1; j >= 0; --j) + { + if (events & (flag[j] | EPOLLERR | EPOLLHUP)) + { + try_speculative_[j] = true; + while (reactor_op* op = op_queue_[j].front()) + { + if (reactor_op::status status = op->perform()) + { + op_queue_[j].pop(); + io_cleanup.ops_.push(op); + if (status == reactor_op::done_and_exhausted) + { + try_speculative_[j] = false; + break; + } + } + else + break; + } + } + } + + // The first operation will be returned for completion now. The others will + // be posted for later by the io_cleanup object's destructor. + io_cleanup.first_op_ = io_cleanup.ops_.front(); + io_cleanup.ops_.pop(); + return io_cleanup.first_op_; +} + +void epoll_reactor::descriptor_state::do_complete( + void* owner, operation* base, + const asio::error_code& ec, std::size_t bytes_transferred) +{ + if (owner) + { + descriptor_state* descriptor_data = static_cast(base); + uint32_t events = static_cast(bytes_transferred); + if (operation* op = descriptor_data->perform_io(events)) + { + op->complete(owner, ec, 0); + } + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_EPOLL) + +#endif // ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/eventfd_select_interrupter.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/eventfd_select_interrupter.ipp new file mode 100644 index 000000000..8ed61f8b7 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/eventfd_select_interrupter.ipp @@ -0,0 +1,171 @@ +// +// detail/impl/eventfd_select_interrupter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP +#define ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_EVENTFD) + +#include +#include +#include +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 && !defined(__UCLIBC__) +# include +#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 && !defined(__UCLIBC__) +# include +#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 && !defined(__UCLIBC__) +#include "asio/detail/cstdint.hpp" +#include "asio/detail/eventfd_select_interrupter.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +eventfd_select_interrupter::eventfd_select_interrupter() +{ + open_descriptors(); +} + +void eventfd_select_interrupter::open_descriptors() +{ +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 && !defined(__UCLIBC__) + write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0); + if (read_descriptor_ != -1) + { + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + ::fcntl(read_descriptor_, F_SETFD, FD_CLOEXEC); + } +#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 && !defined(__UCLIBC__) +# if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) + write_descriptor_ = read_descriptor_ = + ::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); +# else // defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) + errno = EINVAL; + write_descriptor_ = read_descriptor_ = -1; +# endif // defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) + if (read_descriptor_ == -1 && errno == EINVAL) + { + write_descriptor_ = read_descriptor_ = ::eventfd(0, 0); + if (read_descriptor_ != -1) + { + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + ::fcntl(read_descriptor_, F_SETFD, FD_CLOEXEC); + } + } +#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 && !defined(__UCLIBC__) + + if (read_descriptor_ == -1) + { + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + ::fcntl(read_descriptor_, F_SETFD, FD_CLOEXEC); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + ::fcntl(write_descriptor_, F_SETFD, FD_CLOEXEC); + } + else + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "eventfd_select_interrupter"); + } + } +} + +eventfd_select_interrupter::~eventfd_select_interrupter() +{ + close_descriptors(); +} + +void eventfd_select_interrupter::close_descriptors() +{ + if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_) + ::close(write_descriptor_); + if (read_descriptor_ != -1) + ::close(read_descriptor_); +} + +void eventfd_select_interrupter::recreate() +{ + close_descriptors(); + + write_descriptor_ = -1; + read_descriptor_ = -1; + + open_descriptors(); +} + +void eventfd_select_interrupter::interrupt() +{ + uint64_t counter(1UL); + int result = ::write(write_descriptor_, &counter, sizeof(uint64_t)); + (void)result; +} + +bool eventfd_select_interrupter::reset() +{ + if (write_descriptor_ == read_descriptor_) + { + for (;;) + { + // Only perform one read. The kernel maintains an atomic counter. + uint64_t counter(0); + errno = 0; + int bytes_read = ::read(read_descriptor_, &counter, sizeof(uint64_t)); + if (bytes_read < 0 && errno == EINTR) + continue; + return true; + } + } + else + { + for (;;) + { + // Clear all data from the pipe. + char data[1024]; + int bytes_read = ::read(read_descriptor_, data, sizeof(data)); + if (bytes_read == sizeof(data)) + continue; + if (bytes_read > 0) + return true; + if (bytes_read == 0) + return false; + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK) + return true; + if (errno == EAGAIN) + return true; + return false; + } + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_EVENTFD) + +#endif // ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/handler_tracking.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/handler_tracking.ipp new file mode 100644 index 000000000..79a87df09 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/handler_tracking.ipp @@ -0,0 +1,396 @@ +// +// detail/impl/handler_tracking.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_HANDLER_TRACKING_IPP +#define ASIO_DETAIL_IMPL_HANDLER_TRACKING_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_CUSTOM_HANDLER_TRACKING) + +// The handler tracking implementation is provided by the user-specified header. + +#elif defined(ASIO_ENABLE_HANDLER_TRACKING) + +#include +#include +#include "asio/detail/handler_tracking.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) +# include "asio/time_traits.hpp" +#elif defined(ASIO_HAS_CHRONO) +# include "asio/detail/chrono.hpp" +# include "asio/detail/chrono_time_traits.hpp" +# include "asio/wait_traits.hpp" +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/socket_types.hpp" +#elif !defined(ASIO_WINDOWS) +# include +#endif // !defined(ASIO_WINDOWS) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct handler_tracking_timestamp +{ + uint64_t seconds; + uint64_t microseconds; + + handler_tracking_timestamp() + { +#if defined(ASIO_HAS_BOOST_DATE_TIME) + boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); + boost::posix_time::time_duration now = + boost::posix_time::microsec_clock::universal_time() - epoch; +#elif defined(ASIO_HAS_CHRONO) + typedef chrono_time_traits > traits_helper; + traits_helper::posix_time_duration now( + chrono::system_clock::now().time_since_epoch()); +#endif + seconds = static_cast(now.total_seconds()); + microseconds = static_cast(now.total_microseconds() % 1000000); + } +}; + +struct handler_tracking::tracking_state +{ + static_mutex mutex_; + uint64_t next_id_; + tss_ptr* current_completion_; + tss_ptr* current_location_; +}; + +handler_tracking::tracking_state* handler_tracking::get_state() +{ + static tracking_state state = { ASIO_STATIC_MUTEX_INIT, 1, 0, 0 }; + return &state; +} + +void handler_tracking::init() +{ + static tracking_state* state = get_state(); + + state->mutex_.init(); + + static_mutex::scoped_lock lock(state->mutex_); + if (state->current_completion_ == 0) + state->current_completion_ = new tss_ptr; + if (state->current_location_ == 0) + state->current_location_ = new tss_ptr; +} + +handler_tracking::location::location( + const char* file, int line, const char* func) + : file_(file), + line_(line), + func_(func), + next_(*get_state()->current_location_) +{ + if (file_) + *get_state()->current_location_ = this; +} + +handler_tracking::location::~location() +{ + if (file_) + *get_state()->current_location_ = next_; +} + +void handler_tracking::creation(execution_context&, + handler_tracking::tracked_handler& h, + const char* object_type, void* object, + uintmax_t /*native_handle*/, const char* op_name) +{ + static tracking_state* state = get_state(); + + static_mutex::scoped_lock lock(state->mutex_); + h.id_ = state->next_id_++; + lock.unlock(); + + handler_tracking_timestamp timestamp; + + uint64_t current_id = 0; + if (completion* current_completion = *state->current_completion_) + current_id = current_completion->id_; + + for (location* current_location = *state->current_location_; + current_location; current_location = current_location->next_) + { + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|%I64u^%I64u|%s%s%.80s%s(%.80s:%d)\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|%llu^%llu|%s%s%.80s%s(%.80s:%d)\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + current_id, h.id_, + current_location == *state->current_location_ ? "in " : "called from ", + current_location->func_ ? "'" : "", + current_location->func_ ? current_location->func_ : "", + current_location->func_ ? "' " : "", + current_location->file_, current_location->line_); + } + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|%I64u*%I64u|%.20s@%p.%.50s\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|%llu*%llu|%.20s@%p.%.50s\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + current_id, h.id_, object_type, object, op_name); +} + +handler_tracking::completion::completion( + const handler_tracking::tracked_handler& h) + : id_(h.id_), + invoked_(false), + next_(*get_state()->current_completion_) +{ + *get_state()->current_completion_ = this; +} + +handler_tracking::completion::~completion() +{ + if (id_) + { + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|%c%I64u|\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|%c%llu|\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + invoked_ ? '!' : '~', id_); + } + + *get_state()->current_completion_ = next_; +} + +void handler_tracking::completion::invocation_begin() +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|>%I64u|\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|>%llu|\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, id_); + + invoked_ = true; +} + +void handler_tracking::completion::invocation_begin( + const asio::error_code& ec) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|>%I64u|ec=%.20s:%d\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|>%llu|ec=%.20s:%d\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + id_, ec.category().name(), ec.value()); + + invoked_ = true; +} + +void handler_tracking::completion::invocation_begin( + const asio::error_code& ec, std::size_t bytes_transferred) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|>%I64u|ec=%.20s:%d,bytes_transferred=%I64u\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|>%llu|ec=%.20s:%d,bytes_transferred=%llu\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + id_, ec.category().name(), ec.value(), + static_cast(bytes_transferred)); + + invoked_ = true; +} + +void handler_tracking::completion::invocation_begin( + const asio::error_code& ec, int signal_number) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|>%I64u|ec=%.20s:%d,signal_number=%d\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|>%llu|ec=%.20s:%d,signal_number=%d\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + id_, ec.category().name(), ec.value(), signal_number); + + invoked_ = true; +} + +void handler_tracking::completion::invocation_begin( + const asio::error_code& ec, const char* arg) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|>%I64u|ec=%.20s:%d,%.50s\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|>%llu|ec=%.20s:%d,%.50s\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + id_, ec.category().name(), ec.value(), arg); + + invoked_ = true; +} + +void handler_tracking::completion::invocation_end() +{ + if (id_) + { + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|<%I64u|\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|<%llu|\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, id_); + + id_ = 0; + } +} + +void handler_tracking::operation(execution_context&, + const char* object_type, void* object, + uintmax_t /*native_handle*/, const char* op_name) +{ + static tracking_state* state = get_state(); + + handler_tracking_timestamp timestamp; + + unsigned long long current_id = 0; + if (completion* current_completion = *state->current_completion_) + current_id = current_completion->id_; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|%I64u|%.20s@%p.%.50s\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|%llu|%.20s@%p.%.50s\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + current_id, object_type, object, op_name); +} + +void handler_tracking::reactor_registration(execution_context& /*context*/, + uintmax_t /*native_handle*/, uintmax_t /*registration*/) +{ +} + +void handler_tracking::reactor_deregistration(execution_context& /*context*/, + uintmax_t /*native_handle*/, uintmax_t /*registration*/) +{ +} + +void handler_tracking::reactor_events(execution_context& /*context*/, + uintmax_t /*native_handle*/, unsigned /*events*/) +{ +} + +void handler_tracking::reactor_operation( + const tracked_handler& h, const char* op_name, + const asio::error_code& ec) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|.%I64u|%s,ec=%.20s:%d\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|.%llu|%s,ec=%.20s:%d\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + h.id_, op_name, ec.category().name(), ec.value()); +} + +void handler_tracking::reactor_operation( + const tracked_handler& h, const char* op_name, + const asio::error_code& ec, std::size_t bytes_transferred) +{ + handler_tracking_timestamp timestamp; + + write_line( +#if defined(ASIO_WINDOWS) + "@asio|%I64u.%06I64u|.%I64u|%s,ec=%.20s:%d,bytes_transferred=%I64u\n", +#else // defined(ASIO_WINDOWS) + "@asio|%llu.%06llu|.%llu|%s,ec=%.20s:%d,bytes_transferred=%llu\n", +#endif // defined(ASIO_WINDOWS) + timestamp.seconds, timestamp.microseconds, + h.id_, op_name, ec.category().name(), ec.value(), + static_cast(bytes_transferred)); +} + +void handler_tracking::write_line(const char* format, ...) +{ + using namespace std; // For sprintf (or equivalent). + + va_list args; + va_start(args, format); + + char line[256] = ""; +#if defined(ASIO_HAS_SECURE_RTL) + int length = vsprintf_s(line, sizeof(line), format, args); +#else // defined(ASIO_HAS_SECURE_RTL) + int length = vsprintf(line, format, args); +#endif // defined(ASIO_HAS_SECURE_RTL) + + va_end(args); + +#if defined(ASIO_WINDOWS_RUNTIME) + wchar_t wline[256] = L""; + mbstowcs_s(0, wline, sizeof(wline) / sizeof(wchar_t), line, length); + ::OutputDebugStringW(wline); +#elif defined(ASIO_WINDOWS) + HANDLE stderr_handle = ::GetStdHandle(STD_ERROR_HANDLE); + DWORD bytes_written = 0; + ::WriteFile(stderr_handle, line, length, &bytes_written, 0); +#else // defined(ASIO_WINDOWS) + ::write(STDERR_FILENO, line, length); +#endif // defined(ASIO_WINDOWS) +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_ENABLE_HANDLER_TRACKING) + +#endif // ASIO_DETAIL_IMPL_HANDLER_TRACKING_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/kqueue_reactor.hpp b/third_party/asio/1.18.2/include/asio/detail/impl/kqueue_reactor.hpp new file mode 100644 index 000000000..67079f050 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/kqueue_reactor.hpp @@ -0,0 +1,93 @@ +// +// detail/impl/kqueue_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP +#define ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void kqueue_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +// Remove a timer queue from the reactor. +template +void kqueue_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void kqueue_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + scheduler_.post_immediate_completion(op, false); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + scheduler_.work_started(); + if (earliest) + interrupt(); +} + +template +std::size_t kqueue_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + scheduler_.post_deferred_completions(ops); + return n; +} + +template +void kqueue_reactor::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source) +{ + mutex::scoped_lock lock(mutex_); + op_queue ops; + queue.cancel_timer(target, ops); + queue.move_timer(target, source); + lock.unlock(); + scheduler_.post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_KQUEUE) + +#endif // ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/kqueue_reactor.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/kqueue_reactor.ipp new file mode 100644 index 000000000..8a5d26c60 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/kqueue_reactor.ipp @@ -0,0 +1,570 @@ +// +// detail/impl/kqueue_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP +#define ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/scheduler.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#if defined(__NetBSD__) +# include +#endif + +#include "asio/detail/push_options.hpp" + +#if defined(__NetBSD__) && __NetBSD_Version__ < 999001500 +# define ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \ + EV_SET(ev, ident, filt, flags, fflags, data, \ + reinterpret_cast(static_cast(udata))) +#else +# define ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \ + EV_SET(ev, ident, filt, flags, fflags, data, udata) +#endif + +namespace asio { +namespace detail { + +kqueue_reactor::kqueue_reactor(asio::execution_context& ctx) + : execution_context_service_base(ctx), + scheduler_(use_service(ctx)), + mutex_(ASIO_CONCURRENCY_HINT_IS_LOCKING( + REACTOR_REGISTRATION, scheduler_.concurrency_hint())), + kqueue_fd_(do_kqueue_create()), + interrupter_(), + shutdown_(false), + registered_descriptors_mutex_(mutex_.enabled()) +{ + struct kevent events[1]; + ASIO_KQUEUE_EV_SET(&events[0], interrupter_.read_descriptor(), + EVFILT_READ, EV_ADD, 0, 0, &interrupter_); + if (::kevent(kqueue_fd_, events, 1, 0, 0, 0) == -1) + { + asio::error_code error(errno, + asio::error::get_system_category()); + asio::detail::throw_error(error); + } +} + +kqueue_reactor::~kqueue_reactor() +{ + close(kqueue_fd_); +} + +void kqueue_reactor::shutdown() +{ + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue ops; + + while (descriptor_state* state = registered_descriptors_.first()) + { + for (int i = 0; i < max_ops; ++i) + ops.push(state->op_queue_[i]); + state->shutdown_ = true; + registered_descriptors_.free(state); + } + + timer_queues_.get_all_timers(ops); + + scheduler_.abandon_operations(ops); +} + +void kqueue_reactor::notify_fork( + asio::execution_context::fork_event fork_ev) +{ + if (fork_ev == asio::execution_context::fork_child) + { + // The kqueue descriptor is automatically closed in the child. + kqueue_fd_ = -1; + kqueue_fd_ = do_kqueue_create(); + + interrupter_.recreate(); + + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], interrupter_.read_descriptor(), + EVFILT_READ, EV_ADD, 0, 0, &interrupter_); + if (::kevent(kqueue_fd_, events, 1, 0, 0, 0) == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "kqueue interrupter registration"); + } + + // Re-register all descriptors with kqueue. + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + for (descriptor_state* state = registered_descriptors_.first(); + state != 0; state = state->next_) + { + if (state->num_kevents_ > 0) + { + ASIO_KQUEUE_EV_SET(&events[0], state->descriptor_, + EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, state); + ASIO_KQUEUE_EV_SET(&events[1], state->descriptor_, + EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, state); + if (::kevent(kqueue_fd_, events, state->num_kevents_, 0, 0, 0) == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "kqueue re-registration"); + } + } + } + } +} + +void kqueue_reactor::init_task() +{ + scheduler_.init_task(); +} + +int kqueue_reactor::register_descriptor(socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data) +{ + descriptor_data = allocate_descriptor_state(); + + ASIO_HANDLER_REACTOR_REGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + mutex::scoped_lock lock(descriptor_data->mutex_); + + descriptor_data->descriptor_ = descriptor; + descriptor_data->num_kevents_ = 0; + descriptor_data->shutdown_ = false; + + return 0; +} + +int kqueue_reactor::register_internal_descriptor( + int op_type, socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data, reactor_op* op) +{ + descriptor_data = allocate_descriptor_state(); + + ASIO_HANDLER_REACTOR_REGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + mutex::scoped_lock lock(descriptor_data->mutex_); + + descriptor_data->descriptor_ = descriptor; + descriptor_data->num_kevents_ = 1; + descriptor_data->shutdown_ = false; + descriptor_data->op_queue_[op_type].push(op); + + struct kevent events[1]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, EVFILT_READ, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + if (::kevent(kqueue_fd_, events, 1, 0, 0, 0) == -1) + return errno; + + return 0; +} + +void kqueue_reactor::move_descriptor(socket_type, + kqueue_reactor::per_descriptor_data& target_descriptor_data, + kqueue_reactor::per_descriptor_data& source_descriptor_data) +{ + target_descriptor_data = source_descriptor_data; + source_descriptor_data = 0; +} + +void kqueue_reactor::start_op(int op_type, socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data, reactor_op* op, + bool is_continuation, bool allow_speculative) +{ + if (!descriptor_data) + { + op->ec_ = asio::error::bad_descriptor; + post_immediate_completion(op, is_continuation); + return; + } + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (descriptor_data->shutdown_) + { + post_immediate_completion(op, is_continuation); + return; + } + + if (descriptor_data->op_queue_[op_type].empty()) + { + static const int num_kevents[max_ops] = { 1, 2, 1 }; + + if (allow_speculative + && (op_type != read_op + || descriptor_data->op_queue_[except_op].empty())) + { + if (op->perform()) + { + descriptor_lock.unlock(); + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + if (descriptor_data->num_kevents_ < num_kevents[op_type]) + { + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, EVFILT_READ, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + ASIO_KQUEUE_EV_SET(&events[1], descriptor, EVFILT_WRITE, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + if (::kevent(kqueue_fd_, events, num_kevents[op_type], 0, 0, 0) != -1) + { + descriptor_data->num_kevents_ = num_kevents[op_type]; + } + else + { + op->ec_ = asio::error_code(errno, + asio::error::get_system_category()); + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + } + } + else + { + if (descriptor_data->num_kevents_ < num_kevents[op_type]) + descriptor_data->num_kevents_ = num_kevents[op_type]; + + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, EVFILT_READ, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + ASIO_KQUEUE_EV_SET(&events[1], descriptor, EVFILT_WRITE, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + ::kevent(kqueue_fd_, events, descriptor_data->num_kevents_, 0, 0, 0); + } + } + + descriptor_data->op_queue_[op_type].push(op); + scheduler_.work_started(); +} + +void kqueue_reactor::cancel_ops(socket_type, + kqueue_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_lock.unlock(); + + scheduler_.post_deferred_completions(ops); +} + +void kqueue_reactor::deregister_descriptor(socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data, bool closing) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (!descriptor_data->shutdown_) + { + if (closing) + { + // The descriptor will be automatically removed from the kqueue when it + // is closed. + } + else + { + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, + EVFILT_READ, EV_DELETE, 0, 0, 0); + ASIO_KQUEUE_EV_SET(&events[1], descriptor, + EVFILT_WRITE, EV_DELETE, 0, 0, 0); + ::kevent(kqueue_fd_, events, descriptor_data->num_kevents_, 0, 0, 0); + } + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + ASIO_HANDLER_REACTOR_DEREGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + scheduler_.post_deferred_completions(ops); + + // Leave descriptor_data set so that it will be freed by the subsequent + // call to cleanup_descriptor_data. + } + else + { + // We are shutting down, so prevent cleanup_descriptor_data from freeing + // the descriptor_data object and let the destructor free it instead. + descriptor_data = 0; + } +} + +void kqueue_reactor::deregister_internal_descriptor(socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (!descriptor_data->shutdown_) + { + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, + EVFILT_READ, EV_DELETE, 0, 0, 0); + ASIO_KQUEUE_EV_SET(&events[1], descriptor, + EVFILT_WRITE, EV_DELETE, 0, 0, 0); + ::kevent(kqueue_fd_, events, descriptor_data->num_kevents_, 0, 0, 0); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + ops.push(descriptor_data->op_queue_[i]); + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + ASIO_HANDLER_REACTOR_DEREGISTRATION(( + context(), static_cast(descriptor), + reinterpret_cast(descriptor_data))); + + // Leave descriptor_data set so that it will be freed by the subsequent + // call to cleanup_descriptor_data. + } + else + { + // We are shutting down, so prevent cleanup_descriptor_data from freeing + // the descriptor_data object and let the destructor free it instead. + descriptor_data = 0; + } +} + +void kqueue_reactor::cleanup_descriptor_data( + per_descriptor_data& descriptor_data) +{ + if (descriptor_data) + { + free_descriptor_state(descriptor_data); + descriptor_data = 0; + } +} + +void kqueue_reactor::run(long usec, op_queue& ops) +{ + mutex::scoped_lock lock(mutex_); + + // Determine how long to block while waiting for events. + timespec timeout_buf = { 0, 0 }; + timespec* timeout = usec ? get_timeout(usec, timeout_buf) : &timeout_buf; + + lock.unlock(); + + // Block on the kqueue descriptor. + struct kevent events[128]; + int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout); + +#if defined(ASIO_ENABLE_HANDLER_TRACKING) + // Trace the waiting events. + for (int i = 0; i < num_events; ++i) + { + void* ptr = reinterpret_cast(events[i].udata); + if (ptr != &interrupter_) + { + unsigned event_mask = 0; + switch (events[i].filter) + { + case EVFILT_READ: + event_mask |= ASIO_HANDLER_REACTOR_READ_EVENT; + break; + case EVFILT_WRITE: + event_mask |= ASIO_HANDLER_REACTOR_WRITE_EVENT; + break; + } + if ((events[i].flags & (EV_ERROR | EV_OOBAND)) != 0) + event_mask |= ASIO_HANDLER_REACTOR_ERROR_EVENT; + ASIO_HANDLER_REACTOR_EVENTS((context(), + reinterpret_cast(ptr), event_mask)); + } + } +#endif // defined(ASIO_ENABLE_HANDLER_TRACKING) + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + void* ptr = reinterpret_cast(events[i].udata); + if (ptr == &interrupter_) + { + interrupter_.reset(); + } + else + { + descriptor_state* descriptor_data = static_cast(ptr); + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (events[i].filter == EVFILT_WRITE + && descriptor_data->num_kevents_ == 2 + && descriptor_data->op_queue_[write_op].empty()) + { + // Some descriptor types, like serial ports, don't seem to support + // EV_CLEAR with EVFILT_WRITE. Since we have no pending write + // operations we'll remove the EVFILT_WRITE registration here so that + // we don't end up in a tight spin. + struct kevent delete_events[1]; + ASIO_KQUEUE_EV_SET(&delete_events[0], + descriptor_data->descriptor_, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + ::kevent(kqueue_fd_, delete_events, 1, 0, 0, 0); + descriptor_data->num_kevents_ = 1; + } + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. +#if defined(__NetBSD__) + static const unsigned int filter[max_ops] = +#else + static const int filter[max_ops] = +#endif + { EVFILT_READ, EVFILT_WRITE, EVFILT_READ }; + for (int j = max_ops - 1; j >= 0; --j) + { + if (events[i].filter == filter[j]) + { + if (j != except_op || events[i].flags & EV_OOBAND) + { + while (reactor_op* op = descriptor_data->op_queue_[j].front()) + { + if (events[i].flags & EV_ERROR) + { + op->ec_ = asio::error_code( + static_cast(events[i].data), + asio::error::get_system_category()); + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + if (op->perform()) + { + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + else + break; + } + } + } + } + } + } + + lock.lock(); + timer_queues_.get_ready_timers(ops); +} + +void kqueue_reactor::interrupt() +{ + interrupter_.interrupt(); +} + +int kqueue_reactor::do_kqueue_create() +{ + int fd = ::kqueue(); + if (fd == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "kqueue"); + } + return fd; +} + +kqueue_reactor::descriptor_state* kqueue_reactor::allocate_descriptor_state() +{ + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + return registered_descriptors_.alloc(ASIO_CONCURRENCY_HINT_IS_LOCKING( + REACTOR_IO, scheduler_.concurrency_hint())); +} + +void kqueue_reactor::free_descriptor_state(kqueue_reactor::descriptor_state* s) +{ + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + registered_descriptors_.free(s); +} + +void kqueue_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void kqueue_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +timespec* kqueue_reactor::get_timeout(long usec, timespec& ts) +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + const long max_usec = 5 * 60 * 1000 * 1000; + usec = timer_queues_.wait_duration_usec( + (usec < 0 || max_usec < usec) ? max_usec : usec); + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + return &ts; +} + +} // namespace detail +} // namespace asio + +#undef ASIO_KQUEUE_EV_SET + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_KQUEUE) + +#endif // ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/null_event.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/null_event.ipp new file mode 100644 index 000000000..7b8f968fa --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/null_event.ipp @@ -0,0 +1,74 @@ +// +// detail/impl/null_event.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_NULL_EVENT_IPP +#define ASIO_DETAIL_IMPL_NULL_EVENT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/socket_types.hpp" +#else +# include +# if defined(__hpux) +# include +# endif +# if !defined(__hpux) || defined(__SELECT) +# include +# endif +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void null_event::do_wait() +{ +#if defined(ASIO_WINDOWS_RUNTIME) + std::this_thread::sleep_until((std::chrono::steady_clock::time_point::max)()); +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ::Sleep(INFINITE); +#else + ::pause(); +#endif +} + +void null_event::do_wait_for_usec(long usec) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + std::this_thread::sleep_for(std::chrono::microseconds(usec)); +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ::Sleep(usec / 1000); +#elif defined(__hpux) && defined(__SELECT) + timespec ts; + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + ::pselect(0, 0, 0, 0, &ts, 0); +#else + timeval tv; + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + ::select(0, 0, 0, 0, &tv); +#endif +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_NULL_EVENT_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/pipe_select_interrupter.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/pipe_select_interrupter.ipp new file mode 100644 index 000000000..31c1b5c9b --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/pipe_select_interrupter.ipp @@ -0,0 +1,129 @@ +// +// detail/impl/pipe_select_interrupter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP +#define ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) +#if !defined(ASIO_WINDOWS) +#if !defined(__CYGWIN__) +#if !defined(__SYMBIAN32__) +#if !defined(ASIO_HAS_EVENTFD) + +#include +#include +#include +#include +#include "asio/detail/pipe_select_interrupter.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +pipe_select_interrupter::pipe_select_interrupter() +{ + open_descriptors(); +} + +void pipe_select_interrupter::open_descriptors() +{ + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + +#if defined(FD_CLOEXEC) + ::fcntl(read_descriptor_, F_SETFD, FD_CLOEXEC); + ::fcntl(write_descriptor_, F_SETFD, FD_CLOEXEC); +#endif // defined(FD_CLOEXEC) + } + else + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "pipe_select_interrupter"); + } +} + +pipe_select_interrupter::~pipe_select_interrupter() +{ + close_descriptors(); +} + +void pipe_select_interrupter::close_descriptors() +{ + if (read_descriptor_ != -1) + ::close(read_descriptor_); + if (write_descriptor_ != -1) + ::close(write_descriptor_); +} + +void pipe_select_interrupter::recreate() +{ + close_descriptors(); + + write_descriptor_ = -1; + read_descriptor_ = -1; + + open_descriptors(); +} + +void pipe_select_interrupter::interrupt() +{ + char byte = 0; + signed_size_type result = ::write(write_descriptor_, &byte, 1); + (void)result; +} + +bool pipe_select_interrupter::reset() +{ + for (;;) + { + char data[1024]; + signed_size_type bytes_read = ::read(read_descriptor_, data, sizeof(data)); + if (bytes_read == sizeof(data)) + continue; + if (bytes_read > 0) + return true; + if (bytes_read == 0) + return false; + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK || errno == EAGAIN) + return true; + return false; + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_EVENTFD) +#endif // !defined(__SYMBIAN32__) +#endif // !defined(__CYGWIN__) +#endif // !defined(ASIO_WINDOWS) +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/posix_event.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/posix_event.ipp new file mode 100644 index 000000000..a823d8fbe --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/posix_event.ipp @@ -0,0 +1,63 @@ +// +// detail/impl/posix_event.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_EVENT_IPP +#define ASIO_DETAIL_IMPL_POSIX_EVENT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include "asio/detail/posix_event.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +posix_event::posix_event() + : state_(0) +{ +#if (defined(__MACH__) && defined(__APPLE__)) \ + || (defined(__ANDROID__) && (__ANDROID_API__ < 21)) + int error = ::pthread_cond_init(&cond_, 0); +#else // (defined(__MACH__) && defined(__APPLE__)) + // || (defined(__ANDROID__) && (__ANDROID_API__ < 21)) + ::pthread_condattr_t attr; + int error = ::pthread_condattr_init(&attr); + if (error == 0) + { + error = ::pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + if (error == 0) + error = ::pthread_cond_init(&cond_, &attr); + ::pthread_condattr_destroy(&attr); + } +#endif // (defined(__MACH__) && defined(__APPLE__)) + // || (defined(__ANDROID__) && (__ANDROID_API__ < 21)) + + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "event"); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_EVENT_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/posix_mutex.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/posix_mutex.ipp new file mode 100644 index 000000000..c553ab4ee --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/posix_mutex.ipp @@ -0,0 +1,46 @@ +// +// detail/impl/posix_mutex.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP +#define ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include "asio/detail/posix_mutex.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +posix_mutex::posix_mutex() +{ + int error = ::pthread_mutex_init(&mutex_, 0); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "mutex"); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/posix_thread.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/posix_thread.ipp new file mode 100644 index 000000000..94373ee02 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/posix_thread.ipp @@ -0,0 +1,84 @@ +// +// detail/impl/posix_thread.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_THREAD_IPP +#define ASIO_DETAIL_IMPL_POSIX_THREAD_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include "asio/detail/posix_thread.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +posix_thread::~posix_thread() +{ + if (!joined_) + ::pthread_detach(thread_); +} + +void posix_thread::join() +{ + if (!joined_) + { + ::pthread_join(thread_, 0); + joined_ = true; + } +} + +std::size_t posix_thread::hardware_concurrency() +{ +#if defined(_SC_NPROCESSORS_ONLN) + long result = sysconf(_SC_NPROCESSORS_ONLN); + if (result > 0) + return result; +#endif // defined(_SC_NPROCESSORS_ONLN) + return 0; +} + +void posix_thread::start_thread(func_base* arg) +{ + int error = ::pthread_create(&thread_, 0, + asio_detail_posix_thread_function, arg); + if (error != 0) + { + delete arg; + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); + } +} + +void* asio_detail_posix_thread_function(void* arg) +{ + posix_thread::auto_func_base_ptr func = { + static_cast(arg) }; + func.ptr->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_THREAD_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/posix_tss_ptr.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/posix_tss_ptr.ipp new file mode 100644 index 000000000..6a368e0ff --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/posix_tss_ptr.ipp @@ -0,0 +1,46 @@ +// +// detail/impl/posix_tss_ptr.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP +#define ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include "asio/detail/posix_tss_ptr.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void posix_tss_ptr_create(pthread_key_t& key) +{ + int error = ::pthread_key_create(&key, 0); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "tss"); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/reactive_descriptor_service.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/reactive_descriptor_service.ipp new file mode 100644 index 000000000..9caf06877 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/reactive_descriptor_service.ipp @@ -0,0 +1,223 @@ +// +// detail/impl/reactive_descriptor_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP +#define ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + +#include "asio/error.hpp" +#include "asio/detail/reactive_descriptor_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +reactive_descriptor_service::reactive_descriptor_service( + execution_context& context) + : execution_context_service_base(context), + reactor_(asio::use_service(context)) +{ + reactor_.init_task(); +} + +void reactive_descriptor_service::shutdown() +{ +} + +void reactive_descriptor_service::construct( + reactive_descriptor_service::implementation_type& impl) +{ + impl.descriptor_ = -1; + impl.state_ = 0; +} + +void reactive_descriptor_service::move_construct( + reactive_descriptor_service::implementation_type& impl, + reactive_descriptor_service::implementation_type& other_impl) + ASIO_NOEXCEPT +{ + impl.descriptor_ = other_impl.descriptor_; + other_impl.descriptor_ = -1; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + reactor_.move_descriptor(impl.descriptor_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_descriptor_service::move_assign( + reactive_descriptor_service::implementation_type& impl, + reactive_descriptor_service& other_service, + reactive_descriptor_service::implementation_type& other_impl) +{ + destroy(impl); + + impl.descriptor_ = other_impl.descriptor_; + other_impl.descriptor_ = -1; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + other_service.reactor_.move_descriptor(impl.descriptor_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_descriptor_service::destroy( + reactive_descriptor_service::implementation_type& impl) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((reactor_.context(), + "descriptor", &impl, impl.descriptor_, "close")); + + reactor_.deregister_descriptor(impl.descriptor_, impl.reactor_data_, + (impl.state_ & descriptor_ops::possible_dup) == 0); + + asio::error_code ignored_ec; + descriptor_ops::close(impl.descriptor_, impl.state_, ignored_ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } +} + +asio::error_code reactive_descriptor_service::assign( + reactive_descriptor_service::implementation_type& impl, + const native_handle_type& native_descriptor, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor( + native_descriptor, impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.descriptor_ = native_descriptor; + impl.state_ = descriptor_ops::possible_dup; + ec = asio::error_code(); + return ec; +} + +asio::error_code reactive_descriptor_service::close( + reactive_descriptor_service::implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((reactor_.context(), + "descriptor", &impl, impl.descriptor_, "close")); + + reactor_.deregister_descriptor(impl.descriptor_, impl.reactor_data_, + (impl.state_ & descriptor_ops::possible_dup) == 0); + + descriptor_ops::close(impl.descriptor_, impl.state_, ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } + else + { + ec = asio::error_code(); + } + + // The descriptor is closed by the OS even if close() returns an error. + // + // (Actually, POSIX says the state of the descriptor is unspecified. On + // Linux the descriptor is apparently closed anyway; e.g. see + // http://lkml.org/lkml/2005/9/10/129 + // We'll just have to assume that other OSes follow the same behaviour.) + construct(impl); + + return ec; +} + +reactive_descriptor_service::native_handle_type +reactive_descriptor_service::release( + reactive_descriptor_service::implementation_type& impl) +{ + native_handle_type descriptor = impl.descriptor_; + + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((reactor_.context(), + "descriptor", &impl, impl.descriptor_, "release")); + + reactor_.deregister_descriptor(impl.descriptor_, impl.reactor_data_, false); + reactor_.cleanup_descriptor_data(impl.reactor_data_); + construct(impl); + } + + return descriptor; +} + +asio::error_code reactive_descriptor_service::cancel( + reactive_descriptor_service::implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + ASIO_HANDLER_OPERATION((reactor_.context(), + "descriptor", &impl, impl.descriptor_, "cancel")); + + reactor_.cancel_ops(impl.descriptor_, impl.reactor_data_); + ec = asio::error_code(); + return ec; +} + +void reactive_descriptor_service::start_op( + reactive_descriptor_service::implementation_type& impl, + int op_type, reactor_op* op, bool is_continuation, + bool is_non_blocking, bool noop) +{ + if (!noop) + { + if ((impl.state_ & descriptor_ops::non_blocking) || + descriptor_ops::set_internal_non_blocking( + impl.descriptor_, impl.state_, true, op->ec_)) + { + reactor_.start_op(op_type, impl.descriptor_, + impl.reactor_data_, op, is_continuation, is_non_blocking); + return; + } + } + + reactor_.post_immediate_completion(op, is_continuation); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/reactive_serial_port_service.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/reactive_serial_port_service.ipp new file mode 100644 index 000000000..23515a96c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/reactive_serial_port_service.ipp @@ -0,0 +1,149 @@ +// +// detail/impl/reactive_serial_port_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP +#define ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include +#include "asio/detail/reactive_serial_port_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +reactive_serial_port_service::reactive_serial_port_service( + execution_context& context) + : execution_context_service_base(context), + descriptor_service_(context) +{ +} + +void reactive_serial_port_service::shutdown() +{ + descriptor_service_.shutdown(); +} + +asio::error_code reactive_serial_port_service::open( + reactive_serial_port_service::implementation_type& impl, + const std::string& device, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + descriptor_ops::state_type state = 0; + int fd = descriptor_ops::open(device.c_str(), + O_RDWR | O_NONBLOCK | O_NOCTTY, ec); + if (fd < 0) + return ec; + + int s = descriptor_ops::fcntl(fd, F_GETFL, ec); + if (s >= 0) + s = descriptor_ops::fcntl(fd, F_SETFL, s | O_NONBLOCK, ec); + if (s < 0) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, state, ignored_ec); + return ec; + } + + // Set up default serial port options. + termios ios; + s = ::tcgetattr(fd, &ios); + descriptor_ops::get_last_error(ec, s < 0); + if (s >= 0) + { +#if defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE) + ::cfmakeraw(&ios); +#else + ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK + | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + ios.c_oflag &= ~OPOST; + ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + ios.c_cflag &= ~(CSIZE | PARENB); + ios.c_cflag |= CS8; +#endif + ios.c_iflag |= IGNPAR; + ios.c_cflag |= CREAD | CLOCAL; + s = ::tcsetattr(fd, TCSANOW, &ios); + descriptor_ops::get_last_error(ec, s < 0); + } + if (s < 0) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, state, ignored_ec); + return ec; + } + + // We're done. Take ownership of the serial port descriptor. + if (descriptor_service_.assign(impl, fd, ec)) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, state, ignored_ec); + } + + return ec; +} + +asio::error_code reactive_serial_port_service::do_set_option( + reactive_serial_port_service::implementation_type& impl, + reactive_serial_port_service::store_function_type store, + const void* option, asio::error_code& ec) +{ + termios ios; + int s = ::tcgetattr(descriptor_service_.native_handle(impl), &ios); + descriptor_ops::get_last_error(ec, s < 0); + if (s < 0) + return ec; + + if (store(option, ios, ec)) + return ec; + + s = ::tcsetattr(descriptor_service_.native_handle(impl), TCSANOW, &ios); + descriptor_ops::get_last_error(ec, s < 0); + return ec; +} + +asio::error_code reactive_serial_port_service::do_get_option( + const reactive_serial_port_service::implementation_type& impl, + reactive_serial_port_service::load_function_type load, + void* option, asio::error_code& ec) const +{ + termios ios; + int s = ::tcgetattr(descriptor_service_.native_handle(impl), &ios); + descriptor_ops::get_last_error(ec, s < 0); + if (s < 0) + return ec; + + return load(option, ios, ec); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) +#endif // defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/reactive_socket_service_base.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/reactive_socket_service_base.ipp new file mode 100644 index 000000000..afda16f35 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/reactive_socket_service_base.ipp @@ -0,0 +1,300 @@ +// +// detail/reactive_socket_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_IOCP) \ + && !defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/reactive_socket_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +reactive_socket_service_base::reactive_socket_service_base( + execution_context& context) + : reactor_(use_service(context)) +{ + reactor_.init_task(); +} + +void reactive_socket_service_base::base_shutdown() +{ +} + +void reactive_socket_service_base::construct( + reactive_socket_service_base::base_implementation_type& impl) +{ + impl.socket_ = invalid_socket; + impl.state_ = 0; +} + +void reactive_socket_service_base::base_move_construct( + reactive_socket_service_base::base_implementation_type& impl, + reactive_socket_service_base::base_implementation_type& other_impl) + ASIO_NOEXCEPT +{ + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + reactor_.move_descriptor(impl.socket_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_socket_service_base::base_move_assign( + reactive_socket_service_base::base_implementation_type& impl, + reactive_socket_service_base& other_service, + reactive_socket_service_base::base_implementation_type& other_impl) +{ + destroy(impl); + + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + other_service.reactor_.move_descriptor(impl.socket_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_socket_service_base::destroy( + reactive_socket_service_base::base_implementation_type& impl) +{ + if (impl.socket_ != invalid_socket) + { + ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "close")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, + (impl.state_ & socket_ops::possible_dup) == 0); + + asio::error_code ignored_ec; + socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } +} + +asio::error_code reactive_socket_service_base::close( + reactive_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "close")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, + (impl.state_ & socket_ops::possible_dup) == 0); + + socket_ops::close(impl.socket_, impl.state_, false, ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } + else + { + ec = asio::error_code(); + } + + // The descriptor is closed by the OS even if close() returns an error. + // + // (Actually, POSIX says the state of the descriptor is unspecified. On + // Linux the descriptor is apparently closed anyway; e.g. see + // http://lkml.org/lkml/2005/9/10/129 + // We'll just have to assume that other OSes follow the same behaviour. The + // known exception is when Windows's closesocket() function fails with + // WSAEWOULDBLOCK, but this case is handled inside socket_ops::close(). + construct(impl); + + return ec; +} + +socket_type reactive_socket_service_base::release( + reactive_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return invalid_socket; + } + + ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "release")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, false); + reactor_.cleanup_descriptor_data(impl.reactor_data_); + socket_type sock = impl.socket_; + construct(impl); + ec = asio::error_code(); + return sock; +} + +asio::error_code reactive_socket_service_base::cancel( + reactive_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "cancel")); + + reactor_.cancel_ops(impl.socket_, impl.reactor_data_); + ec = asio::error_code(); + return ec; +} + +asio::error_code reactive_socket_service_base::do_open( + reactive_socket_service_base::base_implementation_type& impl, + int af, int type, int protocol, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(af, type, protocol, ec)); + if (sock.get() == invalid_socket) + return ec; + + if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.socket_ = sock.release(); + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + ec = asio::error_code(); + return ec; +} + +asio::error_code reactive_socket_service_base::do_assign( + reactive_socket_service_base::base_implementation_type& impl, int type, + const reactive_socket_service_base::native_handle_type& native_socket, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor( + native_socket, impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.socket_ = native_socket; + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + impl.state_ |= socket_ops::possible_dup; + ec = asio::error_code(); + return ec; +} + +void reactive_socket_service_base::start_op( + reactive_socket_service_base::base_implementation_type& impl, + int op_type, reactor_op* op, bool is_continuation, + bool is_non_blocking, bool noop) +{ + if (!noop) + { + if ((impl.state_ & socket_ops::non_blocking) + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, true, op->ec_)) + { + reactor_.start_op(op_type, impl.socket_, + impl.reactor_data_, op, is_continuation, is_non_blocking); + return; + } + } + + reactor_.post_immediate_completion(op, is_continuation); +} + +void reactive_socket_service_base::start_accept_op( + reactive_socket_service_base::base_implementation_type& impl, + reactor_op* op, bool is_continuation, bool peer_is_open) +{ + if (!peer_is_open) + start_op(impl, reactor::read_op, op, is_continuation, true, false); + else + { + op->ec_ = asio::error::already_open; + reactor_.post_immediate_completion(op, is_continuation); + } +} + +void reactive_socket_service_base::start_connect_op( + reactive_socket_service_base::base_implementation_type& impl, + reactor_op* op, bool is_continuation, + const socket_addr_type* addr, size_t addrlen) +{ + if ((impl.state_ & socket_ops::non_blocking) + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, true, op->ec_)) + { + if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) + { + if (op->ec_ == asio::error::in_progress + || op->ec_ == asio::error::would_block) + { + op->ec_ = asio::error_code(); + reactor_.start_op(reactor::connect_op, impl.socket_, + impl.reactor_data_, op, is_continuation, false); + return; + } + } + } + + reactor_.post_immediate_completion(op, is_continuation); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_IOCP) + // && !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/resolver_service_base.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/resolver_service_base.ipp new file mode 100644 index 000000000..bd1b0bc02 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/resolver_service_base.ipp @@ -0,0 +1,158 @@ +// +// detail/impl/resolver_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/resolver_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class resolver_service_base::work_scheduler_runner +{ +public: + work_scheduler_runner(scheduler_impl& work_scheduler) + : work_scheduler_(work_scheduler) + { + } + + void operator()() + { + asio::error_code ec; + work_scheduler_.run(ec); + } + +private: + scheduler_impl& work_scheduler_; +}; + +resolver_service_base::resolver_service_base(execution_context& context) + : scheduler_(asio::use_service(context)), + work_scheduler_(new scheduler_impl(context, -1, false)), + work_thread_(0) +{ + work_scheduler_->work_started(); +} + +resolver_service_base::~resolver_service_base() +{ + base_shutdown(); +} + +void resolver_service_base::base_shutdown() +{ + if (work_scheduler_.get()) + { + work_scheduler_->work_finished(); + work_scheduler_->stop(); + if (work_thread_.get()) + { + work_thread_->join(); + work_thread_.reset(); + } + work_scheduler_.reset(); + } +} + +void resolver_service_base::base_notify_fork( + execution_context::fork_event fork_ev) +{ + if (work_thread_.get()) + { + if (fork_ev == execution_context::fork_prepare) + { + work_scheduler_->stop(); + work_thread_->join(); + work_thread_.reset(); + } + } + else if (fork_ev != execution_context::fork_prepare) + { + work_scheduler_->restart(); + } +} + +void resolver_service_base::construct( + resolver_service_base::implementation_type& impl) +{ + impl.reset(static_cast(0), socket_ops::noop_deleter()); +} + +void resolver_service_base::destroy( + resolver_service_base::implementation_type& impl) +{ + ASIO_HANDLER_OPERATION((scheduler_.context(), + "resolver", &impl, 0, "cancel")); + + impl.reset(); +} + +void resolver_service_base::move_construct(implementation_type& impl, + implementation_type& other_impl) +{ + impl = ASIO_MOVE_CAST(implementation_type)(other_impl); +} + +void resolver_service_base::move_assign(implementation_type& impl, + resolver_service_base&, implementation_type& other_impl) +{ + destroy(impl); + impl = ASIO_MOVE_CAST(implementation_type)(other_impl); +} + +void resolver_service_base::cancel( + resolver_service_base::implementation_type& impl) +{ + ASIO_HANDLER_OPERATION((scheduler_.context(), + "resolver", &impl, 0, "cancel")); + + impl.reset(static_cast(0), socket_ops::noop_deleter()); +} + +void resolver_service_base::start_resolve_op(resolve_op* op) +{ + if (ASIO_CONCURRENCY_HINT_IS_LOCKING(SCHEDULER, + scheduler_.concurrency_hint())) + { + start_work_thread(); + scheduler_.work_started(); + work_scheduler_->post_immediate_completion(op, false); + } + else + { + op->ec_ = asio::error::operation_not_supported; + scheduler_.post_immediate_completion(op, false); + } +} + +void resolver_service_base::start_work_thread() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + if (!work_thread_.get()) + { + work_thread_.reset(new asio::detail::thread( + work_scheduler_runner(*work_scheduler_))); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/scheduler.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/scheduler.ipp new file mode 100644 index 000000000..0734fc8e8 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/scheduler.ipp @@ -0,0 +1,659 @@ +// +// detail/impl/scheduler.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SCHEDULER_IPP +#define ASIO_DETAIL_IMPL_SCHEDULER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/concurrency_hint.hpp" +#include "asio/detail/event.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/scheduler.hpp" +#include "asio/detail/scheduler_thread_info.hpp" +#include "asio/detail/signal_blocker.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class scheduler::thread_function +{ +public: + explicit thread_function(scheduler* s) + : this_(s) + { + } + + void operator()() + { + asio::error_code ec; + this_->run(ec); + } + +private: + scheduler* this_; +}; + +struct scheduler::task_cleanup +{ + ~task_cleanup() + { + if (this_thread_->private_outstanding_work > 0) + { + asio::detail::increment( + scheduler_->outstanding_work_, + this_thread_->private_outstanding_work); + } + this_thread_->private_outstanding_work = 0; + + // Enqueue the completed operations and reinsert the task at the end of + // the operation queue. + lock_->lock(); + scheduler_->task_interrupted_ = true; + scheduler_->op_queue_.push(this_thread_->private_op_queue); + scheduler_->op_queue_.push(&scheduler_->task_operation_); + } + + scheduler* scheduler_; + mutex::scoped_lock* lock_; + thread_info* this_thread_; +}; + +struct scheduler::work_cleanup +{ + ~work_cleanup() + { + if (this_thread_->private_outstanding_work > 1) + { + asio::detail::increment( + scheduler_->outstanding_work_, + this_thread_->private_outstanding_work - 1); + } + else if (this_thread_->private_outstanding_work < 1) + { + scheduler_->work_finished(); + } + this_thread_->private_outstanding_work = 0; + +#if defined(ASIO_HAS_THREADS) + if (!this_thread_->private_op_queue.empty()) + { + lock_->lock(); + scheduler_->op_queue_.push(this_thread_->private_op_queue); + } +#endif // defined(ASIO_HAS_THREADS) + } + + scheduler* scheduler_; + mutex::scoped_lock* lock_; + thread_info* this_thread_; +}; + +scheduler::scheduler(asio::execution_context& ctx, + int concurrency_hint, bool own_thread) + : asio::detail::execution_context_service_base(ctx), + one_thread_(concurrency_hint == 1 + || !ASIO_CONCURRENCY_HINT_IS_LOCKING( + SCHEDULER, concurrency_hint) + || !ASIO_CONCURRENCY_HINT_IS_LOCKING( + REACTOR_IO, concurrency_hint)), + mutex_(ASIO_CONCURRENCY_HINT_IS_LOCKING( + SCHEDULER, concurrency_hint)), + task_(0), + task_interrupted_(true), + outstanding_work_(0), + stopped_(false), + shutdown_(false), + concurrency_hint_(concurrency_hint), + thread_(0) +{ + ASIO_HANDLER_TRACKING_INIT; + + if (own_thread) + { + ++outstanding_work_; + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread(thread_function(this)); + } +} + +scheduler::~scheduler() +{ + if (thread_) + { + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + stop_all_threads(lock); + lock.unlock(); + thread_->join(); + delete thread_; + } +} + +void scheduler::shutdown() +{ + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + if (thread_) + stop_all_threads(lock); + lock.unlock(); + + // Join thread to ensure task operation is returned to queue. + if (thread_) + { + thread_->join(); + delete thread_; + thread_ = 0; + } + + // Destroy handler objects. + while (!op_queue_.empty()) + { + operation* o = op_queue_.front(); + op_queue_.pop(); + if (o != &task_operation_) + o->destroy(); + } + + // Reset to initial state. + task_ = 0; +} + +void scheduler::init_task() +{ + mutex::scoped_lock lock(mutex_); + if (!shutdown_ && !task_) + { + task_ = &use_service(this->context()); + op_queue_.push(&task_operation_); + wake_one_thread_and_unlock(lock); + } +} + +std::size_t scheduler::run(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + thread_info this_thread; + this_thread.private_outstanding_work = 0; + thread_call_stack::context ctx(this, this_thread); + + mutex::scoped_lock lock(mutex_); + + std::size_t n = 0; + for (; do_run_one(lock, this_thread, ec); lock.lock()) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +std::size_t scheduler::run_one(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + thread_info this_thread; + this_thread.private_outstanding_work = 0; + thread_call_stack::context ctx(this, this_thread); + + mutex::scoped_lock lock(mutex_); + + return do_run_one(lock, this_thread, ec); +} + +std::size_t scheduler::wait_one(long usec, asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + thread_info this_thread; + this_thread.private_outstanding_work = 0; + thread_call_stack::context ctx(this, this_thread); + + mutex::scoped_lock lock(mutex_); + + return do_wait_one(lock, this_thread, usec, ec); +} + +std::size_t scheduler::poll(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + thread_info this_thread; + this_thread.private_outstanding_work = 0; + thread_call_stack::context ctx(this, this_thread); + + mutex::scoped_lock lock(mutex_); + +#if defined(ASIO_HAS_THREADS) + // We want to support nested calls to poll() and poll_one(), so any handlers + // that are already on a thread-private queue need to be put on to the main + // queue now. + if (one_thread_) + if (thread_info* outer_info = static_cast(ctx.next_by_key())) + op_queue_.push(outer_info->private_op_queue); +#endif // defined(ASIO_HAS_THREADS) + + std::size_t n = 0; + for (; do_poll_one(lock, this_thread, ec); lock.lock()) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +std::size_t scheduler::poll_one(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + thread_info this_thread; + this_thread.private_outstanding_work = 0; + thread_call_stack::context ctx(this, this_thread); + + mutex::scoped_lock lock(mutex_); + +#if defined(ASIO_HAS_THREADS) + // We want to support nested calls to poll() and poll_one(), so any handlers + // that are already on a thread-private queue need to be put on to the main + // queue now. + if (one_thread_) + if (thread_info* outer_info = static_cast(ctx.next_by_key())) + op_queue_.push(outer_info->private_op_queue); +#endif // defined(ASIO_HAS_THREADS) + + return do_poll_one(lock, this_thread, ec); +} + +void scheduler::stop() +{ + mutex::scoped_lock lock(mutex_); + stop_all_threads(lock); +} + +bool scheduler::stopped() const +{ + mutex::scoped_lock lock(mutex_); + return stopped_; +} + +void scheduler::restart() +{ + mutex::scoped_lock lock(mutex_); + stopped_ = false; +} + +void scheduler::compensating_work_started() +{ + thread_info_base* this_thread = thread_call_stack::contains(this); + ++static_cast(this_thread)->private_outstanding_work; +} + +bool scheduler::can_dispatch() +{ + return thread_call_stack::contains(this) != 0; +} + +void scheduler::capture_current_exception() +{ + if (thread_info_base* this_thread = thread_call_stack::contains(this)) + this_thread->capture_current_exception(); +} + +void scheduler::post_immediate_completion( + scheduler::operation* op, bool is_continuation) +{ +#if defined(ASIO_HAS_THREADS) + if (one_thread_ || is_continuation) + { + if (thread_info_base* this_thread = thread_call_stack::contains(this)) + { + ++static_cast(this_thread)->private_outstanding_work; + static_cast(this_thread)->private_op_queue.push(op); + return; + } + } +#else // defined(ASIO_HAS_THREADS) + (void)is_continuation; +#endif // defined(ASIO_HAS_THREADS) + + work_started(); + mutex::scoped_lock lock(mutex_); + op_queue_.push(op); + wake_one_thread_and_unlock(lock); +} + +void scheduler::post_immediate_completions(std::size_t n, + op_queue& ops, bool is_continuation) +{ +#if defined(ASIO_HAS_THREADS) + if (one_thread_ || is_continuation) + { + if (thread_info_base* this_thread = thread_call_stack::contains(this)) + { + static_cast(this_thread)->private_outstanding_work + += static_cast(n); + static_cast(this_thread)->private_op_queue.push(ops); + return; + } + } +#else // defined(ASIO_HAS_THREADS) + (void)is_continuation; +#endif // defined(ASIO_HAS_THREADS) + + increment(outstanding_work_, static_cast(n)); + mutex::scoped_lock lock(mutex_); + op_queue_.push(ops); + wake_one_thread_and_unlock(lock); +} + +void scheduler::post_deferred_completion(scheduler::operation* op) +{ +#if defined(ASIO_HAS_THREADS) + if (one_thread_) + { + if (thread_info_base* this_thread = thread_call_stack::contains(this)) + { + static_cast(this_thread)->private_op_queue.push(op); + return; + } + } +#endif // defined(ASIO_HAS_THREADS) + + mutex::scoped_lock lock(mutex_); + op_queue_.push(op); + wake_one_thread_and_unlock(lock); +} + +void scheduler::post_deferred_completions( + op_queue& ops) +{ + if (!ops.empty()) + { +#if defined(ASIO_HAS_THREADS) + if (one_thread_) + { + if (thread_info_base* this_thread = thread_call_stack::contains(this)) + { + static_cast(this_thread)->private_op_queue.push(ops); + return; + } + } +#endif // defined(ASIO_HAS_THREADS) + + mutex::scoped_lock lock(mutex_); + op_queue_.push(ops); + wake_one_thread_and_unlock(lock); + } +} + +void scheduler::do_dispatch( + scheduler::operation* op) +{ + work_started(); + mutex::scoped_lock lock(mutex_); + op_queue_.push(op); + wake_one_thread_and_unlock(lock); +} + +void scheduler::abandon_operations( + op_queue& ops) +{ + op_queue ops2; + ops2.push(ops); +} + +std::size_t scheduler::do_run_one(mutex::scoped_lock& lock, + scheduler::thread_info& this_thread, + const asio::error_code& ec) +{ + while (!stopped_) + { + if (!op_queue_.empty()) + { + // Prepare to execute first handler from queue. + operation* o = op_queue_.front(); + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); + + if (o == &task_operation_) + { + task_interrupted_ = more_handlers; + + if (more_handlers && !one_thread_) + wakeup_event_.unlock_and_signal_one(lock); + else + lock.unlock(); + + task_cleanup on_exit = { this, &lock, &this_thread }; + (void)on_exit; + + // Run the task. May throw an exception. Only block if the operation + // queue is empty and we're not polling, otherwise we want to return + // as soon as possible. + task_->run(more_handlers ? 0 : -1, this_thread.private_op_queue); + } + else + { + std::size_t task_result = o->task_result_; + + if (more_handlers && !one_thread_) + wake_one_thread_and_unlock(lock); + else + lock.unlock(); + + // Ensure the count of outstanding work is decremented on block exit. + work_cleanup on_exit = { this, &lock, &this_thread }; + (void)on_exit; + + // Complete the operation. May throw an exception. Deletes the object. + o->complete(this, ec, task_result); + this_thread.rethrow_pending_exception(); + + return 1; + } + } + else + { + wakeup_event_.clear(lock); + wakeup_event_.wait(lock); + } + } + + return 0; +} + +std::size_t scheduler::do_wait_one(mutex::scoped_lock& lock, + scheduler::thread_info& this_thread, long usec, + const asio::error_code& ec) +{ + if (stopped_) + return 0; + + operation* o = op_queue_.front(); + if (o == 0) + { + wakeup_event_.clear(lock); + wakeup_event_.wait_for_usec(lock, usec); + usec = 0; // Wait at most once. + o = op_queue_.front(); + } + + if (o == &task_operation_) + { + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); + + task_interrupted_ = more_handlers; + + if (more_handlers && !one_thread_) + wakeup_event_.unlock_and_signal_one(lock); + else + lock.unlock(); + + { + task_cleanup on_exit = { this, &lock, &this_thread }; + (void)on_exit; + + // Run the task. May throw an exception. Only block if the operation + // queue is empty and we're not polling, otherwise we want to return + // as soon as possible. + task_->run(more_handlers ? 0 : usec, this_thread.private_op_queue); + } + + o = op_queue_.front(); + if (o == &task_operation_) + { + if (!one_thread_) + wakeup_event_.maybe_unlock_and_signal_one(lock); + return 0; + } + } + + if (o == 0) + return 0; + + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); + + std::size_t task_result = o->task_result_; + + if (more_handlers && !one_thread_) + wake_one_thread_and_unlock(lock); + else + lock.unlock(); + + // Ensure the count of outstanding work is decremented on block exit. + work_cleanup on_exit = { this, &lock, &this_thread }; + (void)on_exit; + + // Complete the operation. May throw an exception. Deletes the object. + o->complete(this, ec, task_result); + this_thread.rethrow_pending_exception(); + + return 1; +} + +std::size_t scheduler::do_poll_one(mutex::scoped_lock& lock, + scheduler::thread_info& this_thread, + const asio::error_code& ec) +{ + if (stopped_) + return 0; + + operation* o = op_queue_.front(); + if (o == &task_operation_) + { + op_queue_.pop(); + lock.unlock(); + + { + task_cleanup c = { this, &lock, &this_thread }; + (void)c; + + // Run the task. May throw an exception. Only block if the operation + // queue is empty and we're not polling, otherwise we want to return + // as soon as possible. + task_->run(0, this_thread.private_op_queue); + } + + o = op_queue_.front(); + if (o == &task_operation_) + { + wakeup_event_.maybe_unlock_and_signal_one(lock); + return 0; + } + } + + if (o == 0) + return 0; + + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); + + std::size_t task_result = o->task_result_; + + if (more_handlers && !one_thread_) + wake_one_thread_and_unlock(lock); + else + lock.unlock(); + + // Ensure the count of outstanding work is decremented on block exit. + work_cleanup on_exit = { this, &lock, &this_thread }; + (void)on_exit; + + // Complete the operation. May throw an exception. Deletes the object. + o->complete(this, ec, task_result); + this_thread.rethrow_pending_exception(); + + return 1; +} + +void scheduler::stop_all_threads( + mutex::scoped_lock& lock) +{ + stopped_ = true; + wakeup_event_.signal_all(lock); + + if (!task_interrupted_ && task_) + { + task_interrupted_ = true; + task_->interrupt(); + } +} + +void scheduler::wake_one_thread_and_unlock( + mutex::scoped_lock& lock) +{ + if (!wakeup_event_.maybe_unlock_and_signal_one(lock)) + { + if (!task_interrupted_ && task_) + { + task_interrupted_ = true; + task_->interrupt(); + } + lock.unlock(); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_SCHEDULER_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/select_reactor.hpp b/third_party/asio/1.18.2/include/asio/detail/impl/select_reactor.hpp new file mode 100644 index 000000000..f37769fe4 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/select_reactor.hpp @@ -0,0 +1,100 @@ +// +// detail/impl/select_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP +#define ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) \ + || (!defined(ASIO_HAS_DEV_POLL) \ + && !defined(ASIO_HAS_EPOLL) \ + && !defined(ASIO_HAS_KQUEUE) \ + && !defined(ASIO_WINDOWS_RUNTIME)) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void select_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +// Remove a timer queue from the reactor. +template +void select_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void select_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + scheduler_.post_immediate_completion(op, false); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + scheduler_.work_started(); + if (earliest) + interrupter_.interrupt(); +} + +template +std::size_t select_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + scheduler_.post_deferred_completions(ops); + return n; +} + +template +void select_reactor::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + queue.cancel_timer(target, ops); + queue.move_timer(target, source); + lock.unlock(); + scheduler_.post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + // || (!defined(ASIO_HAS_DEV_POLL) + // && !defined(ASIO_HAS_EPOLL) + // && !defined(ASIO_HAS_KQUEUE) + // && !defined(ASIO_WINDOWS_RUNTIME)) + +#endif // ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/select_reactor.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/select_reactor.ipp new file mode 100644 index 000000000..4c855e2d4 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/select_reactor.ipp @@ -0,0 +1,338 @@ +// +// detail/impl/select_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP +#define ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) \ + || (!defined(ASIO_HAS_DEV_POLL) \ + && !defined(ASIO_HAS_EPOLL) \ + && !defined(ASIO_HAS_KQUEUE) \ + && !defined(ASIO_WINDOWS_RUNTIME)) + +#include "asio/detail/fd_set_adapter.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_IOCP) +class select_reactor::thread_function +{ +public: + explicit thread_function(select_reactor* r) + : this_(r) + { + } + + void operator()() + { + this_->run_thread(); + } + +private: + select_reactor* this_; +}; +#endif // defined(ASIO_HAS_IOCP) + +select_reactor::select_reactor(asio::execution_context& ctx) + : execution_context_service_base(ctx), + scheduler_(use_service(ctx)), + mutex_(), + interrupter_(), +#if defined(ASIO_HAS_IOCP) + stop_thread_(false), + thread_(0), +#endif // defined(ASIO_HAS_IOCP) + shutdown_(false) +{ +#if defined(ASIO_HAS_IOCP) + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread(thread_function(this)); +#endif // defined(ASIO_HAS_IOCP) +} + +select_reactor::~select_reactor() +{ + shutdown(); +} + +void select_reactor::shutdown() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; +#if defined(ASIO_HAS_IOCP) + stop_thread_ = true; + if (thread_) + interrupter_.interrupt(); +#endif // defined(ASIO_HAS_IOCP) + lock.unlock(); + +#if defined(ASIO_HAS_IOCP) + if (thread_) + { + thread_->join(); + delete thread_; + thread_ = 0; + } +#endif // defined(ASIO_HAS_IOCP) + + op_queue ops; + + for (int i = 0; i < max_ops; ++i) + op_queue_[i].get_all_operations(ops); + + timer_queues_.get_all_timers(ops); + + scheduler_.abandon_operations(ops); +} + +void select_reactor::notify_fork( + asio::execution_context::fork_event fork_ev) +{ + if (fork_ev == asio::execution_context::fork_child) + interrupter_.recreate(); +} + +void select_reactor::init_task() +{ + scheduler_.init_task(); +} + +int select_reactor::register_descriptor(socket_type, + select_reactor::per_descriptor_data&) +{ + return 0; +} + +int select_reactor::register_internal_descriptor( + int op_type, socket_type descriptor, + select_reactor::per_descriptor_data&, reactor_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + op_queue_[op_type].enqueue_operation(descriptor, op); + interrupter_.interrupt(); + + return 0; +} + +void select_reactor::move_descriptor(socket_type, + select_reactor::per_descriptor_data&, + select_reactor::per_descriptor_data&) +{ +} + +void select_reactor::start_op(int op_type, socket_type descriptor, + select_reactor::per_descriptor_data&, reactor_op* op, + bool is_continuation, bool) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + post_immediate_completion(op, is_continuation); + return; + } + + bool first = op_queue_[op_type].enqueue_operation(descriptor, op); + scheduler_.work_started(); + if (first) + interrupter_.interrupt(); +} + +void select_reactor::cancel_ops(socket_type descriptor, + select_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void select_reactor::deregister_descriptor(socket_type descriptor, + select_reactor::per_descriptor_data&, bool) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void select_reactor::deregister_internal_descriptor( + socket_type descriptor, select_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + for (int i = 0; i < max_ops; ++i) + op_queue_[i].cancel_operations(descriptor, ops); +} + +void select_reactor::cleanup_descriptor_data( + select_reactor::per_descriptor_data&) +{ +} + +void select_reactor::run(long usec, op_queue& ops) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + +#if defined(ASIO_HAS_IOCP) + // Check if the thread is supposed to stop. + if (stop_thread_) + return; +#endif // defined(ASIO_HAS_IOCP) + + // Set up the descriptor sets. + for (int i = 0; i < max_select_ops; ++i) + fd_sets_[i].reset(); + fd_sets_[read_op].set(interrupter_.read_descriptor()); + socket_type max_fd = 0; + bool have_work_to_do = !timer_queues_.all_empty(); + for (int i = 0; i < max_select_ops; ++i) + { + have_work_to_do = have_work_to_do || !op_queue_[i].empty(); + fd_sets_[i].set(op_queue_[i], ops); + if (fd_sets_[i].max_descriptor() > max_fd) + max_fd = fd_sets_[i].max_descriptor(); + } + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Connection operations on Windows use both except and write fd_sets. + have_work_to_do = have_work_to_do || !op_queue_[connect_op].empty(); + fd_sets_[write_op].set(op_queue_[connect_op], ops); + if (fd_sets_[write_op].max_descriptor() > max_fd) + max_fd = fd_sets_[write_op].max_descriptor(); + fd_sets_[except_op].set(op_queue_[connect_op], ops); + if (fd_sets_[except_op].max_descriptor() > max_fd) + max_fd = fd_sets_[except_op].max_descriptor(); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (!usec && !have_work_to_do) + return; + + // Determine how long to block while waiting for events. + timeval tv_buf = { 0, 0 }; + timeval* tv = usec ? get_timeout(usec, tv_buf) : &tv_buf; + + lock.unlock(); + + // Block on the select call until descriptors become ready. + asio::error_code ec; + int retval = socket_ops::select(static_cast(max_fd + 1), + fd_sets_[read_op], fd_sets_[write_op], fd_sets_[except_op], tv, ec); + + // Reset the interrupter. + if (retval > 0 && fd_sets_[read_op].is_set(interrupter_.read_descriptor())) + { + if (!interrupter_.reset()) + { + lock.lock(); + interrupter_.recreate(); + } + --retval; + } + + lock.lock(); + + // Dispatch all ready operations. + if (retval > 0) + { +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Connection operations on Windows use both except and write fd_sets. + fd_sets_[except_op].perform(op_queue_[connect_op], ops); + fd_sets_[write_op].perform(op_queue_[connect_op], ops); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + for (int i = max_select_ops - 1; i >= 0; --i) + fd_sets_[i].perform(op_queue_[i], ops); + } + timer_queues_.get_ready_timers(ops); +} + +void select_reactor::interrupt() +{ + interrupter_.interrupt(); +} + +#if defined(ASIO_HAS_IOCP) +void select_reactor::run_thread() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + op_queue ops; + run(true, ops); + scheduler_.post_deferred_completions(ops); + lock.lock(); + } +} +#endif // defined(ASIO_HAS_IOCP) + +void select_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void select_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +timeval* select_reactor::get_timeout(long usec, timeval& tv) +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + const long max_usec = 5 * 60 * 1000 * 1000; + usec = timer_queues_.wait_duration_usec( + (usec < 0 || max_usec < usec) ? max_usec : usec); + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + return &tv; +} + +void select_reactor::cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec) +{ + bool need_interrupt = false; + op_queue ops; + for (int i = 0; i < max_ops; ++i) + need_interrupt = op_queue_[i].cancel_operations( + descriptor, ops, ec) || need_interrupt; + scheduler_.post_deferred_completions(ops); + if (need_interrupt) + interrupter_.interrupt(); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + // || (!defined(ASIO_HAS_DEV_POLL) + // && !defined(ASIO_HAS_EPOLL) + // && !defined(ASIO_HAS_KQUEUE)) + // && !defined(ASIO_WINDOWS_RUNTIME)) + +#endif // ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/service_registry.hpp b/third_party/asio/1.18.2/include/asio/detail/impl/service_registry.hpp new file mode 100644 index 000000000..8b9566e10 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/service_registry.hpp @@ -0,0 +1,94 @@ +// +// detail/impl/service_registry.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP +#define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +Service& service_registry::use_service() +{ + execution_context::service::key key; + init_key(key, 0); + factory_type factory = &service_registry::create; + return *static_cast(do_use_service(key, factory, &owner_)); +} + +template +Service& service_registry::use_service(io_context& owner) +{ + execution_context::service::key key; + init_key(key, 0); + factory_type factory = &service_registry::create; + return *static_cast(do_use_service(key, factory, &owner)); +} + +template +void service_registry::add_service(Service* new_service) +{ + execution_context::service::key key; + init_key(key, 0); + return do_add_service(key, new_service); +} + +template +bool service_registry::has_service() const +{ + execution_context::service::key key; + init_key(key, 0); + return do_has_service(key); +} + +template +inline void service_registry::init_key( + execution_context::service::key& key, ...) +{ + init_key_from_id(key, Service::id); +} + +#if !defined(ASIO_NO_TYPEID) +template +void service_registry::init_key(execution_context::service::key& key, + typename enable_if< + is_base_of::value>::type*) +{ + key.type_info_ = &typeid(typeid_wrapper); + key.id_ = 0; +} + +template +void service_registry::init_key_from_id(execution_context::service::key& key, + const service_id& /*id*/) +{ + key.type_info_ = &typeid(typeid_wrapper); + key.id_ = 0; +} +#endif // !defined(ASIO_NO_TYPEID) + +template +execution_context::service* service_registry::create(void* owner) +{ + return new Service(*static_cast(owner)); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/service_registry.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/service_registry.ipp new file mode 100644 index 000000000..363a56fb8 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/service_registry.ipp @@ -0,0 +1,197 @@ +// +// detail/impl/service_registry.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP +#define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/service_registry.hpp" +#include "asio/detail/throw_exception.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +service_registry::service_registry(execution_context& owner) + : owner_(owner), + first_service_(0) +{ +} + +service_registry::~service_registry() +{ +} + +void service_registry::shutdown_services() +{ + execution_context::service* service = first_service_; + while (service) + { + service->shutdown(); + service = service->next_; + } +} + +void service_registry::destroy_services() +{ + while (first_service_) + { + execution_context::service* next_service = first_service_->next_; + destroy(first_service_); + first_service_ = next_service; + } +} + +void service_registry::notify_fork(execution_context::fork_event fork_ev) +{ + // Make a copy of all of the services while holding the lock. We don't want + // to hold the lock while calling into each service, as it may try to call + // back into this class. + std::vector services; + { + asio::detail::mutex::scoped_lock lock(mutex_); + execution_context::service* service = first_service_; + while (service) + { + services.push_back(service); + service = service->next_; + } + } + + // If processing the fork_prepare event, we want to go in reverse order of + // service registration, which happens to be the existing order of the + // services in the vector. For the other events we want to go in the other + // direction. + std::size_t num_services = services.size(); + if (fork_ev == execution_context::fork_prepare) + for (std::size_t i = 0; i < num_services; ++i) + services[i]->notify_fork(fork_ev); + else + for (std::size_t i = num_services; i > 0; --i) + services[i - 1]->notify_fork(fork_ev); +} + +void service_registry::init_key_from_id(execution_context::service::key& key, + const execution_context::id& id) +{ + key.type_info_ = 0; + key.id_ = &id; +} + +bool service_registry::keys_match( + const execution_context::service::key& key1, + const execution_context::service::key& key2) +{ + if (key1.id_ && key2.id_) + if (key1.id_ == key2.id_) + return true; + if (key1.type_info_ && key2.type_info_) + if (*key1.type_info_ == *key2.type_info_) + return true; + return false; +} + +void service_registry::destroy(execution_context::service* service) +{ + delete service; +} + +execution_context::service* service_registry::do_use_service( + const execution_context::service::key& key, + factory_type factory, void* owner) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // First see if there is an existing service object with the given key. + execution_context::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return service; + service = service->next_; + } + + // Create a new service object. The service registry's mutex is not locked + // at this time to allow for nested calls into this function from the new + // service's constructor. + lock.unlock(); + auto_service_ptr new_service = { factory(owner) }; + new_service.ptr_->key_ = key; + lock.lock(); + + // Check that nobody else created another service object of the same type + // while the lock was released. + service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return service; + service = service->next_; + } + + // Service was successfully initialised, pass ownership to registry. + new_service.ptr_->next_ = first_service_; + first_service_ = new_service.ptr_; + new_service.ptr_ = 0; + return first_service_; +} + +void service_registry::do_add_service( + const execution_context::service::key& key, + execution_context::service* new_service) +{ + if (&owner_ != &new_service->context()) + asio::detail::throw_exception(invalid_service_owner()); + + asio::detail::mutex::scoped_lock lock(mutex_); + + // Check if there is an existing service object with the given key. + execution_context::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + asio::detail::throw_exception(service_already_exists()); + service = service->next_; + } + + // Take ownership of the service object. + new_service->key_ = key; + new_service->next_ = first_service_; + first_service_ = new_service; +} + +bool service_registry::do_has_service( + const execution_context::service::key& key) const +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + execution_context::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return true; + service = service->next_; + } + + return false; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/signal_set_service.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/signal_set_service.ipp new file mode 100644 index 000000000..b657d0e58 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/signal_set_service.ipp @@ -0,0 +1,668 @@ +// +// detail/impl/signal_set_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP +#define ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include +#include "asio/detail/reactor.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/signal_set_service.hpp" +#include "asio/detail/static_mutex.hpp" +#include "asio/detail/throw_exception.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct signal_state +{ + // Mutex used for protecting global state. + static_mutex mutex_; + + // The read end of the pipe used for signal notifications. + int read_descriptor_; + + // The write end of the pipe used for signal notifications. + int write_descriptor_; + + // Whether the signal state has been prepared for a fork. + bool fork_prepared_; + + // The head of a linked list of all signal_set_service instances. + class signal_set_service* service_list_; + + // A count of the number of objects that are registered for each signal. + std::size_t registration_count_[max_signal_number]; +}; + +signal_state* get_signal_state() +{ + static signal_state state = { + ASIO_STATIC_MUTEX_INIT, -1, -1, false, 0, { 0 } }; + return &state; +} + +void asio_signal_handler(int signal_number) +{ +#if defined(ASIO_WINDOWS) \ + || defined(ASIO_WINDOWS_RUNTIME) \ + || defined(__CYGWIN__) + signal_set_service::deliver_signal(signal_number); +#else // defined(ASIO_WINDOWS) + // || defined(ASIO_WINDOWS_RUNTIME) + // || defined(__CYGWIN__) + int saved_errno = errno; + signal_state* state = get_signal_state(); + signed_size_type result = ::write(state->write_descriptor_, + &signal_number, sizeof(signal_number)); + (void)result; + errno = saved_errno; +#endif // defined(ASIO_WINDOWS) + // || defined(ASIO_WINDOWS_RUNTIME) + // || defined(__CYGWIN__) + +#if defined(ASIO_HAS_SIGNAL) && !defined(ASIO_HAS_SIGACTION) + ::signal(signal_number, asio_signal_handler); +#endif // defined(ASIO_HAS_SIGNAL) && !defined(ASIO_HAS_SIGACTION) +} + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) +class signal_set_service::pipe_read_op : public reactor_op +{ +public: + pipe_read_op() + : reactor_op(asio::error_code(), + &pipe_read_op::do_perform, pipe_read_op::do_complete) + { + } + + static status do_perform(reactor_op*) + { + signal_state* state = get_signal_state(); + + int fd = state->read_descriptor_; + int signal_number = 0; + while (::read(fd, &signal_number, sizeof(int)) == sizeof(int)) + if (signal_number >= 0 && signal_number < max_signal_number) + signal_set_service::deliver_signal(signal_number); + + return not_done; + } + + static void do_complete(void* /*owner*/, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + pipe_read_op* o(static_cast(base)); + delete o; + } +}; +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + +signal_set_service::signal_set_service(execution_context& context) + : execution_context_service_base(context), + scheduler_(asio::use_service(context)), +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + reactor_(asio::use_service(context)), +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + next_(0), + prev_(0) +{ + get_signal_state()->mutex_.init(); + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + reactor_.init_task(); +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + + for (int i = 0; i < max_signal_number; ++i) + registrations_[i] = 0; + + add_service(this); +} + +signal_set_service::~signal_set_service() +{ + remove_service(this); +} + +void signal_set_service::shutdown() +{ + remove_service(this); + + op_queue ops; + + for (int i = 0; i < max_signal_number; ++i) + { + registration* reg = registrations_[i]; + while (reg) + { + ops.push(*reg->queue_); + reg = reg->next_in_table_; + } + } + + scheduler_.abandon_operations(ops); +} + +void signal_set_service::notify_fork(execution_context::fork_event fork_ev) +{ +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + switch (fork_ev) + { + case execution_context::fork_prepare: + { + int read_descriptor = state->read_descriptor_; + state->fork_prepared_ = true; + lock.unlock(); + reactor_.deregister_internal_descriptor(read_descriptor, reactor_data_); + reactor_.cleanup_descriptor_data(reactor_data_); + } + break; + case execution_context::fork_parent: + if (state->fork_prepared_) + { + int read_descriptor = state->read_descriptor_; + state->fork_prepared_ = false; + lock.unlock(); + reactor_.register_internal_descriptor(reactor::read_op, + read_descriptor, reactor_data_, new pipe_read_op); + } + break; + case execution_context::fork_child: + if (state->fork_prepared_) + { + asio::detail::signal_blocker blocker; + close_descriptors(); + open_descriptors(); + int read_descriptor = state->read_descriptor_; + state->fork_prepared_ = false; + lock.unlock(); + reactor_.register_internal_descriptor(reactor::read_op, + read_descriptor, reactor_data_, new pipe_read_op); + } + break; + default: + break; + } +#else // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + (void)fork_ev; +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +} + +void signal_set_service::construct( + signal_set_service::implementation_type& impl) +{ + impl.signals_ = 0; +} + +void signal_set_service::destroy( + signal_set_service::implementation_type& impl) +{ + asio::error_code ignored_ec; + clear(impl, ignored_ec); + cancel(impl, ignored_ec); +} + +asio::error_code signal_set_service::add( + signal_set_service::implementation_type& impl, + int signal_number, asio::error_code& ec) +{ + // Check that the signal number is valid. + if (signal_number < 0 || signal_number >= max_signal_number) + { + ec = asio::error::invalid_argument; + return ec; + } + + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + // Find the appropriate place to insert the registration. + registration** insertion_point = &impl.signals_; + registration* next = impl.signals_; + while (next && next->signal_number_ < signal_number) + { + insertion_point = &next->next_in_set_; + next = next->next_in_set_; + } + + // Only do something if the signal is not already registered. + if (next == 0 || next->signal_number_ != signal_number) + { + registration* new_registration = new registration; + +#if defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + // Register for the signal if we're the first. + if (state->registration_count_[signal_number] == 0) + { +# if defined(ASIO_HAS_SIGACTION) + using namespace std; // For memset. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = asio_signal_handler; + sigfillset(&sa.sa_mask); + if (::sigaction(signal_number, &sa, 0) == -1) +# else // defined(ASIO_HAS_SIGACTION) + if (::signal(signal_number, asio_signal_handler) == SIG_ERR) +# endif // defined(ASIO_HAS_SIGACTION) + { +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error::invalid_argument; +# else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(errno, + asio::error::get_system_category()); +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + delete new_registration; + return ec; + } + } +#endif // defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + + // Record the new registration in the set. + new_registration->signal_number_ = signal_number; + new_registration->queue_ = &impl.queue_; + new_registration->next_in_set_ = next; + *insertion_point = new_registration; + + // Insert registration into the registration table. + new_registration->next_in_table_ = registrations_[signal_number]; + if (registrations_[signal_number]) + registrations_[signal_number]->prev_in_table_ = new_registration; + registrations_[signal_number] = new_registration; + + ++state->registration_count_[signal_number]; + } + + ec = asio::error_code(); + return ec; +} + +asio::error_code signal_set_service::remove( + signal_set_service::implementation_type& impl, + int signal_number, asio::error_code& ec) +{ + // Check that the signal number is valid. + if (signal_number < 0 || signal_number >= max_signal_number) + { + ec = asio::error::invalid_argument; + return ec; + } + + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + // Find the signal number in the list of registrations. + registration** deletion_point = &impl.signals_; + registration* reg = impl.signals_; + while (reg && reg->signal_number_ < signal_number) + { + deletion_point = ®->next_in_set_; + reg = reg->next_in_set_; + } + + if (reg != 0 && reg->signal_number_ == signal_number) + { +#if defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + // Set signal handler back to the default if we're the last. + if (state->registration_count_[signal_number] == 1) + { +# if defined(ASIO_HAS_SIGACTION) + using namespace std; // For memset. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (::sigaction(signal_number, &sa, 0) == -1) +# else // defined(ASIO_HAS_SIGACTION) + if (::signal(signal_number, SIG_DFL) == SIG_ERR) +# endif // defined(ASIO_HAS_SIGACTION) + { +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error::invalid_argument; +# else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(errno, + asio::error::get_system_category()); +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + return ec; + } + } +#endif // defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + + // Remove the registration from the set. + *deletion_point = reg->next_in_set_; + + // Remove the registration from the registration table. + if (registrations_[signal_number] == reg) + registrations_[signal_number] = reg->next_in_table_; + if (reg->prev_in_table_) + reg->prev_in_table_->next_in_table_ = reg->next_in_table_; + if (reg->next_in_table_) + reg->next_in_table_->prev_in_table_ = reg->prev_in_table_; + + --state->registration_count_[signal_number]; + + delete reg; + } + + ec = asio::error_code(); + return ec; +} + +asio::error_code signal_set_service::clear( + signal_set_service::implementation_type& impl, + asio::error_code& ec) +{ + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + while (registration* reg = impl.signals_) + { +#if defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + // Set signal handler back to the default if we're the last. + if (state->registration_count_[reg->signal_number_] == 1) + { +# if defined(ASIO_HAS_SIGACTION) + using namespace std; // For memset. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (::sigaction(reg->signal_number_, &sa, 0) == -1) +# else // defined(ASIO_HAS_SIGACTION) + if (::signal(reg->signal_number_, SIG_DFL) == SIG_ERR) +# endif // defined(ASIO_HAS_SIGACTION) + { +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error::invalid_argument; +# else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(errno, + asio::error::get_system_category()); +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + return ec; + } + } +#endif // defined(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION) + + // Remove the registration from the registration table. + if (registrations_[reg->signal_number_] == reg) + registrations_[reg->signal_number_] = reg->next_in_table_; + if (reg->prev_in_table_) + reg->prev_in_table_->next_in_table_ = reg->next_in_table_; + if (reg->next_in_table_) + reg->next_in_table_->prev_in_table_ = reg->prev_in_table_; + + --state->registration_count_[reg->signal_number_]; + + impl.signals_ = reg->next_in_set_; + delete reg; + } + + ec = asio::error_code(); + return ec; +} + +asio::error_code signal_set_service::cancel( + signal_set_service::implementation_type& impl, + asio::error_code& ec) +{ + ASIO_HANDLER_OPERATION((scheduler_.context(), + "signal_set", &impl, 0, "cancel")); + + op_queue ops; + { + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + while (signal_op* op = impl.queue_.front()) + { + op->ec_ = asio::error::operation_aborted; + impl.queue_.pop(); + ops.push(op); + } + } + + scheduler_.post_deferred_completions(ops); + + ec = asio::error_code(); + return ec; +} + +void signal_set_service::deliver_signal(int signal_number) +{ + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + signal_set_service* service = state->service_list_; + while (service) + { + op_queue ops; + + registration* reg = service->registrations_[signal_number]; + while (reg) + { + if (reg->queue_->empty()) + { + ++reg->undelivered_; + } + else + { + while (signal_op* op = reg->queue_->front()) + { + op->signal_number_ = signal_number; + reg->queue_->pop(); + ops.push(op); + } + } + + reg = reg->next_in_table_; + } + + service->scheduler_.post_deferred_completions(ops); + + service = service->next_; + } +} + +void signal_set_service::add_service(signal_set_service* service) +{ + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + // If this is the first service to be created, open a new pipe. + if (state->service_list_ == 0) + open_descriptors(); +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + + // If a scheduler_ object is thread-unsafe then it must be the only + // scheduler used to create signal_set objects. + if (state->service_list_ != 0) + { + if (!ASIO_CONCURRENCY_HINT_IS_LOCKING(SCHEDULER, + service->scheduler_.concurrency_hint()) + || !ASIO_CONCURRENCY_HINT_IS_LOCKING(SCHEDULER, + state->service_list_->scheduler_.concurrency_hint())) + { + std::logic_error ex( + "Thread-unsafe execution context objects require " + "exclusive access to signal handling."); + asio::detail::throw_exception(ex); + } + } + + // Insert service into linked list of all services. + service->next_ = state->service_list_; + service->prev_ = 0; + if (state->service_list_) + state->service_list_->prev_ = service; + state->service_list_ = service; + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + // Register for pipe readiness notifications. + int read_descriptor = state->read_descriptor_; + lock.unlock(); + service->reactor_.register_internal_descriptor(reactor::read_op, + read_descriptor, service->reactor_data_, new pipe_read_op); +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +} + +void signal_set_service::remove_service(signal_set_service* service) +{ + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + if (service->next_ || service->prev_ || state->service_list_ == service) + { +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + // Disable the pipe readiness notifications. + int read_descriptor = state->read_descriptor_; + lock.unlock(); + service->reactor_.deregister_internal_descriptor( + read_descriptor, service->reactor_data_); + service->reactor_.cleanup_descriptor_data(service->reactor_data_); + lock.lock(); +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + + // Remove service from linked list of all services. + if (state->service_list_ == service) + state->service_list_ = service->next_; + if (service->prev_) + service->prev_->next_ = service->next_; + if (service->next_) + service->next_->prev_= service->prev_; + service->next_ = 0; + service->prev_ = 0; + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + // If this is the last service to be removed, close the pipe. + if (state->service_list_ == 0) + close_descriptors(); +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + } +} + +void signal_set_service::open_descriptors() +{ +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + signal_state* state = get_signal_state(); + + int pipe_fds[2]; + if (::pipe(pipe_fds) == 0) + { + state->read_descriptor_ = pipe_fds[0]; + ::fcntl(state->read_descriptor_, F_SETFL, O_NONBLOCK); + + state->write_descriptor_ = pipe_fds[1]; + ::fcntl(state->write_descriptor_, F_SETFL, O_NONBLOCK); + +#if defined(FD_CLOEXEC) + ::fcntl(state->read_descriptor_, F_SETFD, FD_CLOEXEC); + ::fcntl(state->write_descriptor_, F_SETFD, FD_CLOEXEC); +#endif // defined(FD_CLOEXEC) + } + else + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "signal_set_service pipe"); + } +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +} + +void signal_set_service::close_descriptors() +{ +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + signal_state* state = get_signal_state(); + + if (state->read_descriptor_ != -1) + ::close(state->read_descriptor_); + state->read_descriptor_ = -1; + + if (state->write_descriptor_ != -1) + ::close(state->write_descriptor_); + state->write_descriptor_ = -1; +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) +} + +void signal_set_service::start_wait_op( + signal_set_service::implementation_type& impl, signal_op* op) +{ + scheduler_.work_started(); + + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + registration* reg = impl.signals_; + while (reg) + { + if (reg->undelivered_ > 0) + { + --reg->undelivered_; + op->signal_number_ = reg->signal_number_; + scheduler_.post_deferred_completion(op); + return; + } + + reg = reg->next_in_set_; + } + + impl.queue_.push(op); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/socket_ops.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/socket_ops.ipp new file mode 100644 index 000000000..77edf9e15 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/socket_ops.ipp @@ -0,0 +1,3964 @@ +// +// detail/impl/socket_ops.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_OPS_IPP +#define ASIO_DETAIL_SOCKET_OPS_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include +#include +#include +#include +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include +# include +# include +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) \ + || defined(__MACH__) && defined(__APPLE__) +# if defined(ASIO_HAS_PTHREADS) +# include +# endif // defined(ASIO_HAS_PTHREADS) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // || defined(__MACH__) && defined(__APPLE__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace socket_ops { + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +struct msghdr { int msg_namelen; }; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#if defined(__hpux) +// HP-UX doesn't declare these functions extern "C", so they are declared again +// here to avoid linker errors about undefined symbols. +extern "C" char* if_indextoname(unsigned int, char*); +extern "C" unsigned int if_nametoindex(const char*); +#endif // defined(__hpux) + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +inline void clear_last_error() +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + WSASetLastError(0); +#else + errno = 0; +#endif +} + +#if !defined(ASIO_WINDOWS_RUNTIME) + +inline void get_last_error( + asio::error_code& ec, bool is_error_condition) +{ + if (!is_error_condition) + { + ec.assign(0, ec.category()); + } + else + { +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(WSAGetLastError(), + asio::error::get_system_category()); +#else + ec = asio::error_code(errno, + asio::error::get_system_category()); +#endif + } +} + +template +inline socket_type call_accept(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; + socket_type result = ::accept(s, addr, addrlen ? &tmp_addrlen : 0); + if (addrlen) + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +socket_type accept(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return invalid_socket; + } + + socket_type new_s = call_accept(&msghdr::msg_namelen, s, addr, addrlen); + get_last_error(ec, new_s == invalid_socket); + if (new_s == invalid_socket) + return new_s; + +#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) + int optval = 1; + int result = ::setsockopt(new_s, SOL_SOCKET, + SO_NOSIGPIPE, &optval, sizeof(optval)); + get_last_error(ec, result != 0); + if (result != 0) + { + ::close(new_s); + return invalid_socket; + } +#endif + + ec.assign(0, ec.category()); + return new_s; +} + +socket_type sync_accept(socket_type s, state_type state, + socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec) +{ + // Accept a socket. + for (;;) + { + // Try to complete the operation without blocking. + socket_type new_socket = socket_ops::accept(s, addr, addrlen, ec); + + // Check if operation succeeded. + if (new_socket != invalid_socket) + return new_socket; + + // Operation failed. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + { + if (state & user_set_non_blocking) + return invalid_socket; + // Fall through to retry operation. + } + else if (ec == asio::error::connection_aborted) + { + if (state & enable_connection_aborted) + return invalid_socket; + // Fall through to retry operation. + } +#if defined(EPROTO) + else if (ec.value() == EPROTO) + { + if (state & enable_connection_aborted) + return invalid_socket; + // Fall through to retry operation. + } +#endif // defined(EPROTO) + else + return invalid_socket; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return invalid_socket; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_accept(socket_type s, + void* output_buffer, DWORD address_length, + socket_addr_type* addr, std::size_t* addrlen, + socket_type new_socket, asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_aborted; + + if (!ec) + { + // Get the address of the peer. + if (addr && addrlen) + { + LPSOCKADDR local_addr = 0; + int local_addr_length = 0; + LPSOCKADDR remote_addr = 0; + int remote_addr_length = 0; + GetAcceptExSockaddrs(output_buffer, 0, address_length, + address_length, &local_addr, &local_addr_length, + &remote_addr, &remote_addr_length); + if (static_cast(remote_addr_length) > *addrlen) + { + ec = asio::error::invalid_argument; + } + else + { + using namespace std; // For memcpy. + memcpy(addr, remote_addr, remote_addr_length); + *addrlen = static_cast(remote_addr_length); + } + } + + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname + // and getpeername will work on the accepted socket. + SOCKET update_ctx_param = s; + socket_ops::state_type state = 0; + socket_ops::setsockopt(new_socket, state, + SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + &update_ctx_param, sizeof(SOCKET), ec); + } +} + +#else // defined(ASIO_HAS_IOCP) + +bool non_blocking_accept(socket_type s, + state_type state, socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, socket_type& new_socket) +{ + for (;;) + { + // Accept the waiting connection. + new_socket = socket_ops::accept(s, addr, addrlen, ec); + + // Check if operation succeeded. + if (new_socket != invalid_socket) + return true; + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Operation failed. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + { + // Fall through to retry operation. + } + else if (ec == asio::error::connection_aborted) + { + if (state & enable_connection_aborted) + return true; + // Fall through to retry operation. + } +#if defined(EPROTO) + else if (ec.value() == EPROTO) + { + if (state & enable_connection_aborted) + return true; + // Fall through to retry operation. + } +#endif // defined(EPROTO) + else + return true; + + return false; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +template +inline int call_bind(SockLenType msghdr::*, + socket_type s, const socket_addr_type* addr, std::size_t addrlen) +{ + return ::bind(s, addr, (SockLenType)addrlen); +} + +int bind(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + int result = call_bind(&msghdr::msg_namelen, s, addr, addrlen); + get_last_error(ec, result != 0); + return result; +} + +int close(socket_type s, state_type& state, + bool destruction, asio::error_code& ec) +{ + int result = 0; + if (s != invalid_socket) + { + // We don't want the destructor to block, so set the socket to linger in + // the background. If the user doesn't like this behaviour then they need + // to explicitly close the socket. + if (destruction && (state & user_set_linger)) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + asio::error_code ignored_ec; + socket_ops::setsockopt(s, state, SOL_SOCKET, + SO_LINGER, &opt, sizeof(opt), ignored_ec); + } + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + result = ::closesocket(s); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + result = ::close(s); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + get_last_error(ec, result != 0); + + if (result != 0 + && (ec == asio::error::would_block + || ec == asio::error::try_again)) + { + // According to UNIX Network Programming Vol. 1, it is possible for + // close() to fail with EWOULDBLOCK under certain circumstances. What + // isn't clear is the state of the descriptor after this error. The one + // current OS where this behaviour is seen, Windows, says that the socket + // remains open. Therefore we'll put the descriptor back into blocking + // mode and have another attempt at closing it. +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ioctl_arg_type arg = 0; + ::ioctlsocket(s, FIONBIO, &arg); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + int flags = ::fcntl(s, F_GETFL, 0); + if (flags >= 0) + ::fcntl(s, F_SETFL, flags & ~O_NONBLOCK); +# else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + ioctl_arg_type arg = 0; + ::ioctl(s, FIONBIO, &arg); +# endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + state &= ~non_blocking; + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + result = ::closesocket(s); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + result = ::close(s); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + get_last_error(ec, result != 0); + } + } + + return result; +} + +bool set_user_non_blocking(socket_type s, + state_type& state, bool value, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return false; + } + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ioctl_arg_type arg = (value ? 1 : 0); + int result = ::ioctlsocket(s, FIONBIO, &arg); + get_last_error(ec, result < 0); +#elif defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + int result = ::fcntl(s, F_GETFL, 0); + get_last_error(ec, result < 0); + if (result >= 0) + { + int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); + result = ::fcntl(s, F_SETFL, flag); + get_last_error(ec, result < 0); + } +#else + ioctl_arg_type arg = (value ? 1 : 0); + int result = ::ioctl(s, FIONBIO, &arg); + get_last_error(ec, result < 0); +#endif + + if (result >= 0) + { + if (value) + state |= user_set_non_blocking; + else + { + // Clearing the user-set non-blocking mode always overrides any + // internally-set non-blocking flag. Any subsequent asynchronous + // operations will need to re-enable non-blocking I/O. + state &= ~(user_set_non_blocking | internal_non_blocking); + } + return true; + } + + return false; +} + +bool set_internal_non_blocking(socket_type s, + state_type& state, bool value, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return false; + } + + if (!value && (state & user_set_non_blocking)) + { + // It does not make sense to clear the internal non-blocking flag if the + // user still wants non-blocking behaviour. Return an error and let the + // caller figure out whether to update the user-set non-blocking flag. + ec = asio::error::invalid_argument; + return false; + } + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + ioctl_arg_type arg = (value ? 1 : 0); + int result = ::ioctlsocket(s, FIONBIO, &arg); + get_last_error(ec, result < 0); +#elif defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) + int result = ::fcntl(s, F_GETFL, 0); + get_last_error(ec, result < 0); + if (result >= 0) + { + int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); + result = ::fcntl(s, F_SETFL, flag); + get_last_error(ec, result < 0); + } +#else + ioctl_arg_type arg = (value ? 1 : 0); + int result = ::ioctl(s, FIONBIO, &arg); + get_last_error(ec, result < 0); +#endif + + if (result >= 0) + { + if (value) + state |= internal_non_blocking; + else + state &= ~internal_non_blocking; + return true; + } + + return false; +} + +int shutdown(socket_type s, int what, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + int result = ::shutdown(s, what); + get_last_error(ec, result != 0); + return result; +} + +template +inline int call_connect(SockLenType msghdr::*, + socket_type s, const socket_addr_type* addr, std::size_t addrlen) +{ + return ::connect(s, addr, (SockLenType)addrlen); +} + +int connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + int result = call_connect(&msghdr::msg_namelen, s, addr, addrlen); + get_last_error(ec, result != 0); +#if defined(__linux__) + if (result != 0 && ec == asio::error::try_again) + ec = asio::error::no_buffer_space; +#endif // defined(__linux__) + return result; +} + +void sync_connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + // Perform the connect operation. + socket_ops::connect(s, addr, addrlen, ec); + if (ec != asio::error::in_progress + && ec != asio::error::would_block) + { + // The connect operation finished immediately. + return; + } + + // Wait for socket to become ready. + if (socket_ops::poll_connect(s, -1, ec) < 0) + return; + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, ec) == socket_error_retval) + return; + + // Return the result of the connect operation. + ec = asio::error_code(connect_error, + asio::error::get_system_category()); +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_connect(socket_type s, asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + switch (ec.value()) + { + case ERROR_CONNECTION_REFUSED: + ec = asio::error::connection_refused; + break; + case ERROR_NETWORK_UNREACHABLE: + ec = asio::error::network_unreachable; + break; + case ERROR_HOST_UNREACHABLE: + ec = asio::error::host_unreachable; + break; + case ERROR_SEM_TIMEOUT: + ec = asio::error::timed_out; + break; + default: + break; + } + + if (!ec) + { + // Need to set the SO_UPDATE_CONNECT_CONTEXT option so that getsockname + // and getpeername will work on the connected socket. + socket_ops::state_type state = 0; + const int so_update_connect_context = 0x7010; + socket_ops::setsockopt(s, state, SOL_SOCKET, + so_update_connect_context, 0, 0, ec); + } +} + +#endif // defined(ASIO_HAS_IOCP) + +bool non_blocking_connect(socket_type s, asio::error_code& ec) +{ + // Check if the connect operation has finished. This is required since we may + // get spurious readiness notifications from the reactor. +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set write_fds; + FD_ZERO(&write_fds); + FD_SET(s, &write_fds); + fd_set except_fds; + FD_ZERO(&except_fds); + FD_SET(s, &except_fds); + timeval zero_timeout; + zero_timeout.tv_sec = 0; + zero_timeout.tv_usec = 0; + int ready = ::select(s + 1, 0, &write_fds, &except_fds, &zero_timeout); +#else // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + int ready = ::poll(&fds, 1, 0); +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + if (ready == 0) + { + // The asynchronous connect operation is still in progress. + return false; + } + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, ec) == 0) + { + if (connect_error) + { + ec = asio::error_code(connect_error, + asio::error::get_system_category()); + } + else + ec.assign(0, ec.category()); + } + + return true; +} + +int socketpair(int af, int type, int protocol, + socket_type sv[2], asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + (void)(af); + (void)(type); + (void)(protocol); + (void)(sv); + ec = asio::error::operation_not_supported; + return socket_error_retval; +#else + int result = ::socketpair(af, type, protocol, sv); + get_last_error(ec, result != 0); + return result; +#endif +} + +bool sockatmark(socket_type s, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return false; + } + +#if defined(SIOCATMARK) + ioctl_arg_type value = 0; +# if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = ::ioctlsocket(s, SIOCATMARK, &value); +# else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = ::ioctl(s, SIOCATMARK, &value); +# endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + get_last_error(ec, result < 0); +# if defined(ENOTTY) + if (ec.value() == ENOTTY) + ec = asio::error::not_socket; +# endif // defined(ENOTTY) +#else // defined(SIOCATMARK) + int value = ::sockatmark(s); + get_last_error(ec, result < 0); +#endif // defined(SIOCATMARK) + + return ec ? false : value != 0; +} + +size_t available(socket_type s, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + ioctl_arg_type value = 0; +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = ::ioctlsocket(s, FIONREAD, &value); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = ::ioctl(s, FIONREAD, &value); +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + get_last_error(ec, result < 0); +#if defined(ENOTTY) + if (ec.value() == ENOTTY) + ec = asio::error::not_socket; +#endif // defined(ENOTTY) + + return ec ? static_cast(0) : static_cast(value); +} + +int listen(socket_type s, int backlog, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + int result = ::listen(s, backlog); + get_last_error(ec, result != 0); + return result; +} + +inline void init_buf_iov_base(void*& base, void* addr) +{ + base = addr; +} + +template +inline void init_buf_iov_base(T& base, void* addr) +{ + base = static_cast(addr); +} + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef WSABUF buf; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef iovec buf; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +void init_buf(buf& b, void* data, size_t size) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast(data); + b.len = static_cast(size); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + init_buf_iov_base(b.iov_base, data); + b.iov_len = size; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +void init_buf(buf& b, const void* data, size_t size) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast(const_cast(data)); + b.len = static_cast(size); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + init_buf_iov_base(b.iov_base, const_cast(data)); + b.iov_len = size; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +inline void init_msghdr_msg_name(void*& name, socket_addr_type* addr) +{ + name = addr; +} + +inline void init_msghdr_msg_name(void*& name, const socket_addr_type* addr) +{ + name = const_cast(addr); +} + +template +inline void init_msghdr_msg_name(T& name, socket_addr_type* addr) +{ + name = reinterpret_cast(addr); +} + +template +inline void init_msghdr_msg_name(T& name, const socket_addr_type* addr) +{ + name = reinterpret_cast(const_cast(addr)); +} + +signed_size_type recv(socket_type s, buf* bufs, size_t count, + int flags, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(s, bufs, recv_buf_count, + &bytes_transferred, &recv_flags, 0, 0); + get_last_error(ec, true); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + result = 0; + if (result != 0) + return socket_error_retval; + ec.assign(0, ec.category()); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = bufs; + msg.msg_iovlen = static_cast(count); + signed_size_type result = ::recvmsg(s, &msg, flags); + get_last_error(ec, result < 0); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +signed_size_type recv1(socket_type s, void* data, size_t size, + int flags, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + WSABUF buf; + buf.buf = const_cast(static_cast(data)); + buf.len = static_cast(size); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(s, &buf, 1, + &bytes_transferred, &recv_flags, 0, 0); + get_last_error(ec, true); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + result = 0; + if (result != 0) + return socket_error_retval; + ec.assign(0, ec.category()); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + signed_size_type result = ::recv(s, static_cast(data), size, flags); + get_last_error(ec, result < 0); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_recv(socket_type s, state_type state, buf* bufs, + size_t count, int flags, bool all_empty, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream is a no-op. + if (all_empty && (state & stream_oriented)) + { + ec.assign(0, ec.category()); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::recv(s, bufs, count, flags, ec); + + // Check for EOF. + if ((state & stream_oriented) && bytes == 0) + { + ec = asio::error::eof; + return 0; + } + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return 0; + } +} + +size_t sync_recv1(socket_type s, state_type state, void* data, + size_t size, int flags, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream is a no-op. + if (size == 0 && (state & stream_oriented)) + { + ec.assign(0, ec.category()); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::recv1(s, data, size, flags, ec); + + // Check for EOF. + if ((state & stream_oriented) && bytes == 0) + { + ec = asio::error::eof; + return 0; + } + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_recv(state_type state, + const weak_cancel_token_type& cancel_token, bool all_empty, + asio::error_code& ec, size_t bytes_transferred) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + { + ec.assign(0, ec.category()); + } + + // Check for connection closed. + else if (!ec && bytes_transferred == 0 + && (state & stream_oriented) != 0 + && !all_empty) + { + ec = asio::error::eof; + } +} + +#else // defined(ASIO_HAS_IOCP) + +bool non_blocking_recv(socket_type s, + buf* bufs, size_t count, int flags, bool is_stream, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recv(s, bufs, count, flags, ec); + + // Check for end of stream. + if (is_stream && bytes == 0) + { + ec = asio::error::eof; + return true; + } + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +bool non_blocking_recv1(socket_type s, + void* data, size_t size, int flags, bool is_stream, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recv1(s, data, size, flags, ec); + + // Check for end of stream. + if (is_stream && bytes == 0) + { + ec = asio::error::eof; + return true; + } + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +signed_size_type recvfrom(socket_type s, buf* bufs, size_t count, + int flags, socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int tmp_addrlen = (int)*addrlen; + int result = ::WSARecvFrom(s, bufs, recv_buf_count, + &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0); + get_last_error(ec, true); + *addrlen = (std::size_t)tmp_addrlen; + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + result = 0; + if (result != 0) + return socket_error_retval; + ec.assign(0, ec.category()); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + init_msghdr_msg_name(msg.msg_name, addr); + msg.msg_namelen = static_cast(*addrlen); + msg.msg_iov = bufs; + msg.msg_iovlen = static_cast(count); + signed_size_type result = ::recvmsg(s, &msg, flags); + get_last_error(ec, result < 0); + *addrlen = msg.msg_namelen; + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +template +inline signed_size_type call_recvfrom(SockLenType msghdr::*, + socket_type s, void* data, size_t size, int flags, + socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; + signed_size_type result = ::recvfrom(s, static_cast(data), + size, flags, addr, addrlen ? &tmp_addrlen : 0); + if (addrlen) + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +signed_size_type recvfrom1(socket_type s, void* data, size_t size, + int flags, socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + WSABUF buf; + buf.buf = static_cast(data); + buf.len = static_cast(size); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int tmp_addrlen = (int)*addrlen; + int result = ::WSARecvFrom(s, &buf, 1, &bytes_transferred, + &recv_flags, addr, &tmp_addrlen, 0, 0); + get_last_error(ec, true); + *addrlen = (std::size_t)tmp_addrlen; + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + result = 0; + if (result != 0) + return socket_error_retval; + ec.assign(0, ec.category()); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + signed_size_type result = call_recvfrom(&msghdr::msg_namelen, + s, data, size, flags, addr, addrlen); + get_last_error(ec, result < 0); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_recvfrom(socket_type s, state_type state, buf* bufs, + size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::recvfrom( + s, bufs, count, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return 0; + } +} + +size_t sync_recvfrom1(socket_type s, state_type state, void* data, + size_t size, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::recvfrom1( + s, data, size, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_recvfrom( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + { + ec.assign(0, ec.category()); + } +} + +#else // defined(ASIO_HAS_IOCP) + +bool non_blocking_recvfrom(socket_type s, + buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recvfrom( + s, bufs, count, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +bool non_blocking_recvfrom1(socket_type s, + void* data, size_t size, int flags, + socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recvfrom1( + s, data, size, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +signed_size_type recvmsg(socket_type s, buf* bufs, size_t count, + int in_flags, int& out_flags, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + out_flags = 0; + return socket_ops::recv(s, bufs, count, in_flags, ec); +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = bufs; + msg.msg_iovlen = static_cast(count); + signed_size_type result = ::recvmsg(s, &msg, in_flags); + get_last_error(ec, result < 0); + if (result >= 0) + out_flags = msg.msg_flags; + else + out_flags = 0; + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_recvmsg(socket_type s, state_type state, + buf* bufs, size_t count, int in_flags, int& out_flags, + asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::recvmsg( + s, bufs, count, in_flags, out_flags, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_recvmsg( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) + { + ec.assign(0, ec.category()); + } +} + +#else // defined(ASIO_HAS_IOCP) + +bool non_blocking_recvmsg(socket_type s, + buf* bufs, size_t count, int in_flags, int& out_flags, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recvmsg( + s, bufs, count, in_flags, out_flags, ec); + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +signed_size_type send(socket_type s, const buf* bufs, size_t count, + int flags, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD send_flags = flags; + int result = ::WSASend(s, const_cast(bufs), + send_buf_count, &bytes_transferred, send_flags, 0, 0); + get_last_error(ec, true); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec.assign(0, ec.category()); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = const_cast(bufs); + msg.msg_iovlen = static_cast(count); +#if defined(ASIO_HAS_MSG_NOSIGNAL) + flags |= MSG_NOSIGNAL; +#endif // defined(ASIO_HAS_MSG_NOSIGNAL) + signed_size_type result = ::sendmsg(s, &msg, flags); + get_last_error(ec, result < 0); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +signed_size_type send1(socket_type s, const void* data, size_t size, + int flags, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Send the data. + WSABUF buf; + buf.buf = const_cast(static_cast(data)); + buf.len = static_cast(size); + DWORD bytes_transferred = 0; + DWORD send_flags = flags; + int result = ::WSASend(s, &buf, 1, + &bytes_transferred, send_flags, 0, 0); + get_last_error(ec, true); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec.assign(0, ec.category()); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +#if defined(ASIO_HAS_MSG_NOSIGNAL) + flags |= MSG_NOSIGNAL; +#endif // defined(ASIO_HAS_MSG_NOSIGNAL) + signed_size_type result = ::send(s, + static_cast(data), size, flags); + get_last_error(ec, result < 0); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_send(socket_type s, state_type state, const buf* bufs, + size_t count, int flags, bool all_empty, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes to a stream is a no-op. + if (all_empty && (state & stream_oriented)) + { + ec.assign(0, ec.category()); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::send(s, bufs, count, flags, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(s, 0, -1, ec) < 0) + return 0; + } +} + +size_t sync_send1(socket_type s, state_type state, const void* data, + size_t size, int flags, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes to a stream is a no-op. + if (size == 0 && (state & stream_oriented)) + { + ec.assign(0, ec.category()); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::send1(s, data, size, flags, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(s, 0, -1, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_send( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } +} + +#else // defined(ASIO_HAS_IOCP) + +bool non_blocking_send(socket_type s, + const buf* bufs, size_t count, int flags, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + signed_size_type bytes = socket_ops::send(s, bufs, count, flags, ec); + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +bool non_blocking_send1(socket_type s, + const void* data, size_t size, int flags, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + signed_size_type bytes = socket_ops::send1(s, data, size, flags, ec); + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +signed_size_type sendto(socket_type s, const buf* bufs, size_t count, + int flags, const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + int result = ::WSASendTo(s, const_cast(bufs), + send_buf_count, &bytes_transferred, flags, addr, + static_cast(addrlen), 0, 0); + get_last_error(ec, true); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec.assign(0, ec.category()); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + init_msghdr_msg_name(msg.msg_name, addr); + msg.msg_namelen = static_cast(addrlen); + msg.msg_iov = const_cast(bufs); + msg.msg_iovlen = static_cast(count); +#if defined(ASIO_HAS_MSG_NOSIGNAL) + flags |= MSG_NOSIGNAL; +#endif // defined(ASIO_HAS_MSG_NOSIGNAL) + signed_size_type result = ::sendmsg(s, &msg, flags); + get_last_error(ec, result < 0); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +template +inline signed_size_type call_sendto(SockLenType msghdr::*, + socket_type s, const void* data, size_t size, int flags, + const socket_addr_type* addr, std::size_t addrlen) +{ + return ::sendto(s, static_cast(const_cast(data)), + size, flags, addr, (SockLenType)addrlen); +} + +signed_size_type sendto1(socket_type s, const void* data, size_t size, + int flags, const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + // Send the data. + WSABUF buf; + buf.buf = const_cast(static_cast(data)); + buf.len = static_cast(size); + DWORD bytes_transferred = 0; + int result = ::WSASendTo(s, &buf, 1, &bytes_transferred, + flags, addr, static_cast(addrlen), 0, 0); + get_last_error(ec, true); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec.assign(0, ec.category()); + return bytes_transferred; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +#if defined(ASIO_HAS_MSG_NOSIGNAL) + flags |= MSG_NOSIGNAL; +#endif // defined(ASIO_HAS_MSG_NOSIGNAL) + signed_size_type result = call_sendto(&msghdr::msg_namelen, + s, data, size, flags, addr, addrlen); + get_last_error(ec, result < 0); + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_sendto(socket_type s, state_type state, const buf* bufs, + size_t count, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Write some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::sendto( + s, bufs, count, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(s, 0, -1, ec) < 0) + return 0; + } +} + +size_t sync_sendto1(socket_type s, state_type state, const void* data, + size_t size, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Write some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::sendto1( + s, data, size, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(s, 0, -1, ec) < 0) + return 0; + } +} + +#if !defined(ASIO_HAS_IOCP) + +bool non_blocking_sendto(socket_type s, + const buf* bufs, size_t count, int flags, + const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + signed_size_type bytes = socket_ops::sendto( + s, bufs, count, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +bool non_blocking_sendto1(socket_type s, + const void* data, size_t size, int flags, + const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + signed_size_type bytes = socket_ops::sendto1( + s, data, size, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + { + bytes_transferred = bytes; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation failed. + bytes_transferred = 0; + return true; + } +} + +#endif // !defined(ASIO_HAS_IOCP) + +socket_type socket(int af, int type, int protocol, + asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + socket_type s = ::WSASocketW(af, type, protocol, 0, 0, WSA_FLAG_OVERLAPPED); + get_last_error(ec, s == invalid_socket); + if (s == invalid_socket) + return s; + + if (af == ASIO_OS_DEF(AF_INET6)) + { + // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to + // false. This will only succeed on Windows Vista and later versions of + // Windows, where a dual-stack IPv4/v6 implementation is available. + DWORD optval = 0; + ::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast(&optval), sizeof(optval)); + } + + return s; +#elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) + socket_type s = ::socket(af, type, protocol); + get_last_error(ec, s == invalid_socket); + if (s == invalid_socket) + return s; + + int optval = 1; + int result = ::setsockopt(s, SOL_SOCKET, + SO_NOSIGPIPE, &optval, sizeof(optval)); + get_last_error(ec, result != 0); + if (result != 0) + { + ::close(s); + return invalid_socket; + } + + return s; +#else + int s = ::socket(af, type, protocol); + get_last_error(ec, s < 0); + return s; +#endif +} + +template +inline int call_setsockopt(SockLenType msghdr::*, + socket_type s, int level, int optname, + const void* optval, std::size_t optlen) +{ + return ::setsockopt(s, level, optname, + (const char*)optval, (SockLenType)optlen); +} + +int setsockopt(socket_type s, state_type& state, int level, int optname, + const void* optval, std::size_t optlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + if (level == custom_socket_option_level && optname == always_fail_option) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + if (level == custom_socket_option_level + && optname == enable_connection_aborted_option) + { + if (optlen != sizeof(int)) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + if (*static_cast(optval)) + state |= enable_connection_aborted; + else + state &= ~enable_connection_aborted; + ec.assign(0, ec.category()); + return 0; + } + + if (level == SOL_SOCKET && optname == SO_LINGER) + state |= user_set_linger; + +#if defined(__BORLANDC__) + // Mysteriously, using the getsockopt and setsockopt functions directly with + // Borland C++ results in incorrect values being set and read. The bug can be + // worked around by using function addresses resolved with GetProcAddress. + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int); + if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt")) + { + int result = sso(s, level, optname, + reinterpret_cast(optval), + static_cast(optlen)); + get_last_error(ec, result != 0); + return result; + } + } + ec = asio::error::fault; + return socket_error_retval; +#else // defined(__BORLANDC__) + int result = call_setsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen); + get_last_error(ec, result != 0); + if (result == 0) + { +#if defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) \ + || defined(__OpenBSD__) || defined(__QNX__) + // To implement portable behaviour for SO_REUSEADDR with UDP sockets we + // need to also set SO_REUSEPORT on BSD-based platforms. + if ((state & datagram_oriented) + && level == SOL_SOCKET && optname == SO_REUSEADDR) + { + call_setsockopt(&msghdr::msg_namelen, s, + SOL_SOCKET, SO_REUSEPORT, optval, optlen); + } +#endif + } + + return result; +#endif // defined(__BORLANDC__) +} + +template +inline int call_getsockopt(SockLenType msghdr::*, + socket_type s, int level, int optname, + void* optval, std::size_t* optlen) +{ + SockLenType tmp_optlen = (SockLenType)*optlen; + int result = ::getsockopt(s, level, optname, (char*)optval, &tmp_optlen); + *optlen = (std::size_t)tmp_optlen; + return result; +} + +int getsockopt(socket_type s, state_type state, int level, int optname, + void* optval, size_t* optlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + if (level == custom_socket_option_level && optname == always_fail_option) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + if (level == custom_socket_option_level + && optname == enable_connection_aborted_option) + { + if (*optlen != sizeof(int)) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + *static_cast(optval) = (state & enable_connection_aborted) ? 1 : 0; + ec.assign(0, ec.category()); + return 0; + } + +#if defined(__BORLANDC__) + // Mysteriously, using the getsockopt and setsockopt functions directly with + // Borland C++ results in incorrect values being set and read. The bug can be + // worked around by using function addresses resolved with GetProcAddress. + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*); + if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt")) + { + int tmp_optlen = static_cast(*optlen); + int result = gso(s, level, optname, + reinterpret_cast(optval), &tmp_optlen); + get_last_error(ec, result != 0); + *optlen = static_cast(tmp_optlen); + if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY + && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) + { + // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are + // only supported on Windows Vista and later. To simplify program logic + // we will fake success of getting this option and specify that the + // value is non-zero (i.e. true). This corresponds to the behavior of + // IPv6 sockets on Windows platforms pre-Vista. + *static_cast(optval) = 1; + ec.assign(0, ec.category()); + } + return result; + } + } + ec = asio::error::fault; + return socket_error_retval; +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = call_getsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen); + get_last_error(ec, result != 0); + if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY + && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) + { + // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only + // supported on Windows Vista and later. To simplify program logic we will + // fake success of getting this option and specify that the value is + // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets + // on Windows platforms pre-Vista. + *static_cast(optval) = 1; + ec.assign(0, ec.category()); + } + return result; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = call_getsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen); + get_last_error(ec, result != 0); +#if defined(__linux__) + if (result == 0 && level == SOL_SOCKET && *optlen == sizeof(int) + && (optname == SO_SNDBUF || optname == SO_RCVBUF)) + { + // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel + // to set the buffer size to N*2. Linux puts additional stuff into the + // buffers so that only about half is actually available to the application. + // The retrieved value is divided by 2 here to make it appear as though the + // correct value has been set. + *static_cast(optval) /= 2; + } +#endif // defined(__linux__) + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +template +inline int call_getpeername(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = (SockLenType)*addrlen; + int result = ::getpeername(s, addr, &tmp_addrlen); + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +int getpeername(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, bool cached, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) && !defined(ASIO_WINDOWS_APP) \ + || defined(__CYGWIN__) + if (cached) + { + // Check if socket is still connected. + DWORD connect_time = 0; + size_t connect_time_len = sizeof(connect_time); + if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_CONNECT_TIME, + &connect_time, &connect_time_len, ec) == socket_error_retval) + { + return socket_error_retval; + } + if (connect_time == 0xFFFFFFFF) + { + ec = asio::error::not_connected; + return socket_error_retval; + } + + // The cached value is still valid. + ec.assign(0, ec.category()); + return 0; + } +#else // defined(ASIO_WINDOWS) && !defined(ASIO_WINDOWS_APP) + // || defined(__CYGWIN__) + (void)cached; +#endif // defined(ASIO_WINDOWS) && !defined(ASIO_WINDOWS_APP) + // || defined(__CYGWIN__) + + int result = call_getpeername(&msghdr::msg_namelen, s, addr, addrlen); + get_last_error(ec, result != 0); + return result; +} + +template +inline int call_getsockname(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = (SockLenType)*addrlen; + int result = ::getsockname(s, addr, &tmp_addrlen); + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +int getsockname(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + int result = call_getsockname(&msghdr::msg_namelen, s, addr, addrlen); + get_last_error(ec, result != 0); + return result; +} + +int ioctl(socket_type s, state_type& state, int cmd, + ioctl_arg_type* arg, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + int result = ::ioctlsocket(s, cmd, arg); +#elif defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + int result = ::ioctl(s, static_cast(cmd), arg); +#else + int result = ::ioctl(s, cmd, arg); +#endif + get_last_error(ec, result < 0); + if (result >= 0) + { + // When updating the non-blocking mode we always perform the ioctl syscall, + // even if the flags would otherwise indicate that the socket is already in + // the correct state. This ensures that the underlying socket is put into + // the state that has been requested by the user. If the ioctl syscall was + // successful then we need to update the flags to match. + if (cmd == static_cast(FIONBIO)) + { + if (*arg) + { + state |= user_set_non_blocking; + } + else + { + // Clearing the non-blocking mode always overrides any internally-set + // non-blocking flag. Any subsequent asynchronous operations will need + // to re-enable non-blocking I/O. + state &= ~(user_set_non_blocking | internal_non_blocking); + } + } + } + + return result; +} + +int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, timeval* timeout, asio::error_code& ec) +{ +#if defined(__EMSCRIPTEN__) + exceptfds = 0; +#endif // defined(__EMSCRIPTEN__) +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + if (!readfds && !writefds && !exceptfds && timeout) + { + DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + if (milliseconds == 0) + milliseconds = 1; // Force context switch. + ::Sleep(milliseconds); + ec.assign(0, ec.category()); + return 0; + } + + // The select() call allows timeout values measured in microseconds, but the + // system clock (as wrapped by boost::posix_time::microsec_clock) typically + // has a resolution of 10 milliseconds. This can lead to a spinning select + // reactor, meaning increased CPU usage, when waiting for the earliest + // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight + // spin we'll use a minimum timeout of 1 millisecond. + if (timeout && timeout->tv_sec == 0 + && timeout->tv_usec > 0 && timeout->tv_usec < 1000) + timeout->tv_usec = 1000; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#if defined(__hpux) && defined(__SELECT) + timespec ts; + ts.tv_sec = timeout ? timeout->tv_sec : 0; + ts.tv_nsec = timeout ? timeout->tv_usec * 1000 : 0; + int result = ::pselect(nfds, readfds, + writefds, exceptfds, timeout ? &ts : 0, 0); +#else + int result = ::select(nfds, readfds, writefds, exceptfds, timeout); +#endif + get_last_error(ec, result < 0); + return result; +} + +int poll_read(socket_type s, state_type state, + int msec, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + timeval timeout_obj; + timeval* timeout; + if (state & user_set_non_blocking) + { + timeout_obj.tv_sec = 0; + timeout_obj.tv_usec = 0; + timeout = &timeout_obj; + } + else if (msec >= 0) + { + timeout_obj.tv_sec = msec / 1000; + timeout_obj.tv_usec = (msec % 1000) * 1000; + timeout = &timeout_obj; + } + else + timeout = 0; + int result = ::select(s + 1, &fds, 0, 0, timeout); + get_last_error(ec, result < 0); +#else // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLIN; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : msec; + int result = ::poll(&fds, 1, timeout); + get_last_error(ec, result < 0); +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + if (result == 0) + if (state & user_set_non_blocking) + ec = asio::error::would_block; + return result; +} + +int poll_write(socket_type s, state_type state, + int msec, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + timeval timeout_obj; + timeval* timeout; + if (state & user_set_non_blocking) + { + timeout_obj.tv_sec = 0; + timeout_obj.tv_usec = 0; + timeout = &timeout_obj; + } + else if (msec >= 0) + { + timeout_obj.tv_sec = msec / 1000; + timeout_obj.tv_usec = (msec % 1000) * 1000; + timeout = &timeout_obj; + } + else + timeout = 0; + int result = ::select(s + 1, 0, &fds, 0, timeout); + get_last_error(ec, result < 0); +#else // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : msec; + int result = ::poll(&fds, 1, timeout); + get_last_error(ec, result < 0); +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + if (result == 0) + if (state & user_set_non_blocking) + ec = asio::error::would_block; + return result; +} + +int poll_error(socket_type s, state_type state, + int msec, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + timeval timeout_obj; + timeval* timeout; + if (state & user_set_non_blocking) + { + timeout_obj.tv_sec = 0; + timeout_obj.tv_usec = 0; + timeout = &timeout_obj; + } + else if (msec >= 0) + { + timeout_obj.tv_sec = msec / 1000; + timeout_obj.tv_usec = (msec % 1000) * 1000; + timeout = &timeout_obj; + } + else + timeout = 0; + int result = ::select(s + 1, 0, 0, &fds, timeout); + get_last_error(ec, result < 0); +#else // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLPRI | POLLERR | POLLHUP; + fds.revents = 0; + int timeout = (state & user_set_non_blocking) ? 0 : msec; + int result = ::poll(&fds, 1, timeout); + get_last_error(ec, result < 0); +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + if (result == 0) + if (state & user_set_non_blocking) + ec = asio::error::would_block; + return result; +} + +int poll_connect(socket_type s, int msec, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set write_fds; + FD_ZERO(&write_fds); + FD_SET(s, &write_fds); + fd_set except_fds; + FD_ZERO(&except_fds); + FD_SET(s, &except_fds); + timeval timeout_obj; + timeval* timeout; + if (msec >= 0) + { + timeout_obj.tv_sec = msec / 1000; + timeout_obj.tv_usec = (msec % 1000) * 1000; + timeout = &timeout_obj; + } + else + timeout = 0; + int result = ::select(s + 1, 0, &write_fds, &except_fds, timeout); + get_last_error(ec, result < 0); + return result; +#else // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + int result = ::poll(&fds, 1, msec); + get_last_error(ec, result < 0); + return result; +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) +} + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +const char* inet_ntop(int af, const void* src, char* dest, size_t length, + unsigned long scope_id, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS_RUNTIME) + using namespace std; // For sprintf. + const unsigned char* bytes = static_cast(src); + if (af == ASIO_OS_DEF(AF_INET)) + { + sprintf_s(dest, length, "%u.%u.%u.%u", + bytes[0], bytes[1], bytes[2], bytes[3]); + return dest; + } + else if (af == ASIO_OS_DEF(AF_INET6)) + { + size_t n = 0, b = 0, z = 0; + while (n < length && b < 16) + { + if (bytes[b] == 0 && bytes[b + 1] == 0 && z == 0) + { + do b += 2; while (b < 16 && bytes[b] == 0 && bytes[b + 1] == 0); + n += sprintf_s(dest + n, length - n, ":%s", b < 16 ? "" : ":"), ++z; + } + else + { + n += sprintf_s(dest + n, length - n, "%s%x", b ? ":" : "", + (static_cast(bytes[b]) << 8) | bytes[b + 1]); + b += 2; + } + } + if (scope_id) + n += sprintf_s(dest + n, length - n, "%%%lu", scope_id); + return dest; + } + else + { + ec = asio::error::address_family_not_supported; + return 0; + } +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy. + + if (af != ASIO_OS_DEF(AF_INET) && af != ASIO_OS_DEF(AF_INET6)) + { + ec = asio::error::address_family_not_supported; + return 0; + } + + union + { + socket_addr_type base; + sockaddr_storage_type storage; + sockaddr_in4_type v4; + sockaddr_in6_type v6; + } address; + DWORD address_length; + if (af == ASIO_OS_DEF(AF_INET)) + { + address_length = sizeof(sockaddr_in4_type); + address.v4.sin_family = ASIO_OS_DEF(AF_INET); + address.v4.sin_port = 0; + memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type)); + } + else // AF_INET6 + { + address_length = sizeof(sockaddr_in6_type); + address.v6.sin6_family = ASIO_OS_DEF(AF_INET6); + address.v6.sin6_port = 0; + address.v6.sin6_flowinfo = 0; + address.v6.sin6_scope_id = scope_id; + memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type)); + } + + DWORD string_length = static_cast(length); +#if defined(BOOST_NO_ANSI_APIS) || (defined(_MSC_VER) && (_MSC_VER >= 1800)) + LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR)); + int result = ::WSAAddressToStringW(&address.base, + address_length, 0, string_buffer, &string_length); + get_last_error(ec, true); + ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, + dest, static_cast(length), 0, 0); +#else + int result = ::WSAAddressToStringA(&address.base, + address_length, 0, dest, &string_length); + get_last_error(ec, true); +#endif + + // Windows may set error code on success. + if (result != socket_error_retval) + ec.assign(0, ec.category()); + + // Windows may not set an error code on failure. + else if (result == socket_error_retval && !ec) + ec = asio::error::invalid_argument; + + return result == socket_error_retval ? 0 : dest; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + const char* result = ::inet_ntop(af, src, dest, static_cast(length)); + get_last_error(ec, true); + if (result == 0 && !ec) + ec = asio::error::invalid_argument; + if (result != 0 && af == ASIO_OS_DEF(AF_INET6) && scope_id != 0) + { + using namespace std; // For strcat and sprintf. + char if_name[(IF_NAMESIZE > 21 ? IF_NAMESIZE : 21) + 1] = "%"; + const in6_addr_type* ipv6_address = static_cast(src); + bool is_link_local = ((ipv6_address->s6_addr[0] == 0xfe) + && ((ipv6_address->s6_addr[1] & 0xc0) == 0x80)); + bool is_multicast_link_local = ((ipv6_address->s6_addr[0] == 0xff) + && ((ipv6_address->s6_addr[1] & 0x0f) == 0x02)); + if ((!is_link_local && !is_multicast_link_local) + || if_indextoname(static_cast(scope_id), if_name + 1) == 0) + sprintf(if_name + 1, "%lu", scope_id); + strcat(dest, if_name); + } + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +int inet_pton(int af, const char* src, void* dest, + unsigned long* scope_id, asio::error_code& ec) +{ + clear_last_error(); +#if defined(ASIO_WINDOWS_RUNTIME) + using namespace std; // For sscanf. + unsigned char* bytes = static_cast(dest); + if (af == ASIO_OS_DEF(AF_INET)) + { + unsigned int b0, b1, b2, b3; + if (sscanf_s(src, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) != 4) + { + ec = asio::error::invalid_argument; + return -1; + } + if (b0 > 255 || b1 > 255 || b2 > 255 || b3 > 255) + { + ec = asio::error::invalid_argument; + return -1; + } + bytes[0] = static_cast(b0); + bytes[1] = static_cast(b1); + bytes[2] = static_cast(b2); + bytes[3] = static_cast(b3); + ec.assign(0, ec.category()); + return 1; + } + else if (af == ASIO_OS_DEF(AF_INET6)) + { + unsigned char* bytes = static_cast(dest); + std::memset(bytes, 0, 16); + unsigned char back_bytes[16] = { 0 }; + int num_front_bytes = 0, num_back_bytes = 0; + const char* p = src; + + enum { fword, fcolon, bword, scope, done } state = fword; + unsigned long current_word = 0; + while (state != done) + { + if (current_word > 0xFFFF) + { + ec = asio::error::invalid_argument; + return -1; + } + + switch (state) + { + case fword: + if (*p >= '0' && *p <= '9') + current_word = current_word * 16 + *p++ - '0'; + else if (*p >= 'a' && *p <= 'f') + current_word = current_word * 16 + *p++ - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + current_word = current_word * 16 + *p++ - 'A' + 10; + else + { + if (num_front_bytes == 16) + { + ec = asio::error::invalid_argument; + return -1; + } + + bytes[num_front_bytes++] = (current_word >> 8) & 0xFF; + bytes[num_front_bytes++] = current_word & 0xFF; + current_word = 0; + + if (*p == ':') + state = fcolon, ++p; + else if (*p == '%') + state = scope, ++p; + else if (*p == 0) + state = done; + else + { + ec = asio::error::invalid_argument; + return -1; + } + } + break; + + case fcolon: + if (*p == ':') + state = bword, ++p; + else + state = fword; + break; + + case bword: + if (*p >= '0' && *p <= '9') + current_word = current_word * 16 + *p++ - '0'; + else if (*p >= 'a' && *p <= 'f') + current_word = current_word * 16 + *p++ - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + current_word = current_word * 16 + *p++ - 'A' + 10; + else + { + if (num_front_bytes + num_back_bytes == 16) + { + ec = asio::error::invalid_argument; + return -1; + } + + back_bytes[num_back_bytes++] = (current_word >> 8) & 0xFF; + back_bytes[num_back_bytes++] = current_word & 0xFF; + current_word = 0; + + if (*p == ':') + state = bword, ++p; + else if (*p == '%') + state = scope, ++p; + else if (*p == 0) + state = done; + else + { + ec = asio::error::invalid_argument; + return -1; + } + } + break; + + case scope: + if (*p >= '0' && *p <= '9') + current_word = current_word * 10 + *p++ - '0'; + else if (*p == 0) + *scope_id = current_word, state = done; + else + { + ec = asio::error::invalid_argument; + return -1; + } + break; + + default: + break; + } + } + + for (int i = 0; i < num_back_bytes; ++i) + bytes[16 - num_back_bytes + i] = back_bytes[i]; + + ec.assign(0, ec.category()); + return 1; + } + else + { + ec = asio::error::address_family_not_supported; + return -1; + } +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy and strcmp. + + if (af != ASIO_OS_DEF(AF_INET) && af != ASIO_OS_DEF(AF_INET6)) + { + ec = asio::error::address_family_not_supported; + return -1; + } + + union + { + socket_addr_type base; + sockaddr_storage_type storage; + sockaddr_in4_type v4; + sockaddr_in6_type v6; + } address; + int address_length = sizeof(sockaddr_storage_type); +#if defined(BOOST_NO_ANSI_APIS) || (defined(_MSC_VER) && (_MSC_VER >= 1800)) + int num_wide_chars = static_cast(strlen(src)) + 1; + LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR)); + ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars); + int result = ::WSAStringToAddressW(wide_buffer, + af, 0, &address.base, &address_length); + get_last_error(ec, true); +#else + int result = ::WSAStringToAddressA(const_cast(src), + af, 0, &address.base, &address_length); + get_last_error(ec, true); +#endif + + if (af == ASIO_OS_DEF(AF_INET)) + { + if (result != socket_error_retval) + { + memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type)); + ec.assign(0, ec.category()); + } + else if (strcmp(src, "255.255.255.255") == 0) + { + static_cast(dest)->s_addr = INADDR_NONE; + ec.assign(0, ec.category()); + } + } + else // AF_INET6 + { + if (result != socket_error_retval) + { + memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type)); + if (scope_id) + *scope_id = address.v6.sin6_scope_id; + ec.assign(0, ec.category()); + } + } + + // Windows may not set an error code on failure. + if (result == socket_error_retval && !ec) + ec = asio::error::invalid_argument; + + if (result != socket_error_retval) + ec.assign(0, ec.category()); + + return result == socket_error_retval ? -1 : 1; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For strchr, memcpy and atoi. + + // On some platforms, inet_pton fails if an address string contains a scope + // id. Detect and remove the scope id before passing the string to inet_pton. + const bool is_v6 = (af == ASIO_OS_DEF(AF_INET6)); + const char* if_name = is_v6 ? strchr(src, '%') : 0; + char src_buf[max_addr_v6_str_len + 1]; + const char* src_ptr = src; + if (if_name != 0) + { + if (if_name - src > max_addr_v6_str_len) + { + ec = asio::error::invalid_argument; + return 0; + } + memcpy(src_buf, src, if_name - src); + src_buf[if_name - src] = 0; + src_ptr = src_buf; + } + + int result = ::inet_pton(af, src_ptr, dest); + get_last_error(ec, true); + if (result <= 0 && !ec) + ec = asio::error::invalid_argument; + if (result > 0 && is_v6 && scope_id) + { + using namespace std; // For strchr and atoi. + *scope_id = 0; + if (if_name != 0) + { + in6_addr_type* ipv6_address = static_cast(dest); + bool is_link_local = ((ipv6_address->s6_addr[0] == 0xfe) + && ((ipv6_address->s6_addr[1] & 0xc0) == 0x80)); + bool is_multicast_link_local = ((ipv6_address->s6_addr[0] == 0xff) + && ((ipv6_address->s6_addr[1] & 0x0f) == 0x02)); + if (is_link_local || is_multicast_link_local) + *scope_id = if_nametoindex(if_name + 1); + if (*scope_id == 0) + *scope_id = atoi(if_name + 1); + } + } + return result; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +} + +int gethostname(char* name, int namelen, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + try + { + using namespace Windows::Foundation::Collections; + using namespace Windows::Networking; + using namespace Windows::Networking::Connectivity; + IVectorView^ hostnames = NetworkInformation::GetHostNames(); + for (unsigned i = 0; i < hostnames->Size; ++i) + { + HostName^ hostname = hostnames->GetAt(i); + if (hostname->Type == HostNameType::DomainName) + { + std::wstring_convert> converter; + std::string raw_name = converter.to_bytes(hostname->RawName->Data()); + if (namelen > 0 && raw_name.size() < static_cast(namelen)) + { + strcpy_s(name, namelen, raw_name.c_str()); + return 0; + } + } + } + return -1; + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + return -1; + } +#else // defined(ASIO_WINDOWS_RUNTIME) + int result = ::gethostname(name, namelen); + get_last_error(ec, result != 0); + return result; +#endif // defined(ASIO_WINDOWS_RUNTIME) +} + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#if !defined(ASIO_HAS_GETADDRINFO) + +// The following functions are only needed for emulation of getaddrinfo and +// getnameinfo. + +inline asio::error_code translate_netdb_error(int error) +{ + switch (error) + { + case 0: + return asio::error_code(); + case HOST_NOT_FOUND: + return asio::error::host_not_found; + case TRY_AGAIN: + return asio::error::host_not_found_try_again; + case NO_RECOVERY: + return asio::error::no_recovery; + case NO_DATA: + return asio::error::no_data; + default: + ASIO_ASSERT(false); + return asio::error::invalid_argument; + } +} + +inline hostent* gethostbyaddr(const char* addr, int length, int af, + hostent* result, char* buffer, int buflength, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + hostent* retval = ::gethostbyaddr(addr, length, af); + get_last_error(ec, !retval); + if (!retval) + return 0; + *result = *retval; + return retval; +#elif defined(__sun) || defined(__QNX__) + int error = 0; + hostent* retval = ::gethostbyaddr_r(addr, length, + af, result, buffer, buflength, &error); + get_last_error(ec, !retval); + if (error) + ec = translate_netdb_error(error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + int error = 0; + hostent* retval = ::getipnodebyaddr(addr, length, af, &error); + get_last_error(ec, !retval); + if (error) + ec = translate_netdb_error(error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + hostent* retval = 0; + int error = 0; + clear_last_error(); + ::gethostbyaddr_r(addr, length, af, result, + buffer, buflength, &retval, &error); + get_last_error(ec, true); + if (error) + ec = translate_netdb_error(error); + return retval; +#endif +} + +inline hostent* gethostbyname(const char* name, int af, struct hostent* result, + char* buffer, int buflength, int ai_flags, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + (void)(ai_flags); + if (af != ASIO_OS_DEF(AF_INET)) + { + ec = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = ::gethostbyname(name); + get_last_error(ec, !retval); + if (!retval) + return 0; + *result = *retval; + return result; +#elif defined(__sun) || defined(__QNX__) + (void)(ai_flags); + if (af != ASIO_OS_DEF(AF_INET)) + { + ec = asio::error::address_family_not_supported; + return 0; + } + int error = 0; + hostent* retval = ::gethostbyname_r(name, result, buffer, buflength, &error); + get_last_error(ec, !retval); + if (error) + ec = translate_netdb_error(error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + int error = 0; + hostent* retval = ::getipnodebyname(name, af, ai_flags, &error); + get_last_error(ec, !retval); + if (error) + ec = translate_netdb_error(error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + (void)(ai_flags); + if (af != ASIO_OS_DEF(AF_INET)) + { + ec = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = 0; + int error = 0; + clear_last_error(); + ::gethostbyname_r(name, result, buffer, buflength, &retval, &error); + get_last_error(ec, true); + if (error) + ec = translate_netdb_error(error); + return retval; +#endif +} + +inline void freehostent(hostent* h) +{ +#if defined(__MACH__) && defined(__APPLE__) + if (h) + ::freehostent(h); +#else + (void)(h); +#endif +} + +// Emulation of getaddrinfo based on implementation in: +// Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998. + +struct gai_search +{ + const char* host; + int family; +}; + +inline int gai_nsearch(const char* host, + const addrinfo_type* hints, gai_search (&search)[2]) +{ + int search_count = 0; + if (host == 0 || host[0] == '\0') + { + if (hints->ai_flags & AI_PASSIVE) + { + // No host and AI_PASSIVE implies wildcard bind. + switch (hints->ai_family) + { + case ASIO_OS_DEF(AF_INET): + search[search_count].host = "0.0.0.0"; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + case ASIO_OS_DEF(AF_INET6): + search[search_count].host = "0::0"; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + break; + case ASIO_OS_DEF(AF_UNSPEC): + search[search_count].host = "0::0"; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + search[search_count].host = "0.0.0.0"; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + default: + break; + } + } + else + { + // No host and not AI_PASSIVE means connect to local host. + switch (hints->ai_family) + { + case ASIO_OS_DEF(AF_INET): + search[search_count].host = "localhost"; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + case ASIO_OS_DEF(AF_INET6): + search[search_count].host = "localhost"; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + break; + case ASIO_OS_DEF(AF_UNSPEC): + search[search_count].host = "localhost"; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + search[search_count].host = "localhost"; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + default: + break; + } + } + } + else + { + // Host is specified. + switch (hints->ai_family) + { + case ASIO_OS_DEF(AF_INET): + search[search_count].host = host; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + case ASIO_OS_DEF(AF_INET6): + search[search_count].host = host; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + break; + case ASIO_OS_DEF(AF_UNSPEC): + search[search_count].host = host; + search[search_count].family = ASIO_OS_DEF(AF_INET6); + ++search_count; + search[search_count].host = host; + search[search_count].family = ASIO_OS_DEF(AF_INET); + ++search_count; + break; + default: + break; + } + } + return search_count; +} + +template +inline T* gai_alloc(std::size_t size = sizeof(T)) +{ + using namespace std; + T* p = static_cast(::operator new(size, std::nothrow)); + if (p) + memset(p, 0, size); + return p; +} + +inline void gai_free(void* p) +{ + ::operator delete(p); +} + +inline void gai_strcpy(char* target, const char* source, std::size_t max_size) +{ + using namespace std; +#if defined(ASIO_HAS_SECURE_RTL) + strcpy_s(target, max_size, source); +#else // defined(ASIO_HAS_SECURE_RTL) + *target = 0; + if (max_size > 0) + strncat(target, source, max_size - 1); +#endif // defined(ASIO_HAS_SECURE_RTL) +} + +enum { gai_clone_flag = 1 << 30 }; + +inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints, + const void* addr, int family) +{ + using namespace std; + + addrinfo_type* ai = gai_alloc(); + if (ai == 0) + return EAI_MEMORY; + + ai->ai_next = 0; + **next = ai; + *next = &ai->ai_next; + + ai->ai_canonname = 0; + ai->ai_socktype = hints->ai_socktype; + if (ai->ai_socktype == 0) + ai->ai_flags |= gai_clone_flag; + ai->ai_protocol = hints->ai_protocol; + ai->ai_family = family; + + switch (ai->ai_family) + { + case ASIO_OS_DEF(AF_INET): + { + sockaddr_in4_type* sinptr = gai_alloc(); + if (sinptr == 0) + return EAI_MEMORY; + sinptr->sin_family = ASIO_OS_DEF(AF_INET); + memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type)); + ai->ai_addr = reinterpret_cast(sinptr); + ai->ai_addrlen = sizeof(sockaddr_in4_type); + break; + } + case ASIO_OS_DEF(AF_INET6): + { + sockaddr_in6_type* sin6ptr = gai_alloc(); + if (sin6ptr == 0) + return EAI_MEMORY; + sin6ptr->sin6_family = ASIO_OS_DEF(AF_INET6); + memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type)); + ai->ai_addr = reinterpret_cast(sin6ptr); + ai->ai_addrlen = sizeof(sockaddr_in6_type); + break; + } + default: + break; + } + + return 0; +} + +inline addrinfo_type* gai_clone(addrinfo_type* ai) +{ + using namespace std; + + addrinfo_type* new_ai = gai_alloc(); + if (new_ai == 0) + return new_ai; + + new_ai->ai_next = ai->ai_next; + ai->ai_next = new_ai; + + new_ai->ai_flags = 0; + new_ai->ai_family = ai->ai_family; + new_ai->ai_socktype = ai->ai_socktype; + new_ai->ai_protocol = ai->ai_protocol; + new_ai->ai_canonname = 0; + new_ai->ai_addrlen = ai->ai_addrlen; + new_ai->ai_addr = gai_alloc(ai->ai_addrlen); + memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen); + + return new_ai; +} + +inline int gai_port(addrinfo_type* aihead, int port, int socktype) +{ + int num_found = 0; + + for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next) + { + if (ai->ai_flags & gai_clone_flag) + { + if (ai->ai_socktype != 0) + { + ai = gai_clone(ai); + if (ai == 0) + return -1; + // ai now points to newly cloned entry. + } + } + else if (ai->ai_socktype != socktype) + { + // Ignore if mismatch on socket type. + continue; + } + + ai->ai_socktype = socktype; + + switch (ai->ai_family) + { + case ASIO_OS_DEF(AF_INET): + { + sockaddr_in4_type* sinptr = + reinterpret_cast(ai->ai_addr); + sinptr->sin_port = port; + ++num_found; + break; + } + case ASIO_OS_DEF(AF_INET6): + { + sockaddr_in6_type* sin6ptr = + reinterpret_cast(ai->ai_addr); + sin6ptr->sin6_port = port; + ++num_found; + break; + } + default: + break; + } + } + + return num_found; +} + +inline int gai_serv(addrinfo_type* aihead, + const addrinfo_type* hints, const char* serv) +{ + using namespace std; + + int num_found = 0; + + if ( +#if defined(AI_NUMERICSERV) + (hints->ai_flags & AI_NUMERICSERV) || +#endif + isdigit(static_cast(serv[0]))) + { + int port = htons(atoi(serv)); + if (hints->ai_socktype) + { + // Caller specifies socket type. + int rc = gai_port(aihead, port, hints->ai_socktype); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + else + { + // Caller does not specify socket type. + int rc = gai_port(aihead, port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + rc = gai_port(aihead, port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + else + { + // Try service name with TCP first, then UDP. + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM) + { + servent* sptr = getservbyname(serv, "tcp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM) + { + servent* sptr = getservbyname(serv, "udp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + } + + if (num_found == 0) + { + if (hints->ai_socktype == 0) + { + // All calls to getservbyname() failed. + return EAI_NONAME; + } + else + { + // Service not supported for socket type. + return EAI_SERVICE; + } + } + + return 0; +} + +inline int gai_echeck(const char* host, const char* service, + int flags, int family, int socktype, int protocol) +{ + (void)(flags); + (void)(protocol); + + // Host or service must be specified. + if (host == 0 || host[0] == '\0') + if (service == 0 || service[0] == '\0') + return EAI_NONAME; + + // Check combination of family and socket type. + switch (family) + { + case ASIO_OS_DEF(AF_UNSPEC): + break; + case ASIO_OS_DEF(AF_INET): + case ASIO_OS_DEF(AF_INET6): + if (service != 0 && service[0] != '\0') + if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM) + return EAI_SOCKTYPE; + break; + default: + return EAI_FAMILY; + } + + return 0; +} + +inline void freeaddrinfo_emulation(addrinfo_type* aihead) +{ + addrinfo_type* ai = aihead; + while (ai) + { + gai_free(ai->ai_addr); + gai_free(ai->ai_canonname); + addrinfo_type* ainext = ai->ai_next; + gai_free(ai); + ai = ainext; + } +} + +inline int getaddrinfo_emulation(const char* host, const char* service, + const addrinfo_type* hintsp, addrinfo_type** result) +{ + // Set up linked list of addrinfo structures. + addrinfo_type* aihead = 0; + addrinfo_type** ainext = &aihead; + char* canon = 0; + + // Supply default hints if not specified by caller. + addrinfo_type hints = addrinfo_type(); + hints.ai_family = ASIO_OS_DEF(AF_UNSPEC); + if (hintsp) + hints = *hintsp; + + // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED + // and AI_ALL flags. +#if defined(AI_V4MAPPED) + if (hints.ai_family != ASIO_OS_DEF(AF_INET6)) + hints.ai_flags &= ~AI_V4MAPPED; +#endif +#if defined(AI_ALL) + if (hints.ai_family != ASIO_OS_DEF(AF_INET6)) + hints.ai_flags &= ~AI_ALL; +#endif + + // Basic error checking. + int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family, + hints.ai_socktype, hints.ai_protocol); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + + gai_search search[2]; + int search_count = gai_nsearch(host, &hints, search); + for (gai_search* sptr = search; sptr < search + search_count; ++sptr) + { + // Check for IPv4 dotted decimal string. + in4_addr_type inaddr; + asio::error_code ec; + if (socket_ops::inet_pton(ASIO_OS_DEF(AF_INET), + sptr->host, &inaddr, 0, ec) == 1) + { + if (hints.ai_family != ASIO_OS_DEF(AF_UNSPEC) + && hints.ai_family != ASIO_OS_DEF(AF_INET)) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == ASIO_OS_DEF(AF_INET)) + { + rc = gai_aistruct(&ainext, &hints, &inaddr, ASIO_OS_DEF(AF_INET)); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Check for IPv6 hex string. + in6_addr_type in6addr; + if (socket_ops::inet_pton(ASIO_OS_DEF(AF_INET6), + sptr->host, &in6addr, 0, ec) == 1) + { + if (hints.ai_family != ASIO_OS_DEF(AF_UNSPEC) + && hints.ai_family != ASIO_OS_DEF(AF_INET6)) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == ASIO_OS_DEF(AF_INET6)) + { + rc = gai_aistruct(&ainext, &hints, &in6addr, + ASIO_OS_DEF(AF_INET6)); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Look up hostname. + hostent hent; + char hbuf[8192] = ""; + hostent* hptr = socket_ops::gethostbyname(sptr->host, + sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec); + if (hptr == 0) + { + if (search_count == 2) + { + // Failure is OK if there are multiple searches. + continue; + } + freeaddrinfo_emulation(aihead); + gai_free(canon); + if (ec == asio::error::host_not_found) + return EAI_NONAME; + if (ec == asio::error::host_not_found_try_again) + return EAI_AGAIN; + if (ec == asio::error::no_recovery) + return EAI_FAIL; + if (ec == asio::error::no_data) + return EAI_NONAME; + return EAI_NONAME; + } + + // Check for address family mismatch if one was specified. + if (hints.ai_family != ASIO_OS_DEF(AF_UNSPEC) + && hints.ai_family != hptr->h_addrtype) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + + // Save canonical name first time. + if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0] + && (hints.ai_flags & AI_CANONNAME) && canon == 0) + { + std::size_t canon_len = strlen(hptr->h_name) + 1; + canon = gai_alloc(canon_len); + if (canon == 0) + { + freeaddrinfo_emulation(aihead); + socket_ops::freehostent(hptr); + return EAI_MEMORY; + } + gai_strcpy(canon, hptr->h_name, canon_len); + } + + // Create an addrinfo structure for each returned address. + for (char** ap = hptr->h_addr_list; *ap; ++ap) + { + rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + } + + socket_ops::freehostent(hptr); + } + + // Check if we found anything. + if (aihead == 0) + { + gai_free(canon); + return EAI_NONAME; + } + + // Return canonical name in first entry. + if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME)) + { + if (canon) + { + aihead->ai_canonname = canon; + canon = 0; + } + else + { + std::size_t canonname_len = strlen(search[0].host) + 1; + aihead->ai_canonname = gai_alloc(canonname_len); + if (aihead->ai_canonname == 0) + { + freeaddrinfo_emulation(aihead); + return EAI_MEMORY; + } + gai_strcpy(aihead->ai_canonname, search[0].host, canonname_len); + } + } + gai_free(canon); + + // Process the service name. + if (service != 0 && service[0] != '\0') + { + rc = gai_serv(aihead, &hints, service); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + } + + // Return result to caller. + *result = aihead; + return 0; +} + +inline asio::error_code getnameinfo_emulation( + const socket_addr_type* sa, std::size_t salen, char* host, + std::size_t hostlen, char* serv, std::size_t servlen, int flags, + asio::error_code& ec) +{ + using namespace std; + + const char* addr; + size_t addr_len; + unsigned short port; + switch (sa->sa_family) + { + case ASIO_OS_DEF(AF_INET): + if (salen != sizeof(sockaddr_in4_type)) + { + return ec = asio::error::invalid_argument; + } + addr = reinterpret_cast( + &reinterpret_cast(sa)->sin_addr); + addr_len = sizeof(in4_addr_type); + port = reinterpret_cast(sa)->sin_port; + break; + case ASIO_OS_DEF(AF_INET6): + if (salen != sizeof(sockaddr_in6_type)) + { + return ec = asio::error::invalid_argument; + } + addr = reinterpret_cast( + &reinterpret_cast(sa)->sin6_addr); + addr_len = sizeof(in6_addr_type); + port = reinterpret_cast(sa)->sin6_port; + break; + default: + return ec = asio::error::address_family_not_supported; + } + + if (host && hostlen > 0) + { + if (flags & NI_NUMERICHOST) + { + if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0) + { + return ec; + } + } + else + { + hostent hent; + char hbuf[8192] = ""; + hostent* hptr = socket_ops::gethostbyaddr(addr, + static_cast(addr_len), sa->sa_family, + &hent, hbuf, sizeof(hbuf), ec); + if (hptr && hptr->h_name && hptr->h_name[0] != '\0') + { + if (flags & NI_NOFQDN) + { + char* dot = strchr(hptr->h_name, '.'); + if (dot) + { + *dot = 0; + } + } + gai_strcpy(host, hptr->h_name, hostlen); + socket_ops::freehostent(hptr); + } + else + { + socket_ops::freehostent(hptr); + if (flags & NI_NAMEREQD) + { + return ec = asio::error::host_not_found; + } + if (socket_ops::inet_ntop(sa->sa_family, + addr, host, hostlen, 0, ec) == 0) + { + return ec; + } + } + } + } + + if (serv && servlen > 0) + { + if (flags & NI_NUMERICSERV) + { + if (servlen < 6) + { + return ec = asio::error::no_buffer_space; + } +#if defined(ASIO_HAS_SECURE_RTL) + sprintf_s(serv, servlen, "%u", ntohs(port)); +#else // defined(ASIO_HAS_SECURE_RTL) + sprintf(serv, "%u", ntohs(port)); +#endif // defined(ASIO_HAS_SECURE_RTL) + } + else + { +#if defined(ASIO_HAS_PTHREADS) + static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + ::pthread_mutex_lock(&mutex); +#endif // defined(ASIO_HAS_PTHREADS) + servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0); + if (sptr && sptr->s_name && sptr->s_name[0] != '\0') + { + gai_strcpy(serv, sptr->s_name, servlen); + } + else + { + if (servlen < 6) + { + return ec = asio::error::no_buffer_space; + } +#if defined(ASIO_HAS_SECURE_RTL) + sprintf_s(serv, servlen, "%u", ntohs(port)); +#else // defined(ASIO_HAS_SECURE_RTL) + sprintf(serv, "%u", ntohs(port)); +#endif // defined(ASIO_HAS_SECURE_RTL) + } +#if defined(ASIO_HAS_PTHREADS) + ::pthread_mutex_unlock(&mutex); +#endif // defined(ASIO_HAS_PTHREADS) + } + } + + ec.assign(0, ec.category()); + return ec; +} + +#endif // !defined(ASIO_HAS_GETADDRINFO) + +inline asio::error_code translate_addrinfo_error(int error) +{ + switch (error) + { + case 0: + return asio::error_code(); + case EAI_AGAIN: + return asio::error::host_not_found_try_again; + case EAI_BADFLAGS: + return asio::error::invalid_argument; + case EAI_FAIL: + return asio::error::no_recovery; + case EAI_FAMILY: + return asio::error::address_family_not_supported; + case EAI_MEMORY: + return asio::error::no_memory; + case EAI_NONAME: +#if defined(EAI_ADDRFAMILY) + case EAI_ADDRFAMILY: +#endif +#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) + case EAI_NODATA: +#endif + return asio::error::host_not_found; + case EAI_SERVICE: + return asio::error::service_not_found; + case EAI_SOCKTYPE: + return asio::error::socket_type_not_supported; + default: // Possibly the non-portable EAI_SYSTEM. +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + return asio::error_code( + WSAGetLastError(), asio::error::get_system_category()); +#else + return asio::error_code( + errno, asio::error::get_system_category()); +#endif + } +} + +asio::error_code getaddrinfo(const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec) +{ + host = (host && *host) ? host : 0; + service = (service && *service) ? service : 0; + clear_last_error(); +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(ASIO_HAS_GETADDRINFO) + // Building for Windows XP, Windows Server 2003, or later. + int error = ::getaddrinfo(host, service, &hints, result); + return ec = translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gai_t)(const char*, + const char*, const addrinfo_type*, addrinfo_type**); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) + { + int error = gai(host, service, &hints, result); + return ec = translate_addrinfo_error(error); + } + } + int error = getaddrinfo_emulation(host, service, &hints, result); + return ec = translate_addrinfo_error(error); +# endif +#elif !defined(ASIO_HAS_GETADDRINFO) + int error = getaddrinfo_emulation(host, service, &hints, result); + return ec = translate_addrinfo_error(error); +#else + int error = ::getaddrinfo(host, service, &hints, result); +#if defined(__MACH__) && defined(__APPLE__) + using namespace std; // For isdigit and atoi. + if (error == 0 && service && isdigit(static_cast(service[0]))) + { + u_short_type port = host_to_network_short(atoi(service)); + for (addrinfo_type* ai = *result; ai; ai = ai->ai_next) + { + switch (ai->ai_family) + { + case ASIO_OS_DEF(AF_INET): + { + sockaddr_in4_type* sinptr = + reinterpret_cast(ai->ai_addr); + if (sinptr->sin_port == 0) + sinptr->sin_port = port; + break; + } + case ASIO_OS_DEF(AF_INET6): + { + sockaddr_in6_type* sin6ptr = + reinterpret_cast(ai->ai_addr); + if (sin6ptr->sin6_port == 0) + sin6ptr->sin6_port = port; + break; + } + default: + break; + } + } + } +#endif + return ec = translate_addrinfo_error(error); +#endif +} + +asio::error_code background_getaddrinfo( + const weak_cancel_token_type& cancel_token, const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec) +{ + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + socket_ops::getaddrinfo(host, service, hints, result, ec); + return ec; +} + +void freeaddrinfo(addrinfo_type* ai) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(ASIO_HAS_GETADDRINFO) + // Building for Windows XP, Windows Server 2003, or later. + ::freeaddrinfo(ai); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *fai_t)(addrinfo_type*); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) + { + fai(ai); + return; + } + } + freeaddrinfo_emulation(ai); +# endif +#elif !defined(ASIO_HAS_GETADDRINFO) + freeaddrinfo_emulation(ai); +#else + ::freeaddrinfo(ai); +#endif +} + +asio::error_code getnameinfo(const socket_addr_type* addr, + std::size_t addrlen, char* host, std::size_t hostlen, + char* serv, std::size_t servlen, int flags, asio::error_code& ec) +{ +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(ASIO_HAS_GETADDRINFO) + // Building for Windows XP, Windows Server 2003, or later. + clear_last_error(); + int error = ::getnameinfo(addr, static_cast(addrlen), + host, static_cast(hostlen), + serv, static_cast(servlen), flags); + return ec = translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gni_t)(const socket_addr_type*, + int, char*, DWORD, char*, DWORD, int); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo")) + { + clear_last_error(); + int error = gni(addr, static_cast(addrlen), + host, static_cast(hostlen), + serv, static_cast(servlen), flags); + return ec = translate_addrinfo_error(error); + } + } + clear_last_error(); + return getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags, ec); +# endif +#elif !defined(ASIO_HAS_GETADDRINFO) + using namespace std; // For memcpy. + sockaddr_storage_type tmp_addr; + memcpy(&tmp_addr, addr, addrlen); + addr = reinterpret_cast(&tmp_addr); + clear_last_error(); + return getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags, ec); +#else + clear_last_error(); + int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); + return ec = translate_addrinfo_error(error); +#endif +} + +asio::error_code sync_getnameinfo( + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec) +{ + // First try resolving with the service name. If that fails try resolving + // but allow the service to be returned as a number. + int flags = (sock_type == SOCK_DGRAM) ? NI_DGRAM : 0; + socket_ops::getnameinfo(addr, addrlen, host, + hostlen, serv, servlen, flags, ec); + if (ec) + { + socket_ops::getnameinfo(addr, addrlen, host, hostlen, + serv, servlen, flags | NI_NUMERICSERV, ec); + } + + return ec; +} + +asio::error_code background_getnameinfo( + const weak_cancel_token_type& cancel_token, + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec) +{ + if (cancel_token.expired()) + { + ec = asio::error::operation_aborted; + } + else + { + // First try resolving with the service name. If that fails try resolving + // but allow the service to be returned as a number. + int flags = (sock_type == SOCK_DGRAM) ? NI_DGRAM : 0; + socket_ops::getnameinfo(addr, addrlen, host, + hostlen, serv, servlen, flags, ec); + if (ec) + { + socket_ops::getnameinfo(addr, addrlen, host, hostlen, + serv, servlen, flags | NI_NUMERICSERV, ec); + } + } + + return ec; +} + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +u_long_type network_to_host_long(u_long_type value) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + unsigned char* value_p = reinterpret_cast(&value); + u_long_type result = (static_cast(value_p[0]) << 24) + | (static_cast(value_p[1]) << 16) + | (static_cast(value_p[2]) << 8) + | static_cast(value_p[3]); + return result; +#else // defined(ASIO_WINDOWS_RUNTIME) + return ntohl(value); +#endif // defined(ASIO_WINDOWS_RUNTIME) +} + +u_long_type host_to_network_long(u_long_type value) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + u_long_type result; + unsigned char* result_p = reinterpret_cast(&result); + result_p[0] = static_cast((value >> 24) & 0xFF); + result_p[1] = static_cast((value >> 16) & 0xFF); + result_p[2] = static_cast((value >> 8) & 0xFF); + result_p[3] = static_cast(value & 0xFF); + return result; +#else // defined(ASIO_WINDOWS_RUNTIME) + return htonl(value); +#endif // defined(ASIO_WINDOWS_RUNTIME) +} + +u_short_type network_to_host_short(u_short_type value) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + unsigned char* value_p = reinterpret_cast(&value); + u_short_type result = (static_cast(value_p[0]) << 8) + | static_cast(value_p[1]); + return result; +#else // defined(ASIO_WINDOWS_RUNTIME) + return ntohs(value); +#endif // defined(ASIO_WINDOWS_RUNTIME) +} + +u_short_type host_to_network_short(u_short_type value) +{ +#if defined(ASIO_WINDOWS_RUNTIME) + u_short_type result; + unsigned char* result_p = reinterpret_cast(&result); + result_p[0] = static_cast((value >> 8) & 0xFF); + result_p[1] = static_cast(value & 0xFF); + return result; +#else // defined(ASIO_WINDOWS_RUNTIME) + return htons(value); +#endif // defined(ASIO_WINDOWS_RUNTIME) +} + +} // namespace socket_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPS_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/socket_select_interrupter.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/socket_select_interrupter.ipp new file mode 100644 index 000000000..81813db0b --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/socket_select_interrupter.ipp @@ -0,0 +1,185 @@ +// +// detail/impl/socket_select_interrupter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP +#define ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + +#include +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_select_interrupter.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +socket_select_interrupter::socket_select_interrupter() +{ + open_descriptors(); +} + +void socket_select_interrupter::open_descriptors() +{ + asio::error_code ec; + socket_holder acceptor(socket_ops::socket( + AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); + if (acceptor.get() == invalid_socket) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + int opt = 1; + socket_ops::state_type acceptor_state = 0; + socket_ops::setsockopt(acceptor.get(), acceptor_state, + SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec); + + using namespace std; // For memset. + sockaddr_in4_type addr; + std::size_t addr_len = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = socket_ops::host_to_network_long(INADDR_LOOPBACK); + addr.sin_port = 0; + if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr, + addr_len, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr, + &addr_len, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + // Some broken firewalls on Windows will intermittently cause getsockname to + // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We + // explicitly specify the target address here to work around this problem. + if (addr.sin_addr.s_addr == socket_ops::host_to_network_long(INADDR_ANY)) + addr.sin_addr.s_addr = socket_ops::host_to_network_long(INADDR_LOOPBACK); + + if (socket_ops::listen(acceptor.get(), + SOMAXCONN, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + socket_holder client(socket_ops::socket( + AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); + if (client.get() == invalid_socket) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr, + addr_len, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec)); + if (server.get() == invalid_socket) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + ioctl_arg_type non_blocking = 1; + socket_ops::state_type client_state = 0; + if (socket_ops::ioctl(client.get(), client_state, + FIONBIO, &non_blocking, ec)) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + opt = 1; + socket_ops::setsockopt(client.get(), client_state, + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); + + non_blocking = 1; + socket_ops::state_type server_state = 0; + if (socket_ops::ioctl(server.get(), server_state, + FIONBIO, &non_blocking, ec)) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + opt = 1; + socket_ops::setsockopt(server.get(), server_state, + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); + + read_descriptor_ = server.release(); + write_descriptor_ = client.release(); +} + +socket_select_interrupter::~socket_select_interrupter() +{ + close_descriptors(); +} + +void socket_select_interrupter::close_descriptors() +{ + asio::error_code ec; + socket_ops::state_type state = socket_ops::internal_non_blocking; + if (read_descriptor_ != invalid_socket) + socket_ops::close(read_descriptor_, state, true, ec); + if (write_descriptor_ != invalid_socket) + socket_ops::close(write_descriptor_, state, true, ec); +} + +void socket_select_interrupter::recreate() +{ + close_descriptors(); + + write_descriptor_ = invalid_socket; + read_descriptor_ = invalid_socket; + + open_descriptors(); +} + +void socket_select_interrupter::interrupt() +{ + char byte = 0; + socket_ops::buf b; + socket_ops::init_buf(b, &byte, 1); + asio::error_code ec; + socket_ops::send(write_descriptor_, &b, 1, 0, ec); +} + +bool socket_select_interrupter::reset() +{ + char data[1024]; + socket_ops::buf b; + socket_ops::init_buf(b, data, sizeof(data)); + asio::error_code ec; + for (;;) + { + int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); + if (bytes_read == sizeof(data)) + continue; + if (bytes_read > 0) + return true; + if (bytes_read == 0) + return false; + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return true; + return false; + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/strand_executor_service.hpp b/third_party/asio/1.18.2/include/asio/detail/impl/strand_executor_service.hpp new file mode 100644 index 000000000..bf2555c37 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/strand_executor_service.hpp @@ -0,0 +1,352 @@ +// +// detail/impl/strand_executor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP +#define ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/recycling_allocator.hpp" +#include "asio/executor_work_guard.hpp" +#include "asio/defer.hpp" +#include "asio/dispatch.hpp" +#include "asio/post.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class strand_executor_service::allocator_binder +{ +public: + typedef Allocator allocator_type; + + allocator_binder(ASIO_MOVE_ARG(F) f, const Allocator& a) + : f_(ASIO_MOVE_CAST(F)(f)), + allocator_(a) + { + } + + allocator_binder(const allocator_binder& other) + : f_(other.f_), + allocator_(other.allocator_) + { + } + +#if defined(ASIO_HAS_MOVE) + allocator_binder(allocator_binder&& other) + : f_(ASIO_MOVE_CAST(F)(other.f_)), + allocator_(ASIO_MOVE_CAST(allocator_type)(other.allocator_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + allocator_type get_allocator() const ASIO_NOEXCEPT + { + return allocator_; + } + + void operator()() + { + f_(); + } + +private: + F f_; + allocator_type allocator_; +}; + +template +class strand_executor_service::invoker::value + >::type> +{ +public: + invoker(const implementation_type& impl, Executor& ex) + : impl_(impl), + executor_(asio::prefer(ex, execution::outstanding_work.tracked)) + { + } + + invoker(const invoker& other) + : impl_(other.impl_), + executor_(other.executor_) + { + } + +#if defined(ASIO_HAS_MOVE) + invoker(invoker&& other) + : impl_(ASIO_MOVE_CAST(implementation_type)(other.impl_)), + executor_(ASIO_MOVE_CAST(executor_type)(other.executor_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + struct on_invoker_exit + { + invoker* this_; + + ~on_invoker_exit() + { + if (push_waiting_to_ready(this_->impl_)) + { + recycling_allocator allocator; + execution::execute( + asio::prefer( + asio::require(this_->executor_, + execution::blocking.never), + execution::allocator(allocator)), + ASIO_MOVE_CAST(invoker)(*this_)); + } + } + }; + + void operator()() + { + // Ensure the next handler, if any, is scheduled on block exit. + on_invoker_exit on_exit = { this }; + (void)on_exit; + + run_ready_handlers(impl_); + } + +private: + typedef typename decay< + typename prefer_result< + Executor, + execution::outstanding_work_t::tracked_t + >::type + >::type executor_type; + + implementation_type impl_; + executor_type executor_; +}; + +#if !defined(ASIO_NO_TS_EXECUTORS) + +template +class strand_executor_service::invoker::value + >::type> +{ +public: + invoker(const implementation_type& impl, Executor& ex) + : impl_(impl), + work_(ex) + { + } + + invoker(const invoker& other) + : impl_(other.impl_), + work_(other.work_) + { + } + +#if defined(ASIO_HAS_MOVE) + invoker(invoker&& other) + : impl_(ASIO_MOVE_CAST(implementation_type)(other.impl_)), + work_(ASIO_MOVE_CAST(executor_work_guard)(other.work_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + struct on_invoker_exit + { + invoker* this_; + + ~on_invoker_exit() + { + if (push_waiting_to_ready(this_->impl_)) + { + Executor ex(this_->work_.get_executor()); + recycling_allocator allocator; + ex.post(ASIO_MOVE_CAST(invoker)(*this_), allocator); + } + } + }; + + void operator()() + { + // Ensure the next handler, if any, is scheduled on block exit. + on_invoker_exit on_exit = { this }; + (void)on_exit; + + run_ready_handlers(impl_); + } + +private: + implementation_type impl_; + executor_work_guard work_; +}; + +#endif // !defined(ASIO_NO_TS_EXECUTORS) + +template +inline void strand_executor_service::execute(const implementation_type& impl, + Executor& ex, ASIO_MOVE_ARG(Function) function, + typename enable_if< + can_query >::value + >::type*) +{ + return strand_executor_service::do_execute(impl, ex, + ASIO_MOVE_CAST(Function)(function), + asio::query(ex, execution::allocator)); +} + +template +inline void strand_executor_service::execute(const implementation_type& impl, + Executor& ex, ASIO_MOVE_ARG(Function) function, + typename enable_if< + !can_query >::value + >::type*) +{ + return strand_executor_service::do_execute(impl, ex, + ASIO_MOVE_CAST(Function)(function), + std::allocator()); +} + +template +void strand_executor_service::do_execute(const implementation_type& impl, + Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a) +{ + typedef typename decay::type function_type; + + // If the executor is not never-blocking, and we are already in the strand, + // then the function can run immediately. + if (asio::query(ex, execution::blocking) != execution::blocking.never + && running_in_this_thread(impl)) + { + // Make a local, non-const copy of the function. + function_type tmp(ASIO_MOVE_CAST(Function)(function)); + + fenced_block b(fenced_block::full); + asio_handler_invoke_helpers::invoke(tmp, tmp); + return; + } + + // Allocate and construct an operation to wrap the function. + typedef executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a); + + ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, + "strand_executor", impl.get(), 0, "execute")); + + // Add the function to the strand and schedule the strand if required. + bool first = enqueue(impl, p.p); + p.v = p.p = 0; + if (first) + { + execution::execute(ex, invoker(impl, ex)); + } +} + +template +void strand_executor_service::dispatch(const implementation_type& impl, + Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a) +{ + typedef typename decay::type function_type; + + // If we are already in the strand then the function can run immediately. + if (running_in_this_thread(impl)) + { + // Make a local, non-const copy of the function. + function_type tmp(ASIO_MOVE_CAST(Function)(function)); + + fenced_block b(fenced_block::full); + asio_handler_invoke_helpers::invoke(tmp, tmp); + return; + } + + // Allocate and construct an operation to wrap the function. + typedef executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a); + + ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, + "strand_executor", impl.get(), 0, "dispatch")); + + // Add the function to the strand and schedule the strand if required. + bool first = enqueue(impl, p.p); + p.v = p.p = 0; + if (first) + { + asio::dispatch(ex, + allocator_binder, Allocator>( + invoker(impl, ex), a)); + } +} + +// Request invocation of the given function and return immediately. +template +void strand_executor_service::post(const implementation_type& impl, + Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a) +{ + typedef typename decay::type function_type; + + // Allocate and construct an operation to wrap the function. + typedef executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a); + + ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, + "strand_executor", impl.get(), 0, "post")); + + // Add the function to the strand and schedule the strand if required. + bool first = enqueue(impl, p.p); + p.v = p.p = 0; + if (first) + { + asio::post(ex, + allocator_binder, Allocator>( + invoker(impl, ex), a)); + } +} + +// Request invocation of the given function and return immediately. +template +void strand_executor_service::defer(const implementation_type& impl, + Executor& ex, ASIO_MOVE_ARG(Function) function, const Allocator& a) +{ + typedef typename decay::type function_type; + + // Allocate and construct an operation to wrap the function. + typedef executor_op op; + typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; + p.p = new (p.v) op(ASIO_MOVE_CAST(Function)(function), a); + + ASIO_HANDLER_CREATION((impl->service_->context(), *p.p, + "strand_executor", impl.get(), 0, "defer")); + + // Add the function to the strand and schedule the strand if required. + bool first = enqueue(impl, p.p); + p.v = p.p = 0; + if (first) + { + asio::defer(ex, + allocator_binder, Allocator>( + invoker(impl, ex), a)); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/strand_executor_service.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/strand_executor_service.ipp new file mode 100644 index 000000000..c97b6df5a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/strand_executor_service.ipp @@ -0,0 +1,158 @@ +// +// detail/impl/strand_executor_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_IPP +#define ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/strand_executor_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +strand_executor_service::strand_executor_service(execution_context& ctx) + : execution_context_service_base(ctx), + mutex_(), + salt_(0), + impl_list_(0) +{ +} + +void strand_executor_service::shutdown() +{ + op_queue ops; + + asio::detail::mutex::scoped_lock lock(mutex_); + + strand_impl* impl = impl_list_; + while (impl) + { + impl->mutex_->lock(); + impl->shutdown_ = true; + ops.push(impl->waiting_queue_); + ops.push(impl->ready_queue_); + impl->mutex_->unlock(); + impl = impl->next_; + } +} + +strand_executor_service::implementation_type +strand_executor_service::create_implementation() +{ + implementation_type new_impl(new strand_impl); + new_impl->locked_ = false; + new_impl->shutdown_ = false; + + asio::detail::mutex::scoped_lock lock(mutex_); + + // Select a mutex from the pool of shared mutexes. + std::size_t salt = salt_++; + std::size_t mutex_index = reinterpret_cast(new_impl.get()); + mutex_index += (reinterpret_cast(new_impl.get()) >> 3); + mutex_index ^= salt + 0x9e3779b9 + (mutex_index << 6) + (mutex_index >> 2); + mutex_index = mutex_index % num_mutexes; + if (!mutexes_[mutex_index].get()) + mutexes_[mutex_index].reset(new mutex); + new_impl->mutex_ = mutexes_[mutex_index].get(); + + // Insert implementation into linked list of all implementations. + new_impl->next_ = impl_list_; + new_impl->prev_ = 0; + if (impl_list_) + impl_list_->prev_ = new_impl.get(); + impl_list_ = new_impl.get(); + new_impl->service_ = this; + + return new_impl; +} + +strand_executor_service::strand_impl::~strand_impl() +{ + asio::detail::mutex::scoped_lock lock(service_->mutex_); + + // Remove implementation from linked list of all implementations. + if (service_->impl_list_ == this) + service_->impl_list_ = next_; + if (prev_) + prev_->next_ = next_; + if (next_) + next_->prev_= prev_; +} + +bool strand_executor_service::enqueue(const implementation_type& impl, + scheduler_operation* op) +{ + impl->mutex_->lock(); + if (impl->shutdown_) + { + impl->mutex_->unlock(); + op->destroy(); + return false; + } + else if (impl->locked_) + { + // Some other function already holds the strand lock. Enqueue for later. + impl->waiting_queue_.push(op); + impl->mutex_->unlock(); + return false; + } + else + { + // The function is acquiring the strand lock and so is responsible for + // scheduling the strand. + impl->locked_ = true; + impl->mutex_->unlock(); + impl->ready_queue_.push(op); + return true; + } +} + +bool strand_executor_service::running_in_this_thread( + const implementation_type& impl) +{ + return !!call_stack::contains(impl.get()); +} + +bool strand_executor_service::push_waiting_to_ready(implementation_type& impl) +{ + impl->mutex_->lock(); + impl->ready_queue_.push(impl->waiting_queue_); + bool more_handlers = impl->locked_ = !impl->ready_queue_.empty(); + impl->mutex_->unlock(); + return more_handlers; +} + +void strand_executor_service::run_ready_handlers(implementation_type& impl) +{ + // Indicate that this strand is executing on the current thread. + call_stack::context ctx(impl.get()); + + // Run all ready handlers. No lock is required since the ready queue is + // accessed only within the strand. + asio::error_code ec; + while (scheduler_operation* o = impl->ready_queue_.front()) + { + impl->ready_queue_.pop(); + o->complete(impl.get(), ec, 0); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/strand_service.hpp b/third_party/asio/1.18.2/include/asio/detail/impl/strand_service.hpp new file mode 100644 index 000000000..3e395d6c9 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/strand_service.hpp @@ -0,0 +1,87 @@ +// +// detail/impl/strand_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP +#define ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +inline strand_service::strand_impl::strand_impl() + : operation(&strand_service::do_complete), + locked_(false) +{ +} + +template +void strand_service::dispatch(strand_service::implementation_type& impl, + Handler& handler) +{ + // If we are already in the strand then the handler can run immediately. + if (running_in_this_thread(impl)) + { + fenced_block b(fenced_block::full); + asio_handler_invoke_helpers::invoke(handler, handler); + return; + } + + // Allocate and construct an operation to wrap the handler. + typedef completion_handler op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler, io_context_.get_executor()); + + ASIO_HANDLER_CREATION((this->context(), + *p.p, "strand", impl, 0, "dispatch")); + + operation* o = p.p; + p.v = p.p = 0; + do_dispatch(impl, o); +} + +// Request the io_context to invoke the given handler and return immediately. +template +void strand_service::post(strand_service::implementation_type& impl, + Handler& handler) +{ + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef completion_handler op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler, io_context_.get_executor()); + + ASIO_HANDLER_CREATION((this->context(), + *p.p, "strand", impl, 0, "post")); + + do_post(impl, p.p, is_continuation); + p.v = p.p = 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/strand_service.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/strand_service.ipp new file mode 100644 index 000000000..58d657c98 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/strand_service.ipp @@ -0,0 +1,202 @@ +// +// detail/impl/strand_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP +#define ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/strand_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct strand_service::on_do_complete_exit +{ + io_context_impl* owner_; + strand_impl* impl_; + + ~on_do_complete_exit() + { + impl_->mutex_.lock(); + impl_->ready_queue_.push(impl_->waiting_queue_); + bool more_handlers = impl_->locked_ = !impl_->ready_queue_.empty(); + impl_->mutex_.unlock(); + + if (more_handlers) + owner_->post_immediate_completion(impl_, true); + } +}; + +strand_service::strand_service(asio::io_context& io_context) + : asio::detail::service_base(io_context), + io_context_(io_context), + io_context_impl_(asio::use_service(io_context)), + mutex_(), + salt_(0) +{ +} + +void strand_service::shutdown() +{ + op_queue ops; + + asio::detail::mutex::scoped_lock lock(mutex_); + + for (std::size_t i = 0; i < num_implementations; ++i) + { + if (strand_impl* impl = implementations_[i].get()) + { + ops.push(impl->waiting_queue_); + ops.push(impl->ready_queue_); + } + } +} + +void strand_service::construct(strand_service::implementation_type& impl) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + std::size_t salt = salt_++; +#if defined(ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) + std::size_t index = salt; +#else // defined(ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) + std::size_t index = reinterpret_cast(&impl); + index += (reinterpret_cast(&impl) >> 3); + index ^= salt + 0x9e3779b9 + (index << 6) + (index >> 2); +#endif // defined(ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) + index = index % num_implementations; + + if (!implementations_[index].get()) + implementations_[index].reset(new strand_impl); + impl = implementations_[index].get(); +} + +bool strand_service::running_in_this_thread( + const implementation_type& impl) const +{ + return call_stack::contains(impl) != 0; +} + +struct strand_service::on_dispatch_exit +{ + io_context_impl* io_context_impl_; + strand_impl* impl_; + + ~on_dispatch_exit() + { + impl_->mutex_.lock(); + impl_->ready_queue_.push(impl_->waiting_queue_); + bool more_handlers = impl_->locked_ = !impl_->ready_queue_.empty(); + impl_->mutex_.unlock(); + + if (more_handlers) + io_context_impl_->post_immediate_completion(impl_, false); + } +}; + +void strand_service::do_dispatch(implementation_type& impl, operation* op) +{ + // If we are running inside the io_context, and no other handler already + // holds the strand lock, then the handler can run immediately. + bool can_dispatch = io_context_impl_.can_dispatch(); + impl->mutex_.lock(); + if (can_dispatch && !impl->locked_) + { + // Immediate invocation is allowed. + impl->locked_ = true; + impl->mutex_.unlock(); + + // Indicate that this strand is executing on the current thread. + call_stack::context ctx(impl); + + // Ensure the next handler, if any, is scheduled on block exit. + on_dispatch_exit on_exit = { &io_context_impl_, impl }; + (void)on_exit; + + op->complete(&io_context_impl_, asio::error_code(), 0); + return; + } + + if (impl->locked_) + { + // Some other handler already holds the strand lock. Enqueue for later. + impl->waiting_queue_.push(op); + impl->mutex_.unlock(); + } + else + { + // The handler is acquiring the strand lock and so is responsible for + // scheduling the strand. + impl->locked_ = true; + impl->mutex_.unlock(); + impl->ready_queue_.push(op); + io_context_impl_.post_immediate_completion(impl, false); + } +} + +void strand_service::do_post(implementation_type& impl, + operation* op, bool is_continuation) +{ + impl->mutex_.lock(); + if (impl->locked_) + { + // Some other handler already holds the strand lock. Enqueue for later. + impl->waiting_queue_.push(op); + impl->mutex_.unlock(); + } + else + { + // The handler is acquiring the strand lock and so is responsible for + // scheduling the strand. + impl->locked_ = true; + impl->mutex_.unlock(); + impl->ready_queue_.push(op); + io_context_impl_.post_immediate_completion(impl, is_continuation); + } +} + +void strand_service::do_complete(void* owner, operation* base, + const asio::error_code& ec, std::size_t /*bytes_transferred*/) +{ + if (owner) + { + strand_impl* impl = static_cast(base); + + // Indicate that this strand is executing on the current thread. + call_stack::context ctx(impl); + + // Ensure the next handler, if any, is scheduled on block exit. + on_do_complete_exit on_exit; + on_exit.owner_ = static_cast(owner); + on_exit.impl_ = impl; + + // Run all ready handlers. No lock is required since the ready queue is + // accessed only within the strand. + while (operation* o = impl->ready_queue_.front()) + { + impl->ready_queue_.pop(); + o->complete(owner, ec, 0); + } + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/thread_context.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/thread_context.ipp new file mode 100644 index 000000000..65cc275df --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/thread_context.ipp @@ -0,0 +1,35 @@ +// +// detail/impl/thread_context.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_THREAD_CONTEXT_IPP +#define ASIO_DETAIL_IMPL_THREAD_CONTEXT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +thread_info_base* thread_context::top_of_thread_call_stack() +{ + return thread_call_stack::top(); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_THREAD_CONTEXT_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/throw_error.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/throw_error.ipp new file mode 100644 index 000000000..c22cefc1f --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/throw_error.ipp @@ -0,0 +1,60 @@ +// +// detail/impl/throw_error.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_THROW_ERROR_IPP +#define ASIO_DETAIL_IMPL_THROW_ERROR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/system_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void do_throw_error(const asio::error_code& err) +{ + asio::system_error e(err); + asio::detail::throw_exception(e); +} + +void do_throw_error(const asio::error_code& err, const char* location) +{ + // boostify: non-boost code starts here +#if defined(ASIO_MSVC) && defined(ASIO_HAS_STD_SYSTEM_ERROR) + // Microsoft's implementation of std::system_error is non-conformant in that + // it ignores the error code's message when a "what" string is supplied. We'll + // work around this by explicitly formatting the "what" string. + std::string what_msg = location; + what_msg += ": "; + what_msg += err.message(); + asio::system_error e(err, what_msg); + asio::detail::throw_exception(e); +#else // defined(ASIO_MSVC) && defined(ASIO_HAS_STD_SYSTEM_ERROR) + // boostify: non-boost code ends here + asio::system_error e(err, location); + asio::detail::throw_exception(e); + // boostify: non-boost code starts here +#endif // defined(ASIO_MSVC) && defined(ASIO_HAS_STD_SYSTEM_ERROR) + // boostify: non-boost code ends here +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_THROW_ERROR_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/timer_queue_ptime.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/timer_queue_ptime.ipp new file mode 100644 index 000000000..7873a9523 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/timer_queue_ptime.ipp @@ -0,0 +1,91 @@ +// +// detail/impl/timer_queue_ptime.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_TIMER_QUEUE_PTIME_IPP +#define ASIO_DETAIL_IMPL_TIMER_QUEUE_PTIME_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) + +#include "asio/detail/timer_queue_ptime.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +timer_queue >::timer_queue() +{ +} + +timer_queue >::~timer_queue() +{ +} + +bool timer_queue >::enqueue_timer( + const time_type& time, per_timer_data& timer, wait_op* op) +{ + return impl_.enqueue_timer(time, timer, op); +} + +bool timer_queue >::empty() const +{ + return impl_.empty(); +} + +long timer_queue >::wait_duration_msec( + long max_duration) const +{ + return impl_.wait_duration_msec(max_duration); +} + +long timer_queue >::wait_duration_usec( + long max_duration) const +{ + return impl_.wait_duration_usec(max_duration); +} + +void timer_queue >::get_ready_timers( + op_queue& ops) +{ + impl_.get_ready_timers(ops); +} + +void timer_queue >::get_all_timers( + op_queue& ops) +{ + impl_.get_all_timers(ops); +} + +std::size_t timer_queue >::cancel_timer( + per_timer_data& timer, op_queue& ops, std::size_t max_cancelled) +{ + return impl_.cancel_timer(timer, ops, max_cancelled); +} + +void timer_queue >::move_timer( + per_timer_data& target, per_timer_data& source) +{ + impl_.move_timer(target, source); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + +#endif // ASIO_DETAIL_IMPL_TIMER_QUEUE_PTIME_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/timer_queue_set.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/timer_queue_set.ipp new file mode 100644 index 000000000..04c62c3b5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/timer_queue_set.ipp @@ -0,0 +1,101 @@ +// +// detail/impl/timer_queue_set.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP +#define ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/timer_queue_set.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +timer_queue_set::timer_queue_set() + : first_(0) +{ +} + +void timer_queue_set::insert(timer_queue_base* q) +{ + q->next_ = first_; + first_ = q; +} + +void timer_queue_set::erase(timer_queue_base* q) +{ + if (first_) + { + if (q == first_) + { + first_ = q->next_; + q->next_ = 0; + return; + } + + for (timer_queue_base* p = first_; p->next_; p = p->next_) + { + if (p->next_ == q) + { + p->next_ = q->next_; + q->next_ = 0; + return; + } + } + } +} + +bool timer_queue_set::all_empty() const +{ + for (timer_queue_base* p = first_; p; p = p->next_) + if (!p->empty()) + return false; + return true; +} + +long timer_queue_set::wait_duration_msec(long max_duration) const +{ + long min_duration = max_duration; + for (timer_queue_base* p = first_; p; p = p->next_) + min_duration = p->wait_duration_msec(min_duration); + return min_duration; +} + +long timer_queue_set::wait_duration_usec(long max_duration) const +{ + long min_duration = max_duration; + for (timer_queue_base* p = first_; p; p = p->next_) + min_duration = p->wait_duration_usec(min_duration); + return min_duration; +} + +void timer_queue_set::get_ready_timers(op_queue& ops) +{ + for (timer_queue_base* p = first_; p; p = p->next_) + p->get_ready_timers(ops); +} + +void timer_queue_set::get_all_timers(op_queue& ops) +{ + for (timer_queue_base* p = first_; p; p = p->next_) + p->get_all_timers(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_event.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/win_event.ipp new file mode 100644 index 000000000..d442c4d0e --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_event.ipp @@ -0,0 +1,76 @@ +// +// detail/win_event.ipp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_EVENT_IPP +#define ASIO_DETAIL_IMPL_WIN_EVENT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_event.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_event::win_event() + : state_(0) +{ +#if defined(ASIO_WINDOWS_APP) + events_[0] = ::CreateEventExW(0, 0, + CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); +#else // defined(ASIO_WINDOWS_APP) + events_[0] = ::CreateEventW(0, true, false, 0); +#endif // defined(ASIO_WINDOWS_APP) + if (!events_[0]) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "event"); + } + +#if defined(ASIO_WINDOWS_APP) + events_[1] = ::CreateEventExW(0, 0, 0, EVENT_ALL_ACCESS); +#else // defined(ASIO_WINDOWS_APP) + events_[1] = ::CreateEventW(0, false, false, 0); +#endif // defined(ASIO_WINDOWS_APP) + if (!events_[1]) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(events_[0]); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "event"); + } +} + +win_event::~win_event() +{ + ::CloseHandle(events_[0]); + ::CloseHandle(events_[1]); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_EVENT_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_handle_service.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_handle_service.ipp new file mode 100644 index 000000000..7cefc1563 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_handle_service.ipp @@ -0,0 +1,525 @@ +// +// detail/impl/win_iocp_handle_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/win_iocp_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_handle_service::overlapped_wrapper + : public OVERLAPPED +{ +public: + explicit overlapped_wrapper(asio::error_code& ec) + { + Internal = 0; + InternalHigh = 0; + Offset = 0; + OffsetHigh = 0; + + // Create a non-signalled manual-reset event, for GetOverlappedResult. + hEvent = ::CreateEventW(0, TRUE, FALSE, 0); + if (hEvent) + { + // As documented in GetQueuedCompletionStatus, setting the low order + // bit of this event prevents our synchronous writes from being treated + // as completion port events. + DWORD_PTR tmp = reinterpret_cast(hEvent); + hEvent = reinterpret_cast(tmp | 1); + } + else + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + + ~overlapped_wrapper() + { + if (hEvent) + { + ::CloseHandle(hEvent); + } + } +}; + +win_iocp_handle_service::win_iocp_handle_service(execution_context& context) + : execution_context_service_base(context), + iocp_service_(asio::use_service(context)), + mutex_(), + impl_list_(0) +{ +} + +void win_iocp_handle_service::shutdown() +{ + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + implementation_type* impl = impl_list_; + while (impl) + { + close_for_destruction(*impl); + impl = impl->next_; + } +} + +void win_iocp_handle_service::construct( + win_iocp_handle_service::implementation_type& impl) +{ + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void win_iocp_handle_service::move_construct( + win_iocp_handle_service::implementation_type& impl, + win_iocp_handle_service::implementation_type& other_impl) +{ + impl.handle_ = other_impl.handle_; + other_impl.handle_ = INVALID_HANDLE_VALUE; + + impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_; + other_impl.safe_cancellation_thread_id_ = 0; + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void win_iocp_handle_service::move_assign( + win_iocp_handle_service::implementation_type& impl, + win_iocp_handle_service& other_service, + win_iocp_handle_service::implementation_type& other_impl) +{ + close_for_destruction(impl); + + if (this != &other_service) + { + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + impl.handle_ = other_impl.handle_; + other_impl.handle_ = INVALID_HANDLE_VALUE; + + impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_; + other_impl.safe_cancellation_thread_id_ = 0; + + if (this != &other_service) + { + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(other_service.mutex_); + impl.next_ = other_service.impl_list_; + impl.prev_ = 0; + if (other_service.impl_list_) + other_service.impl_list_->prev_ = &impl; + other_service.impl_list_ = &impl; + } +} + +void win_iocp_handle_service::destroy( + win_iocp_handle_service::implementation_type& impl) +{ + close_for_destruction(impl); + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; +} + +asio::error_code win_iocp_handle_service::assign( + win_iocp_handle_service::implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (iocp_service_.register_handle(handle, ec)) + return ec; + + impl.handle_ = handle; + ec = asio::error_code(); + return ec; +} + +asio::error_code win_iocp_handle_service::close( + win_iocp_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle", + &impl, reinterpret_cast(impl.handle_), "close")); + + if (!::CloseHandle(impl.handle_)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + } + else + { + ec = asio::error_code(); + } + + return ec; +} + +asio::error_code win_iocp_handle_service::cancel( + win_iocp_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle", + &impl, reinterpret_cast(impl.handle_), "cancel")); + + if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( + ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) + { + // The version of Windows supports cancellation from any thread. + typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); + cancel_io_ex_t cancel_io_ex = reinterpret_cast( + reinterpret_cast(cancel_io_ex_ptr)); + if (!cancel_io_ex(impl.handle_, 0)) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_NOT_FOUND) + { + // ERROR_NOT_FOUND means that there were no operations to be + // cancelled. We swallow this error to match the behaviour on other + // platforms. + ec = asio::error_code(); + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + else + { + ec = asio::error_code(); + } + } + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + ec = asio::error_code(); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + if (!::CancelIo(impl.handle_)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + ec = asio::error::operation_not_supported; + } + + return ec; +} + +size_t win_iocp_handle_service::do_write( + win_iocp_handle_service::implementation_type& impl, uint64_t offset, + const asio::const_buffer& buffer, asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes on a handle is a no-op. + if (buffer.size() == 0) + { + ec = asio::error_code(); + return 0; + } + + overlapped_wrapper overlapped(ec); + if (ec) + { + return 0; + } + + // Write the data. + overlapped.Offset = offset & 0xFFFFFFFF; + overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::WriteFile(impl.handle_, buffer.data(), + static_cast(buffer.size()), 0, &overlapped); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error != ERROR_IO_PENDING) + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + } + + // Wait for the operation to complete. + DWORD bytes_transferred = 0; + ok = ::GetOverlappedResult(impl.handle_, + &overlapped, &bytes_transferred, TRUE); + if (!ok) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; +} + +void win_iocp_handle_service::start_write_op( + win_iocp_handle_service::implementation_type& impl, uint64_t offset, + const asio::const_buffer& buffer, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + { + iocp_service_.on_completion(op, asio::error::bad_descriptor); + } + else if (buffer.size() == 0) + { + // A request to write 0 bytes on a handle is a no-op. + iocp_service_.on_completion(op); + } + else + { + DWORD bytes_transferred = 0; + op->Offset = offset & 0xFFFFFFFF; + op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::WriteFile(impl.handle_, buffer.data(), + static_cast(buffer.size()), + &bytes_transferred, op); + DWORD last_error = ::GetLastError(); + if (!ok && last_error != ERROR_IO_PENDING + && last_error != ERROR_MORE_DATA) + { + iocp_service_.on_completion(op, last_error, bytes_transferred); + } + else + { + iocp_service_.on_pending(op); + } + } +} + +size_t win_iocp_handle_service::do_read( + win_iocp_handle_service::implementation_type& impl, uint64_t offset, + const asio::mutable_buffer& buffer, asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream handle is a no-op. + if (buffer.size() == 0) + { + ec = asio::error_code(); + return 0; + } + + overlapped_wrapper overlapped(ec); + if (ec) + { + return 0; + } + + // Read some data. + overlapped.Offset = offset & 0xFFFFFFFF; + overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::ReadFile(impl.handle_, buffer.data(), + static_cast(buffer.size()), 0, &overlapped); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA) + { + if (last_error == ERROR_HANDLE_EOF) + { + ec = asio::error::eof; + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + return 0; + } + } + + // Wait for the operation to complete. + DWORD bytes_transferred = 0; + ok = ::GetOverlappedResult(impl.handle_, + &overlapped, &bytes_transferred, TRUE); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_HANDLE_EOF) + { + ec = asio::error::eof; + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + return (last_error == ERROR_MORE_DATA) ? bytes_transferred : 0; + } + + ec = asio::error_code(); + return bytes_transferred; +} + +void win_iocp_handle_service::start_read_op( + win_iocp_handle_service::implementation_type& impl, uint64_t offset, + const asio::mutable_buffer& buffer, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + { + iocp_service_.on_completion(op, asio::error::bad_descriptor); + } + else if (buffer.size() == 0) + { + // A request to read 0 bytes on a handle is a no-op. + iocp_service_.on_completion(op); + } + else + { + DWORD bytes_transferred = 0; + op->Offset = offset & 0xFFFFFFFF; + op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::ReadFile(impl.handle_, buffer.data(), + static_cast(buffer.size()), + &bytes_transferred, op); + DWORD last_error = ::GetLastError(); + if (!ok && last_error != ERROR_IO_PENDING + && last_error != ERROR_MORE_DATA) + { + iocp_service_.on_completion(op, last_error, bytes_transferred); + } + else + { + iocp_service_.on_pending(op); + } + } +} + +void win_iocp_handle_service::update_cancellation_thread_id( + win_iocp_handle_service::implementation_type& impl) +{ + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); +} + +void win_iocp_handle_service::close_for_destruction(implementation_type& impl) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle", + &impl, reinterpret_cast(impl.handle_), "close")); + + ::CloseHandle(impl.handle_); + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_io_context.hpp b/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_io_context.hpp new file mode 100644 index 000000000..9f7f9bbce --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_io_context.hpp @@ -0,0 +1,104 @@ +// +// detail/impl/win_iocp_io_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_HPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void win_iocp_io_context::add_timer_queue( + timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +template +void win_iocp_io_context::remove_timer_queue( + timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void win_iocp_io_context::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + // If the service has been shut down we silently discard the timer. + if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) + { + post_immediate_completion(op, false); + return; + } + + mutex::scoped_lock lock(dispatch_mutex_); + + bool earliest = queue.enqueue_timer(time, timer, op); + work_started(); + if (earliest) + update_timeout(); +} + +template +std::size_t win_iocp_io_context::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + // If the service has been shut down we silently ignore the cancellation. + if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) + return 0; + + mutex::scoped_lock lock(dispatch_mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + post_deferred_completions(ops); + return n; +} + +template +void win_iocp_io_context::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& to, + typename timer_queue::per_timer_data& from) +{ + asio::detail::mutex::scoped_lock lock(dispatch_mutex_); + op_queue ops; + queue.cancel_timer(to, ops); + queue.move_timer(to, from); + lock.unlock(); + post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_io_context.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_io_context.ipp new file mode 100644 index 000000000..37e325072 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_io_context.ipp @@ -0,0 +1,608 @@ +// +// detail/impl/win_iocp_io_context.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include "asio/detail/cstdint.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_iocp_io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct win_iocp_io_context::thread_function +{ + explicit thread_function(win_iocp_io_context* s) + : this_(s) + { + } + + void operator()() + { + asio::error_code ec; + this_->run(ec); + } + + win_iocp_io_context* this_; +}; + +struct win_iocp_io_context::work_finished_on_block_exit +{ + ~work_finished_on_block_exit() + { + io_context_->work_finished(); + } + + win_iocp_io_context* io_context_; +}; + +struct win_iocp_io_context::timer_thread_function +{ + void operator()() + { + while (::InterlockedExchangeAdd(&io_context_->shutdown_, 0) == 0) + { + if (::WaitForSingleObject(io_context_->waitable_timer_.handle, + INFINITE) == WAIT_OBJECT_0) + { + ::InterlockedExchange(&io_context_->dispatch_required_, 1); + ::PostQueuedCompletionStatus(io_context_->iocp_.handle, + 0, wake_for_dispatch, 0); + } + } + } + + win_iocp_io_context* io_context_; +}; + +win_iocp_io_context::win_iocp_io_context( + asio::execution_context& ctx, int concurrency_hint, bool own_thread) + : execution_context_service_base(ctx), + iocp_(), + outstanding_work_(0), + stopped_(0), + stop_event_posted_(0), + shutdown_(0), + gqcs_timeout_(get_gqcs_timeout()), + dispatch_required_(0), + concurrency_hint_(concurrency_hint) +{ + ASIO_HANDLER_TRACKING_INIT; + + iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, + static_cast(concurrency_hint >= 0 ? concurrency_hint : DWORD(~0))); + if (!iocp_.handle) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "iocp"); + } + + if (own_thread) + { + ::InterlockedIncrement(&outstanding_work_); + thread_.reset(new asio::detail::thread(thread_function(this))); + } +} + +win_iocp_io_context::~win_iocp_io_context() +{ + if (thread_.get()) + { + stop(); + thread_->join(); + thread_.reset(); + } +} + +void win_iocp_io_context::shutdown() +{ + ::InterlockedExchange(&shutdown_, 1); + + if (timer_thread_.get()) + { + LARGE_INTEGER timeout; + timeout.QuadPart = 1; + ::SetWaitableTimer(waitable_timer_.handle, &timeout, 1, 0, 0, FALSE); + } + + if (thread_.get()) + { + stop(); + thread_->join(); + thread_.reset(); + ::InterlockedDecrement(&outstanding_work_); + } + + while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0) + { + op_queue ops; + timer_queues_.get_all_timers(ops); + ops.push(completed_ops_); + if (!ops.empty()) + { + while (win_iocp_operation* op = ops.front()) + { + ops.pop(); + ::InterlockedDecrement(&outstanding_work_); + op->destroy(); + } + } + else + { + DWORD bytes_transferred = 0; + dword_ptr_t completion_key = 0; + LPOVERLAPPED overlapped = 0; + ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, + &completion_key, &overlapped, gqcs_timeout_); + if (overlapped) + { + ::InterlockedDecrement(&outstanding_work_); + static_cast(overlapped)->destroy(); + } + } + } + + if (timer_thread_.get()) + timer_thread_->join(); +} + +asio::error_code win_iocp_io_context::register_handle( + HANDLE handle, asio::error_code& ec) +{ + if (::CreateIoCompletionPort(handle, iocp_.handle, 0, 0) == 0) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + return ec; +} + +size_t win_iocp_io_context::run(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + win_iocp_thread_info this_thread; + thread_call_stack::context ctx(this, this_thread); + + size_t n = 0; + while (do_one(INFINITE, this_thread, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +size_t win_iocp_io_context::run_one(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + win_iocp_thread_info this_thread; + thread_call_stack::context ctx(this, this_thread); + + return do_one(INFINITE, this_thread, ec); +} + +size_t win_iocp_io_context::wait_one(long usec, asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + win_iocp_thread_info this_thread; + thread_call_stack::context ctx(this, this_thread); + + return do_one(usec < 0 ? INFINITE : ((usec - 1) / 1000 + 1), this_thread, ec); +} + +size_t win_iocp_io_context::poll(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + win_iocp_thread_info this_thread; + thread_call_stack::context ctx(this, this_thread); + + size_t n = 0; + while (do_one(0, this_thread, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +size_t win_iocp_io_context::poll_one(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + win_iocp_thread_info this_thread; + thread_call_stack::context ctx(this, this_thread); + + return do_one(0, this_thread, ec); +} + +void win_iocp_io_context::stop() +{ + if (::InterlockedExchange(&stopped_, 1) == 0) + { + if (::InterlockedExchange(&stop_event_posted_, 1) == 0) + { + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "pqcs"); + } + } + } +} + +bool win_iocp_io_context::can_dispatch() +{ + return thread_call_stack::contains(this) != 0; +} + +void win_iocp_io_context::capture_current_exception() +{ + if (thread_info_base* this_thread = thread_call_stack::contains(this)) + this_thread->capture_current_exception(); +} + +void win_iocp_io_context::post_deferred_completion(win_iocp_operation* op) +{ + // Flag the operation as ready. + op->ready_ = 1; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } +} + +void win_iocp_io_context::post_deferred_completions( + op_queue& ops) +{ + while (win_iocp_operation* op = ops.front()) + { + ops.pop(); + + // Flag the operation as ready. + op->ready_ = 1; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + completed_ops_.push(ops); + ::InterlockedExchange(&dispatch_required_, 1); + } + } +} + +void win_iocp_io_context::abandon_operations( + op_queue& ops) +{ + while (win_iocp_operation* op = ops.front()) + { + ops.pop(); + ::InterlockedDecrement(&outstanding_work_); + op->destroy(); + } +} + +void win_iocp_io_context::on_pending(win_iocp_operation* op) +{ + if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) + { + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } + } +} + +void win_iocp_io_context::on_completion(win_iocp_operation* op, + DWORD last_error, DWORD bytes_transferred) +{ + // Flag that the operation is ready for invocation. + op->ready_ = 1; + + // Store results in the OVERLAPPED structure. + op->Internal = reinterpret_cast( + &asio::error::get_system_category()); + op->Offset = last_error; + op->OffsetHigh = bytes_transferred; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } +} + +void win_iocp_io_context::on_completion(win_iocp_operation* op, + const asio::error_code& ec, DWORD bytes_transferred) +{ + // Flag that the operation is ready for invocation. + op->ready_ = 1; + + // Store results in the OVERLAPPED structure. + op->Internal = reinterpret_cast(&ec.category()); + op->Offset = ec.value(); + op->OffsetHigh = bytes_transferred; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } +} + +size_t win_iocp_io_context::do_one(DWORD msec, + win_iocp_thread_info& this_thread, asio::error_code& ec) +{ + for (;;) + { + // Try to acquire responsibility for dispatching timers and completed ops. + if (::InterlockedCompareExchange(&dispatch_required_, 0, 1) == 1) + { + mutex::scoped_lock lock(dispatch_mutex_); + + // Dispatch pending timers and operations. + op_queue ops; + ops.push(completed_ops_); + timer_queues_.get_ready_timers(ops); + post_deferred_completions(ops); + update_timeout(); + } + + // Get the next operation from the queue. + DWORD bytes_transferred = 0; + dword_ptr_t completion_key = 0; + LPOVERLAPPED overlapped = 0; + ::SetLastError(0); + BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, + &bytes_transferred, &completion_key, &overlapped, + msec < gqcs_timeout_ ? msec : gqcs_timeout_); + DWORD last_error = ::GetLastError(); + + if (overlapped) + { + win_iocp_operation* op = static_cast(overlapped); + asio::error_code result_ec(last_error, + asio::error::get_system_category()); + + // We may have been passed the last_error and bytes_transferred in the + // OVERLAPPED structure itself. + if (completion_key == overlapped_contains_result) + { + result_ec = asio::error_code(static_cast(op->Offset), + *reinterpret_cast(op->Internal)); + bytes_transferred = op->OffsetHigh; + } + + // Otherwise ensure any result has been saved into the OVERLAPPED + // structure. + else + { + op->Internal = reinterpret_cast(&result_ec.category()); + op->Offset = result_ec.value(); + op->OffsetHigh = bytes_transferred; + } + + // Dispatch the operation only if ready. The operation may not be ready + // if the initiating function (e.g. a call to WSARecv) has not yet + // returned. This is because the initiating function still wants access + // to the operation's OVERLAPPED structure. + if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) + { + // Ensure the count of outstanding work is decremented on block exit. + work_finished_on_block_exit on_exit = { this }; + (void)on_exit; + + op->complete(this, result_ec, bytes_transferred); + this_thread.rethrow_pending_exception(); + ec = asio::error_code(); + return 1; + } + } + else if (!ok) + { + if (last_error != WAIT_TIMEOUT) + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + // If we're waiting indefinitely we need to keep going until we get a + // real handler. + if (msec == INFINITE) + continue; + + ec = asio::error_code(); + return 0; + } + else if (completion_key == wake_for_dispatch) + { + // We have been woken up to try to acquire responsibility for dispatching + // timers and completed operations. + } + else + { + // Indicate that there is no longer an in-flight stop event. + ::InterlockedExchange(&stop_event_posted_, 0); + + // The stopped_ flag is always checked to ensure that any leftover + // stop events from a previous run invocation are ignored. + if (::InterlockedExchangeAdd(&stopped_, 0) != 0) + { + // Wake up next thread that is blocked on GetQueuedCompletionStatus. + if (::InterlockedExchange(&stop_event_posted_, 1) == 0) + { + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + } + + ec = asio::error_code(); + return 0; + } + } + } +} + +DWORD win_iocp_io_context::get_gqcs_timeout() +{ + OSVERSIONINFOEX osvi; + ZeroMemory(&osvi, sizeof(osvi)); + osvi.dwOSVersionInfoSize = sizeof(osvi); + osvi.dwMajorVersion = 6ul; + + const uint64_t condition_mask = ::VerSetConditionMask( + 0, VER_MAJORVERSION, VER_GREATER_EQUAL); + + if (!!::VerifyVersionInfo(&osvi, VER_MAJORVERSION, condition_mask)) + return INFINITE; + + return default_gqcs_timeout; +} + +void win_iocp_io_context::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(dispatch_mutex_); + + timer_queues_.insert(&queue); + + if (!waitable_timer_.handle) + { + waitable_timer_.handle = ::CreateWaitableTimer(0, FALSE, 0); + if (waitable_timer_.handle == 0) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "timer"); + } + + LARGE_INTEGER timeout; + timeout.QuadPart = -max_timeout_usec; + timeout.QuadPart *= 10; + ::SetWaitableTimer(waitable_timer_.handle, + &timeout, max_timeout_msec, 0, 0, FALSE); + } + + if (!timer_thread_.get()) + { + timer_thread_function thread_function = { this }; + timer_thread_.reset(new thread(thread_function, 65536)); + } +} + +void win_iocp_io_context::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(dispatch_mutex_); + + timer_queues_.erase(&queue); +} + +void win_iocp_io_context::update_timeout() +{ + if (timer_thread_.get()) + { + // There's no point updating the waitable timer if the new timeout period + // exceeds the maximum timeout. In that case, we might as well wait for the + // existing period of the timer to expire. + long timeout_usec = timer_queues_.wait_duration_usec(max_timeout_usec); + if (timeout_usec < max_timeout_usec) + { + LARGE_INTEGER timeout; + timeout.QuadPart = -timeout_usec; + timeout.QuadPart *= 10; + ::SetWaitableTimer(waitable_timer_.handle, + &timeout, max_timeout_msec, 0, 0, FALSE); + } + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_IO_CONTEXT_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_serial_port_service.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_serial_port_service.ipp new file mode 100644 index 000000000..8b47920e6 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_serial_port_service.ipp @@ -0,0 +1,192 @@ +// +// detail/impl/win_iocp_serial_port_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + +#include +#include "asio/detail/win_iocp_serial_port_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_iocp_serial_port_service::win_iocp_serial_port_service( + execution_context& context) + : execution_context_service_base(context), + handle_service_(context) +{ +} + +void win_iocp_serial_port_service::shutdown() +{ +} + +asio::error_code win_iocp_serial_port_service::open( + win_iocp_serial_port_service::implementation_type& impl, + const std::string& device, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + // For convenience, add a leading \\.\ sequence if not already present. + std::string name = (device[0] == '\\') ? device : "\\\\.\\" + device; + + // Open a handle to the serial port. + ::HANDLE handle = ::CreateFileA(name.c_str(), + GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (handle == INVALID_HANDLE_VALUE) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Determine the initial serial port parameters. + using namespace std; // For memset. + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle, &dcb)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Set some default serial port parameters. This implementation does not + // support changing all of these, so they might as well be in a known state. + dcb.fBinary = TRUE; // Win32 only supports binary mode. + dcb.fNull = FALSE; // Do not ignore NULL characters. + dcb.fAbortOnError = FALSE; // Ignore serial framing errors. + dcb.BaudRate = CBR_9600; // 9600 baud by default + dcb.ByteSize = 8; // 8 bit bytes + dcb.fOutxCtsFlow = FALSE; // No flow control + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fDsrSensitivity = FALSE; + dcb.fOutX = FALSE; + dcb.fInX = FALSE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + dcb.fParity = FALSE; // No parity + dcb.Parity = NOPARITY; + dcb.StopBits = ONESTOPBIT; // One stop bit + if (!::SetCommState(handle, &dcb)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Set up timeouts so that the serial port will behave similarly to a + // network socket. Reads wait for at least one byte, then return with + // whatever they have. Writes return once everything is out the door. + ::COMMTIMEOUTS timeouts; + timeouts.ReadIntervalTimeout = 1; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + if (!::SetCommTimeouts(handle, &timeouts)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // We're done. Take ownership of the serial port handle. + if (handle_service_.assign(impl, handle, ec)) + ::CloseHandle(handle); + return ec; +} + +asio::error_code win_iocp_serial_port_service::do_set_option( + win_iocp_serial_port_service::implementation_type& impl, + win_iocp_serial_port_service::store_function_type store, + const void* option, asio::error_code& ec) +{ + using namespace std; // For memcpy. + + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle_service_.native_handle(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + if (store(option, dcb, ec)) + return ec; + + if (!::SetCommState(handle_service_.native_handle(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + ec = asio::error_code(); + return ec; +} + +asio::error_code win_iocp_serial_port_service::do_get_option( + const win_iocp_serial_port_service::implementation_type& impl, + win_iocp_serial_port_service::load_function_type load, + void* option, asio::error_code& ec) const +{ + using namespace std; // For memset. + + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle_service_.native_handle(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + return load(option, dcb, ec); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_socket_service_base.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_socket_service_base.ipp new file mode 100644 index 000000000..da5beaf14 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_iocp_socket_service_base.ipp @@ -0,0 +1,801 @@ +// +// detail/impl/win_iocp_socket_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/win_iocp_socket_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_iocp_socket_service_base::win_iocp_socket_service_base( + execution_context& context) + : context_(context), + iocp_service_(use_service(context)), + reactor_(0), + connect_ex_(0), + nt_set_info_(0), + mutex_(), + impl_list_(0) +{ +} + +void win_iocp_socket_service_base::base_shutdown() +{ + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + base_implementation_type* impl = impl_list_; + while (impl) + { + close_for_destruction(*impl); + impl = impl->next_; + } +} + +void win_iocp_socket_service_base::construct( + win_iocp_socket_service_base::base_implementation_type& impl) +{ + impl.socket_ = invalid_socket; + impl.state_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void win_iocp_socket_service_base::base_move_construct( + win_iocp_socket_service_base::base_implementation_type& impl, + win_iocp_socket_service_base::base_implementation_type& other_impl) + ASIO_NOEXCEPT +{ + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + impl.cancel_token_ = other_impl.cancel_token_; + other_impl.cancel_token_.reset(); + +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_; + other_impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void win_iocp_socket_service_base::base_move_assign( + win_iocp_socket_service_base::base_implementation_type& impl, + win_iocp_socket_service_base& other_service, + win_iocp_socket_service_base::base_implementation_type& other_impl) +{ + close_for_destruction(impl); + + if (this != &other_service) + { + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + impl.cancel_token_ = other_impl.cancel_token_; + other_impl.cancel_token_.reset(); + +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_; + other_impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + + if (this != &other_service) + { + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(other_service.mutex_); + impl.next_ = other_service.impl_list_; + impl.prev_ = 0; + if (other_service.impl_list_) + other_service.impl_list_->prev_ = &impl; + other_service.impl_list_ = &impl; + } +} + +void win_iocp_socket_service_base::destroy( + win_iocp_socket_service_base::base_implementation_type& impl) +{ + close_for_destruction(impl); + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; +} + +asio::error_code win_iocp_socket_service_base::close( + win_iocp_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((iocp_service_.context(), + "socket", &impl, impl.socket_, "close")); + + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + select_reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (r) + r->deregister_descriptor(impl.socket_, impl.reactor_data_, true); + + socket_ops::close(impl.socket_, impl.state_, false, ec); + + if (r) + r->cleanup_descriptor_data(impl.reactor_data_); + } + else + { + ec = asio::error_code(); + } + + impl.socket_ = invalid_socket; + impl.state_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + + return ec; +} + +socket_type win_iocp_socket_service_base::release( + win_iocp_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + return invalid_socket; + + cancel(impl, ec); + if (ec) + return invalid_socket; + + nt_set_info_fn fn = get_nt_set_info(); + if (fn == 0) + { + ec = asio::error::operation_not_supported; + return invalid_socket; + } + + HANDLE sock_as_handle = reinterpret_cast(impl.socket_); + ULONG_PTR iosb[2] = { 0, 0 }; + void* info[2] = { 0, 0 }; + if (fn(sock_as_handle, iosb, &info, sizeof(info), + 61 /* FileReplaceCompletionInformation */)) + { + ec = asio::error::operation_not_supported; + return invalid_socket; + } + + socket_type tmp = impl.socket_; + impl.socket_ = invalid_socket; + return tmp; +} + +asio::error_code win_iocp_socket_service_base::cancel( + win_iocp_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + ASIO_HANDLER_OPERATION((iocp_service_.context(), + "socket", &impl, impl.socket_, "cancel")); + + if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( + ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) + { + // The version of Windows supports cancellation from any thread. + typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); + cancel_io_ex_t cancel_io_ex = reinterpret_cast( + reinterpret_cast(cancel_io_ex_ptr)); + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast(sock); + if (!cancel_io_ex(sock_as_handle, 0)) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_NOT_FOUND) + { + // ERROR_NOT_FOUND means that there were no operations to be + // cancelled. We swallow this error to match the behaviour on other + // platforms. + ec = asio::error_code(); + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + else + { + ec = asio::error_code(); + } + } +#if defined(ASIO_ENABLE_CANCELIO) + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + ec = asio::error_code(); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast(sock); + if (!::CancelIo(sock_as_handle)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + ec = asio::error::operation_not_supported; + } +#else // defined(ASIO_ENABLE_CANCELIO) + else + { + // Cancellation is not supported as CancelIo may not be used. + ec = asio::error::operation_not_supported; + } +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Cancel any operations started via the reactor. + if (!ec) + { + select_reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (r) + r->cancel_ops(impl.socket_, impl.reactor_data_); + } + + return ec; +} + +asio::error_code win_iocp_socket_service_base::do_open( + win_iocp_socket_service_base::base_implementation_type& impl, + int family, int type, int protocol, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(family, type, protocol, ec)); + if (sock.get() == invalid_socket) + return ec; + + HANDLE sock_as_handle = reinterpret_cast(sock.get()); + if (iocp_service_.register_handle(sock_as_handle, ec)) + return ec; + + impl.socket_ = sock.release(); + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + impl.cancel_token_.reset(static_cast(0), socket_ops::noop_deleter()); + ec = asio::error_code(); + return ec; +} + +asio::error_code win_iocp_socket_service_base::do_assign( + win_iocp_socket_service_base::base_implementation_type& impl, + int type, socket_type native_socket, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + HANDLE sock_as_handle = reinterpret_cast(native_socket); + if (iocp_service_.register_handle(sock_as_handle, ec)) + return ec; + + impl.socket_ = native_socket; + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + impl.cancel_token_.reset(static_cast(0), socket_ops::noop_deleter()); + ec = asio::error_code(); + return ec; +} + +void win_iocp_socket_service_base::start_send_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (noop) + iocp_service_.on_completion(op); + else if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + int result = ::WSASend(impl.socket_, buffers, + static_cast(buffer_count), &bytes_transferred, flags, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_send_to_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + const socket_addr_type* addr, int addrlen, + socket_base::message_flags flags, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + int result = ::WSASendTo(impl.socket_, buffers, + static_cast(buffer_count), + &bytes_transferred, flags, addr, addrlen, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_receive_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (noop) + iocp_service_.on_completion(op); + else if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl.socket_, buffers, + static_cast(buffer_count), + &bytes_transferred, &recv_flags, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + else if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_null_buffers_receive_op( + win_iocp_socket_service_base::base_implementation_type& impl, + socket_base::message_flags flags, reactor_op* op) +{ + if ((impl.state_ & socket_ops::stream_oriented) != 0) + { + // For stream sockets on Windows, we may issue a 0-byte overlapped + // WSARecv to wait until there is data available on the socket. + ::WSABUF buf = { 0, 0 }; + start_receive_op(impl, &buf, 1, flags, false, op); + } + else + { + start_reactor_op(impl, + (flags & socket_base::message_out_of_band) + ? select_reactor::except_op : select_reactor::read_op, + op); + } +} + +void win_iocp_socket_service_base::start_receive_from_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr, + socket_base::message_flags flags, int* addrlen, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecvFrom(impl.socket_, buffers, + static_cast(buffer_count), + &bytes_transferred, &recv_flags, addr, addrlen, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_accept_op( + win_iocp_socket_service_base::base_implementation_type& impl, + bool peer_is_open, socket_holder& new_socket, int family, int type, + int protocol, void* output_buffer, DWORD address_length, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else if (peer_is_open) + iocp_service_.on_completion(op, asio::error::already_open); + else + { + asio::error_code ec; + new_socket.reset(socket_ops::socket(family, type, protocol, ec)); + if (new_socket.get() == invalid_socket) + iocp_service_.on_completion(op, ec); + else + { + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(impl.socket_, new_socket.get(), output_buffer, + 0, address_length, address_length, &bytes_read, op); + DWORD last_error = ::WSAGetLastError(); + if (!result && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error); + else + iocp_service_.on_pending(op); + } + } +} + +void win_iocp_socket_service_base::restart_accept_op( + socket_type s, socket_holder& new_socket, int family, int type, + int protocol, void* output_buffer, DWORD address_length, operation* op) +{ + new_socket.reset(); + iocp_service_.work_started(); + + asio::error_code ec; + new_socket.reset(socket_ops::socket(family, type, protocol, ec)); + if (new_socket.get() == invalid_socket) + iocp_service_.on_completion(op, ec); + else + { + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(s, new_socket.get(), output_buffer, + 0, address_length, address_length, &bytes_read, op); + DWORD last_error = ::WSAGetLastError(); + if (!result && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_reactor_op( + win_iocp_socket_service_base::base_implementation_type& impl, + int op_type, reactor_op* op) +{ + select_reactor& r = get_reactor(); + update_cancellation_thread_id(impl); + + if (is_open(impl)) + { + r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false, false); + return; + } + else + op->ec_ = asio::error::bad_descriptor; + + iocp_service_.post_immediate_completion(op, false); +} + +void win_iocp_socket_service_base::start_connect_op( + win_iocp_socket_service_base::base_implementation_type& impl, + int family, int type, const socket_addr_type* addr, + std::size_t addrlen, win_iocp_socket_connect_op_base* op) +{ + // If ConnectEx is available, use that. + if (family == ASIO_OS_DEF(AF_INET) + || family == ASIO_OS_DEF(AF_INET6)) + { + if (connect_ex_fn connect_ex = get_connect_ex(impl, type)) + { + union address_union + { + socket_addr_type base; + sockaddr_in4_type v4; + sockaddr_in6_type v6; + } a; + + using namespace std; // For memset. + memset(&a, 0, sizeof(a)); + a.base.sa_family = family; + + socket_ops::bind(impl.socket_, &a.base, + family == ASIO_OS_DEF(AF_INET) + ? sizeof(a.v4) : sizeof(a.v6), op->ec_); + if (op->ec_ && op->ec_ != asio::error::invalid_argument) + { + iocp_service_.post_immediate_completion(op, false); + return; + } + + op->connect_ex_ = true; + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + BOOL result = connect_ex(impl.socket_, + addr, static_cast(addrlen), 0, 0, 0, op); + DWORD last_error = ::WSAGetLastError(); + if (!result && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error); + else + iocp_service_.on_pending(op); + return; + } + } + + // Otherwise, fall back to a reactor-based implementation. + select_reactor& r = get_reactor(); + update_cancellation_thread_id(impl); + + if ((impl.state_ & socket_ops::non_blocking) != 0 + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, true, op->ec_)) + { + if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) + { + if (op->ec_ == asio::error::in_progress + || op->ec_ == asio::error::would_block) + { + op->ec_ = asio::error_code(); + r.start_op(select_reactor::connect_op, impl.socket_, + impl.reactor_data_, op, false, false); + return; + } + } + } + + r.post_immediate_completion(op, false); +} + +void win_iocp_socket_service_base::close_for_destruction( + win_iocp_socket_service_base::base_implementation_type& impl) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((iocp_service_.context(), + "socket", &impl, impl.socket_, "close")); + + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + select_reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (r) + r->deregister_descriptor(impl.socket_, impl.reactor_data_, true); + + asio::error_code ignored_ec; + socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); + + if (r) + r->cleanup_descriptor_data(impl.reactor_data_); + } + + impl.socket_ = invalid_socket; + impl.state_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) +} + +void win_iocp_socket_service_base::update_cancellation_thread_id( + win_iocp_socket_service_base::base_implementation_type& impl) +{ +#if defined(ASIO_ENABLE_CANCELIO) + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); +#else // defined(ASIO_ENABLE_CANCELIO) + (void)impl; +#endif // defined(ASIO_ENABLE_CANCELIO) +} + +select_reactor& win_iocp_socket_service_base::get_reactor() +{ + select_reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (!r) + { + r = &(use_service(context_)); + interlocked_exchange_pointer(reinterpret_cast(&reactor_), r); + } + return *r; +} + +win_iocp_socket_service_base::connect_ex_fn +win_iocp_socket_service_base::get_connect_ex( + win_iocp_socket_service_base::base_implementation_type& impl, int type) +{ +#if defined(ASIO_DISABLE_CONNECTEX) + (void)impl; + (void)type; + return 0; +#else // defined(ASIO_DISABLE_CONNECTEX) + if (type != ASIO_OS_DEF(SOCK_STREAM) + && type != ASIO_OS_DEF(SOCK_SEQPACKET)) + return 0; + + void* ptr = interlocked_compare_exchange_pointer(&connect_ex_, 0, 0); + if (!ptr) + { + GUID guid = { 0x25a207b9, 0xddf3, 0x4660, + { 0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e } }; + + DWORD bytes = 0; + if (::WSAIoctl(impl.socket_, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid, sizeof(guid), &ptr, sizeof(ptr), &bytes, 0, 0) != 0) + { + // Set connect_ex_ to a special value to indicate that ConnectEx is + // unavailable. That way we won't bother trying to look it up again. + ptr = this; + } + + interlocked_exchange_pointer(&connect_ex_, ptr); + } + + return reinterpret_cast(ptr == this ? 0 : ptr); +#endif // defined(ASIO_DISABLE_CONNECTEX) +} + +win_iocp_socket_service_base::nt_set_info_fn +win_iocp_socket_service_base::get_nt_set_info() +{ + void* ptr = interlocked_compare_exchange_pointer(&nt_set_info_, 0, 0); + if (!ptr) + { + if (HMODULE h = ::GetModuleHandleA("NTDLL.DLL")) + ptr = reinterpret_cast(GetProcAddress(h, "NtSetInformationFile")); + + // On failure, set nt_set_info_ to a special value to indicate that the + // NtSetInformationFile function is unavailable. That way we won't bother + // trying to look it up again. + interlocked_exchange_pointer(&nt_set_info_, ptr ? ptr : this); + } + + return reinterpret_cast(ptr == this ? 0 : ptr); +} + +void* win_iocp_socket_service_base::interlocked_compare_exchange_pointer( + void** dest, void* exch, void* cmp) +{ +#if defined(_M_IX86) + return reinterpret_cast(InterlockedCompareExchange( + reinterpret_cast(dest), reinterpret_cast(exch), + reinterpret_cast(cmp))); +#else + return InterlockedCompareExchangePointer(dest, exch, cmp); +#endif +} + +void* win_iocp_socket_service_base::interlocked_exchange_pointer( + void** dest, void* val) +{ +#if defined(_M_IX86) + return reinterpret_cast(InterlockedExchange( + reinterpret_cast(dest), reinterpret_cast(val))); +#else + return InterlockedExchangePointer(dest, val); +#endif +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_mutex.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/win_mutex.ipp new file mode 100644 index 000000000..8a2db6f38 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_mutex.ipp @@ -0,0 +1,84 @@ +// +// detail/impl/win_mutex.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_MUTEX_IPP +#define ASIO_DETAIL_IMPL_WIN_MUTEX_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_mutex.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_mutex::win_mutex() +{ + int error = do_init(); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "mutex"); +} + +int win_mutex::do_init() +{ +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. +# if defined(UNDER_CE) + ::InitializeCriticalSection(&crit_section_); +# elif defined(ASIO_WINDOWS_APP) + if (!::InitializeCriticalSectionEx(&crit_section_, 0, 0)) + return ::GetLastError(); +# else + if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) + return ::GetLastError(); +# endif + return 0; +#else + __try + { +# if defined(UNDER_CE) + ::InitializeCriticalSection(&crit_section_); +# elif defined(ASIO_WINDOWS_APP) + if (!::InitializeCriticalSectionEx(&crit_section_, 0, 0)) + return ::GetLastError(); +# else + if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) + return ::GetLastError(); +# endif + } + __except(GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + return ERROR_OUTOFMEMORY; + } + + return 0; +#endif +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_MUTEX_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_object_handle_service.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/win_object_handle_service.ipp new file mode 100644 index 000000000..0c44a4123 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_object_handle_service.ipp @@ -0,0 +1,448 @@ +// +// detail/impl/win_object_handle_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2011 Boris Schaeling (boris@highscore.de) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP +#define ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + +#include "asio/detail/win_object_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_object_handle_service::win_object_handle_service(execution_context& context) + : execution_context_service_base(context), + scheduler_(asio::use_service(context)), + mutex_(), + impl_list_(0), + shutdown_(false) +{ +} + +void win_object_handle_service::shutdown() +{ + mutex::scoped_lock lock(mutex_); + + // Setting this flag to true prevents new objects from being registered, and + // new asynchronous wait operations from being started. We only need to worry + // about cleaning up the operations that are currently in progress. + shutdown_ = true; + + op_queue ops; + for (implementation_type* impl = impl_list_; impl; impl = impl->next_) + ops.push(impl->op_queue_); + + lock.unlock(); + + scheduler_.abandon_operations(ops); +} + +void win_object_handle_service::construct( + win_object_handle_service::implementation_type& impl) +{ + impl.handle_ = INVALID_HANDLE_VALUE; + impl.wait_handle_ = INVALID_HANDLE_VALUE; + impl.owner_ = this; + + // Insert implementation into linked list of all implementations. + mutex::scoped_lock lock(mutex_); + if (!shutdown_) + { + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; + } +} + +void win_object_handle_service::move_construct( + win_object_handle_service::implementation_type& impl, + win_object_handle_service::implementation_type& other_impl) +{ + mutex::scoped_lock lock(mutex_); + + // Insert implementation into linked list of all implementations. + if (!shutdown_) + { + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; + } + + impl.handle_ = other_impl.handle_; + other_impl.handle_ = INVALID_HANDLE_VALUE; + impl.wait_handle_ = other_impl.wait_handle_; + other_impl.wait_handle_ = INVALID_HANDLE_VALUE; + impl.op_queue_.push(other_impl.op_queue_); + impl.owner_ = this; + + // We must not hold the lock while calling UnregisterWaitEx. This is because + // the registered callback function might be invoked while we are waiting for + // UnregisterWaitEx to complete. + lock.unlock(); + + if (impl.wait_handle_ != INVALID_HANDLE_VALUE) + ::UnregisterWaitEx(impl.wait_handle_, INVALID_HANDLE_VALUE); + + if (!impl.op_queue_.empty()) + register_wait_callback(impl, lock); +} + +void win_object_handle_service::move_assign( + win_object_handle_service::implementation_type& impl, + win_object_handle_service& other_service, + win_object_handle_service::implementation_type& other_impl) +{ + asio::error_code ignored_ec; + close(impl, ignored_ec); + + mutex::scoped_lock lock(mutex_); + + if (this != &other_service) + { + // Remove implementation from linked list of all implementations. + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + impl.handle_ = other_impl.handle_; + other_impl.handle_ = INVALID_HANDLE_VALUE; + impl.wait_handle_ = other_impl.wait_handle_; + other_impl.wait_handle_ = INVALID_HANDLE_VALUE; + impl.op_queue_.push(other_impl.op_queue_); + impl.owner_ = this; + + if (this != &other_service) + { + // Insert implementation into linked list of all implementations. + impl.next_ = other_service.impl_list_; + impl.prev_ = 0; + if (other_service.impl_list_) + other_service.impl_list_->prev_ = &impl; + other_service.impl_list_ = &impl; + } + + // We must not hold the lock while calling UnregisterWaitEx. This is because + // the registered callback function might be invoked while we are waiting for + // UnregisterWaitEx to complete. + lock.unlock(); + + if (impl.wait_handle_ != INVALID_HANDLE_VALUE) + ::UnregisterWaitEx(impl.wait_handle_, INVALID_HANDLE_VALUE); + + if (!impl.op_queue_.empty()) + register_wait_callback(impl, lock); +} + +void win_object_handle_service::destroy( + win_object_handle_service::implementation_type& impl) +{ + mutex::scoped_lock lock(mutex_); + + // Remove implementation from linked list of all implementations. + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle", + &impl, reinterpret_cast(impl.wait_handle_), "close")); + + HANDLE wait_handle = impl.wait_handle_; + impl.wait_handle_ = INVALID_HANDLE_VALUE; + + op_queue ops; + while (wait_op* op = impl.op_queue_.front()) + { + op->ec_ = asio::error::operation_aborted; + impl.op_queue_.pop(); + ops.push(op); + } + + // We must not hold the lock while calling UnregisterWaitEx. This is + // because the registered callback function might be invoked while we are + // waiting for UnregisterWaitEx to complete. + lock.unlock(); + + if (wait_handle != INVALID_HANDLE_VALUE) + ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE); + + ::CloseHandle(impl.handle_); + impl.handle_ = INVALID_HANDLE_VALUE; + + scheduler_.post_deferred_completions(ops); + } +} + +asio::error_code win_object_handle_service::assign( + win_object_handle_service::implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + impl.handle_ = handle; + ec = asio::error_code(); + return ec; +} + +asio::error_code win_object_handle_service::close( + win_object_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle", + &impl, reinterpret_cast(impl.wait_handle_), "close")); + + mutex::scoped_lock lock(mutex_); + + HANDLE wait_handle = impl.wait_handle_; + impl.wait_handle_ = INVALID_HANDLE_VALUE; + + op_queue completed_ops; + while (wait_op* op = impl.op_queue_.front()) + { + impl.op_queue_.pop(); + op->ec_ = asio::error::operation_aborted; + completed_ops.push(op); + } + + // We must not hold the lock while calling UnregisterWaitEx. This is + // because the registered callback function might be invoked while we are + // waiting for UnregisterWaitEx to complete. + lock.unlock(); + + if (wait_handle != INVALID_HANDLE_VALUE) + ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE); + + if (::CloseHandle(impl.handle_)) + { + impl.handle_ = INVALID_HANDLE_VALUE; + ec = asio::error_code(); + } + else + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + + scheduler_.post_deferred_completions(completed_ops); + } + else + { + ec = asio::error_code(); + } + + return ec; +} + +asio::error_code win_object_handle_service::cancel( + win_object_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ASIO_HANDLER_OPERATION((scheduler_.context(), "object_handle", + &impl, reinterpret_cast(impl.wait_handle_), "cancel")); + + mutex::scoped_lock lock(mutex_); + + HANDLE wait_handle = impl.wait_handle_; + impl.wait_handle_ = INVALID_HANDLE_VALUE; + + op_queue completed_ops; + while (wait_op* op = impl.op_queue_.front()) + { + op->ec_ = asio::error::operation_aborted; + impl.op_queue_.pop(); + completed_ops.push(op); + } + + // We must not hold the lock while calling UnregisterWaitEx. This is + // because the registered callback function might be invoked while we are + // waiting for UnregisterWaitEx to complete. + lock.unlock(); + + if (wait_handle != INVALID_HANDLE_VALUE) + ::UnregisterWaitEx(wait_handle, INVALID_HANDLE_VALUE); + + ec = asio::error_code(); + + scheduler_.post_deferred_completions(completed_ops); + } + else + { + ec = asio::error::bad_descriptor; + } + + return ec; +} + +void win_object_handle_service::wait( + win_object_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + switch (::WaitForSingleObject(impl.handle_, INFINITE)) + { + case WAIT_FAILED: + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + break; + } + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + default: + ec = asio::error_code(); + break; + } +} + +void win_object_handle_service::start_wait_op( + win_object_handle_service::implementation_type& impl, wait_op* op) +{ + scheduler_.work_started(); + + if (is_open(impl)) + { + mutex::scoped_lock lock(mutex_); + + if (!shutdown_) + { + impl.op_queue_.push(op); + + // Only the first operation to be queued gets to register a wait callback. + // Subsequent operations have to wait for the first to finish. + if (impl.op_queue_.front() == op) + register_wait_callback(impl, lock); + } + else + { + lock.unlock(); + scheduler_.post_deferred_completion(op); + } + } + else + { + op->ec_ = asio::error::bad_descriptor; + scheduler_.post_deferred_completion(op); + } +} + +void win_object_handle_service::register_wait_callback( + win_object_handle_service::implementation_type& impl, + mutex::scoped_lock& lock) +{ + lock.lock(); + + if (!RegisterWaitForSingleObject(&impl.wait_handle_, + impl.handle_, &win_object_handle_service::wait_callback, + &impl, INFINITE, WT_EXECUTEONLYONCE)) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + + op_queue completed_ops; + while (wait_op* op = impl.op_queue_.front()) + { + op->ec_ = ec; + impl.op_queue_.pop(); + completed_ops.push(op); + } + + lock.unlock(); + scheduler_.post_deferred_completions(completed_ops); + } +} + +void win_object_handle_service::wait_callback(PVOID param, BOOLEAN) +{ + implementation_type* impl = static_cast(param); + mutex::scoped_lock lock(impl->owner_->mutex_); + + if (impl->wait_handle_ != INVALID_HANDLE_VALUE) + { + ::UnregisterWaitEx(impl->wait_handle_, NULL); + impl->wait_handle_ = INVALID_HANDLE_VALUE; + } + + if (wait_op* op = impl->op_queue_.front()) + { + op_queue completed_ops; + + op->ec_ = asio::error_code(); + impl->op_queue_.pop(); + completed_ops.push(op); + + if (!impl->op_queue_.empty()) + { + if (!RegisterWaitForSingleObject(&impl->wait_handle_, + impl->handle_, &win_object_handle_service::wait_callback, + param, INFINITE, WT_EXECUTEONLYONCE)) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + + while ((op = impl->op_queue_.front()) != 0) + { + op->ec_ = ec; + impl->op_queue_.pop(); + completed_ops.push(op); + } + } + } + + scheduler_impl& sched = impl->owner_->scheduler_; + lock.unlock(); + sched.post_deferred_completions(completed_ops); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + +#endif // ASIO_DETAIL_IMPL_WIN_OBJECT_HANDLE_SERVICE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_static_mutex.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/win_static_mutex.ipp new file mode 100644 index 000000000..070eb5951 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_static_mutex.ipp @@ -0,0 +1,136 @@ +// +// detail/impl/win_static_mutex.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_STATIC_MUTEX_IPP +#define ASIO_DETAIL_IMPL_WIN_STATIC_MUTEX_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_static_mutex.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void win_static_mutex::init() +{ + int error = do_init(); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "static_mutex"); +} + +int win_static_mutex::do_init() +{ + using namespace std; // For sprintf. + wchar_t mutex_name[128]; +#if defined(ASIO_HAS_SECURE_RTL) + swprintf_s( +#else // defined(ASIO_HAS_SECURE_RTL) + _snwprintf( +#endif // defined(ASIO_HAS_SECURE_RTL) + mutex_name, 128, L"asio-58CCDC44-6264-4842-90C2-F3C545CB8AA7-%u-%p", + static_cast(::GetCurrentProcessId()), this); + +#if defined(ASIO_WINDOWS_APP) + HANDLE mutex = ::CreateMutexExW(0, mutex_name, CREATE_MUTEX_INITIAL_OWNER, 0); +#else // defined(ASIO_WINDOWS_APP) + HANDLE mutex = ::CreateMutexW(0, TRUE, mutex_name); +#endif // defined(ASIO_WINDOWS_APP) + DWORD last_error = ::GetLastError(); + if (mutex == 0) + return ::GetLastError(); + + if (last_error == ERROR_ALREADY_EXISTS) + { +#if defined(ASIO_WINDOWS_APP) + ::WaitForSingleObjectEx(mutex, INFINITE, false); +#else // defined(ASIO_WINDOWS_APP) + ::WaitForSingleObject(mutex, INFINITE); +#endif // defined(ASIO_WINDOWS_APP) + } + + if (initialised_) + { + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return 0; + } + +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. +# if defined(UNDER_CE) + ::InitializeCriticalSection(&crit_section_); +# else + if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) + { + last_error = ::GetLastError(); + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return last_error; + } +# endif +#else + __try + { +# if defined(UNDER_CE) + ::InitializeCriticalSection(&crit_section_); +# elif defined(ASIO_WINDOWS_APP) + if (!::InitializeCriticalSectionEx(&crit_section_, 0, 0)) + { + last_error = ::GetLastError(); + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return last_error; + } +# else + if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) + { + last_error = ::GetLastError(); + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return last_error; + } +# endif + } + __except(GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return ERROR_OUTOFMEMORY; + } +#endif + + initialised_ = true; + ::ReleaseMutex(mutex); + ::CloseHandle(mutex); + return 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_STATIC_MUTEX_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_thread.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/win_thread.ipp new file mode 100644 index 000000000..142ba67fc --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_thread.ipp @@ -0,0 +1,150 @@ +// +// detail/impl/win_thread.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_THREAD_IPP +#define ASIO_DETAIL_IMPL_WIN_THREAD_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_APP) \ + && !defined(UNDER_CE) + +#include +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_thread.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_thread::~win_thread() +{ + ::CloseHandle(thread_); + + // The exit_event_ handle is deliberately allowed to leak here since it + // is an error for the owner of an internal thread not to join() it. +} + +void win_thread::join() +{ + HANDLE handles[2] = { exit_event_, thread_ }; + ::WaitForMultipleObjects(2, handles, FALSE, INFINITE); + ::CloseHandle(exit_event_); + if (terminate_threads()) + { + ::TerminateThread(thread_, 0); + } + else + { + ::QueueUserAPC(apc_function, thread_, 0); + ::WaitForSingleObject(thread_, INFINITE); + } +} + +std::size_t win_thread::hardware_concurrency() +{ + SYSTEM_INFO system_info; + ::GetSystemInfo(&system_info); + return system_info.dwNumberOfProcessors; +} + +void win_thread::start_thread(func_base* arg, unsigned int stack_size) +{ + ::HANDLE entry_event = 0; + arg->entry_event_ = entry_event = ::CreateEventW(0, true, false, 0); + if (!entry_event) + { + DWORD last_error = ::GetLastError(); + delete arg; + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread.entry_event"); + } + + arg->exit_event_ = exit_event_ = ::CreateEventW(0, true, false, 0); + if (!exit_event_) + { + DWORD last_error = ::GetLastError(); + delete arg; + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread.exit_event"); + } + + unsigned int thread_id = 0; + thread_ = reinterpret_cast(::_beginthreadex(0, + stack_size, win_thread_function, arg, 0, &thread_id)); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + delete arg; + if (entry_event) + ::CloseHandle(entry_event); + if (exit_event_) + ::CloseHandle(exit_event_); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); + } + + if (entry_event) + { + ::WaitForSingleObject(entry_event, INFINITE); + ::CloseHandle(entry_event); + } +} + +unsigned int __stdcall win_thread_function(void* arg) +{ + win_thread::auto_func_base_ptr func = { + static_cast(arg) }; + + ::SetEvent(func.ptr->entry_event_); + + func.ptr->run(); + + // Signal that the thread has finished its work, but rather than returning go + // to sleep to put the thread into a well known state. If the thread is being + // joined during global object destruction then it may be killed using + // TerminateThread (to avoid a deadlock in DllMain). Otherwise, the SleepEx + // call will be interrupted using QueueUserAPC and the thread will shut down + // cleanly. + HANDLE exit_event = func.ptr->exit_event_; + delete func.ptr; + func.ptr = 0; + ::SetEvent(exit_event); + ::SleepEx(INFINITE, TRUE); + + return 0; +} + +#if defined(WINVER) && (WINVER < 0x0500) +void __stdcall apc_function(ULONG) {} +#else +void __stdcall apc_function(ULONG_PTR) {} +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_APP) + // && !defined(UNDER_CE) + +#endif // ASIO_DETAIL_IMPL_WIN_THREAD_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/win_tss_ptr.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/win_tss_ptr.ipp new file mode 100644 index 000000000..c6ece2e20 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/win_tss_ptr.ipp @@ -0,0 +1,57 @@ +// +// detail/impl/win_tss_ptr.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP +#define ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_tss_ptr.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +DWORD win_tss_ptr_create() +{ +#if defined(UNDER_CE) + const DWORD out_of_indexes = 0xFFFFFFFF; +#else + const DWORD out_of_indexes = TLS_OUT_OF_INDEXES; +#endif + + DWORD tss_key = ::TlsAlloc(); + if (tss_key == out_of_indexes) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "tss"); + } + return tss_key; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/winrt_ssocket_service_base.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/winrt_ssocket_service_base.ipp new file mode 100644 index 000000000..92a29f064 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/winrt_ssocket_service_base.ipp @@ -0,0 +1,626 @@ +// +// detail/impl/winrt_ssocket_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WINRT_SSOCKET_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_WINRT_SSOCKET_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include +#include "asio/detail/winrt_ssocket_service_base.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/detail/winrt_utils.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +winrt_ssocket_service_base::winrt_ssocket_service_base( + execution_context& context) + : scheduler_(use_service(context)), + async_manager_(use_service(context)), + mutex_(), + impl_list_(0) +{ +} + +void winrt_ssocket_service_base::base_shutdown() +{ + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + base_implementation_type* impl = impl_list_; + while (impl) + { + asio::error_code ignored_ec; + close(*impl, ignored_ec); + impl = impl->next_; + } +} + +void winrt_ssocket_service_base::construct( + winrt_ssocket_service_base::base_implementation_type& impl) +{ + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void winrt_ssocket_service_base::base_move_construct( + winrt_ssocket_service_base::base_implementation_type& impl, + winrt_ssocket_service_base::base_implementation_type& other_impl) + ASIO_NOEXCEPT +{ + impl.socket_ = other_impl.socket_; + other_impl.socket_ = nullptr; + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void winrt_ssocket_service_base::base_move_assign( + winrt_ssocket_service_base::base_implementation_type& impl, + winrt_ssocket_service_base& other_service, + winrt_ssocket_service_base::base_implementation_type& other_impl) +{ + asio::error_code ignored_ec; + close(impl, ignored_ec); + + if (this != &other_service) + { + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + impl.socket_ = other_impl.socket_; + other_impl.socket_ = nullptr; + + if (this != &other_service) + { + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(other_service.mutex_); + impl.next_ = other_service.impl_list_; + impl.prev_ = 0; + if (other_service.impl_list_) + other_service.impl_list_->prev_ = &impl; + other_service.impl_list_ = &impl; + } +} + +void winrt_ssocket_service_base::destroy( + winrt_ssocket_service_base::base_implementation_type& impl) +{ + asio::error_code ignored_ec; + close(impl, ignored_ec); + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; +} + +asio::error_code winrt_ssocket_service_base::close( + winrt_ssocket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + delete impl.socket_; + impl.socket_ = nullptr; + ec = asio::error_code(); + return ec; +} + +winrt_ssocket_service_base::native_handle_type +winrt_ssocket_service_base::release( + winrt_ssocket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + return nullptr; + + cancel(impl, ec); + if (ec) + return nullptr; + + native_handle_type tmp = impl.socket_; + impl.socket_ = nullptr; + return tmp; +} + +std::size_t winrt_ssocket_service_base::do_get_endpoint( + const base_implementation_type& impl, bool local, + void* addr, std::size_t addr_len, asio::error_code& ec) const +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return addr_len; + } + + try + { + std::string addr_string = winrt_utils::string(local + ? impl.socket_->Information->LocalAddress->CanonicalName + : impl.socket_->Information->RemoteAddress->CanonicalName); + unsigned short port = winrt_utils::integer(local + ? impl.socket_->Information->LocalPort + : impl.socket_->Information->RemotePort); + unsigned long scope = 0; + + switch (reinterpret_cast(addr)->sa_family) + { + case ASIO_OS_DEF(AF_INET): + if (addr_len < sizeof(sockaddr_in4_type)) + { + ec = asio::error::invalid_argument; + return addr_len; + } + else + { + socket_ops::inet_pton(ASIO_OS_DEF(AF_INET), addr_string.c_str(), + &reinterpret_cast(addr)->sin_addr, &scope, ec); + reinterpret_cast(addr)->sin_port + = socket_ops::host_to_network_short(port); + ec = asio::error_code(); + return sizeof(sockaddr_in4_type); + } + case ASIO_OS_DEF(AF_INET6): + if (addr_len < sizeof(sockaddr_in6_type)) + { + ec = asio::error::invalid_argument; + return addr_len; + } + else + { + socket_ops::inet_pton(ASIO_OS_DEF(AF_INET6), addr_string.c_str(), + &reinterpret_cast(addr)->sin6_addr, &scope, ec); + reinterpret_cast(addr)->sin6_port + = socket_ops::host_to_network_short(port); + ec = asio::error_code(); + return sizeof(sockaddr_in6_type); + } + default: + ec = asio::error::address_family_not_supported; + return addr_len; + } + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + return addr_len; + } +} + +asio::error_code winrt_ssocket_service_base::do_set_option( + winrt_ssocket_service_base::base_implementation_type& impl, + int level, int optname, const void* optval, + std::size_t optlen, asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + try + { + if (level == ASIO_OS_DEF(SOL_SOCKET) + && optname == ASIO_OS_DEF(SO_KEEPALIVE)) + { + if (optlen == sizeof(int)) + { + int value = 0; + std::memcpy(&value, optval, optlen); + impl.socket_->Control->KeepAlive = !!value; + ec = asio::error_code(); + } + else + { + ec = asio::error::invalid_argument; + } + } + else if (level == ASIO_OS_DEF(IPPROTO_TCP) + && optname == ASIO_OS_DEF(TCP_NODELAY)) + { + if (optlen == sizeof(int)) + { + int value = 0; + std::memcpy(&value, optval, optlen); + impl.socket_->Control->NoDelay = !!value; + ec = asio::error_code(); + } + else + { + ec = asio::error::invalid_argument; + } + } + else + { + ec = asio::error::invalid_argument; + } + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + } + + return ec; +} + +void winrt_ssocket_service_base::do_get_option( + const winrt_ssocket_service_base::base_implementation_type& impl, + int level, int optname, void* optval, + std::size_t* optlen, asio::error_code& ec) const +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return; + } + + try + { + if (level == ASIO_OS_DEF(SOL_SOCKET) + && optname == ASIO_OS_DEF(SO_KEEPALIVE)) + { + if (*optlen >= sizeof(int)) + { + int value = impl.socket_->Control->KeepAlive ? 1 : 0; + std::memcpy(optval, &value, sizeof(int)); + *optlen = sizeof(int); + ec = asio::error_code(); + } + else + { + ec = asio::error::invalid_argument; + } + } + else if (level == ASIO_OS_DEF(IPPROTO_TCP) + && optname == ASIO_OS_DEF(TCP_NODELAY)) + { + if (*optlen >= sizeof(int)) + { + int value = impl.socket_->Control->NoDelay ? 1 : 0; + std::memcpy(optval, &value, sizeof(int)); + *optlen = sizeof(int); + ec = asio::error_code(); + } + else + { + ec = asio::error::invalid_argument; + } + } + else + { + ec = asio::error::invalid_argument; + } + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + } +} + +asio::error_code winrt_ssocket_service_base::do_connect( + winrt_ssocket_service_base::base_implementation_type& impl, + const void* addr, asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + char addr_string[max_addr_v6_str_len]; + unsigned short port; + switch (reinterpret_cast(addr)->sa_family) + { + case ASIO_OS_DEF(AF_INET): + socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET), + &reinterpret_cast(addr)->sin_addr, + addr_string, sizeof(addr_string), 0, ec); + port = socket_ops::network_to_host_short( + reinterpret_cast(addr)->sin_port); + break; + case ASIO_OS_DEF(AF_INET6): + socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET6), + &reinterpret_cast(addr)->sin6_addr, + addr_string, sizeof(addr_string), 0, ec); + port = socket_ops::network_to_host_short( + reinterpret_cast(addr)->sin6_port); + break; + default: + ec = asio::error::address_family_not_supported; + return ec; + } + + if (!ec) try + { + async_manager_.sync(impl.socket_->ConnectAsync( + ref new Windows::Networking::HostName( + winrt_utils::string(addr_string)), + winrt_utils::string(port)), ec); + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + } + + return ec; +} + +void winrt_ssocket_service_base::start_connect_op( + winrt_ssocket_service_base::base_implementation_type& impl, + const void* addr, winrt_async_op* op, bool is_continuation) +{ + if (!is_open(impl)) + { + op->ec_ = asio::error::bad_descriptor; + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + char addr_string[max_addr_v6_str_len]; + unsigned short port = 0; + switch (reinterpret_cast(addr)->sa_family) + { + case ASIO_OS_DEF(AF_INET): + socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET), + &reinterpret_cast(addr)->sin_addr, + addr_string, sizeof(addr_string), 0, op->ec_); + port = socket_ops::network_to_host_short( + reinterpret_cast(addr)->sin_port); + break; + case ASIO_OS_DEF(AF_INET6): + socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET6), + &reinterpret_cast(addr)->sin6_addr, + addr_string, sizeof(addr_string), 0, op->ec_); + port = socket_ops::network_to_host_short( + reinterpret_cast(addr)->sin6_port); + break; + default: + op->ec_ = asio::error::address_family_not_supported; + break; + } + + if (op->ec_) + { + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + try + { + async_manager_.async(impl.socket_->ConnectAsync( + ref new Windows::Networking::HostName( + winrt_utils::string(addr_string)), + winrt_utils::string(port)), op); + } + catch (Platform::Exception^ e) + { + op->ec_ = asio::error_code( + e->HResult, asio::system_category()); + scheduler_.post_immediate_completion(op, is_continuation); + } +} + +std::size_t winrt_ssocket_service_base::do_send( + winrt_ssocket_service_base::base_implementation_type& impl, + const asio::const_buffer& data, + socket_base::message_flags flags, asio::error_code& ec) +{ + if (flags) + { + ec = asio::error::operation_not_supported; + return 0; + } + + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + try + { + buffer_sequence_adapter bufs(asio::buffer(data)); + + if (bufs.all_empty()) + { + ec = asio::error_code(); + return 0; + } + + return async_manager_.sync( + impl.socket_->OutputStream->WriteAsync(bufs.buffers()[0]), ec); + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + return 0; + } +} + +void winrt_ssocket_service_base::start_send_op( + winrt_ssocket_service_base::base_implementation_type& impl, + const asio::const_buffer& data, socket_base::message_flags flags, + winrt_async_op* op, bool is_continuation) +{ + if (flags) + { + op->ec_ = asio::error::operation_not_supported; + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + if (!is_open(impl)) + { + op->ec_ = asio::error::bad_descriptor; + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + try + { + buffer_sequence_adapter bufs(asio::buffer(data)); + + if (bufs.all_empty()) + { + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + async_manager_.async( + impl.socket_->OutputStream->WriteAsync(bufs.buffers()[0]), op); + } + catch (Platform::Exception^ e) + { + op->ec_ = asio::error_code(e->HResult, + asio::system_category()); + scheduler_.post_immediate_completion(op, is_continuation); + } +} + +std::size_t winrt_ssocket_service_base::do_receive( + winrt_ssocket_service_base::base_implementation_type& impl, + const asio::mutable_buffer& data, + socket_base::message_flags flags, asio::error_code& ec) +{ + if (flags) + { + ec = asio::error::operation_not_supported; + return 0; + } + + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + try + { + buffer_sequence_adapter bufs(asio::buffer(data)); + + if (bufs.all_empty()) + { + ec = asio::error_code(); + return 0; + } + + async_manager_.sync( + impl.socket_->InputStream->ReadAsync( + bufs.buffers()[0], bufs.buffers()[0]->Capacity, + Windows::Storage::Streams::InputStreamOptions::Partial), ec); + + std::size_t bytes_transferred = bufs.buffers()[0]->Length; + if (bytes_transferred == 0 && !ec) + { + ec = asio::error::eof; + } + + return bytes_transferred; + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + return 0; + } +} + +void winrt_ssocket_service_base::start_receive_op( + winrt_ssocket_service_base::base_implementation_type& impl, + const asio::mutable_buffer& data, socket_base::message_flags flags, + winrt_async_op* op, + bool is_continuation) +{ + if (flags) + { + op->ec_ = asio::error::operation_not_supported; + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + if (!is_open(impl)) + { + op->ec_ = asio::error::bad_descriptor; + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + try + { + buffer_sequence_adapter bufs(asio::buffer(data)); + + if (bufs.all_empty()) + { + scheduler_.post_immediate_completion(op, is_continuation); + return; + } + + async_manager_.async( + impl.socket_->InputStream->ReadAsync( + bufs.buffers()[0], bufs.buffers()[0]->Capacity, + Windows::Storage::Streams::InputStreamOptions::Partial), op); + } + catch (Platform::Exception^ e) + { + op->ec_ = asio::error_code(e->HResult, + asio::system_category()); + scheduler_.post_immediate_completion(op, is_continuation); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_WINRT_SSOCKET_SERVICE_BASE_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/winrt_timer_scheduler.hpp b/third_party/asio/1.18.2/include/asio/detail/impl/winrt_timer_scheduler.hpp new file mode 100644 index 000000000..7c7da52bb --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/winrt_timer_scheduler.hpp @@ -0,0 +1,92 @@ +// +// detail/impl/winrt_timer_scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_HPP +#define ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void winrt_timer_scheduler::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +// Remove a timer queue from the reactor. +template +void winrt_timer_scheduler::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void winrt_timer_scheduler::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + scheduler_.post_immediate_completion(op, false); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + scheduler_.work_started(); + if (earliest) + event_.signal(lock); +} + +template +std::size_t winrt_timer_scheduler::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops, max_cancelled); + lock.unlock(); + scheduler_.post_deferred_completions(ops); + return n; +} + +template +void winrt_timer_scheduler::move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& to, + typename timer_queue::per_timer_data& from) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + queue.cancel_timer(to, ops); + queue.move_timer(to, from); + lock.unlock(); + scheduler_.post_deferred_completions(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/winrt_timer_scheduler.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/winrt_timer_scheduler.ipp new file mode 100644 index 000000000..d3d6103a6 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/winrt_timer_scheduler.ipp @@ -0,0 +1,121 @@ +// +// detail/impl/winrt_timer_scheduler.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_IPP +#define ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/winrt_timer_scheduler.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +winrt_timer_scheduler::winrt_timer_scheduler(execution_context& context) + : execution_context_service_base(context), + scheduler_(use_service(context)), + mutex_(), + event_(), + timer_queues_(), + thread_(0), + stop_thread_(false), + shutdown_(false) +{ + thread_ = new asio::detail::thread( + bind_handler(&winrt_timer_scheduler::call_run_thread, this)); +} + +winrt_timer_scheduler::~winrt_timer_scheduler() +{ + shutdown(); +} + +void winrt_timer_scheduler::shutdown() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + stop_thread_ = true; + event_.signal(lock); + lock.unlock(); + + if (thread_) + { + thread_->join(); + delete thread_; + thread_ = 0; + } + + op_queue ops; + timer_queues_.get_all_timers(ops); + scheduler_.abandon_operations(ops); +} + +void winrt_timer_scheduler::notify_fork(execution_context::fork_event) +{ +} + +void winrt_timer_scheduler::init_task() +{ +} + +void winrt_timer_scheduler::run_thread() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + const long max_wait_duration = 5 * 60 * 1000000; + long wait_duration = timer_queues_.wait_duration_usec(max_wait_duration); + event_.wait_for_usec(lock, wait_duration); + event_.clear(lock); + op_queue ops; + timer_queues_.get_ready_timers(ops); + if (!ops.empty()) + { + lock.unlock(); + scheduler_.post_deferred_completions(ops); + lock.lock(); + } + } +} + +void winrt_timer_scheduler::call_run_thread(winrt_timer_scheduler* scheduler) +{ + scheduler->run_thread(); +} + +void winrt_timer_scheduler::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void winrt_timer_scheduler::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_IMPL_WINRT_TIMER_SCHEDULER_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/impl/winsock_init.ipp b/third_party/asio/1.18.2/include/asio/detail/impl/winsock_init.ipp new file mode 100644 index 000000000..f2e8cb51a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/impl/winsock_init.ipp @@ -0,0 +1,82 @@ +// +// detail/impl/winsock_init.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP +#define ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/socket_types.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void winsock_init_base::startup(data& d, + unsigned char major, unsigned char minor) +{ + if (::InterlockedIncrement(&d.init_count_) == 1) + { + WSADATA wsa_data; + long result = ::WSAStartup(MAKEWORD(major, minor), &wsa_data); + ::InterlockedExchange(&d.result_, result); + } +} + +void winsock_init_base::manual_startup(data& d) +{ + if (::InterlockedIncrement(&d.init_count_) == 1) + { + ::InterlockedExchange(&d.result_, 0); + } +} + +void winsock_init_base::cleanup(data& d) +{ + if (::InterlockedDecrement(&d.init_count_) == 0) + { + ::WSACleanup(); + } +} + +void winsock_init_base::manual_cleanup(data& d) +{ + ::InterlockedDecrement(&d.init_count_); +} + +void winsock_init_base::throw_on_error(data& d) +{ + long result = ::InterlockedExchangeAdd(&d.result_, 0); + if (result != 0) + { + asio::error_code ec(result, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "winsock"); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP diff --git a/third_party/asio/1.18.2/include/asio/detail/io_control.hpp b/third_party/asio/1.18.2/include/asio/detail/io_control.hpp new file mode 100644 index 000000000..2b6d59389 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/io_control.hpp @@ -0,0 +1,84 @@ +// +// detail/io_control.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IO_CONTROL_HPP +#define ASIO_DETAIL_IO_CONTROL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace io_control { + +// I/O control command for getting number of bytes available. +class bytes_readable +{ +public: + // Default constructor. + bytes_readable() + : value_(0) + { + } + + // Construct with a specific command value. + bytes_readable(std::size_t value) + : value_(static_cast(value)) + { + } + + // Get the name of the IO control command. + int name() const + { + return static_cast(ASIO_OS_DEF(FIONREAD)); + } + + // Set the value of the I/O control command. + void set(std::size_t value) + { + value_ = static_cast(value); + } + + // Get the current value of the I/O control command. + std::size_t get() const + { + return static_cast(value_); + } + + // Get the address of the command data. + detail::ioctl_arg_type* data() + { + return &value_; + } + + // Get the address of the command data. + const detail::ioctl_arg_type* data() const + { + return &value_; + } + +private: + detail::ioctl_arg_type value_; +}; + +} // namespace io_control +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IO_CONTROL_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/io_object_impl.hpp b/third_party/asio/1.18.2/include/asio/detail/io_object_impl.hpp new file mode 100644 index 000000000..9efd0d620 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/io_object_impl.hpp @@ -0,0 +1,172 @@ +// +// io_object_impl.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IO_OBJECT_IMPL_HPP +#define ASIO_DETAIL_IO_OBJECT_IMPL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/context.hpp" +#include "asio/io_context.hpp" +#include "asio/query.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class io_object_impl +{ +public: + // The type of the service that will be used to provide I/O operations. + typedef IoObjectService service_type; + + // The underlying implementation type of I/O object. + typedef typename service_type::implementation_type implementation_type; + + // The type of the executor associated with the object. + typedef Executor executor_type; + + // Construct an I/O object using an executor. + explicit io_object_impl(int, const executor_type& ex) + : service_(&asio::use_service( + io_object_impl::get_context(ex))), + executor_(ex) + { + service_->construct(implementation_); + } + + // Construct an I/O object using an execution context. + template + explicit io_object_impl(int, int, ExecutionContext& context) + : service_(&asio::use_service(context)), + executor_(context.get_executor()) + { + service_->construct(implementation_); + } + +#if defined(ASIO_HAS_MOVE) + // Move-construct an I/O object. + io_object_impl(io_object_impl&& other) + : service_(&other.get_service()), + executor_(other.get_executor()) + { + service_->move_construct(implementation_, other.implementation_); + } + + // Perform a converting move-construction of an I/O object. + template + io_object_impl(io_object_impl&& other) + : service_(&asio::use_service( + io_object_impl::get_context(other.get_executor()))), + executor_(other.get_executor()) + { + service_->converting_move_construct(implementation_, + other.get_service(), other.get_implementation()); + } +#endif // defined(ASIO_HAS_MOVE) + + // Destructor. + ~io_object_impl() + { + service_->destroy(implementation_); + } + +#if defined(ASIO_HAS_MOVE) + // Move-assign an I/O object. + io_object_impl& operator=(io_object_impl&& other) + { + if (this != &other) + { + service_->move_assign(implementation_, + *other.service_, other.implementation_); + executor_.~executor_type(); + new (&executor_) executor_type(other.executor_); + service_ = other.service_; + } + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + // Get the executor associated with the object. + const executor_type& get_executor() ASIO_NOEXCEPT + { + return executor_; + } + + // Get the service associated with the I/O object. + service_type& get_service() + { + return *service_; + } + + // Get the service associated with the I/O object. + const service_type& get_service() const + { + return *service_; + } + + // Get the underlying implementation of the I/O object. + implementation_type& get_implementation() + { + return implementation_; + } + + // Get the underlying implementation of the I/O object. + const implementation_type& get_implementation() const + { + return implementation_; + } + +private: + // Helper function to get an executor's context. + template + static execution_context& get_context(const T& t, + typename enable_if::value>::type* = 0) + { + return asio::query(t, execution::context); + } + + // Helper function to get an executor's context. + template + static execution_context& get_context(const T& t, + typename enable_if::value>::type* = 0) + { + return t.context(); + } + + // Disallow copying and copy assignment. + io_object_impl(const io_object_impl&); + io_object_impl& operator=(const io_object_impl&); + + // The service associated with the I/O object. + service_type* service_; + + // The underlying implementation of the I/O object. + implementation_type implementation_; + + // The associated executor. + executor_type executor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IO_OBJECT_IMPL_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/is_buffer_sequence.hpp b/third_party/asio/1.18.2/include/asio/detail/is_buffer_sequence.hpp new file mode 100644 index 000000000..e3d8de3d8 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/is_buffer_sequence.hpp @@ -0,0 +1,312 @@ +// +// detail/is_buffer_sequence.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IS_BUFFER_SEQUENCE_HPP +#define ASIO_DETAIL_IS_BUFFER_SEQUENCE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class mutable_buffer; +class const_buffer; + +namespace detail { + +struct buffer_sequence_memfns_base +{ + void begin(); + void end(); + void size(); + void max_size(); + void capacity(); + void data(); + void prepare(); + void commit(); + void consume(); + void grow(); + void shrink(); +}; + +template +struct buffer_sequence_memfns_derived + : T, buffer_sequence_memfns_base +{ +}; + +template +struct buffer_sequence_memfns_check +{ +}; + +#if defined(ASIO_HAS_DECLTYPE) + +template +char buffer_sequence_begin_helper(...); + +template +char (&buffer_sequence_begin_helper(T* t, + typename enable_if::value>::type*))[2]; + +#else // defined(ASIO_HAS_DECLTYPE) + +template +char (&buffer_sequence_begin_helper(...))[2]; + +template +char buffer_sequence_begin_helper(T* t, + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::begin>*); + +#endif // defined(ASIO_HAS_DECLTYPE) + +#if defined(ASIO_HAS_DECLTYPE) + +template +char buffer_sequence_end_helper(...); + +template +char (&buffer_sequence_end_helper(T* t, + typename enable_if::value>::type*))[2]; + +#else // defined(ASIO_HAS_DECLTYPE) + +template +char (&buffer_sequence_end_helper(...))[2]; + +template +char buffer_sequence_end_helper(T* t, + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::end>*); + +#endif // defined(ASIO_HAS_DECLTYPE) + +template +char (&size_memfn_helper(...))[2]; + +template +char size_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::size>*); + +template +char (&max_size_memfn_helper(...))[2]; + +template +char max_size_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::max_size>*); + +template +char (&capacity_memfn_helper(...))[2]; + +template +char capacity_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::capacity>*); + +template +char (&data_memfn_helper(...))[2]; + +template +char data_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::data>*); + +template +char (&prepare_memfn_helper(...))[2]; + +template +char prepare_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::prepare>*); + +template +char (&commit_memfn_helper(...))[2]; + +template +char commit_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::commit>*); + +template +char (&consume_memfn_helper(...))[2]; + +template +char consume_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::consume>*); + +template +char (&grow_memfn_helper(...))[2]; + +template +char grow_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::grow>*); + +template +char (&shrink_memfn_helper(...))[2]; + +template +char shrink_memfn_helper( + buffer_sequence_memfns_check< + void (buffer_sequence_memfns_base::*)(), + &buffer_sequence_memfns_derived::shrink>*); + +template +char (&buffer_sequence_element_type_helper(...))[2]; + +#if defined(ASIO_HAS_DECLTYPE) + +template +char buffer_sequence_element_type_helper(T* t, + typename enable_if::value>::type*); + +#else // defined(ASIO_HAS_DECLTYPE) + +template +char buffer_sequence_element_type_helper( + typename T::const_iterator*, + typename enable_if::value>::type*); + +#endif // defined(ASIO_HAS_DECLTYPE) + +template +char (&const_buffers_type_typedef_helper(...))[2]; + +template +char const_buffers_type_typedef_helper( + typename T::const_buffers_type*); + +template +char (&mutable_buffers_type_typedef_helper(...))[2]; + +template +char mutable_buffers_type_typedef_helper( + typename T::mutable_buffers_type*); + +template +struct is_buffer_sequence_class + : integral_constant(0, 0)) != 1 && + sizeof(buffer_sequence_end_helper(0, 0)) != 1 && + sizeof(buffer_sequence_element_type_helper(0, 0)) == 1> +{ +}; + +template +struct is_buffer_sequence + : conditional::value, + is_buffer_sequence_class, + false_type>::type +{ +}; + +template <> +struct is_buffer_sequence + : true_type +{ +}; + +template <> +struct is_buffer_sequence + : true_type +{ +}; + +template <> +struct is_buffer_sequence + : true_type +{ +}; + +template <> +struct is_buffer_sequence + : false_type +{ +}; + +template +struct is_dynamic_buffer_class_v1 + : integral_constant(0)) != 1 && + sizeof(max_size_memfn_helper(0)) != 1 && + sizeof(capacity_memfn_helper(0)) != 1 && + sizeof(data_memfn_helper(0)) != 1 && + sizeof(consume_memfn_helper(0)) != 1 && + sizeof(prepare_memfn_helper(0)) != 1 && + sizeof(commit_memfn_helper(0)) != 1 && + sizeof(const_buffers_type_typedef_helper(0)) == 1 && + sizeof(mutable_buffers_type_typedef_helper(0)) == 1> +{ +}; + +template +struct is_dynamic_buffer_v1 + : conditional::value, + is_dynamic_buffer_class_v1, + false_type>::type +{ +}; + +template +struct is_dynamic_buffer_class_v2 + : integral_constant(0)) != 1 && + sizeof(max_size_memfn_helper(0)) != 1 && + sizeof(capacity_memfn_helper(0)) != 1 && + sizeof(data_memfn_helper(0)) != 1 && + sizeof(consume_memfn_helper(0)) != 1 && + sizeof(grow_memfn_helper(0)) != 1 && + sizeof(shrink_memfn_helper(0)) != 1 && + sizeof(const_buffers_type_typedef_helper(0)) == 1 && + sizeof(mutable_buffers_type_typedef_helper(0)) == 1> +{ +}; + +template +struct is_dynamic_buffer_v2 + : conditional::value, + is_dynamic_buffer_class_v2, + false_type>::type +{ +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IS_BUFFER_SEQUENCE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/is_executor.hpp b/third_party/asio/1.18.2/include/asio/detail/is_executor.hpp new file mode 100644 index 000000000..a099eecff --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/is_executor.hpp @@ -0,0 +1,126 @@ +// +// detail/is_executor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IS_EXECUTOR_HPP +#define ASIO_DETAIL_IS_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct executor_memfns_base +{ + void context(); + void on_work_started(); + void on_work_finished(); + void dispatch(); + void post(); + void defer(); +}; + +template +struct executor_memfns_derived + : T, executor_memfns_base +{ +}; + +template +struct executor_memfns_check +{ +}; + +template +char (&context_memfn_helper(...))[2]; + +template +char context_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::context>*); + +template +char (&on_work_started_memfn_helper(...))[2]; + +template +char on_work_started_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::on_work_started>*); + +template +char (&on_work_finished_memfn_helper(...))[2]; + +template +char on_work_finished_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::on_work_finished>*); + +template +char (&dispatch_memfn_helper(...))[2]; + +template +char dispatch_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::dispatch>*); + +template +char (&post_memfn_helper(...))[2]; + +template +char post_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::post>*); + +template +char (&defer_memfn_helper(...))[2]; + +template +char defer_memfn_helper( + executor_memfns_check< + void (executor_memfns_base::*)(), + &executor_memfns_derived::defer>*); + +template +struct is_executor_class + : integral_constant(0)) != 1 && + sizeof(on_work_started_memfn_helper(0)) != 1 && + sizeof(on_work_finished_memfn_helper(0)) != 1 && + sizeof(dispatch_memfn_helper(0)) != 1 && + sizeof(post_memfn_helper(0)) != 1 && + sizeof(defer_memfn_helper(0)) != 1> +{ +}; + +template +struct is_executor + : conditional::value, + is_executor_class, + false_type>::type +{ +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IS_EXECUTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/keyword_tss_ptr.hpp b/third_party/asio/1.18.2/include/asio/detail/keyword_tss_ptr.hpp new file mode 100644 index 000000000..9378af3d4 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/keyword_tss_ptr.hpp @@ -0,0 +1,70 @@ +// +// detail/keyword_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_KEYWORD_TSS_PTR_HPP +#define ASIO_DETAIL_KEYWORD_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_THREAD_KEYWORD_EXTENSION) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class keyword_tss_ptr + : private noncopyable +{ +public: + // Constructor. + keyword_tss_ptr() + { + } + + // Destructor. + ~keyword_tss_ptr() + { + } + + // Get the value. + operator T*() const + { + return value_; + } + + // Set the value. + void operator=(T* value) + { + value_ = value; + } + +private: + static ASIO_THREAD_KEYWORD T* value_; +}; + +template +ASIO_THREAD_KEYWORD T* keyword_tss_ptr::value_; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_THREAD_KEYWORD_EXTENSION) + +#endif // ASIO_DETAIL_KEYWORD_TSS_PTR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/kqueue_reactor.hpp b/third_party/asio/1.18.2/include/asio/detail/kqueue_reactor.hpp new file mode 100644 index 000000000..97e67069f --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/kqueue_reactor.hpp @@ -0,0 +1,242 @@ +// +// detail/kqueue_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_KQUEUE_REACTOR_HPP +#define ASIO_DETAIL_KQUEUE_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include +#include +#include +#include +#include "asio/detail/limits.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/object_pool.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" + +// Older versions of Mac OS X may not define EV_OOBAND. +#if !defined(EV_OOBAND) +# define EV_OOBAND EV_FLAG1 +#endif // !defined(EV_OOBAND) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class scheduler; + +class kqueue_reactor + : public execution_context_service_base +{ +private: + // The mutex type used by this reactor. + typedef conditionally_enabled_mutex mutex; + +public: + enum op_types { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + + // Per-descriptor queues. + struct descriptor_state + { + descriptor_state(bool locking) : mutex_(locking) {} + + friend class kqueue_reactor; + friend class object_pool_access; + + descriptor_state* next_; + descriptor_state* prev_; + + mutex mutex_; + int descriptor_; + int num_kevents_; // 1 == read only, 2 == read and write + op_queue op_queue_[max_ops]; + bool shutdown_; + }; + + // Per-descriptor data. + typedef descriptor_state* per_descriptor_data; + + // Constructor. + ASIO_DECL kqueue_reactor(asio::execution_context& ctx); + + // Destructor. + ASIO_DECL ~kqueue_reactor(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Recreate internal descriptors following a fork. + ASIO_DECL void notify_fork( + asio::execution_context::fork_event fork_ev); + + // Initialise the task. + ASIO_DECL void init_task(); + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + ASIO_DECL int register_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data); + + // Register a descriptor with an associated single operation. Returns 0 on + // success, system error code on failure. + ASIO_DECL int register_internal_descriptor( + int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op); + + // Move descriptor registration from one descriptor_data object to another. + ASIO_DECL void move_descriptor(socket_type descriptor, + per_descriptor_data& target_descriptor_data, + per_descriptor_data& source_descriptor_data); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op, bool is_continuation) + { + scheduler_.post_immediate_completion(op, is_continuation); + } + + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + ASIO_DECL void start_op(int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op, + bool is_continuation, bool allow_speculative); + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + ASIO_DECL void cancel_ops(socket_type descriptor, + per_descriptor_data& descriptor_data); + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. The reactor resources associated with + // the descriptor must be released by calling cleanup_descriptor_data. + ASIO_DECL void deregister_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data, bool closing); + + // Remove the descriptor's registration from the reactor. The reactor + // resources associated with the descriptor must be released by calling + // cleanup_descriptor_data. + ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data& descriptor_data); + + // Perform any post-deregistration cleanup tasks associated with the + // descriptor data. + ASIO_DECL void cleanup_descriptor_data( + per_descriptor_data& descriptor_data); + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& queue); + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source); + + // Run the kqueue loop. + ASIO_DECL void run(long usec, op_queue& ops); + + // Interrupt the kqueue loop. + ASIO_DECL void interrupt(); + +private: + // Create the kqueue file descriptor. Throws an exception if the descriptor + // cannot be created. + ASIO_DECL static int do_kqueue_create(); + + // Allocate a new descriptor state object. + ASIO_DECL descriptor_state* allocate_descriptor_state(); + + // Free an existing descriptor state object. + ASIO_DECL void free_descriptor_state(descriptor_state* s); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Get the timeout value for the kevent call. + ASIO_DECL timespec* get_timeout(long usec, timespec& ts); + + // The scheduler used to post completions. + scheduler& scheduler_; + + // Mutex to protect access to internal data. + mutex mutex_; + + // The kqueue file descriptor. + int kqueue_fd_; + + // The interrupter is used to break a blocking kevent call. + select_interrupter interrupter_; + + // The timer queues. + timer_queue_set timer_queues_; + + // Whether the service has been shut down. + bool shutdown_; + + // Mutex to protect access to the registered descriptors. + mutex registered_descriptors_mutex_; + + // Keep track of all registered descriptors. + object_pool registered_descriptors_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/kqueue_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/kqueue_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_KQUEUE) + +#endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/limits.hpp b/third_party/asio/1.18.2/include/asio/detail/limits.hpp new file mode 100644 index 000000000..d32470d2d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/limits.hpp @@ -0,0 +1,26 @@ +// +// detail/limits.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_LIMITS_HPP +#define ASIO_DETAIL_LIMITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_LIMITS) +# include +#else // defined(ASIO_HAS_BOOST_LIMITS) +# include +#endif // defined(ASIO_HAS_BOOST_LIMITS) + +#endif // ASIO_DETAIL_LIMITS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/local_free_on_block_exit.hpp b/third_party/asio/1.18.2/include/asio/detail/local_free_on_block_exit.hpp new file mode 100644 index 000000000..9ca4b3f68 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/local_free_on_block_exit.hpp @@ -0,0 +1,59 @@ +// +// detail/local_free_on_block_exit.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP +#define ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +#if !defined(ASIO_WINDOWS_APP) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class local_free_on_block_exit + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + explicit local_free_on_block_exit(void* p) + : p_(p) + { + } + + // Destructor restores the previous signal mask. + ~local_free_on_block_exit() + { + ::LocalFree(p_); + } + +private: + void* p_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS_APP) +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/macos_fenced_block.hpp b/third_party/asio/1.18.2/include/asio/detail/macos_fenced_block.hpp new file mode 100644 index 000000000..54768e51f --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/macos_fenced_block.hpp @@ -0,0 +1,62 @@ +// +// detail/macos_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP +#define ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__MACH__) && defined(__APPLE__) + +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class macos_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit macos_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit macos_fenced_block(full_t) + { + OSMemoryBarrier(); + } + + // Destructor. + ~macos_fenced_block() + { + OSMemoryBarrier(); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__MACH__) && defined(__APPLE__) + +#endif // ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/memory.hpp b/third_party/asio/1.18.2/include/asio/detail/memory.hpp new file mode 100644 index 000000000..2afb43e63 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/memory.hpp @@ -0,0 +1,73 @@ +// +// detail/memory.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_MEMORY_HPP +#define ASIO_DETAIL_MEMORY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +#if !defined(ASIO_HAS_STD_SHARED_PTR) +# include +# include +# include +#endif // !defined(ASIO_HAS_STD_SHARED_PTR) + +#if !defined(ASIO_HAS_STD_ADDRESSOF) +# include +#endif // !defined(ASIO_HAS_STD_ADDRESSOF) + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_STD_SHARED_PTR) +using std::make_shared; +using std::shared_ptr; +using std::weak_ptr; +#else // defined(ASIO_HAS_STD_SHARED_PTR) +using boost::make_shared; +using boost::shared_ptr; +using boost::weak_ptr; +#endif // defined(ASIO_HAS_STD_SHARED_PTR) + +#if defined(ASIO_HAS_STD_ADDRESSOF) +using std::addressof; +#else // defined(ASIO_HAS_STD_ADDRESSOF) +using boost::addressof; +#endif // defined(ASIO_HAS_STD_ADDRESSOF) + +} // namespace detail + +#if defined(ASIO_HAS_CXX11_ALLOCATORS) +using std::allocator_arg_t; +# define ASIO_USES_ALLOCATOR(t) \ + namespace std { \ + template \ + struct uses_allocator : true_type {}; \ + } \ + /**/ +# define ASIO_REBIND_ALLOC(alloc, t) \ + typename std::allocator_traits::template rebind_alloc + /**/ +#else // defined(ASIO_HAS_CXX11_ALLOCATORS) +struct allocator_arg_t {}; +# define ASIO_USES_ALLOCATOR(t) +# define ASIO_REBIND_ALLOC(alloc, t) \ + typename alloc::template rebind::other + /**/ +#endif // defined(ASIO_HAS_CXX11_ALLOCATORS) + +} // namespace asio + +#endif // ASIO_DETAIL_MEMORY_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/mutex.hpp new file mode 100644 index 000000000..283fade65 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/mutex.hpp @@ -0,0 +1,48 @@ +// +// detail/mutex.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_MUTEX_HPP +#define ASIO_DETAIL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_mutex.hpp" +#elif defined(ASIO_WINDOWS) +# include "asio/detail/win_mutex.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_mutex.hpp" +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +# include "asio/detail/std_mutex.hpp" +#else +# error Only Windows, POSIX and std::mutex are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) +typedef null_mutex mutex; +#elif defined(ASIO_WINDOWS) +typedef win_mutex mutex; +#elif defined(ASIO_HAS_PTHREADS) +typedef posix_mutex mutex; +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +typedef std_mutex mutex; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/non_const_lvalue.hpp b/third_party/asio/1.18.2/include/asio/detail/non_const_lvalue.hpp new file mode 100644 index 000000000..496036568 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/non_const_lvalue.hpp @@ -0,0 +1,54 @@ +// +// detail/non_const_lvalue.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NON_CONST_LVALUE_HPP +#define ASIO_DETAIL_NON_CONST_LVALUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct non_const_lvalue +{ +#if defined(ASIO_HAS_MOVE) + explicit non_const_lvalue(T& t) + : value(static_cast::type>::value, + typename decay::type&, T&&>::type>(t)) + { + } + + typename conditional::type>::value, + typename decay::type&, typename decay::type>::type value; +#else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + explicit non_const_lvalue(const typename decay::type& t) + : value(t) + { + } + + typename decay::type value; +#endif // defined(ASIO_HAS_MOVE) +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NON_CONST_LVALUE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/noncopyable.hpp b/third_party/asio/1.18.2/include/asio/detail/noncopyable.hpp new file mode 100644 index 000000000..a148e031c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/noncopyable.hpp @@ -0,0 +1,43 @@ +// +// detail/noncopyable.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NONCOPYABLE_HPP +#define ASIO_DETAIL_NONCOPYABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class noncopyable +{ +protected: + noncopyable() {} + ~noncopyable() {} +private: + noncopyable(const noncopyable&); + const noncopyable& operator=(const noncopyable&); +}; + +} // namespace detail + +using asio::detail::noncopyable; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NONCOPYABLE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/null_event.hpp b/third_party/asio/1.18.2/include/asio/detail/null_event.hpp new file mode 100644 index 000000000..5bff3c46a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/null_event.hpp @@ -0,0 +1,106 @@ +// +// detail/null_event.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_EVENT_HPP +#define ASIO_DETAIL_NULL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_event + : private noncopyable +{ +public: + // Constructor. + null_event() + { + } + + // Destructor. + ~null_event() + { + } + + // Signal the event. (Retained for backward compatibility.) + template + void signal(Lock&) + { + } + + // Signal all waiters. + template + void signal_all(Lock&) + { + } + + // Unlock the mutex and signal one waiter. + template + void unlock_and_signal_one(Lock&) + { + } + + // Unlock the mutex and signal one waiter who may destroy us. + template + void unlock_and_signal_one_for_destruction(Lock&) + { + } + + // If there's a waiter, unlock the mutex and signal it. + template + bool maybe_unlock_and_signal_one(Lock&) + { + return false; + } + + // Reset the event. + template + void clear(Lock&) + { + } + + // Wait for the event to become signalled. + template + void wait(Lock&) + { + do_wait(); + } + + // Timed wait for the event to become signalled. + template + bool wait_for_usec(Lock&, long usec) + { + do_wait_for_usec(usec); + return true; + } + +private: + ASIO_DECL static void do_wait(); + ASIO_DECL static void do_wait_for_usec(long usec); +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/null_event.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_NULL_EVENT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/null_fenced_block.hpp b/third_party/asio/1.18.2/include/asio/detail/null_fenced_block.hpp new file mode 100644 index 000000000..265b3bffd --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/null_fenced_block.hpp @@ -0,0 +1,47 @@ +// +// detail/null_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_FENCED_BLOCK_HPP +#define ASIO_DETAIL_NULL_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_fenced_block + : private noncopyable +{ +public: + enum half_or_full_t { half, full }; + + // Constructor. + explicit null_fenced_block(half_or_full_t) + { + } + + // Destructor. + ~null_fenced_block() + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_FENCED_BLOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/null_global.hpp b/third_party/asio/1.18.2/include/asio/detail/null_global.hpp new file mode 100644 index 000000000..7bd12502e --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/null_global.hpp @@ -0,0 +1,59 @@ +// +// detail/null_global.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_GLOBAL_HPP +#define ASIO_DETAIL_NULL_GLOBAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct null_global_impl +{ + null_global_impl() + : ptr_(0) + { + } + + // Destructor automatically cleans up the global. + ~null_global_impl() + { + delete ptr_; + } + + static null_global_impl instance_; + T* ptr_; +}; + +template +null_global_impl null_global_impl::instance_; + +template +T& null_global() +{ + if (null_global_impl::instance_.ptr_ == 0) + null_global_impl::instance_.ptr_ = new T; + return *null_global_impl::instance_.ptr_; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_GLOBAL_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/null_mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/null_mutex.hpp new file mode 100644 index 000000000..b54573398 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/null_mutex.hpp @@ -0,0 +1,64 @@ +// +// detail/null_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_MUTEX_HPP +#define ASIO_DETAIL_NULL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + null_mutex() + { + } + + // Destructor. + ~null_mutex() + { + } + + // Lock the mutex. + void lock() + { + } + + // Unlock the mutex. + void unlock() + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_THREADS) + +#endif // ASIO_DETAIL_NULL_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/null_reactor.hpp b/third_party/asio/1.18.2/include/asio/detail/null_reactor.hpp new file mode 100644 index 000000000..f05c6ee15 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/null_reactor.hpp @@ -0,0 +1,68 @@ +// +// detail/null_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_REACTOR_HPP +#define ASIO_DETAIL_NULL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) || defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/scheduler_operation.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_reactor + : public execution_context_service_base +{ +public: + // Constructor. + null_reactor(asio::execution_context& ctx) + : execution_context_service_base(ctx) + { + } + + // Destructor. + ~null_reactor() + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + } + + // No-op because should never be called. + void run(long /*usec*/, op_queue& /*ops*/) + { + } + + // No-op. + void interrupt() + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) || defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_NULL_REACTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/null_signal_blocker.hpp b/third_party/asio/1.18.2/include/asio/detail/null_signal_blocker.hpp new file mode 100644 index 000000000..8a9bf367a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/null_signal_blocker.hpp @@ -0,0 +1,69 @@ +// +// detail/null_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) \ + || defined(ASIO_WINDOWS) \ + || defined(ASIO_WINDOWS_RUNTIME) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + null_signal_blocker() + { + } + + // Destructor restores the previous signal mask. + ~null_signal_blocker() + { + } + + // Block all signals for the calling thread. + void block() + { + } + + // Restore the previous signal mask. + void unblock() + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_THREADS) + // || defined(ASIO_WINDOWS) + // || defined(ASIO_WINDOWS_RUNTIME) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + +#endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/null_socket_service.hpp b/third_party/asio/1.18.2/include/asio/detail/null_socket_service.hpp new file mode 100644 index 000000000..e6525465c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/null_socket_service.hpp @@ -0,0 +1,519 @@ +// +// detail/null_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_NULL_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/post.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class null_socket_service : + public execution_context_service_base > +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef int native_handle_type; + + // The implementation type of the socket. + struct implementation_type + { + }; + + // Constructor. + null_socket_service(execution_context& context) + : execution_context_service_base >(context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + } + + // Construct a new socket implementation. + void construct(implementation_type&) + { + } + + // Move-construct a new socket implementation. + void move_construct(implementation_type&, implementation_type&) + { + } + + // Move-assign from another socket implementation. + void move_assign(implementation_type&, + null_socket_service&, implementation_type&) + { + } + + // Move-construct a new socket implementation from another protocol type. + template + void converting_move_construct(implementation_type&, + null_socket_service&, + typename null_socket_service::implementation_type&) + { + } + + // Destroy a socket implementation. + void destroy(implementation_type&) + { + } + + // Open a new socket implementation. + asio::error_code open(implementation_type&, + const protocol_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type&, const protocol_type&, + const native_handle_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Determine whether the socket is open. + bool is_open(const implementation_type&) const + { + return false; + } + + // Destroy a socket implementation. + asio::error_code close(implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Release ownership of the socket. + native_handle_type release(implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Get the native socket representation. + native_handle_type native_handle(implementation_type&) + { + return 0; + } + + // Cancel all operations associated with the socket. + asio::error_code cancel(implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return false; + } + + // Determine the number of bytes available for reading. + std::size_t available(const implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(implementation_type&, + int, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(implementation_type&, + IO_Control_Command&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Gets the non-blocking mode of the socket. + bool non_blocking(const implementation_type&) const + { + return false; + } + + // Sets the non-blocking mode of the socket. + asio::error_code non_blocking(implementation_type&, + bool, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const implementation_type&) const + { + return false; + } + + // Sets the non-blocking mode of the native socket implementation. + asio::error_code native_non_blocking(implementation_type&, + bool, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Disable sends or receives on the socket. + asio::error_code shutdown(implementation_type&, + socket_base::shutdown_type, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type&, + const endpoint_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Set a socket option. + template + asio::error_code set_option(implementation_type&, + const Option&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Set a socket option. + template + asio::error_code get_option(const implementation_type&, + Option&, asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return endpoint_type(); + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return endpoint_type(); + } + + // Send the given data to the peer. + template + std::size_t send(implementation_type&, const ConstBufferSequence&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Wait until data can be sent without blocking. + std::size_t send(implementation_type&, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(implementation_type&, const ConstBufferSequence&, + socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, detail::bind_handler( + handler, ec, bytes_transferred)); + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(implementation_type&, const null_buffers&, + socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, detail::bind_handler( + handler, ec, bytes_transferred)); + } + + // Receive some data from the peer. Returns the number of bytes received. + template + std::size_t receive(implementation_type&, const MutableBufferSequence&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Wait until data can be received without blocking. + std::size_t receive(implementation_type&, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(implementation_type&, const MutableBufferSequence&, + socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, detail::bind_handler( + handler, ec, bytes_transferred)); + } + + // Wait until data can be received without blocking. + template + void async_receive(implementation_type&, const null_buffers&, + socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, detail::bind_handler( + handler, ec, bytes_transferred)); + } + + // Receive some data with associated flags. Returns the number of bytes + // received. + template + std::size_t receive_with_flags(implementation_type&, + const MutableBufferSequence&, socket_base::message_flags, + socket_base::message_flags&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Wait until data can be received without blocking. + std::size_t receive_with_flags(implementation_type&, + const null_buffers&, socket_base::message_flags, + socket_base::message_flags&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive_with_flags(implementation_type&, + const MutableBufferSequence&, socket_base::message_flags, + socket_base::message_flags&, Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, detail::bind_handler( + handler, ec, bytes_transferred)); + } + + // Wait until data can be received without blocking. + template + void async_receive_with_flags(implementation_type&, const null_buffers&, + socket_base::message_flags, socket_base::message_flags&, + Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, detail::bind_handler( + handler, ec, bytes_transferred)); + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + std::size_t send_to(implementation_type&, const ConstBufferSequence&, + const endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Wait until data can be sent without blocking. + std::size_t send_to(implementation_type&, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(implementation_type&, const ConstBufferSequence&, + const endpoint_type&, socket_base::message_flags, + Handler& handler) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, detail::bind_handler( + handler, ec, bytes_transferred)); + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send_to(implementation_type&, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, detail::bind_handler( + handler, ec, bytes_transferred)); + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + std::size_t receive_from(implementation_type&, const MutableBufferSequence&, + endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Wait until data can be received without blocking. + std::size_t receive_from(implementation_type&, const null_buffers&, + endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(implementation_type&, const MutableBufferSequence&, + endpoint_type&, socket_base::message_flags, Handler& handler, + const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, detail::bind_handler( + handler, ec, bytes_transferred)); + } + + // Wait until data can be received without blocking. + template + void async_receive_from(implementation_type&, const null_buffers&, + endpoint_type&, socket_base::message_flags, Handler& handler, + const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, detail::bind_handler( + handler, ec, bytes_transferred)); + } + + // Accept a new connection. + template + asio::error_code accept(implementation_type&, + Socket&, endpoint_type*, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept(implementation_type&, Socket&, endpoint_type*, + Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + asio::post(io_ex, detail::bind_handler(handler, ec)); + } + + // Connect the socket to the specified endpoint. + asio::error_code connect(implementation_type&, + const endpoint_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Start an asynchronous connect. + template + void async_connect(implementation_type&, const endpoint_type&, + Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + asio::post(io_ex, detail::bind_handler(handler, ec)); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_NULL_SOCKET_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/null_static_mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/null_static_mutex.hpp new file mode 100644 index 000000000..4053056aa --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/null_static_mutex.hpp @@ -0,0 +1,60 @@ +// +// detail/null_static_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_STATIC_MUTEX_HPP +#define ASIO_DETAIL_NULL_STATIC_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) + +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct null_static_mutex +{ + typedef asio::detail::scoped_lock scoped_lock; + + // Initialise the mutex. + void init() + { + } + + // Lock the mutex. + void lock() + { + } + + // Unlock the mutex. + void unlock() + { + } + + int unused_; +}; + +#define ASIO_NULL_STATIC_MUTEX_INIT { 0 } + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_THREADS) + +#endif // ASIO_DETAIL_NULL_STATIC_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/null_thread.hpp b/third_party/asio/1.18.2/include/asio/detail/null_thread.hpp new file mode 100644 index 000000000..bbdba5562 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/null_thread.hpp @@ -0,0 +1,67 @@ +// +// detail/null_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_THREAD_HPP +#define ASIO_DETAIL_NULL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_thread + : private noncopyable +{ +public: + // Constructor. + template + null_thread(Function, unsigned int = 0) + { + asio::detail::throw_error( + asio::error::operation_not_supported, "thread"); + } + + // Destructor. + ~null_thread() + { + } + + // Wait for the thread to exit. + void join() + { + } + + // Get number of CPUs. + static std::size_t hardware_concurrency() + { + return 1; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_THREADS) + +#endif // ASIO_DETAIL_NULL_THREAD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/null_tss_ptr.hpp b/third_party/asio/1.18.2/include/asio/detail/null_tss_ptr.hpp new file mode 100644 index 000000000..e2b4cf3d6 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/null_tss_ptr.hpp @@ -0,0 +1,68 @@ +// +// detail/null_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_TSS_PTR_HPP +#define ASIO_DETAIL_NULL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class null_tss_ptr + : private noncopyable +{ +public: + // Constructor. + null_tss_ptr() + : value_(0) + { + } + + // Destructor. + ~null_tss_ptr() + { + } + + // Get the value. + operator T*() const + { + return value_; + } + + // Set the value. + void operator=(T* value) + { + value_ = value; + } + +private: + T* value_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_THREADS) + +#endif // ASIO_DETAIL_NULL_TSS_PTR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/object_pool.hpp b/third_party/asio/1.18.2/include/asio/detail/object_pool.hpp new file mode 100644 index 000000000..45524d355 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/object_pool.hpp @@ -0,0 +1,171 @@ +// +// detail/object_pool.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_OBJECT_POOL_HPP +#define ASIO_DETAIL_OBJECT_POOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class object_pool; + +class object_pool_access +{ +public: + template + static Object* create() + { + return new Object; + } + + template + static Object* create(Arg arg) + { + return new Object(arg); + } + + template + static void destroy(Object* o) + { + delete o; + } + + template + static Object*& next(Object* o) + { + return o->next_; + } + + template + static Object*& prev(Object* o) + { + return o->prev_; + } +}; + +template +class object_pool + : private noncopyable +{ +public: + // Constructor. + object_pool() + : live_list_(0), + free_list_(0) + { + } + + // Destructor destroys all objects. + ~object_pool() + { + destroy_list(live_list_); + destroy_list(free_list_); + } + + // Get the object at the start of the live list. + Object* first() + { + return live_list_; + } + + // Allocate a new object. + Object* alloc() + { + Object* o = free_list_; + if (o) + free_list_ = object_pool_access::next(free_list_); + else + o = object_pool_access::create(); + + object_pool_access::next(o) = live_list_; + object_pool_access::prev(o) = 0; + if (live_list_) + object_pool_access::prev(live_list_) = o; + live_list_ = o; + + return o; + } + + // Allocate a new object with an argument. + template + Object* alloc(Arg arg) + { + Object* o = free_list_; + if (o) + free_list_ = object_pool_access::next(free_list_); + else + o = object_pool_access::create(arg); + + object_pool_access::next(o) = live_list_; + object_pool_access::prev(o) = 0; + if (live_list_) + object_pool_access::prev(live_list_) = o; + live_list_ = o; + + return o; + } + + // Free an object. Moves it to the free list. No destructors are run. + void free(Object* o) + { + if (live_list_ == o) + live_list_ = object_pool_access::next(o); + + if (object_pool_access::prev(o)) + { + object_pool_access::next(object_pool_access::prev(o)) + = object_pool_access::next(o); + } + + if (object_pool_access::next(o)) + { + object_pool_access::prev(object_pool_access::next(o)) + = object_pool_access::prev(o); + } + + object_pool_access::next(o) = free_list_; + object_pool_access::prev(o) = 0; + free_list_ = o; + } + +private: + // Helper function to destroy all elements in a list. + void destroy_list(Object* list) + { + while (list) + { + Object* o = list; + list = object_pool_access::next(o); + object_pool_access::destroy(o); + } + } + + // The list of live objects. + Object* live_list_; + + // The free list. + Object* free_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_OBJECT_POOL_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/old_win_sdk_compat.hpp b/third_party/asio/1.18.2/include/asio/detail/old_win_sdk_compat.hpp new file mode 100644 index 000000000..734d72e78 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/old_win_sdk_compat.hpp @@ -0,0 +1,214 @@ +// +// detail/old_win_sdk_compat.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP +#define ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +// Guess whether we are building against on old Platform SDK. +#if !defined(IN6ADDR_ANY_INIT) +#define ASIO_HAS_OLD_WIN_SDK 1 +#endif // !defined(IN6ADDR_ANY_INIT) + +#if defined(ASIO_HAS_OLD_WIN_SDK) + +// Emulation of types that are missing from old Platform SDKs. +// +// N.B. this emulation is also used if building for a Windows 2000 target with +// a recent (i.e. Vista or later) SDK, as the SDK does not provide IPv6 support +// in that case. + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +enum +{ + sockaddr_storage_maxsize = 128, // Maximum size. + sockaddr_storage_alignsize = (sizeof(__int64)), // Desired alignment. + sockaddr_storage_pad1size = (sockaddr_storage_alignsize - sizeof(short)), + sockaddr_storage_pad2size = (sockaddr_storage_maxsize - + (sizeof(short) + sockaddr_storage_pad1size + sockaddr_storage_alignsize)) +}; + +struct sockaddr_storage_emulation +{ + short ss_family; + char __ss_pad1[sockaddr_storage_pad1size]; + __int64 __ss_align; + char __ss_pad2[sockaddr_storage_pad2size]; +}; + +struct in6_addr_emulation +{ + union + { + u_char Byte[16]; + u_short Word[8]; + } u; +}; + +#if !defined(s6_addr) +# define _S6_un u +# define _S6_u8 Byte +# define s6_addr _S6_un._S6_u8 +#endif // !defined(s6_addr) + +struct sockaddr_in6_emulation +{ + short sin6_family; + u_short sin6_port; + u_long sin6_flowinfo; + in6_addr_emulation sin6_addr; + u_long sin6_scope_id; +}; + +struct ipv6_mreq_emulation +{ + in6_addr_emulation ipv6mr_multiaddr; + unsigned int ipv6mr_interface; +}; + +struct addrinfo_emulation +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char* ai_canonname; + sockaddr* ai_addr; + addrinfo_emulation* ai_next; +}; + +#if !defined(AI_PASSIVE) +# define AI_PASSIVE 0x1 +#endif + +#if !defined(AI_CANONNAME) +# define AI_CANONNAME 0x2 +#endif + +#if !defined(AI_NUMERICHOST) +# define AI_NUMERICHOST 0x4 +#endif + +#if !defined(EAI_AGAIN) +# define EAI_AGAIN WSATRY_AGAIN +#endif + +#if !defined(EAI_BADFLAGS) +# define EAI_BADFLAGS WSAEINVAL +#endif + +#if !defined(EAI_FAIL) +# define EAI_FAIL WSANO_RECOVERY +#endif + +#if !defined(EAI_FAMILY) +# define EAI_FAMILY WSAEAFNOSUPPORT +#endif + +#if !defined(EAI_MEMORY) +# define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#endif + +#if !defined(EAI_NODATA) +# define EAI_NODATA WSANO_DATA +#endif + +#if !defined(EAI_NONAME) +# define EAI_NONAME WSAHOST_NOT_FOUND +#endif + +#if !defined(EAI_SERVICE) +# define EAI_SERVICE WSATYPE_NOT_FOUND +#endif + +#if !defined(EAI_SOCKTYPE) +# define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +#endif + +#if !defined(NI_NOFQDN) +# define NI_NOFQDN 0x01 +#endif + +#if !defined(NI_NUMERICHOST) +# define NI_NUMERICHOST 0x02 +#endif + +#if !defined(NI_NAMEREQD) +# define NI_NAMEREQD 0x04 +#endif + +#if !defined(NI_NUMERICSERV) +# define NI_NUMERICSERV 0x08 +#endif + +#if !defined(NI_DGRAM) +# define NI_DGRAM 0x10 +#endif + +#if !defined(IPPROTO_IPV6) +# define IPPROTO_IPV6 41 +#endif + +#if !defined(IPV6_UNICAST_HOPS) +# define IPV6_UNICAST_HOPS 4 +#endif + +#if !defined(IPV6_MULTICAST_IF) +# define IPV6_MULTICAST_IF 9 +#endif + +#if !defined(IPV6_MULTICAST_HOPS) +# define IPV6_MULTICAST_HOPS 10 +#endif + +#if !defined(IPV6_MULTICAST_LOOP) +# define IPV6_MULTICAST_LOOP 11 +#endif + +#if !defined(IPV6_JOIN_GROUP) +# define IPV6_JOIN_GROUP 12 +#endif + +#if !defined(IPV6_LEAVE_GROUP) +# define IPV6_LEAVE_GROUP 13 +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_OLD_WIN_SDK) + +// Even newer Platform SDKs that support IPv6 may not define IPV6_V6ONLY. +#if !defined(IPV6_V6ONLY) +# define IPV6_V6ONLY 27 +#endif + +// Some SDKs (e.g. Windows CE) don't define IPPROTO_ICMPV6. +#if !defined(IPPROTO_ICMPV6) +# define IPPROTO_ICMPV6 58 +#endif + +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/op_queue.hpp b/third_party/asio/1.18.2/include/asio/detail/op_queue.hpp new file mode 100644 index 000000000..126133992 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/op_queue.hpp @@ -0,0 +1,162 @@ +// +// detail/op_queue.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_OP_QUEUE_HPP +#define ASIO_DETAIL_OP_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class op_queue; + +class op_queue_access +{ +public: + template + static Operation* next(Operation* o) + { + return static_cast(o->next_); + } + + template + static void next(Operation1*& o1, Operation2* o2) + { + o1->next_ = o2; + } + + template + static void destroy(Operation* o) + { + o->destroy(); + } + + template + static Operation*& front(op_queue& q) + { + return q.front_; + } + + template + static Operation*& back(op_queue& q) + { + return q.back_; + } +}; + +template +class op_queue + : private noncopyable +{ +public: + // Constructor. + op_queue() + : front_(0), + back_(0) + { + } + + // Destructor destroys all operations. + ~op_queue() + { + while (Operation* op = front_) + { + pop(); + op_queue_access::destroy(op); + } + } + + // Get the operation at the front of the queue. + Operation* front() + { + return front_; + } + + // Pop an operation from the front of the queue. + void pop() + { + if (front_) + { + Operation* tmp = front_; + front_ = op_queue_access::next(front_); + if (front_ == 0) + back_ = 0; + op_queue_access::next(tmp, static_cast(0)); + } + } + + // Push an operation on to the back of the queue. + void push(Operation* h) + { + op_queue_access::next(h, static_cast(0)); + if (back_) + { + op_queue_access::next(back_, h); + back_ = h; + } + else + { + front_ = back_ = h; + } + } + + // Push all operations from another queue on to the back of the queue. The + // source queue may contain operations of a derived type. + template + void push(op_queue& q) + { + if (Operation* other_front = op_queue_access::front(q)) + { + if (back_) + op_queue_access::next(back_, other_front); + else + front_ = other_front; + back_ = op_queue_access::back(q); + op_queue_access::front(q) = 0; + op_queue_access::back(q) = 0; + } + } + + // Whether the queue is empty. + bool empty() const + { + return front_ == 0; + } + + // Test whether an operation is already enqueued. + bool is_enqueued(Operation* o) const + { + return op_queue_access::next(o) != 0 || back_ == o; + } + +private: + friend class op_queue_access; + + // The front of the queue. + Operation* front_; + + // The back of the queue. + Operation* back_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_OP_QUEUE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/operation.hpp b/third_party/asio/1.18.2/include/asio/detail/operation.hpp new file mode 100644 index 000000000..6e58117ff --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/operation.hpp @@ -0,0 +1,38 @@ +// +// detail/operation.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_OPERATION_HPP +#define ASIO_DETAIL_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_operation.hpp" +#else +# include "asio/detail/scheduler_operation.hpp" +#endif + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_IOCP) +typedef win_iocp_operation operation; +#else +typedef scheduler_operation operation; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_OPERATION_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/pipe_select_interrupter.hpp b/third_party/asio/1.18.2/include/asio/detail/pipe_select_interrupter.hpp new file mode 100644 index 000000000..09b3f3bdb --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/pipe_select_interrupter.hpp @@ -0,0 +1,89 @@ +// +// detail/pipe_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) +#if !defined(ASIO_WINDOWS_RUNTIME) +#if !defined(__CYGWIN__) +#if !defined(__SYMBIAN32__) +#if !defined(ASIO_HAS_EVENTFD) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class pipe_select_interrupter +{ +public: + // Constructor. + ASIO_DECL pipe_select_interrupter(); + + // Destructor. + ASIO_DECL ~pipe_select_interrupter(); + + // Recreate the interrupter's descriptors. Used after a fork. + ASIO_DECL void recreate(); + + // Interrupt the select call. + ASIO_DECL void interrupt(); + + // Reset the select interrupter. Returns true if the reset was successful. + ASIO_DECL bool reset(); + + // Get the read descriptor to be passed to select. + int read_descriptor() const + { + return read_descriptor_; + } + +private: + // Open the descriptors. Throws on error. + ASIO_DECL void open_descriptors(); + + // Close the descriptors. + ASIO_DECL void close_descriptors(); + + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + int read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + int write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/pipe_select_interrupter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_HAS_EVENTFD) +#endif // !defined(__SYMBIAN32__) +#endif // !defined(__CYGWIN__) +#endif // !defined(ASIO_WINDOWS_RUNTIME) +#endif // !defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/pop_options.hpp b/third_party/asio/1.18.2/include/asio/detail/pop_options.hpp new file mode 100644 index 000000000..260e5e009 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/pop_options.hpp @@ -0,0 +1,141 @@ +// +// detail/pop_options.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# if !defined(ASIO_DISABLE_VISIBILITY) +# pragma GCC visibility pop +# endif // !defined(ASIO_DISABLE_VISIBILITY) +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) + +#elif defined(__clang__) + +// Clang + +# if defined(__OBJC__) +# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) +# if defined(ASIO_OBJC_WORKAROUND) +# undef Protocol +# undef id +# undef ASIO_OBJC_WORKAROUND +# endif +# endif +# endif + +# if !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32) +# if !defined(ASIO_DISABLE_VISIBILITY) +# pragma GCC visibility pop +# endif // !defined(ASIO_DISABLE_VISIBILITY) +# endif // !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32) + +# pragma GCC diagnostic pop + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined(__MINGW32__) || defined(__CYGWIN__) +# pragma pack (pop) +# endif + +# if defined(__OBJC__) +# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) +# if defined(ASIO_OBJC_WORKAROUND) +# undef Protocol +# undef id +# undef ASIO_OBJC_WORKAROUND +# endif +# endif +# endif + +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# if !defined(ASIO_DISABLE_VISIBILITY) +# pragma GCC visibility pop +# endif // !defined(ASIO_DISABLE_VISIBILITY) +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) + +# pragma GCC diagnostic pop + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) && !defined(__clang__) + +// Borland C++ + +# pragma option pop +# pragma nopushoptwarn +# pragma nopackwarning + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (pop) +# pragma pack (pop) + +# if defined(__cplusplus_cli) || defined(__cplusplus_winrt) +# if defined(ASIO_CLR_WORKAROUND) +# undef generic +# undef ASIO_CLR_WORKAROUND +# endif +# endif + +#endif diff --git a/third_party/asio/1.18.2/include/asio/detail/posix_event.hpp b/third_party/asio/1.18.2/include/asio/detail/posix_event.hpp new file mode 100644 index 000000000..8a06b4224 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/posix_event.hpp @@ -0,0 +1,175 @@ +// +// detail/posix_event.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_EVENT_HPP +#define ASIO_DETAIL_POSIX_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class posix_event + : private noncopyable +{ +public: + // Constructor. + ASIO_DECL posix_event(); + + // Destructor. + ~posix_event() + { + ::pthread_cond_destroy(&cond_); + } + + // Signal the event. (Retained for backward compatibility.) + template + void signal(Lock& lock) + { + this->signal_all(lock); + } + + // Signal all waiters. + template + void signal_all(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + state_ |= 1; + ::pthread_cond_broadcast(&cond_); // Ignore EINVAL. + } + + // Unlock the mutex and signal one waiter. + template + void unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + bool have_waiters = (state_ > 1); + lock.unlock(); + if (have_waiters) + ::pthread_cond_signal(&cond_); // Ignore EINVAL. + } + + // Unlock the mutex and signal one waiter who may destroy us. + template + void unlock_and_signal_one_for_destruction(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + bool have_waiters = (state_ > 1); + if (have_waiters) + ::pthread_cond_signal(&cond_); // Ignore EINVAL. + lock.unlock(); + } + + // If there's a waiter, unlock the mutex and signal it. + template + bool maybe_unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + if (state_ > 1) + { + lock.unlock(); + ::pthread_cond_signal(&cond_); // Ignore EINVAL. + return true; + } + return false; + } + + // Reset the event. + template + void clear(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + state_ &= ~std::size_t(1); + } + + // Wait for the event to become signalled. + template + void wait(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + while ((state_ & 1) == 0) + { + state_ += 2; + ::pthread_cond_wait(&cond_, &lock.mutex().mutex_); // Ignore EINVAL. + state_ -= 2; + } + } + + // Timed wait for the event to become signalled. + template + bool wait_for_usec(Lock& lock, long usec) + { + ASIO_ASSERT(lock.locked()); + if ((state_ & 1) == 0) + { + state_ += 2; + timespec ts; +#if (defined(__MACH__) && defined(__APPLE__)) \ + || (defined(__ANDROID__) && (__ANDROID_API__ < 21) \ + && defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)) + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + ::pthread_cond_timedwait_relative_np( + &cond_, &lock.mutex().mutex_, &ts); // Ignore EINVAL. +#else // (defined(__MACH__) && defined(__APPLE__)) + // || (defined(__ANDROID__) && (__ANDROID_API__ < 21) + // && defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)) + if (::clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + { + ts.tv_sec += usec / 1000000; + ts.tv_nsec += (usec % 1000000) * 1000; + ts.tv_sec += ts.tv_nsec / 1000000000; + ts.tv_nsec = ts.tv_nsec % 1000000000; + ::pthread_cond_timedwait(&cond_, + &lock.mutex().mutex_, &ts); // Ignore EINVAL. + } +#endif // (defined(__MACH__) && defined(__APPLE__)) + // || (defined(__ANDROID__) && (__ANDROID_API__ < 21) + // && defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)) + state_ -= 2; + } + return (state_ & 1) != 0; + } + +private: + ::pthread_cond_t cond_; + std::size_t state_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_event.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_EVENT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/posix_fd_set_adapter.hpp b/third_party/asio/1.18.2/include/asio/detail/posix_fd_set_adapter.hpp new file mode 100644 index 000000000..230160b8e --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/posix_fd_set_adapter.hpp @@ -0,0 +1,118 @@ +// +// detail/posix_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) \ + && !defined(__CYGWIN__) \ + && !defined(ASIO_WINDOWS_RUNTIME) + +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class posix_fd_set_adapter : noncopyable +{ +public: + posix_fd_set_adapter() + : max_descriptor_(invalid_socket) + { + using namespace std; // Needed for memset on Solaris. + FD_ZERO(&fd_set_); + } + + void reset() + { + using namespace std; // Needed for memset on Solaris. + FD_ZERO(&fd_set_); + } + + bool set(socket_type descriptor) + { + if (descriptor < (socket_type)FD_SETSIZE) + { + if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_) + max_descriptor_ = descriptor; + FD_SET(descriptor, &fd_set_); + return true; + } + return false; + } + + void set(reactor_op_queue& operations, op_queue& ops) + { + reactor_op_queue::iterator i = operations.begin(); + while (i != operations.end()) + { + reactor_op_queue::iterator op_iter = i++; + if (!set(op_iter->first)) + { + asio::error_code ec(error::fd_set_failure); + operations.cancel_operations(op_iter, ops, ec); + } + } + } + + bool is_set(socket_type descriptor) const + { + return FD_ISSET(descriptor, &fd_set_) != 0; + } + + operator fd_set*() + { + return &fd_set_; + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + + void perform(reactor_op_queue& operations, + op_queue& ops) const + { + reactor_op_queue::iterator i = operations.begin(); + while (i != operations.end()) + { + reactor_op_queue::iterator op_iter = i++; + if (is_set(op_iter->first)) + operations.perform_operations(op_iter, ops); + } + } + +private: + mutable fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) + // && !defined(__CYGWIN__) + // && !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/posix_global.hpp b/third_party/asio/1.18.2/include/asio/detail/posix_global.hpp new file mode 100644 index 000000000..b7b113a47 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/posix_global.hpp @@ -0,0 +1,80 @@ +// +// detail/posix_global.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_GLOBAL_HPP +#define ASIO_DETAIL_POSIX_GLOBAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct posix_global_impl +{ + // Helper function to perform initialisation. + static void do_init() + { + instance_.static_ptr_ = instance_.ptr_ = new T; + } + + // Destructor automatically cleans up the global. + ~posix_global_impl() + { + delete static_ptr_; + } + + static ::pthread_once_t init_once_; + static T* static_ptr_; + static posix_global_impl instance_; + T* ptr_; +}; + +template +::pthread_once_t posix_global_impl::init_once_ = PTHREAD_ONCE_INIT; + +template +T* posix_global_impl::static_ptr_ = 0; + +template +posix_global_impl posix_global_impl::instance_; + +template +T& posix_global() +{ + int result = ::pthread_once( + &posix_global_impl::init_once_, + &posix_global_impl::do_init); + + if (result != 0) + std::terminate(); + + return *posix_global_impl::instance_.ptr_; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_GLOBAL_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/posix_mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/posix_mutex.hpp new file mode 100644 index 000000000..ee0e8b202 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/posix_mutex.hpp @@ -0,0 +1,76 @@ +// +// detail/posix_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_MUTEX_HPP +#define ASIO_DETAIL_POSIX_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class posix_event; + +class posix_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + ASIO_DECL posix_mutex(); + + // Destructor. + ~posix_mutex() + { + ::pthread_mutex_destroy(&mutex_); // Ignore EBUSY. + } + + // Lock the mutex. + void lock() + { + (void)::pthread_mutex_lock(&mutex_); // Ignore EINVAL. + } + + // Unlock the mutex. + void unlock() + { + (void)::pthread_mutex_unlock(&mutex_); // Ignore EINVAL. + } + +private: + friend class posix_event; + ::pthread_mutex_t mutex_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_mutex.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/posix_signal_blocker.hpp b/third_party/asio/1.18.2/include/asio/detail/posix_signal_blocker.hpp new file mode 100644 index 000000000..e0a4420bf --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/posix_signal_blocker.hpp @@ -0,0 +1,85 @@ +// +// detail/posix_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class posix_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + posix_signal_blocker() + : blocked_(false) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + + // Destructor restores the previous signal mask. + ~posix_signal_blocker() + { + if (blocked_) + pthread_sigmask(SIG_SETMASK, &old_mask_, 0); + } + + // Block all signals for the calling thread. + void block() + { + if (!blocked_) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + } + + // Restore the previous signal mask. + void unblock() + { + if (blocked_) + blocked_ = (pthread_sigmask(SIG_SETMASK, &old_mask_, 0) != 0); + } + +private: + // Have signals been blocked. + bool blocked_; + + // The previous signal mask. + sigset_t old_mask_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/posix_static_mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/posix_static_mutex.hpp new file mode 100644 index 000000000..b9fdd481a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/posix_static_mutex.hpp @@ -0,0 +1,64 @@ +// +// detail/posix_static_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_STATIC_MUTEX_HPP +#define ASIO_DETAIL_POSIX_STATIC_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct posix_static_mutex +{ + typedef asio::detail::scoped_lock scoped_lock; + + // Initialise the mutex. + void init() + { + // Nothing to do. + } + + // Lock the mutex. + void lock() + { + (void)::pthread_mutex_lock(&mutex_); // Ignore EINVAL. + } + + // Unlock the mutex. + void unlock() + { + (void)::pthread_mutex_unlock(&mutex_); // Ignore EINVAL. + } + + ::pthread_mutex_t mutex_; +}; + +#define ASIO_POSIX_STATIC_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER } + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_STATIC_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/posix_thread.hpp b/third_party/asio/1.18.2/include/asio/detail/posix_thread.hpp new file mode 100644 index 000000000..cd80af10f --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/posix_thread.hpp @@ -0,0 +1,109 @@ +// +// detail/posix_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_THREAD_HPP +#define ASIO_DETAIL_POSIX_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +extern "C" +{ + ASIO_DECL void* asio_detail_posix_thread_function(void* arg); +} + +class posix_thread + : private noncopyable +{ +public: + // Constructor. + template + posix_thread(Function f, unsigned int = 0) + : joined_(false) + { + start_thread(new func(f)); + } + + // Destructor. + ASIO_DECL ~posix_thread(); + + // Wait for the thread to exit. + ASIO_DECL void join(); + + // Get number of CPUs. + ASIO_DECL static std::size_t hardware_concurrency(); + +private: + friend void* asio_detail_posix_thread_function(void* arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + struct auto_func_base_ptr + { + func_base* ptr; + ~auto_func_base_ptr() { delete ptr; } + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ASIO_DECL void start_thread(func_base* arg); + + ::pthread_t thread_; + bool joined_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_thread.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_THREAD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/posix_tss_ptr.hpp b/third_party/asio/1.18.2/include/asio/detail/posix_tss_ptr.hpp new file mode 100644 index 000000000..6d8b29f65 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/posix_tss_ptr.hpp @@ -0,0 +1,79 @@ +// +// detail/posix_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_TSS_PTR_HPP +#define ASIO_DETAIL_POSIX_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_PTHREADS) + +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper function to create thread-specific storage. +ASIO_DECL void posix_tss_ptr_create(pthread_key_t& key); + +template +class posix_tss_ptr + : private noncopyable +{ +public: + // Constructor. + posix_tss_ptr() + { + posix_tss_ptr_create(tss_key_); + } + + // Destructor. + ~posix_tss_ptr() + { + ::pthread_key_delete(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast(::pthread_getspecific(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::pthread_setspecific(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + pthread_key_t tss_key_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_tss_ptr.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_PTHREADS) + +#endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/push_options.hpp b/third_party/asio/1.18.2/include/asio/detail/push_options.hpp new file mode 100644 index 000000000..e05b75670 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/push_options.hpp @@ -0,0 +1,191 @@ +// +// detail/push_options.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# if !defined(ASIO_DISABLE_VISIBILITY) +# pragma GCC visibility push (default) +# endif // !defined(ASIO_DISABLE_VISIBILITY) +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) + +#elif defined(__clang__) + +// Clang + +# if defined(__OBJC__) +# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) +# if !defined(ASIO_DISABLE_OBJC_WORKAROUND) +# if !defined(Protocol) && !defined(id) +# define Protocol cpp_Protocol +# define id cpp_id +# define ASIO_OBJC_WORKAROUND +# endif +# endif +# endif +# endif + +# if !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32) +# if !defined(ASIO_DISABLE_VISIBILITY) +# pragma GCC visibility push (default) +# endif // !defined(ASIO_DISABLE_VISIBILITY) +# endif // !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32) + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +# if (__clang_major__ >= 6) +# pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" +# endif // (__clang_major__ >= 6) + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined(__MINGW32__) || defined(__CYGWIN__) +# pragma pack (push, 8) +# endif + +# if defined(__OBJC__) +# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) +# if !defined(ASIO_DISABLE_OBJC_WORKAROUND) +# if !defined(Protocol) && !defined(id) +# define Protocol cpp_Protocol +# define id cpp_id +# define ASIO_OBJC_WORKAROUND +# endif +# endif +# endif +# endif + +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# if !defined(ASIO_DISABLE_VISIBILITY) +# pragma GCC visibility push (default) +# endif // !defined(ASIO_DISABLE_VISIBILITY) +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || (__GNUC__ > 4) +# pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || (__GNUC__ > 4) +# if (__GNUC__ >= 7) +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +# endif // (__GNUC__ >= 7) + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) && !defined(__clang__) + +// Borland C++ + +# pragma option push -a8 -b -Ve- -Vx- -w-inl -vi- +# pragma nopushoptwarn +# pragma nopackwarning +# if !defined(__MT__) +# error Multithreaded RTL must be selected. +# endif // !defined(__MT__) + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (disable:4103) +# pragma warning (push) +# pragma warning (disable:4127) +# pragma warning (disable:4180) +# pragma warning (disable:4244) +# pragma warning (disable:4355) +# pragma warning (disable:4510) +# pragma warning (disable:4512) +# pragma warning (disable:4610) +# pragma warning (disable:4675) +# if (_MSC_VER < 1600) +// Visual Studio 2008 generates spurious warnings about unused parameters. +# pragma warning (disable:4100) +# endif // (_MSC_VER < 1600) +# if defined(_M_IX86) && defined(_Wp64) +// The /Wp64 option is broken. If you want to check 64 bit portability, use a +// 64 bit compiler! +# pragma warning (disable:4311) +# pragma warning (disable:4312) +# endif // defined(_M_IX86) && defined(_Wp64) +# pragma pack (push, 8) +// Note that if the /Og optimisation flag is enabled with MSVC6, the compiler +// has a tendency to incorrectly optimise away some calls to member template +// functions, even though those functions contain code that should not be +// optimised away! Therefore we will always disable this optimisation option +// for the MSVC6 compiler. +# if (_MSC_VER < 1300) +# pragma optimize ("g", off) +# endif +# if !defined(_MT) +# error Multithreaded RTL must be selected. +# endif // !defined(_MT) + +# if defined(__cplusplus_cli) || defined(__cplusplus_winrt) +# if !defined(ASIO_DISABLE_CLR_WORKAROUND) +# if !defined(generic) +# define generic cpp_generic +# define ASIO_CLR_WORKAROUND +# endif +# endif +# endif + +#endif diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_descriptor_service.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_descriptor_service.hpp new file mode 100644 index 000000000..c640147b3 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_descriptor_service.hpp @@ -0,0 +1,416 @@ +// +// detail/reactive_descriptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + +#include "asio/buffer.hpp" +#include "asio/execution_context.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/descriptor_read_op.hpp" +#include "asio/detail/descriptor_write_op.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/reactive_null_buffers_op.hpp" +#include "asio/detail/reactive_wait_op.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/posix/descriptor_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class reactive_descriptor_service : + public execution_context_service_base +{ +public: + // The native type of a descriptor. + typedef int native_handle_type; + + // The implementation type of the descriptor. + class implementation_type + : private asio::detail::noncopyable + { + public: + // Default constructor. + implementation_type() + : descriptor_(-1), + state_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class reactive_descriptor_service; + + // The native descriptor representation. + int descriptor_; + + // The current state of the descriptor. + descriptor_ops::state_type state_; + + // Per-descriptor data used by the reactor. + reactor::per_descriptor_data reactor_data_; + }; + + // Constructor. + ASIO_DECL reactive_descriptor_service(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new descriptor implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Move-construct a new descriptor implementation. + ASIO_DECL void move_construct(implementation_type& impl, + implementation_type& other_impl) ASIO_NOEXCEPT; + + // Move-assign from another descriptor implementation. + ASIO_DECL void move_assign(implementation_type& impl, + reactive_descriptor_service& other_service, + implementation_type& other_impl); + + // Destroy a descriptor implementation. + ASIO_DECL void destroy(implementation_type& impl); + + // Assign a native descriptor to a descriptor implementation. + ASIO_DECL asio::error_code assign(implementation_type& impl, + const native_handle_type& native_descriptor, + asio::error_code& ec); + + // Determine whether the descriptor is open. + bool is_open(const implementation_type& impl) const + { + return impl.descriptor_ != -1; + } + + // Destroy a descriptor implementation. + ASIO_DECL asio::error_code close(implementation_type& impl, + asio::error_code& ec); + + // Get the native descriptor representation. + native_handle_type native_handle(const implementation_type& impl) const + { + return impl.descriptor_; + } + + // Release ownership of the native descriptor representation. + ASIO_DECL native_handle_type release(implementation_type& impl); + + // Cancel all operations associated with the descriptor. + ASIO_DECL asio::error_code cancel(implementation_type& impl, + asio::error_code& ec); + + // Perform an IO control command on the descriptor. + template + asio::error_code io_control(implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + descriptor_ops::ioctl(impl.descriptor_, impl.state_, + command.name(), static_cast(command.data()), ec); + return ec; + } + + // Gets the non-blocking mode of the descriptor. + bool non_blocking(const implementation_type& impl) const + { + return (impl.state_ & descriptor_ops::user_set_non_blocking) != 0; + } + + // Sets the non-blocking mode of the descriptor. + asio::error_code non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + descriptor_ops::set_user_non_blocking( + impl.descriptor_, impl.state_, mode, ec); + return ec; + } + + // Gets the non-blocking mode of the native descriptor implementation. + bool native_non_blocking(const implementation_type& impl) const + { + return (impl.state_ & descriptor_ops::internal_non_blocking) != 0; + } + + // Sets the non-blocking mode of the native descriptor implementation. + asio::error_code native_non_blocking(implementation_type& impl, + bool mode, asio::error_code& ec) + { + descriptor_ops::set_internal_non_blocking( + impl.descriptor_, impl.state_, mode, ec); + return ec; + } + + // Wait for the descriptor to become ready to read, ready to write, or to have + // pending error conditions. + asio::error_code wait(implementation_type& impl, + posix::descriptor_base::wait_type w, asio::error_code& ec) + { + switch (w) + { + case posix::descriptor_base::wait_read: + descriptor_ops::poll_read(impl.descriptor_, impl.state_, ec); + break; + case posix::descriptor_base::wait_write: + descriptor_ops::poll_write(impl.descriptor_, impl.state_, ec); + break; + case posix::descriptor_base::wait_error: + descriptor_ops::poll_error(impl.descriptor_, impl.state_, ec); + break; + default: + ec = asio::error::invalid_argument; + break; + } + + return ec; + } + + // Asynchronously wait for the descriptor to become ready to read, ready to + // write, or to have pending error conditions. + template + void async_wait(implementation_type& impl, + posix::descriptor_base::wait_type w, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_wait_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", + &impl, impl.descriptor_, "async_wait")); + + int op_type; + switch (w) + { + case posix::descriptor_base::wait_read: + op_type = reactor::read_op; + break; + case posix::descriptor_base::wait_write: + op_type = reactor::write_op; + break; + case posix::descriptor_base::wait_error: + op_type = reactor::except_op; + break; + default: + p.p->ec_ = asio::error::invalid_argument; + reactor_.post_immediate_completion(p.p, is_continuation); + p.v = p.p = 0; + return; + } + + start_op(impl, op_type, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Write some data to the descriptor. + template + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + typedef buffer_sequence_adapter bufs_type; + + if (bufs_type::is_single_buffer) + { + return descriptor_ops::sync_write1(impl.descriptor_, + impl.state_, bufs_type::first(buffers).data(), + bufs_type::first(buffers).size(), ec); + } + else + { + bufs_type bufs(buffers); + + return descriptor_ops::sync_write(impl.descriptor_, impl.state_, + bufs.buffers(), bufs.count(), bufs.all_empty(), ec); + } + } + + // Wait until data can be written without blocking. + size_t write_some(implementation_type& impl, + const null_buffers&, asio::error_code& ec) + { + // Wait for descriptor to become ready. + descriptor_ops::poll_write(impl.descriptor_, impl.state_, ec); + + return 0; + } + + // Start an asynchronous write. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, Handler& handler, + const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef descriptor_write_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.descriptor_, buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", + &impl, impl.descriptor_, "async_write_some")); + + start_op(impl, reactor::write_op, p.p, is_continuation, true, + buffer_sequence_adapter::all_empty(buffers)); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be written without blocking. + template + void async_write_some(implementation_type& impl, + const null_buffers&, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", + &impl, impl.descriptor_, "async_write_some(null_buffers)")); + + start_op(impl, reactor::write_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Read some data from the stream. Returns the number of bytes read. + template + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + typedef buffer_sequence_adapter bufs_type; + + if (bufs_type::is_single_buffer) + { + return descriptor_ops::sync_read1(impl.descriptor_, + impl.state_, bufs_type::first(buffers).data(), + bufs_type::first(buffers).size(), ec); + } + else + { + bufs_type bufs(buffers); + + return descriptor_ops::sync_read(impl.descriptor_, impl.state_, + bufs.buffers(), bufs.count(), bufs.all_empty(), ec); + } + } + + // Wait until data can be read without blocking. + size_t read_some(implementation_type& impl, + const null_buffers&, asio::error_code& ec) + { + // Wait for descriptor to become ready. + descriptor_ops::poll_read(impl.descriptor_, impl.state_, ec); + + return 0; + } + + // Start an asynchronous read. The buffer for the data being read must be + // valid for the lifetime of the asynchronous operation. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef descriptor_read_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.descriptor_, buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", + &impl, impl.descriptor_, "async_read_some")); + + start_op(impl, reactor::read_op, p.p, is_continuation, true, + buffer_sequence_adapter::all_empty(buffers)); + p.v = p.p = 0; + } + + // Wait until data can be read without blocking. + template + void async_read_some(implementation_type& impl, + const null_buffers&, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", + &impl, impl.descriptor_, "async_read_some(null_buffers)")); + + start_op(impl, reactor::read_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + +private: + // Start the asynchronous operation. + ASIO_DECL void start_op(implementation_type& impl, int op_type, + reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop); + + // The selector that performs event demultiplexing for the service. + reactor& reactor_; + + // Cached success value to avoid accessing category singleton. + const asio::error_code success_ec_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/reactive_descriptor_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_null_buffers_op.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_null_buffers_op.hpp new file mode 100644 index 000000000..48214dcba --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_null_buffers_op.hpp @@ -0,0 +1,98 @@ +// +// detail/reactive_null_buffers_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP +#define ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_null_buffers_op : public reactor_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_null_buffers_op); + + reactive_null_buffers_op(const asio::error_code& success_ec, + Handler& handler, const IoExecutor& io_ex) + : reactor_op(success_ec, &reactive_null_buffers_op::do_perform, + &reactive_null_buffers_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static status do_perform(reactor_op*) + { + return done; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_null_buffers_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_serial_port_service.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_serial_port_service.hpp new file mode 100644 index 000000000..e5a605c94 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_serial_port_service.hpp @@ -0,0 +1,237 @@ +// +// detail/reactive_serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/serial_port_base.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/reactive_descriptor_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Extend reactive_descriptor_service to provide serial port support. +class reactive_serial_port_service : + public execution_context_service_base +{ +public: + // The native type of a serial port. + typedef reactive_descriptor_service::native_handle_type native_handle_type; + + // The implementation type of the serial port. + typedef reactive_descriptor_service::implementation_type implementation_type; + + ASIO_DECL reactive_serial_port_service(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new serial port implementation. + void construct(implementation_type& impl) + { + descriptor_service_.construct(impl); + } + + // Move-construct a new serial port implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + descriptor_service_.move_construct(impl, other_impl); + } + + // Move-assign from another serial port implementation. + void move_assign(implementation_type& impl, + reactive_serial_port_service& other_service, + implementation_type& other_impl) + { + descriptor_service_.move_assign(impl, + other_service.descriptor_service_, other_impl); + } + + // Destroy a serial port implementation. + void destroy(implementation_type& impl) + { + descriptor_service_.destroy(impl); + } + + // Open the serial port using the specified device name. + ASIO_DECL asio::error_code open(implementation_type& impl, + const std::string& device, asio::error_code& ec); + + // Assign a native descriptor to a serial port implementation. + asio::error_code assign(implementation_type& impl, + const native_handle_type& native_descriptor, + asio::error_code& ec) + { + return descriptor_service_.assign(impl, native_descriptor, ec); + } + + // Determine whether the serial port is open. + bool is_open(const implementation_type& impl) const + { + return descriptor_service_.is_open(impl); + } + + // Destroy a serial port implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return descriptor_service_.close(impl, ec); + } + + // Get the native serial port representation. + native_handle_type native_handle(implementation_type& impl) + { + return descriptor_service_.native_handle(impl); + } + + // Cancel all operations associated with the serial port. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return descriptor_service_.cancel(impl, ec); + } + + // Set an option on the serial port. + template + asio::error_code set_option(implementation_type& impl, + const SettableSerialPortOption& option, asio::error_code& ec) + { + return do_set_option(impl, + &reactive_serial_port_service::store_option, + &option, ec); + } + + // Get an option from the serial port. + template + asio::error_code get_option(const implementation_type& impl, + GettableSerialPortOption& option, asio::error_code& ec) const + { + return do_get_option(impl, + &reactive_serial_port_service::load_option, + &option, ec); + } + + // Send a break sequence to the serial port. + asio::error_code send_break(implementation_type& impl, + asio::error_code& ec) + { + int result = ::tcsendbreak(descriptor_service_.native_handle(impl), 0); + descriptor_ops::get_last_error(ec, result < 0); + return ec; + } + + // Write the given data. Returns the number of bytes sent. + template + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return descriptor_service_.write_some(impl, buffers, ec); + } + + // Start an asynchronous write. The data being written must be valid for the + // lifetime of the asynchronous operation. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + { + descriptor_service_.async_write_some(impl, buffers, handler, io_ex); + } + + // Read some data. Returns the number of bytes received. + template + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return descriptor_service_.read_some(impl, buffers, ec); + } + + // Start an asynchronous read. The buffer for the data being received must be + // valid for the lifetime of the asynchronous operation. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + { + descriptor_service_.async_read_some(impl, buffers, handler, io_ex); + } + +private: + // Function pointer type for storing a serial port option. + typedef asio::error_code (*store_function_type)( + const void*, termios&, asio::error_code&); + + // Helper function template to store a serial port option. + template + static asio::error_code store_option(const void* option, + termios& storage, asio::error_code& ec) + { + static_cast(option)->store(storage, ec); + return ec; + } + + // Helper function to set a serial port option. + ASIO_DECL asio::error_code do_set_option( + implementation_type& impl, store_function_type store, + const void* option, asio::error_code& ec); + + // Function pointer type for loading a serial port option. + typedef asio::error_code (*load_function_type)( + void*, const termios&, asio::error_code&); + + // Helper function template to load a serial port option. + template + static asio::error_code load_option(void* option, + const termios& storage, asio::error_code& ec) + { + static_cast(option)->load(storage, ec); + return ec; + } + + // Helper function to get a serial port option. + ASIO_DECL asio::error_code do_get_option( + const implementation_type& impl, load_function_type load, + void* option, asio::error_code& ec) const; + + // The implementation used for initiating asynchronous operations. + reactive_descriptor_service descriptor_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/reactive_serial_port_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) +#endif // defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_socket_accept_op.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_accept_op.hpp new file mode 100644 index 000000000..ac036717e --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_accept_op.hpp @@ -0,0 +1,242 @@ +// +// detail/reactive_socket_accept_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_accept_op_base : public reactor_op +{ +public: + reactive_socket_accept_op_base(const asio::error_code& success_ec, + socket_type socket, socket_ops::state_type state, Socket& peer, + const Protocol& protocol, typename Protocol::endpoint* peer_endpoint, + func_type complete_func) + : reactor_op(success_ec, + &reactive_socket_accept_op_base::do_perform, complete_func), + socket_(socket), + state_(state), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + addrlen_(peer_endpoint ? peer_endpoint->capacity() : 0) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_accept_op_base* o( + static_cast(base)); + + socket_type new_socket = invalid_socket; + status result = socket_ops::non_blocking_accept(o->socket_, + o->state_, o->peer_endpoint_ ? o->peer_endpoint_->data() : 0, + o->peer_endpoint_ ? &o->addrlen_ : 0, o->ec_, new_socket) + ? done : not_done; + o->new_socket_.reset(new_socket); + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_accept", o->ec_)); + + return result; + } + + void do_assign() + { + if (new_socket_.get() != invalid_socket) + { + if (peer_endpoint_) + peer_endpoint_->resize(addrlen_); + peer_.assign(protocol_, new_socket_.get(), ec_); + if (!ec_) + new_socket_.release(); + } + } + +private: + socket_type socket_; + socket_ops::state_type state_; + socket_holder new_socket_; + Socket& peer_; + Protocol protocol_; + typename Protocol::endpoint* peer_endpoint_; + std::size_t addrlen_; +}; + +template +class reactive_socket_accept_op : + public reactive_socket_accept_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_accept_op); + + reactive_socket_accept_op(const asio::error_code& success_ec, + socket_type socket, socket_ops::state_type state, Socket& peer, + const Protocol& protocol, typename Protocol::endpoint* peer_endpoint, + Handler& handler, const IoExecutor& io_ex) + : reactive_socket_accept_op_base( + success_ec, socket, state, peer, protocol, peer_endpoint, + &reactive_socket_accept_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_accept_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + // On success, assign new connection to peer socket object. + if (owner) + o->do_assign(); + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, o->ec_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +#if defined(ASIO_HAS_MOVE) + +template +class reactive_socket_move_accept_op : + private Protocol::socket::template rebind_executor::other, + public reactive_socket_accept_op_base< + typename Protocol::socket::template rebind_executor::other, + Protocol> +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_move_accept_op); + + reactive_socket_move_accept_op(const asio::error_code& success_ec, + const PeerIoExecutor& peer_io_ex, socket_type socket, + socket_ops::state_type state, const Protocol& protocol, + typename Protocol::endpoint* peer_endpoint, Handler& handler, + const IoExecutor& io_ex) + : peer_socket_type(peer_io_ex), + reactive_socket_accept_op_base( + success_ec, socket, state, *this, protocol, peer_endpoint, + &reactive_socket_move_accept_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_move_accept_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + // On success, assign new connection to peer socket object. + if (owner) + o->do_assign(); + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::move_binder2 + handler(0, ASIO_MOVE_CAST(Handler)(o->handler_), o->ec_, + ASIO_MOVE_CAST(peer_socket_type)(*o)); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + typedef typename Protocol::socket::template + rebind_executor::other peer_socket_type; + + Handler handler_; + handler_work work_; +}; + +#endif // defined(ASIO_HAS_MOVE) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_socket_connect_op.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_connect_op.hpp new file mode 100644 index 000000000..956bc522d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_connect_op.hpp @@ -0,0 +1,123 @@ +// +// detail/reactive_socket_connect_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class reactive_socket_connect_op_base : public reactor_op +{ +public: + reactive_socket_connect_op_base(const asio::error_code& success_ec, + socket_type socket, func_type complete_func) + : reactor_op(success_ec, + &reactive_socket_connect_op_base::do_perform, complete_func), + socket_(socket) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_connect_op_base* o( + static_cast(base)); + + status result = socket_ops::non_blocking_connect( + o->socket_, o->ec_) ? done : not_done; + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_connect", o->ec_)); + + return result; + } + +private: + socket_type socket_; +}; + +template +class reactive_socket_connect_op : public reactive_socket_connect_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_connect_op); + + reactive_socket_connect_op(const asio::error_code& success_ec, + socket_type socket, Handler& handler, const IoExecutor& io_ex) + : reactive_socket_connect_op_base(success_ec, socket, + &reactive_socket_connect_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_connect_op* o + (static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, o->ec_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_socket_recv_op.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_recv_op.hpp new file mode 100644 index 000000000..d30f4ab31 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_recv_op.hpp @@ -0,0 +1,159 @@ +// +// detail/reactive_socket_recv_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_recv_op_base : public reactor_op +{ +public: + reactive_socket_recv_op_base(const asio::error_code& success_ec, + socket_type socket, socket_ops::state_type state, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(success_ec, + &reactive_socket_recv_op_base::do_perform, complete_func), + socket_(socket), + state_(state), + buffers_(buffers), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recv_op_base* o( + static_cast(base)); + + typedef buffer_sequence_adapter bufs_type; + + status result; + if (bufs_type::is_single_buffer) + { + result = socket_ops::non_blocking_recv1(o->socket_, + bufs_type::first(o->buffers_).data(), + bufs_type::first(o->buffers_).size(), o->flags_, + (o->state_ & socket_ops::stream_oriented) != 0, + o->ec_, o->bytes_transferred_) ? done : not_done; + } + else + { + bufs_type bufs(o->buffers_); + result = socket_ops::non_blocking_recv(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + (o->state_ & socket_ops::stream_oriented) != 0, + o->ec_, o->bytes_transferred_) ? done : not_done; + } + + if (result == done) + if ((o->state_ & socket_ops::stream_oriented) != 0) + if (o->bytes_transferred_ == 0) + result = done_and_exhausted; + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recv", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + socket_ops::state_type state_; + MutableBufferSequence buffers_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_recv_op : + public reactive_socket_recv_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_recv_op); + + reactive_socket_recv_op(const asio::error_code& success_ec, + socket_type socket, socket_ops::state_type state, + const MutableBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + : reactive_socket_recv_op_base(success_ec, socket, + state, buffers, flags, &reactive_socket_recv_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recv_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_socket_recvfrom_op.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_recvfrom_op.hpp new file mode 100644 index 000000000..0d7673a11 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_recvfrom_op.hpp @@ -0,0 +1,164 @@ +// +// detail/reactive_socket_recvfrom_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_recvfrom_op_base : public reactor_op +{ +public: + reactive_socket_recvfrom_op_base(const asio::error_code& success_ec, + socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, Endpoint& endpoint, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(success_ec, + &reactive_socket_recvfrom_op_base::do_perform, complete_func), + socket_(socket), + protocol_type_(protocol_type), + buffers_(buffers), + sender_endpoint_(endpoint), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recvfrom_op_base* o( + static_cast(base)); + + typedef buffer_sequence_adapter bufs_type; + + std::size_t addr_len = o->sender_endpoint_.capacity(); + status result; + if (bufs_type::is_single_buffer) + { + result = socket_ops::non_blocking_recvfrom1( + o->socket_, bufs_type::first(o->buffers_).data(), + bufs_type::first(o->buffers_).size(), o->flags_, + o->sender_endpoint_.data(), &addr_len, + o->ec_, o->bytes_transferred_) ? done : not_done; + } + else + { + bufs_type bufs(o->buffers_); + result = socket_ops::non_blocking_recvfrom(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->sender_endpoint_.data(), &addr_len, + o->ec_, o->bytes_transferred_) ? done : not_done; + } + + if (result && !o->ec_) + o->sender_endpoint_.resize(addr_len); + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recvfrom", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + int protocol_type_; + MutableBufferSequence buffers_; + Endpoint& sender_endpoint_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_recvfrom_op : + public reactive_socket_recvfrom_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvfrom_op); + + reactive_socket_recvfrom_op(const asio::error_code& success_ec, + socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, Endpoint& endpoint, + socket_base::message_flags flags, Handler& handler, + const IoExecutor& io_ex) + : reactive_socket_recvfrom_op_base( + success_ec, socket, protocol_type, buffers, endpoint, flags, + &reactive_socket_recvfrom_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recvfrom_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_socket_recvmsg_op.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_recvmsg_op.hpp new file mode 100644 index 000000000..b387f048f --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_recvmsg_op.hpp @@ -0,0 +1,145 @@ +// +// detail/reactive_socket_recvmsg_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/socket_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_recvmsg_op_base : public reactor_op +{ +public: + reactive_socket_recvmsg_op_base(const asio::error_code& success_ec, + socket_type socket, const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, func_type complete_func) + : reactor_op(success_ec, + &reactive_socket_recvmsg_op_base::do_perform, complete_func), + socket_(socket), + buffers_(buffers), + in_flags_(in_flags), + out_flags_(out_flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recvmsg_op_base* o( + static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + status result = socket_ops::non_blocking_recvmsg(o->socket_, + bufs.buffers(), bufs.count(), + o->in_flags_, o->out_flags_, + o->ec_, o->bytes_transferred_) ? done : not_done; + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recvmsg", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + MutableBufferSequence buffers_; + socket_base::message_flags in_flags_; + socket_base::message_flags& out_flags_; +}; + +template +class reactive_socket_recvmsg_op : + public reactive_socket_recvmsg_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvmsg_op); + + reactive_socket_recvmsg_op(const asio::error_code& success_ec, + socket_type socket, const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + : reactive_socket_recvmsg_op_base( + success_ec, socket, buffers, in_flags, out_flags, + &reactive_socket_recvmsg_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recvmsg_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_socket_send_op.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_send_op.hpp new file mode 100644 index 000000000..5a778566a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_send_op.hpp @@ -0,0 +1,162 @@ +// +// detail/reactive_socket_send_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_send_op_base : public reactor_op +{ +public: + reactive_socket_send_op_base(const asio::error_code& success_ec, + socket_type socket, socket_ops::state_type state, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(success_ec, + &reactive_socket_send_op_base::do_perform, complete_func), + socket_(socket), + state_(state), + buffers_(buffers), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_send_op_base* o( + static_cast(base)); + + typedef buffer_sequence_adapter bufs_type; + + status result; + if (bufs_type::is_single_buffer) + { + result = socket_ops::non_blocking_send1(o->socket_, + bufs_type::first(o->buffers_).data(), + bufs_type::first(o->buffers_).size(), o->flags_, + o->ec_, o->bytes_transferred_) ? done : not_done; + + if (result == done) + if ((o->state_ & socket_ops::stream_oriented) != 0) + if (o->bytes_transferred_ < bufs_type::first(o->buffers_).size()) + result = done_and_exhausted; + } + else + { + bufs_type bufs(o->buffers_); + result = socket_ops::non_blocking_send(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->ec_, o->bytes_transferred_) ? done : not_done; + + if (result == done) + if ((o->state_ & socket_ops::stream_oriented) != 0) + if (o->bytes_transferred_ < bufs.total_size()) + result = done_and_exhausted; + } + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_send", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + socket_ops::state_type state_; + ConstBufferSequence buffers_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_send_op : + public reactive_socket_send_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_send_op); + + reactive_socket_send_op(const asio::error_code& success_ec, + socket_type socket, socket_ops::state_type state, + const ConstBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + : reactive_socket_send_op_base(success_ec, socket, + state, buffers, flags, &reactive_socket_send_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_send_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_socket_sendto_op.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_sendto_op.hpp new file mode 100644 index 000000000..28bff6698 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_sendto_op.hpp @@ -0,0 +1,156 @@ +// +// detail/reactive_socket_sendto_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_sendto_op_base : public reactor_op +{ +public: + reactive_socket_sendto_op_base(const asio::error_code& success_ec, + socket_type socket, const ConstBufferSequence& buffers, + const Endpoint& endpoint, socket_base::message_flags flags, + func_type complete_func) + : reactor_op(success_ec, + &reactive_socket_sendto_op_base::do_perform, complete_func), + socket_(socket), + buffers_(buffers), + destination_(endpoint), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_sendto_op_base* o( + static_cast(base)); + + typedef buffer_sequence_adapter bufs_type; + + status result; + if (bufs_type::is_single_buffer) + { + result = socket_ops::non_blocking_sendto1(o->socket_, + bufs_type::first(o->buffers_).data(), + bufs_type::first(o->buffers_).size(), o->flags_, + o->destination_.data(), o->destination_.size(), + o->ec_, o->bytes_transferred_) ? done : not_done; + } + else + { + bufs_type bufs(o->buffers_); + result = socket_ops::non_blocking_sendto(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->destination_.data(), o->destination_.size(), + o->ec_, o->bytes_transferred_) ? done : not_done; + } + + ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_sendto", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + ConstBufferSequence buffers_; + Endpoint destination_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_sendto_op : + public reactive_socket_sendto_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_sendto_op); + + reactive_socket_sendto_op(const asio::error_code& success_ec, + socket_type socket, const ConstBufferSequence& buffers, + const Endpoint& endpoint, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + : reactive_socket_sendto_op_base( + success_ec, socket, buffers, endpoint, flags, + &reactive_socket_sendto_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_sendto_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_socket_service.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_service.hpp new file mode 100644 index 000000000..64c772290 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_service.hpp @@ -0,0 +1,528 @@ +// +// detail/reactive_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_IOCP) + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/reactive_null_buffers_op.hpp" +#include "asio/detail/reactive_socket_accept_op.hpp" +#include "asio/detail/reactive_socket_connect_op.hpp" +#include "asio/detail/reactive_socket_recvfrom_op.hpp" +#include "asio/detail/reactive_socket_sendto_op.hpp" +#include "asio/detail/reactive_socket_service_base.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_service : + public execution_context_service_base >, + public reactive_socket_service_base +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef socket_type native_handle_type; + + // The implementation type of the socket. + struct implementation_type : + reactive_socket_service_base::base_implementation_type + { + // Default constructor. + implementation_type() + : protocol_(endpoint_type().protocol()) + { + } + + // The protocol associated with the socket. + protocol_type protocol_; + }; + + // Constructor. + reactive_socket_service(execution_context& context) + : execution_context_service_base< + reactive_socket_service >(context), + reactive_socket_service_base(context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + this->base_shutdown(); + } + + // Move-construct a new socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) ASIO_NOEXCEPT + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-assign from another socket implementation. + void move_assign(implementation_type& impl, + reactive_socket_service_base& other_service, + implementation_type& other_impl) + { + this->base_move_assign(impl, other_service, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-construct a new socket implementation from another protocol type. + template + void converting_move_construct(implementation_type& impl, + reactive_socket_service&, + typename reactive_socket_service< + Protocol1>::implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = protocol_type(other_impl.protocol_); + other_impl.protocol_ = typename Protocol1::endpoint().protocol(); + } + + // Open a new socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (!do_open(impl, protocol.family(), + protocol.type(), protocol.protocol(), ec)) + impl.protocol_ = protocol; + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + asio::error_code& ec) + { + if (!do_assign(impl, protocol.type(), native_socket, ec)) + impl.protocol_ = protocol; + return ec; + } + + // Get the native socket representation. + native_handle_type native_handle(implementation_type& impl) + { + return impl.socket_; + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); + return ec; + } + + // Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const Option& option, asio::error_code& ec) + { + socket_ops::setsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; + } + + // Set a socket option. + template + asio::error_code get_option(const implementation_type& impl, + Option& option, asio::error_code& ec) const + { + std::size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, + endpoint.data(), &addr_len, false, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Disable sends or receives on the socket. + asio::error_code shutdown(base_implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + typedef buffer_sequence_adapter bufs_type; + + if (bufs_type::is_single_buffer) + { + return socket_ops::sync_sendto1(impl.socket_, impl.state_, + bufs_type::first(buffers).data(), + bufs_type::first(buffers).size(), flags, + destination.data(), destination.size(), ec); + } + else + { + bufs_type bufs(buffers); + return socket_ops::sync_sendto(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, + destination.data(), destination.size(), ec); + } + } + + // Wait until data can be sent without blocking. + size_t send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_sendto_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, + buffers, destination, flags, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send_to")); + + start_op(impl, reactor::write_op, p.p, is_continuation, true, false); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send_to(null_buffers)")); + + start_op(impl, reactor::write_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + typedef buffer_sequence_adapter bufs_type; + + std::size_t addr_len = sender_endpoint.capacity(); + std::size_t bytes_recvd; + if (bufs_type::is_single_buffer) + { + bytes_recvd = socket_ops::sync_recvfrom1(impl.socket_, + impl.state_, bufs_type::first(buffers).data(), + bufs_type::first(buffers).size(), flags, + sender_endpoint.data(), &addr_len, ec); + } + else + { + bufs_type bufs(buffers); + bytes_recvd = socket_ops::sync_recvfrom( + impl.socket_, impl.state_, bufs.buffers(), bufs.count(), + flags, sender_endpoint.data(), &addr_len, ec); + } + + if (!ec) + sender_endpoint.resize(addr_len); + + return bytes_recvd; + } + + // Wait until data can be received without blocking. + size_t receive_from(implementation_type& impl, const null_buffers&, + endpoint_type& sender_endpoint, socket_base::message_flags, + asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler& handler, + const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recvfrom_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + int protocol = impl.protocol_.type(); + p.p = new (p.v) op(success_ec_, impl.socket_, protocol, + buffers, sender_endpoint, flags, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_from")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, true, false); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive_from(implementation_type& impl, const null_buffers&, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_from(null_buffers)")); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Accept a new connection. + template + asio::error_code accept(implementation_type& impl, + Socket& peer, endpoint_type* peer_endpoint, asio::error_code& ec) + { + // We cannot accept a socket that is already open. + if (peer.is_open()) + { + ec = asio::error::already_open; + return ec; + } + + std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; + socket_holder new_socket(socket_ops::sync_accept(impl.socket_, + impl.state_, peer_endpoint ? peer_endpoint->data() : 0, + peer_endpoint ? &addr_len : 0, ec)); + + // On success, assign new connection to peer socket object. + if (new_socket.get() != invalid_socket) + { + if (peer_endpoint) + peer_endpoint->resize(addr_len); + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + } + + return ec; + } + + // Start an asynchronous accept. The peer and peer_endpoint objects must be + // valid until the accept's handler is invoked. + template + void async_accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_accept_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, impl.state_, + peer, impl.protocol_, peer_endpoint, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, p.p, is_continuation, peer.is_open()); + p.v = p.p = 0; + } + +#if defined(ASIO_HAS_MOVE) + // Start an asynchronous accept. The peer_endpoint object must be valid until + // the accept's handler is invoked. + template + void async_move_accept(implementation_type& impl, + const PeerIoExecutor& peer_io_ex, endpoint_type* peer_endpoint, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_move_accept_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, peer_io_ex, impl.socket_, + impl.state_, impl.protocol_, peer_endpoint, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, p.p, is_continuation, false); + p.v = p.p = 0; + } +#endif // defined(ASIO_HAS_MOVE) + + // Connect the socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + socket_ops::sync_connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size(), ec); + return ec; + } + + // Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_connect_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_connect")); + + start_connect_op(impl, p.p, is_continuation, + peer_endpoint.data(), peer_endpoint.size()); + p.v = p.p = 0; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_socket_service_base.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_service_base.hpp new file mode 100644 index 000000000..e544188e5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_socket_service_base.hpp @@ -0,0 +1,541 @@ +// +// detail/reactive_socket_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_IOCP) \ + && !defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactive_null_buffers_op.hpp" +#include "asio/detail/reactive_socket_recv_op.hpp" +#include "asio/detail/reactive_socket_recvmsg_op.hpp" +#include "asio/detail/reactive_socket_send_op.hpp" +#include "asio/detail/reactive_wait_op.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class reactive_socket_service_base +{ +public: + // The native type of a socket. + typedef socket_type native_handle_type; + + // The implementation type of the socket. + struct base_implementation_type + { + // The native socket representation. + socket_type socket_; + + // The current state of the socket. + socket_ops::state_type state_; + + // Per-descriptor data used by the reactor. + reactor::per_descriptor_data reactor_data_; + }; + + // Constructor. + ASIO_DECL reactive_socket_service_base(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void base_shutdown(); + + // Construct a new socket implementation. + ASIO_DECL void construct(base_implementation_type& impl); + + // Move-construct a new socket implementation. + ASIO_DECL void base_move_construct(base_implementation_type& impl, + base_implementation_type& other_impl) ASIO_NOEXCEPT; + + // Move-assign from another socket implementation. + ASIO_DECL void base_move_assign(base_implementation_type& impl, + reactive_socket_service_base& other_service, + base_implementation_type& other_impl); + + // Destroy a socket implementation. + ASIO_DECL void destroy(base_implementation_type& impl); + + // Determine whether the socket is open. + bool is_open(const base_implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + ASIO_DECL asio::error_code close( + base_implementation_type& impl, asio::error_code& ec); + + // Release ownership of the socket. + ASIO_DECL socket_type release( + base_implementation_type& impl, asio::error_code& ec); + + // Get the native socket representation. + native_handle_type native_handle(base_implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + ASIO_DECL asio::error_code cancel( + base_implementation_type& impl, asio::error_code& ec); + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::sockatmark(impl.socket_, ec); + } + + // Determine the number of bytes available for reading. + std::size_t available(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::available(impl.socket_, ec); + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(base_implementation_type& impl, + int backlog, asio::error_code& ec) + { + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(base_implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + socket_ops::ioctl(impl.socket_, impl.state_, command.name(), + static_cast(command.data()), ec); + return ec; + } + + // Gets the non-blocking mode of the socket. + bool non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::user_set_non_blocking) != 0; + } + + // Sets the non-blocking mode of the socket. + asio::error_code non_blocking(base_implementation_type& impl, + bool mode, asio::error_code& ec) + { + socket_ops::set_user_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::internal_non_blocking) != 0; + } + + // Sets the non-blocking mode of the native socket implementation. + asio::error_code native_non_blocking(base_implementation_type& impl, + bool mode, asio::error_code& ec) + { + socket_ops::set_internal_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Wait for the socket to become ready to read, ready to write, or to have + // pending error conditions. + asio::error_code wait(base_implementation_type& impl, + socket_base::wait_type w, asio::error_code& ec) + { + switch (w) + { + case socket_base::wait_read: + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_write: + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_error: + socket_ops::poll_error(impl.socket_, impl.state_, -1, ec); + break; + default: + ec = asio::error::invalid_argument; + break; + } + + return ec; + } + + // Asynchronously wait for the socket to become ready to read, ready to + // write, or to have pending error conditions. + template + void async_wait(base_implementation_type& impl, + socket_base::wait_type w, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_wait_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_wait")); + + int op_type; + switch (w) + { + case socket_base::wait_read: + op_type = reactor::read_op; + break; + case socket_base::wait_write: + op_type = reactor::write_op; + break; + case socket_base::wait_error: + op_type = reactor::except_op; + break; + default: + p.p->ec_ = asio::error::invalid_argument; + reactor_.post_immediate_completion(p.p, is_continuation); + p.v = p.p = 0; + return; + } + + start_op(impl, op_type, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Send the given data to the peer. + template + size_t send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + typedef buffer_sequence_adapter bufs_type; + + if (bufs_type::is_single_buffer) + { + return socket_ops::sync_send1(impl.socket_, + impl.state_, bufs_type::first(buffers).data(), + bufs_type::first(buffers).size(), flags, ec); + } + else + { + bufs_type bufs(buffers); + return socket_ops::sync_send(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + } + + // Wait until data can be sent without blocking. + size_t send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(base_implementation_type& impl, + const ConstBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_send_op< + ConstBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, + impl.state_, buffers, flags, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send")); + + start_op(impl, reactor::write_op, p.p, is_continuation, true, + ((impl.state_ & socket_ops::stream_oriented) + && buffer_sequence_adapter::all_empty(buffers))); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send(null_buffers)")); + + start_op(impl, reactor::write_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive some data from the peer. Returns the number of bytes received. + template + size_t receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + typedef buffer_sequence_adapter bufs_type; + + if (bufs_type::is_single_buffer) + { + return socket_ops::sync_recv1(impl.socket_, + impl.state_, bufs_type::first(buffers).data(), + bufs_type::first(buffers).size(), flags, ec); + } + else + { + bufs_type bufs(buffers); + return socket_ops::sync_recv(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + } + + // Wait until data can be received without blocking. + size_t receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recv_op< + MutableBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, + impl.state_, buffers, flags, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, + (flags & socket_base::message_out_of_band) == 0, + ((impl.state_ & socket_ops::stream_oriented) + && buffer_sequence_adapter::all_empty(buffers))); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive(null_buffers)")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive some data with associated flags. Returns the number of bytes + // received. + template + size_t receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_recvmsg(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), in_flags, out_flags, ec); + } + + // Wait until data can be received without blocking. + size_t receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Clear out_flags, since we cannot give it any other sensible value when + // performing a null_buffers operation. + out_flags = 0; + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recvmsg_op< + MutableBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, + buffers, in_flags, out_flags, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags")); + + start_op(impl, + (in_flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, + (in_flags & socket_base::message_out_of_band) == 0, false); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags(null_buffers)")); + + // Clear out_flags, since we cannot give it any other sensible value when + // performing a null_buffers operation. + out_flags = 0; + + start_op(impl, + (in_flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + +protected: + // Open a new socket implementation. + ASIO_DECL asio::error_code do_open( + base_implementation_type& impl, int af, + int type, int protocol, asio::error_code& ec); + + // Assign a native socket to a socket implementation. + ASIO_DECL asio::error_code do_assign( + base_implementation_type& impl, int type, + const native_handle_type& native_socket, asio::error_code& ec); + + // Start the asynchronous read or write operation. + ASIO_DECL void start_op(base_implementation_type& impl, int op_type, + reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop); + + // Start the asynchronous accept operation. + ASIO_DECL void start_accept_op(base_implementation_type& impl, + reactor_op* op, bool is_continuation, bool peer_is_open); + + // Start the asynchronous connect operation. + ASIO_DECL void start_connect_op(base_implementation_type& impl, + reactor_op* op, bool is_continuation, + const socket_addr_type* addr, size_t addrlen); + + // The selector that performs event demultiplexing for the service. + reactor& reactor_; + + // Cached success value to avoid accessing category singleton. + const asio::error_code success_ec_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/reactive_socket_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_HAS_IOCP) + // && !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactive_wait_op.hpp b/third_party/asio/1.18.2/include/asio/detail/reactive_wait_op.hpp new file mode 100644 index 000000000..c7e2eb8d3 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactive_wait_op.hpp @@ -0,0 +1,98 @@ +// +// detail/reactive_wait_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_WAIT_OP_HPP +#define ASIO_DETAIL_REACTIVE_WAIT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_wait_op : public reactor_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_wait_op); + + reactive_wait_op(const asio::error_code& success_ec, + Handler& handler, const IoExecutor& io_ex) + : reactor_op(success_ec, &reactive_wait_op::do_perform, + &reactive_wait_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static status do_perform(reactor_op*) + { + return done; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_wait_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, o->ec_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_WAIT_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactor.hpp b/third_party/asio/1.18.2/include/asio/detail/reactor.hpp new file mode 100644 index 000000000..03a601c7d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactor.hpp @@ -0,0 +1,32 @@ +// +// detail/reactor.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTOR_HPP +#define ASIO_DETAIL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/reactor_fwd.hpp" + +#if defined(ASIO_HAS_EPOLL) +# include "asio/detail/epoll_reactor.hpp" +#elif defined(ASIO_HAS_KQUEUE) +# include "asio/detail/kqueue_reactor.hpp" +#elif defined(ASIO_HAS_DEV_POLL) +# include "asio/detail/dev_poll_reactor.hpp" +#elif defined(ASIO_HAS_IOCP) || defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_reactor.hpp" +#else +# include "asio/detail/select_reactor.hpp" +#endif + +#endif // ASIO_DETAIL_REACTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactor_fwd.hpp b/third_party/asio/1.18.2/include/asio/detail/reactor_fwd.hpp new file mode 100644 index 000000000..b9c084614 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactor_fwd.hpp @@ -0,0 +1,40 @@ +// +// detail/reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTOR_FWD_HPP +#define ASIO_DETAIL_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_IOCP) || defined(ASIO_WINDOWS_RUNTIME) +typedef class null_reactor reactor; +#elif defined(ASIO_HAS_IOCP) +typedef class select_reactor reactor; +#elif defined(ASIO_HAS_EPOLL) +typedef class epoll_reactor reactor; +#elif defined(ASIO_HAS_KQUEUE) +typedef class kqueue_reactor reactor; +#elif defined(ASIO_HAS_DEV_POLL) +typedef class dev_poll_reactor reactor; +#else +typedef class select_reactor reactor; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_REACTOR_FWD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactor_op.hpp b/third_party/asio/1.18.2/include/asio/detail/reactor_op.hpp new file mode 100644 index 000000000..6eda89e2c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactor_op.hpp @@ -0,0 +1,67 @@ +// +// detail/reactor_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTOR_OP_HPP +#define ASIO_DETAIL_REACTOR_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class reactor_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + + // The number of bytes transferred, to be passed to the completion handler. + std::size_t bytes_transferred_; + + // Status returned by perform function. May be used to decide whether it is + // worth performing more operations on the descriptor immediately. + enum status { not_done, done, done_and_exhausted }; + + // Perform the operation. Returns true if it is finished. + status perform() + { + return perform_func_(this); + } + +protected: + typedef status (*perform_func_type)(reactor_op*); + + reactor_op(const asio::error_code& success_ec, + perform_func_type perform_func, func_type complete_func) + : operation(complete_func), + ec_(success_ec), + bytes_transferred_(0), + perform_func_(perform_func) + { + } + +private: + perform_func_type perform_func_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTOR_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/reactor_op_queue.hpp b/third_party/asio/1.18.2/include/asio/detail/reactor_op_queue.hpp new file mode 100644 index 000000000..c31752bff --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/reactor_op_queue.hpp @@ -0,0 +1,168 @@ +// +// detail/reactor_op_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTOR_OP_QUEUE_HPP +#define ASIO_DETAIL_REACTOR_OP_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactor_op_queue + : private noncopyable +{ +public: + typedef Descriptor key_type; + + struct mapped_type : op_queue + { + mapped_type() {} + mapped_type(const mapped_type&) {} + void operator=(const mapped_type&) {} + }; + + typedef typename hash_map::value_type value_type; + typedef typename hash_map::iterator iterator; + + // Constructor. + reactor_op_queue() + : operations_() + { + } + + // Obtain iterators to all registered descriptors. + iterator begin() { return operations_.begin(); } + iterator end() { return operations_.end(); } + + // Add a new operation to the queue. Returns true if this is the only + // operation for the given descriptor, in which case the reactor's event + // demultiplexing function call may need to be interrupted and restarted. + bool enqueue_operation(Descriptor descriptor, reactor_op* op) + { + std::pair entry = + operations_.insert(value_type(descriptor, mapped_type())); + entry.first->second.push(op); + return entry.second; + } + + // Cancel all operations associated with the descriptor identified by the + // supplied iterator. Any operations pending for the descriptor will be + // cancelled. Returns true if any operations were cancelled, in which case + // the reactor's event demultiplexing function may need to be interrupted and + // restarted. + bool cancel_operations(iterator i, op_queue& ops, + const asio::error_code& ec = + asio::error::operation_aborted) + { + if (i != operations_.end()) + { + while (reactor_op* op = i->second.front()) + { + op->ec_ = ec; + i->second.pop(); + ops.push(op); + } + operations_.erase(i); + return true; + } + + return false; + } + + // Cancel all operations associated with the descriptor. Any operations + // pending for the descriptor will be cancelled. Returns true if any + // operations were cancelled, in which case the reactor's event + // demultiplexing function may need to be interrupted and restarted. + bool cancel_operations(Descriptor descriptor, op_queue& ops, + const asio::error_code& ec = + asio::error::operation_aborted) + { + return this->cancel_operations(operations_.find(descriptor), ops, ec); + } + + // Whether there are no operations in the queue. + bool empty() const + { + return operations_.empty(); + } + + // Determine whether there are any operations associated with the descriptor. + bool has_operation(Descriptor descriptor) const + { + return operations_.find(descriptor) != operations_.end(); + } + + // Perform the operations corresponding to the descriptor identified by the + // supplied iterator. Returns true if there are still unfinished operations + // queued for the descriptor. + bool perform_operations(iterator i, op_queue& ops) + { + if (i != operations_.end()) + { + while (reactor_op* op = i->second.front()) + { + if (op->perform()) + { + i->second.pop(); + ops.push(op); + } + else + { + return true; + } + } + operations_.erase(i); + } + return false; + } + + // Perform the operations corresponding to the descriptor. Returns true if + // there are still unfinished operations queued for the descriptor. + bool perform_operations(Descriptor descriptor, op_queue& ops) + { + return this->perform_operations(operations_.find(descriptor), ops); + } + + // Get all operations owned by the queue. + void get_all_operations(op_queue& ops) + { + iterator i = operations_.begin(); + while (i != operations_.end()) + { + iterator op_iter = i++; + ops.push(op_iter->second); + operations_.erase(op_iter); + } + } + +private: + // The operations that are currently executing asynchronously. + hash_map operations_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTOR_OP_QUEUE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/recycling_allocator.hpp b/third_party/asio/1.18.2/include/asio/detail/recycling_allocator.hpp new file mode 100644 index 000000000..23e59b199 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/recycling_allocator.hpp @@ -0,0 +1,104 @@ +// +// detail/recycling_allocator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RECYCLING_ALLOCATOR_HPP +#define ASIO_DETAIL_RECYCLING_ALLOCATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/thread_context.hpp" +#include "asio/detail/thread_info_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class recycling_allocator +{ +public: + typedef T value_type; + + template + struct rebind + { + typedef recycling_allocator other; + }; + + recycling_allocator() + { + } + + template + recycling_allocator(const recycling_allocator&) + { + } + + T* allocate(std::size_t n) + { + void* p = thread_info_base::allocate(Purpose(), + thread_context::top_of_thread_call_stack(), sizeof(T) * n); + return static_cast(p); + } + + void deallocate(T* p, std::size_t n) + { + thread_info_base::deallocate(Purpose(), + thread_context::top_of_thread_call_stack(), p, sizeof(T) * n); + } +}; + +template +class recycling_allocator +{ +public: + typedef void value_type; + + template + struct rebind + { + typedef recycling_allocator other; + }; + + recycling_allocator() + { + } + + template + recycling_allocator(const recycling_allocator&) + { + } +}; + +template +struct get_recycling_allocator +{ + typedef Allocator type; + static type get(const Allocator& a) { return a; } +}; + +template +struct get_recycling_allocator, Purpose> +{ + typedef recycling_allocator type; + static type get(const std::allocator&) { return type(); } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RECYCLING_ALLOCATOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/regex_fwd.hpp b/third_party/asio/1.18.2/include/asio/detail/regex_fwd.hpp new file mode 100644 index 000000000..bde7402ee --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/regex_fwd.hpp @@ -0,0 +1,44 @@ +// +// detail/regex_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REGEX_FWD_HPP +#define ASIO_DETAIL_REGEX_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if defined(ASIO_HAS_BOOST_REGEX) + +#include +#include +#if BOOST_VERSION >= 107600 +# if defined(BOOST_REGEX_CXX03) +# include +# else // defined(BOOST_REGEX_CXX03) +# include +# endif // defined(BOOST_REGEX_CXX03) +#else // BOOST_VERSION >= 107600 +# include +#endif // BOOST_VERSION >= 107600 + +namespace boost { + +template +struct sub_match; + +template +class match_results; + +} // namespace boost + +#endif // defined(ASIO_HAS_BOOST_REGEX) + +#endif // ASIO_DETAIL_REGEX_FWD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/resolve_endpoint_op.hpp b/third_party/asio/1.18.2/include/asio/detail/resolve_endpoint_op.hpp new file mode 100644 index 000000000..bf494f5ea --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/resolve_endpoint_op.hpp @@ -0,0 +1,140 @@ +// +// detail/resolve_endpoint_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP +#define ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/resolve_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" +#include "asio/ip/basic_resolver_results.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else // defined(ASIO_HAS_IOCP) +# include "asio/detail/scheduler.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class resolve_endpoint_op : public resolve_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(resolve_endpoint_op); + + typedef typename Protocol::endpoint endpoint_type; + typedef asio::ip::basic_resolver_results results_type; + +#if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_impl; +#else + typedef class scheduler scheduler_impl; +#endif + + resolve_endpoint_op(socket_ops::weak_cancel_token_type cancel_token, + const endpoint_type& endpoint, scheduler_impl& sched, + Handler& handler, const IoExecutor& io_ex) + : resolve_op(&resolve_endpoint_op::do_complete), + cancel_token_(cancel_token), + endpoint_(endpoint), + scheduler_(sched), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the operation object. + resolve_endpoint_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + if (owner && owner != &o->scheduler_) + { + // The operation is being run on the worker io_context. Time to perform + // the resolver operation. + + // Perform the blocking endpoint resolution operation. + char host_name[NI_MAXHOST]; + char service_name[NI_MAXSERV]; + socket_ops::background_getnameinfo(o->cancel_token_, o->endpoint_.data(), + o->endpoint_.size(), host_name, NI_MAXHOST, service_name, NI_MAXSERV, + o->endpoint_.protocol().type(), o->ec_); + o->results_ = results_type::create(o->endpoint_, host_name, service_name); + + // Pass operation back to main io_context for completion. + o->scheduler_.post_deferred_completion(o); + p.v = p.p = 0; + } + else + { + // The operation has been returned to the main io_context. The completion + // handler is ready to be delivered. + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an upcall, + // a sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->results_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + endpoint_type endpoint_; + scheduler_impl& scheduler_; + Handler handler_; + handler_work work_; + results_type results_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/resolve_op.hpp b/third_party/asio/1.18.2/include/asio/detail/resolve_op.hpp new file mode 100644 index 000000000..e0ac00bd7 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/resolve_op.hpp @@ -0,0 +1,45 @@ +// +// detail/resolve_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVE_OP_HPP +#define ASIO_DETAIL_RESOLVE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class resolve_op : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + +protected: + resolve_op(func_type complete_func) + : operation(complete_func) + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVE_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/resolve_query_op.hpp b/third_party/asio/1.18.2/include/asio/detail/resolve_query_op.hpp new file mode 100644 index 000000000..5a2ffa71a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/resolve_query_op.hpp @@ -0,0 +1,150 @@ +// +// detail/resolve_query_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVE_QUERY_OP_HPP +#define ASIO_DETAIL_RESOLVE_QUERY_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/resolve_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/basic_resolver_results.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else // defined(ASIO_HAS_IOCP) +# include "asio/detail/scheduler.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class resolve_query_op : public resolve_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(resolve_query_op); + + typedef asio::ip::basic_resolver_query query_type; + typedef asio::ip::basic_resolver_results results_type; + +#if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_impl; +#else + typedef class scheduler scheduler_impl; +#endif + + resolve_query_op(socket_ops::weak_cancel_token_type cancel_token, + const query_type& qry, scheduler_impl& sched, + Handler& handler, const IoExecutor& io_ex) + : resolve_op(&resolve_query_op::do_complete), + cancel_token_(cancel_token), + query_(qry), + scheduler_(sched), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex), + addrinfo_(0) + { + } + + ~resolve_query_op() + { + if (addrinfo_) + socket_ops::freeaddrinfo(addrinfo_); + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the operation object. + resolve_query_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + if (owner && owner != &o->scheduler_) + { + // The operation is being run on the worker io_context. Time to perform + // the resolver operation. + + // Perform the blocking host resolution operation. + socket_ops::background_getaddrinfo(o->cancel_token_, + o->query_.host_name().c_str(), o->query_.service_name().c_str(), + o->query_.hints(), &o->addrinfo_, o->ec_); + + // Pass operation back to main io_context for completion. + o->scheduler_.post_deferred_completion(o); + p.v = p.p = 0; + } + else + { + // The operation has been returned to the main io_context. The completion + // handler is ready to be delivered. + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an upcall, + // a sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, results_type()); + p.h = asio::detail::addressof(handler.handler_); + if (o->addrinfo_) + { + handler.arg2_ = results_type::create(o->addrinfo_, + o->query_.host_name(), o->query_.service_name()); + } + p.reset(); + + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + query_type query_; + scheduler_impl& scheduler_; + Handler handler_; + handler_work work_; + asio::detail::addrinfo_type* addrinfo_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVE_QUERY_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/resolver_service.hpp b/third_party/asio/1.18.2/include/asio/detail/resolver_service.hpp new file mode 100644 index 000000000..006b35c7c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/resolver_service.hpp @@ -0,0 +1,145 @@ +// +// detail/resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVER_SERVICE_HPP +#define ASIO_DETAIL_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/basic_resolver_results.hpp" +#include "asio/detail/concurrency_hint.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/resolve_endpoint_op.hpp" +#include "asio/detail/resolve_query_op.hpp" +#include "asio/detail/resolver_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class resolver_service : + public execution_context_service_base >, + public resolver_service_base +{ +public: + // The implementation type of the resolver. A cancellation token is used to + // indicate to the background thread that the operation has been cancelled. + typedef socket_ops::shared_cancel_token_type implementation_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The query type. + typedef asio::ip::basic_resolver_query query_type; + + // The results type. + typedef asio::ip::basic_resolver_results results_type; + + // Constructor. + resolver_service(execution_context& context) + : execution_context_service_base >(context), + resolver_service_base(context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + this->base_shutdown(); + } + + // Perform any fork-related housekeeping. + void notify_fork(execution_context::fork_event fork_ev) + { + this->base_notify_fork(fork_ev); + } + + // Resolve a query to a list of entries. + results_type resolve(implementation_type&, const query_type& qry, + asio::error_code& ec) + { + asio::detail::addrinfo_type* address_info = 0; + + socket_ops::getaddrinfo(qry.host_name().c_str(), + qry.service_name().c_str(), qry.hints(), &address_info, ec); + auto_addrinfo auto_address_info(address_info); + + return ec ? results_type() : results_type::create( + address_info, qry.host_name(), qry.service_name()); + } + + // Asynchronously resolve a query to a list of entries. + template + void async_resolve(implementation_type& impl, const query_type& qry, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef resolve_query_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl, qry, scheduler_, handler, io_ex); + + ASIO_HANDLER_CREATION((scheduler_.context(), + *p.p, "resolver", &impl, 0, "async_resolve")); + + start_resolve_op(p.p); + p.v = p.p = 0; + } + + // Resolve an endpoint to a list of entries. + results_type resolve(implementation_type&, + const endpoint_type& endpoint, asio::error_code& ec) + { + char host_name[NI_MAXHOST]; + char service_name[NI_MAXSERV]; + socket_ops::sync_getnameinfo(endpoint.data(), endpoint.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, + endpoint.protocol().type(), ec); + + return ec ? results_type() : results_type::create( + endpoint, host_name, service_name); + } + + // Asynchronously resolve an endpoint to a list of entries. + template + void async_resolve(implementation_type& impl, const endpoint_type& endpoint, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef resolve_endpoint_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl, endpoint, scheduler_, handler, io_ex); + + ASIO_HANDLER_CREATION((scheduler_.context(), + *p.p, "resolver", &impl, 0, "async_resolve")); + + start_resolve_op(p.p); + p.v = p.p = 0; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/resolver_service_base.hpp b/third_party/asio/1.18.2/include/asio/detail/resolver_service_base.hpp new file mode 100644 index 000000000..0df30f406 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/resolver_service_base.hpp @@ -0,0 +1,158 @@ +// +// detail/resolver_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP +#define ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/resolve_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/thread.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else // defined(ASIO_HAS_IOCP) +# include "asio/detail/scheduler.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class resolver_service_base +{ +public: + // The implementation type of the resolver. A cancellation token is used to + // indicate to the background thread that the operation has been cancelled. + typedef socket_ops::shared_cancel_token_type implementation_type; + + // Constructor. + ASIO_DECL resolver_service_base(execution_context& context); + + // Destructor. + ASIO_DECL ~resolver_service_base(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void base_shutdown(); + + // Perform any fork-related housekeeping. + ASIO_DECL void base_notify_fork( + execution_context::fork_event fork_ev); + + // Construct a new resolver implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Destroy a resolver implementation. + ASIO_DECL void destroy(implementation_type&); + + // Move-construct a new resolver implementation. + ASIO_DECL void move_construct(implementation_type& impl, + implementation_type& other_impl); + + // Move-assign from another resolver implementation. + ASIO_DECL void move_assign(implementation_type& impl, + resolver_service_base& other_service, + implementation_type& other_impl); + + // Move-construct a new timer implementation. + void converting_move_construct(implementation_type& impl, + resolver_service_base&, implementation_type& other_impl) + { + move_construct(impl, other_impl); + } + + // Move-assign from another timer implementation. + void converting_move_assign(implementation_type& impl, + resolver_service_base& other_service, + implementation_type& other_impl) + { + move_assign(impl, other_service, other_impl); + } + + // Cancel pending asynchronous operations. + ASIO_DECL void cancel(implementation_type& impl); + +protected: + // Helper function to start an asynchronous resolve operation. + ASIO_DECL void start_resolve_op(resolve_op* op); + +#if !defined(ASIO_WINDOWS_RUNTIME) + // Helper class to perform exception-safe cleanup of addrinfo objects. + class auto_addrinfo + : private asio::detail::noncopyable + { + public: + explicit auto_addrinfo(asio::detail::addrinfo_type* ai) + : ai_(ai) + { + } + + ~auto_addrinfo() + { + if (ai_) + socket_ops::freeaddrinfo(ai_); + } + + operator asio::detail::addrinfo_type*() + { + return ai_; + } + + private: + asio::detail::addrinfo_type* ai_; + }; +#endif // !defined(ASIO_WINDOWS_RUNTIME) + + // Helper class to run the work scheduler in a thread. + class work_scheduler_runner; + + // Start the work scheduler if it's not already running. + ASIO_DECL void start_work_thread(); + + // The scheduler implementation used to post completions. +#if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_impl; +#else + typedef class scheduler scheduler_impl; +#endif + scheduler_impl& scheduler_; + +private: + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // Private scheduler used for performing asynchronous host resolution. + asio::detail::scoped_ptr work_scheduler_; + + // Thread used for running the work io_context's run loop. + asio::detail::scoped_ptr work_thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/resolver_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/scheduler.hpp b/third_party/asio/1.18.2/include/asio/detail/scheduler.hpp new file mode 100644 index 000000000..ce8a7e17b --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/scheduler.hpp @@ -0,0 +1,229 @@ +// +// detail/scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCHEDULER_HPP +#define ASIO_DETAIL_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/error_code.hpp" +#include "asio/execution_context.hpp" +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/conditionally_enabled_event.hpp" +#include "asio/detail/conditionally_enabled_mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_fwd.hpp" +#include "asio/detail/scheduler_operation.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/thread_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct scheduler_thread_info; + +class scheduler + : public execution_context_service_base, + public thread_context +{ +public: + typedef scheduler_operation operation; + + // Constructor. Specifies the number of concurrent threads that are likely to + // run the scheduler. If set to 1 certain optimisation are performed. + ASIO_DECL scheduler(asio::execution_context& ctx, + int concurrency_hint = 0, bool own_thread = true); + + // Destructor. + ASIO_DECL ~scheduler(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Initialise the task, if required. + ASIO_DECL void init_task(); + + // Run the event loop until interrupted or no more work. + ASIO_DECL std::size_t run(asio::error_code& ec); + + // Run until interrupted or one operation is performed. + ASIO_DECL std::size_t run_one(asio::error_code& ec); + + // Run until timeout, interrupted, or one operation is performed. + ASIO_DECL std::size_t wait_one( + long usec, asio::error_code& ec); + + // Poll for operations without blocking. + ASIO_DECL std::size_t poll(asio::error_code& ec); + + // Poll for one operation without blocking. + ASIO_DECL std::size_t poll_one(asio::error_code& ec); + + // Interrupt the event processing loop. + ASIO_DECL void stop(); + + // Determine whether the scheduler is stopped. + ASIO_DECL bool stopped() const; + + // Restart in preparation for a subsequent run invocation. + ASIO_DECL void restart(); + + // Notify that some work has started. + void work_started() + { + ++outstanding_work_; + } + + // Used to compensate for a forthcoming work_finished call. Must be called + // from within a scheduler-owned thread. + ASIO_DECL void compensating_work_started(); + + // Notify that some work has finished. + void work_finished() + { + if (--outstanding_work_ == 0) + stop(); + } + + // Return whether a handler can be dispatched immediately. + ASIO_DECL bool can_dispatch(); + + /// Capture the current exception so it can be rethrown from a run function. + ASIO_DECL void capture_current_exception(); + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() has not yet been called for the operation. + ASIO_DECL void post_immediate_completion( + operation* op, bool is_continuation); + + // Request invocation of the given operations and return immediately. Assumes + // that work_started() has not yet been called for the operations. + ASIO_DECL void post_immediate_completions(std::size_t n, + op_queue& ops, bool is_continuation); + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() was previously called for the operation. + ASIO_DECL void post_deferred_completion(operation* op); + + // Request invocation of the given operations and return immediately. Assumes + // that work_started() was previously called for each operation. + ASIO_DECL void post_deferred_completions(op_queue& ops); + + // Enqueue the given operation following a failed attempt to dispatch the + // operation for immediate invocation. + ASIO_DECL void do_dispatch(operation* op); + + // Process unfinished operations as part of a shutdownoperation. Assumes that + // work_started() was previously called for the operations. + ASIO_DECL void abandon_operations(op_queue& ops); + + // Get the concurrency hint that was used to initialise the scheduler. + int concurrency_hint() const + { + return concurrency_hint_; + } + +private: + // The mutex type used by this scheduler. + typedef conditionally_enabled_mutex mutex; + + // The event type used by this scheduler. + typedef conditionally_enabled_event event; + + // Structure containing thread-specific data. + typedef scheduler_thread_info thread_info; + + // Run at most one operation. May block. + ASIO_DECL std::size_t do_run_one(mutex::scoped_lock& lock, + thread_info& this_thread, const asio::error_code& ec); + + // Run at most one operation with a timeout. May block. + ASIO_DECL std::size_t do_wait_one(mutex::scoped_lock& lock, + thread_info& this_thread, long usec, const asio::error_code& ec); + + // Poll for at most one operation. + ASIO_DECL std::size_t do_poll_one(mutex::scoped_lock& lock, + thread_info& this_thread, const asio::error_code& ec); + + // Stop the task and all idle threads. + ASIO_DECL void stop_all_threads(mutex::scoped_lock& lock); + + // Wake a single idle thread, or the task, and always unlock the mutex. + ASIO_DECL void wake_one_thread_and_unlock( + mutex::scoped_lock& lock); + + // Helper class to run the scheduler in its own thread. + class thread_function; + friend class thread_function; + + // Helper class to perform task-related operations on block exit. + struct task_cleanup; + friend struct task_cleanup; + + // Helper class to call work-related operations on block exit. + struct work_cleanup; + friend struct work_cleanup; + + // Whether to optimise for single-threaded use cases. + const bool one_thread_; + + // Mutex to protect access to internal data. + mutable mutex mutex_; + + // Event to wake up blocked threads. + event wakeup_event_; + + // The task to be run by this service. + reactor* task_; + + // Operation object to represent the position of the task in the queue. + struct task_operation : operation + { + task_operation() : operation(0) {} + } task_operation_; + + // Whether the task has been interrupted. + bool task_interrupted_; + + // The count of unfinished work. + atomic_count outstanding_work_; + + // The queue of handlers that are ready to be delivered. + op_queue op_queue_; + + // Flag to indicate that the dispatcher has been stopped. + bool stopped_; + + // Flag to indicate that the dispatcher has been shut down. + bool shutdown_; + + // The concurrency hint used to initialise the scheduler. + const int concurrency_hint_; + + // The thread that is running the scheduler. + asio::detail::thread* thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/scheduler.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_SCHEDULER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/scheduler_operation.hpp b/third_party/asio/1.18.2/include/asio/detail/scheduler_operation.hpp new file mode 100644 index 000000000..1ca4964f5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/scheduler_operation.hpp @@ -0,0 +1,78 @@ +// +// detail/scheduler_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCHEDULER_OPERATION_HPP +#define ASIO_DETAIL_SCHEDULER_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/error_code.hpp" +#include "asio/detail/handler_tracking.hpp" +#include "asio/detail/op_queue.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class scheduler; + +// Base class for all operations. A function pointer is used instead of virtual +// functions to avoid the associated overhead. +class scheduler_operation ASIO_INHERIT_TRACKED_HANDLER +{ +public: + typedef scheduler_operation operation_type; + + void complete(void* owner, const asio::error_code& ec, + std::size_t bytes_transferred) + { + func_(owner, this, ec, bytes_transferred); + } + + void destroy() + { + func_(0, this, asio::error_code(), 0); + } + +protected: + typedef void (*func_type)(void*, + scheduler_operation*, + const asio::error_code&, std::size_t); + + scheduler_operation(func_type func) + : next_(0), + func_(func), + task_result_(0) + { + } + + // Prevents deletion through this type. + ~scheduler_operation() + { + } + +private: + friend class op_queue_access; + scheduler_operation* next_; + func_type func_; +protected: + friend class scheduler; + unsigned int task_result_; // Passed into bytes transferred. +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCHEDULER_OPERATION_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/scheduler_thread_info.hpp b/third_party/asio/1.18.2/include/asio/detail/scheduler_thread_info.hpp new file mode 100644 index 000000000..3202e3c57 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/scheduler_thread_info.hpp @@ -0,0 +1,40 @@ +// +// detail/scheduler_thread_info.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCHEDULER_THREAD_INFO_HPP +#define ASIO_DETAIL_SCHEDULER_THREAD_INFO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/op_queue.hpp" +#include "asio/detail/thread_info_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class scheduler; +class scheduler_operation; + +struct scheduler_thread_info : public thread_info_base +{ + op_queue private_op_queue; + long private_outstanding_work; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCHEDULER_THREAD_INFO_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/scoped_lock.hpp b/third_party/asio/1.18.2/include/asio/detail/scoped_lock.hpp new file mode 100644 index 000000000..273d70237 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/scoped_lock.hpp @@ -0,0 +1,101 @@ +// +// detail/scoped_lock.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCOPED_LOCK_HPP +#define ASIO_DETAIL_SCOPED_LOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper class to lock and unlock a mutex automatically. +template +class scoped_lock + : private noncopyable +{ +public: + // Tag type used to distinguish constructors. + enum adopt_lock_t { adopt_lock }; + + // Constructor adopts a lock that is already held. + scoped_lock(Mutex& m, adopt_lock_t) + : mutex_(m), + locked_(true) + { + } + + // Constructor acquires the lock. + explicit scoped_lock(Mutex& m) + : mutex_(m) + { + mutex_.lock(); + locked_ = true; + } + + // Destructor releases the lock. + ~scoped_lock() + { + if (locked_) + mutex_.unlock(); + } + + // Explicitly acquire the lock. + void lock() + { + if (!locked_) + { + mutex_.lock(); + locked_ = true; + } + } + + // Explicitly release the lock. + void unlock() + { + if (locked_) + { + mutex_.unlock(); + locked_ = false; + } + } + + // Test whether the lock is held. + bool locked() const + { + return locked_; + } + + // Get the underlying mutex. + Mutex& mutex() + { + return mutex_; + } + +private: + // The underlying mutex. + Mutex& mutex_; + + // Whether the mutex is currently locked or unlocked. + bool locked_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCOPED_LOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/scoped_ptr.hpp b/third_party/asio/1.18.2/include/asio/detail/scoped_ptr.hpp new file mode 100644 index 000000000..2eab4c99a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/scoped_ptr.hpp @@ -0,0 +1,87 @@ +// +// detail/scoped_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCOPED_PTR_HPP +#define ASIO_DETAIL_SCOPED_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class scoped_ptr +{ +public: + // Constructor. + explicit scoped_ptr(T* p = 0) + : p_(p) + { + } + + // Destructor. + ~scoped_ptr() + { + delete p_; + } + + // Access. + T* get() + { + return p_; + } + + // Access. + T* operator->() + { + return p_; + } + + // Dereference. + T& operator*() + { + return *p_; + } + + // Reset pointer. + void reset(T* p = 0) + { + delete p_; + p_ = p; + } + + // Release ownership of the pointer. + T* release() + { + T* tmp = p_; + p_ = 0; + return tmp; + } + +private: + // Disallow copying and assignment. + scoped_ptr(const scoped_ptr&); + scoped_ptr& operator=(const scoped_ptr&); + + T* p_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCOPED_PTR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/select_interrupter.hpp b/third_party/asio/1.18.2/include/asio/detail/select_interrupter.hpp new file mode 100644 index 000000000..a542373cc --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/select_interrupter.hpp @@ -0,0 +1,46 @@ +// +// detail/select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) +# include "asio/detail/socket_select_interrupter.hpp" +#elif defined(ASIO_HAS_EVENTFD) +# include "asio/detail/eventfd_select_interrupter.hpp" +#else +# include "asio/detail/pipe_select_interrupter.hpp" +#endif + +namespace asio { +namespace detail { + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) +typedef socket_select_interrupter select_interrupter; +#elif defined(ASIO_HAS_EVENTFD) +typedef eventfd_select_interrupter select_interrupter; +#else +typedef pipe_select_interrupter select_interrupter; +#endif + +} // namespace detail +} // namespace asio + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/select_reactor.hpp b/third_party/asio/1.18.2/include/asio/detail/select_reactor.hpp new file mode 100644 index 000000000..50cb57440 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/select_reactor.hpp @@ -0,0 +1,238 @@ +// +// detail/select_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SELECT_REACTOR_HPP +#define ASIO_DETAIL_SELECT_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) \ + || (!defined(ASIO_HAS_DEV_POLL) \ + && !defined(ASIO_HAS_EPOLL) \ + && !defined(ASIO_HAS_KQUEUE) \ + && !defined(ASIO_WINDOWS_RUNTIME)) + +#include +#include "asio/detail/fd_set_adapter.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/execution_context.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/thread.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class select_reactor + : public execution_context_service_base +{ +public: +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + enum op_types { read_op = 0, write_op = 1, except_op = 2, + max_select_ops = 3, connect_op = 3, max_ops = 4 }; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + enum op_types { read_op = 0, write_op = 1, except_op = 2, + max_select_ops = 3, connect_op = 1, max_ops = 3 }; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + + // Per-descriptor data. + struct per_descriptor_data + { + }; + + // Constructor. + ASIO_DECL select_reactor(asio::execution_context& ctx); + + // Destructor. + ASIO_DECL ~select_reactor(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Recreate internal descriptors following a fork. + ASIO_DECL void notify_fork( + asio::execution_context::fork_event fork_ev); + + // Initialise the task, but only if the reactor is not in its own thread. + ASIO_DECL void init_task(); + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + ASIO_DECL int register_descriptor(socket_type, per_descriptor_data&); + + // Register a descriptor with an associated single operation. Returns 0 on + // success, system error code on failure. + ASIO_DECL int register_internal_descriptor( + int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, reactor_op* op); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op, bool is_continuation) + { + scheduler_.post_immediate_completion(op, is_continuation); + } + + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + ASIO_DECL void start_op(int op_type, socket_type descriptor, + per_descriptor_data&, reactor_op* op, bool is_continuation, bool); + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data&); + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. The reactor resources associated with + // the descriptor must be released by calling cleanup_descriptor_data. + ASIO_DECL void deregister_descriptor(socket_type descriptor, + per_descriptor_data&, bool closing); + + // Remove the descriptor's registration from the reactor. The reactor + // resources associated with the descriptor must be released by calling + // cleanup_descriptor_data. + ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data&); + + // Perform any post-deregistration cleanup tasks associated with the + // descriptor data. + ASIO_DECL void cleanup_descriptor_data(per_descriptor_data&); + + // Move descriptor registration from one descriptor_data object to another. + ASIO_DECL void move_descriptor(socket_type descriptor, + per_descriptor_data& target_descriptor_data, + per_descriptor_data& source_descriptor_data); + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& queue); + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& target, + typename timer_queue::per_timer_data& source); + + // Run select once until interrupted or events are ready to be dispatched. + ASIO_DECL void run(long usec, op_queue& ops); + + // Interrupt the select loop. + ASIO_DECL void interrupt(); + +private: +#if defined(ASIO_HAS_IOCP) + // Run the select loop in the thread. + ASIO_DECL void run_thread(); +#endif // defined(ASIO_HAS_IOCP) + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Get the timeout value for the select call. + ASIO_DECL timeval* get_timeout(long usec, timeval& tv); + + // Cancel all operations associated with the given descriptor. This function + // does not acquire the select_reactor's mutex. + ASIO_DECL void cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec); + + // The scheduler implementation used to post completions. +# if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_type; +# else // defined(ASIO_HAS_IOCP) + typedef class scheduler scheduler_type; +# endif // defined(ASIO_HAS_IOCP) + scheduler_type& scheduler_; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The interrupter is used to break a blocking select call. + select_interrupter interrupter_; + + // The queues of read, write and except operations. + reactor_op_queue op_queue_[max_ops]; + + // The file descriptor sets to be passed to the select system call. + fd_set_adapter fd_sets_[max_select_ops]; + + // The timer queues. + timer_queue_set timer_queues_; + +#if defined(ASIO_HAS_IOCP) + // Helper class to run the reactor loop in a thread. + class thread_function; + friend class thread_function; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + asio::detail::thread* thread_; +#endif // defined(ASIO_HAS_IOCP) + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/select_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/select_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + // || (!defined(ASIO_HAS_DEV_POLL) + // && !defined(ASIO_HAS_EPOLL) + // && !defined(ASIO_HAS_KQUEUE) + // && !defined(ASIO_WINDOWS_RUNTIME)) + +#endif // ASIO_DETAIL_SELECT_REACTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/service_registry.hpp b/third_party/asio/1.18.2/include/asio/detail/service_registry.hpp new file mode 100644 index 000000000..2c7f4a520 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/service_registry.hpp @@ -0,0 +1,164 @@ +// +// detail/service_registry.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SERVICE_REGISTRY_HPP +#define ASIO_DETAIL_SERVICE_REGISTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +class io_context; + +namespace detail { + +template +class typeid_wrapper {}; + +class service_registry + : private noncopyable +{ +public: + // Constructor. + ASIO_DECL service_registry(execution_context& owner); + + // Destructor. + ASIO_DECL ~service_registry(); + + // Shutdown all services. + ASIO_DECL void shutdown_services(); + + // Destroy all services. + ASIO_DECL void destroy_services(); + + // Notify all services of a fork event. + ASIO_DECL void notify_fork(execution_context::fork_event fork_ev); + + // Get the service object corresponding to the specified service type. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + template + Service& use_service(); + + // Get the service object corresponding to the specified service type. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + // This overload is used for backwards compatibility with services that + // inherit from io_context::service. + template + Service& use_service(io_context& owner); + + // Add a service object. Throws on error, in which case ownership of the + // object is retained by the caller. + template + void add_service(Service* new_service); + + // Check whether a service object of the specified type already exists. + template + bool has_service() const; + +private: + // Initalise a service's key when the key_type typedef is not available. + template + static void init_key(execution_context::service::key& key, ...); + +#if !defined(ASIO_NO_TYPEID) + // Initalise a service's key when the key_type typedef is available. + template + static void init_key(execution_context::service::key& key, + typename enable_if< + is_base_of::value>::type*); +#endif // !defined(ASIO_NO_TYPEID) + + // Initialise a service's key based on its id. + ASIO_DECL static void init_key_from_id( + execution_context::service::key& key, + const execution_context::id& id); + +#if !defined(ASIO_NO_TYPEID) + // Initialise a service's key based on its id. + template + static void init_key_from_id(execution_context::service::key& key, + const service_id& /*id*/); +#endif // !defined(ASIO_NO_TYPEID) + + // Check if a service matches the given id. + ASIO_DECL static bool keys_match( + const execution_context::service::key& key1, + const execution_context::service::key& key2); + + // The type of a factory function used for creating a service instance. + typedef execution_context::service*(*factory_type)(void*); + + // Factory function for creating a service instance. + template + static execution_context::service* create(void* owner); + + // Destroy a service instance. + ASIO_DECL static void destroy(execution_context::service* service); + + // Helper class to manage service pointers. + struct auto_service_ptr; + friend struct auto_service_ptr; + struct auto_service_ptr + { + execution_context::service* ptr_; + ~auto_service_ptr() { destroy(ptr_); } + }; + + // Get the service object corresponding to the specified service key. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + ASIO_DECL execution_context::service* do_use_service( + const execution_context::service::key& key, + factory_type factory, void* owner); + + // Add a service object. Throws on error, in which case ownership of the + // object is retained by the caller. + ASIO_DECL void do_add_service( + const execution_context::service::key& key, + execution_context::service* new_service); + + // Check whether a service object with the specified key already exists. + ASIO_DECL bool do_has_service( + const execution_context::service::key& key) const; + + // Mutex to protect access to internal data. + mutable asio::detail::mutex mutex_; + + // The owner of this service registry and the services it contains. + execution_context& owner_; + + // The first service in the list of contained services. + execution_context::service* first_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/service_registry.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/service_registry.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_SERVICE_REGISTRY_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/signal_blocker.hpp b/third_party/asio/1.18.2/include/asio/detail/signal_blocker.hpp new file mode 100644 index 000000000..bbe7b3fed --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/signal_blocker.hpp @@ -0,0 +1,44 @@ +// +// detail/signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) || defined(ASIO_WINDOWS) \ + || defined(ASIO_WINDOWS_RUNTIME) \ + || defined(__CYGWIN__) || defined(__SYMBIAN32__) +# include "asio/detail/null_signal_blocker.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_signal_blocker.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) || defined(ASIO_WINDOWS) \ + || defined(ASIO_WINDOWS_RUNTIME) \ + || defined(__CYGWIN__) || defined(__SYMBIAN32__) +typedef null_signal_blocker signal_blocker; +#elif defined(ASIO_HAS_PTHREADS) +typedef posix_signal_blocker signal_blocker; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_SIGNAL_BLOCKER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/signal_handler.hpp b/third_party/asio/1.18.2/include/asio/detail/signal_handler.hpp new file mode 100644 index 000000000..0153133f1 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/signal_handler.hpp @@ -0,0 +1,90 @@ +// +// detail/signal_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_HANDLER_HPP +#define ASIO_DETAIL_SIGNAL_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/signal_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class signal_handler : public signal_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(signal_handler); + + signal_handler(Handler& h, const IoExecutor& io_ex) + : signal_op(&signal_handler::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(h)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + signal_handler* h(static_cast(base)); + ptr p = { asio::detail::addressof(h->handler_), h, h }; + + ASIO_HANDLER_COMPLETION((*h)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + h->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(h->handler_, h->ec_, h->signal_number_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SIGNAL_HANDLER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/signal_init.hpp b/third_party/asio/1.18.2/include/asio/detail/signal_init.hpp new file mode 100644 index 000000000..80c7a7adc --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/signal_init.hpp @@ -0,0 +1,47 @@ +// +// detail/signal_init.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_INIT_HPP +#define ASIO_DETAIL_SIGNAL_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class signal_init +{ +public: + // Constructor. + signal_init() + { + std::signal(Signal, SIG_IGN); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_SIGNAL_INIT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/signal_op.hpp b/third_party/asio/1.18.2/include/asio/detail/signal_op.hpp new file mode 100644 index 000000000..297c0d9fa --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/signal_op.hpp @@ -0,0 +1,49 @@ +// +// detail/signal_op.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_OP_HPP +#define ASIO_DETAIL_SIGNAL_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class signal_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + + // The signal number to be passed to the completion handler. + int signal_number_; + +protected: + signal_op(func_type func) + : operation(func), + signal_number_(0) + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SIGNAL_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/signal_set_service.hpp b/third_party/asio/1.18.2/include/asio/detail/signal_set_service.hpp new file mode 100644 index 000000000..7652fe200 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/signal_set_service.hpp @@ -0,0 +1,229 @@ +// +// detail/signal_set_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_SET_SERVICE_HPP +#define ASIO_DETAIL_SIGNAL_SET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/signal_handler.hpp" +#include "asio/detail/signal_op.hpp" +#include "asio/detail/socket_types.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else // defined(ASIO_HAS_IOCP) +# include "asio/detail/scheduler.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) +# include "asio/detail/reactor.hpp" +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(NSIG) && (NSIG > 0) +enum { max_signal_number = NSIG }; +#else +enum { max_signal_number = 128 }; +#endif + +extern ASIO_DECL struct signal_state* get_signal_state(); + +extern "C" ASIO_DECL void asio_signal_handler(int signal_number); + +class signal_set_service : + public execution_context_service_base +{ +public: + // Type used for tracking an individual signal registration. + class registration + { + public: + // Default constructor. + registration() + : signal_number_(0), + queue_(0), + undelivered_(0), + next_in_table_(0), + prev_in_table_(0), + next_in_set_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class signal_set_service; + + // The signal number that is registered. + int signal_number_; + + // The waiting signal handlers. + op_queue* queue_; + + // The number of undelivered signals. + std::size_t undelivered_; + + // Pointers to adjacent registrations in the registrations_ table. + registration* next_in_table_; + registration* prev_in_table_; + + // Link to next registration in the signal set. + registration* next_in_set_; + }; + + // The implementation type of the signal_set. + class implementation_type + { + public: + // Default constructor. + implementation_type() + : signals_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class signal_set_service; + + // The pending signal handlers. + op_queue queue_; + + // Linked list of registered signals. + registration* signals_; + }; + + // Constructor. + ASIO_DECL signal_set_service(execution_context& context); + + // Destructor. + ASIO_DECL ~signal_set_service(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Perform fork-related housekeeping. + ASIO_DECL void notify_fork( + asio::execution_context::fork_event fork_ev); + + // Construct a new signal_set implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Destroy a signal_set implementation. + ASIO_DECL void destroy(implementation_type& impl); + + // Add a signal to a signal_set. + ASIO_DECL asio::error_code add(implementation_type& impl, + int signal_number, asio::error_code& ec); + + // Remove a signal to a signal_set. + ASIO_DECL asio::error_code remove(implementation_type& impl, + int signal_number, asio::error_code& ec); + + // Remove all signals from a signal_set. + ASIO_DECL asio::error_code clear(implementation_type& impl, + asio::error_code& ec); + + // Cancel all operations associated with the signal set. + ASIO_DECL asio::error_code cancel(implementation_type& impl, + asio::error_code& ec); + + // Start an asynchronous operation to wait for a signal to be delivered. + template + void async_wait(implementation_type& impl, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef signal_handler op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler, io_ex); + + ASIO_HANDLER_CREATION((scheduler_.context(), + *p.p, "signal_set", &impl, 0, "async_wait")); + + start_wait_op(impl, p.p); + p.v = p.p = 0; + } + + // Deliver notification that a particular signal occurred. + ASIO_DECL static void deliver_signal(int signal_number); + +private: + // Helper function to add a service to the global signal state. + ASIO_DECL static void add_service(signal_set_service* service); + + // Helper function to remove a service from the global signal state. + ASIO_DECL static void remove_service(signal_set_service* service); + + // Helper function to create the pipe descriptors. + ASIO_DECL static void open_descriptors(); + + // Helper function to close the pipe descriptors. + ASIO_DECL static void close_descriptors(); + + // Helper function to start a wait operation. + ASIO_DECL void start_wait_op(implementation_type& impl, signal_op* op); + + // The scheduler used for dispatching handlers. +#if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_impl; +#else + typedef class scheduler scheduler_impl; +#endif + scheduler_impl& scheduler_; + +#if !defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_RUNTIME) \ + && !defined(__CYGWIN__) + // The type used for registering for pipe reactor notifications. + class pipe_read_op; + + // The reactor used for waiting for pipe readiness. + reactor& reactor_; + + // The per-descriptor reactor data used for the pipe. + reactor::per_descriptor_data reactor_data_; +#endif // !defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_RUNTIME) + // && !defined(__CYGWIN__) + + // A mapping from signal number to the registered signal sets. + registration* registrations_[max_signal_number]; + + // Pointers to adjacent services in linked list. + signal_set_service* next_; + signal_set_service* prev_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/signal_set_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_SIGNAL_SET_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/socket_holder.hpp b/third_party/asio/1.18.2/include/asio/detail/socket_holder.hpp new file mode 100644 index 000000000..571b9f67c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/socket_holder.hpp @@ -0,0 +1,98 @@ +// +// detail/socket_holder.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_HOLDER_HPP +#define ASIO_DETAIL_SOCKET_HOLDER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Implement the resource acquisition is initialisation idiom for sockets. +class socket_holder + : private noncopyable +{ +public: + // Construct as an uninitialised socket. + socket_holder() + : socket_(invalid_socket) + { + } + + // Construct to take ownership of the specified socket. + explicit socket_holder(socket_type s) + : socket_(s) + { + } + + // Destructor. + ~socket_holder() + { + if (socket_ != invalid_socket) + { + asio::error_code ec; + socket_ops::state_type state = 0; + socket_ops::close(socket_, state, true, ec); + } + } + + // Get the underlying socket. + socket_type get() const + { + return socket_; + } + + // Reset to an uninitialised socket. + void reset() + { + if (socket_ != invalid_socket) + { + asio::error_code ec; + socket_ops::state_type state = 0; + socket_ops::close(socket_, state, true, ec); + socket_ = invalid_socket; + } + } + + // Reset to take ownership of the specified socket. + void reset(socket_type s) + { + reset(); + socket_ = s; + } + + // Release ownership of the socket. + socket_type release() + { + socket_type tmp = socket_; + socket_ = invalid_socket; + return tmp; + } + +private: + // The underlying socket. + socket_type socket_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_HOLDER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/socket_ops.hpp b/third_party/asio/1.18.2/include/asio/detail/socket_ops.hpp new file mode 100644 index 000000000..a37684e1c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/socket_ops.hpp @@ -0,0 +1,383 @@ +// +// detail/socket_ops.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_OPS_HPP +#define ASIO_DETAIL_SOCKET_OPS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include "asio/error_code.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace socket_ops { + +// Socket state bits. +enum +{ + // The user wants a non-blocking socket. + user_set_non_blocking = 1, + + // The socket has been set non-blocking. + internal_non_blocking = 2, + + // Helper "state" used to determine whether the socket is non-blocking. + non_blocking = user_set_non_blocking | internal_non_blocking, + + // User wants connection_aborted errors, which are disabled by default. + enable_connection_aborted = 4, + + // The user set the linger option. Needs to be checked when closing. + user_set_linger = 8, + + // The socket is stream-oriented. + stream_oriented = 16, + + // The socket is datagram-oriented. + datagram_oriented = 32, + + // The socket may have been dup()-ed. + possible_dup = 64 +}; + +typedef unsigned char state_type; + +struct noop_deleter { void operator()(void*) {} }; +typedef shared_ptr shared_cancel_token_type; +typedef weak_ptr weak_cancel_token_type; + +#if !defined(ASIO_WINDOWS_RUNTIME) + +ASIO_DECL socket_type accept(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +ASIO_DECL socket_type sync_accept(socket_type s, + state_type state, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_accept(socket_type s, + void* output_buffer, DWORD address_length, + socket_addr_type* addr, std::size_t* addrlen, + socket_type new_socket, asio::error_code& ec); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_accept(socket_type s, + state_type state, socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, socket_type& new_socket); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL int bind(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL int close(socket_type s, state_type& state, + bool destruction, asio::error_code& ec); + +ASIO_DECL bool set_user_non_blocking(socket_type s, + state_type& state, bool value, asio::error_code& ec); + +ASIO_DECL bool set_internal_non_blocking(socket_type s, + state_type& state, bool value, asio::error_code& ec); + +ASIO_DECL int shutdown(socket_type s, + int what, asio::error_code& ec); + +ASIO_DECL int connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL void sync_connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_connect(socket_type s, + asio::error_code& ec); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_connect(socket_type s, + asio::error_code& ec); + +ASIO_DECL int socketpair(int af, int type, int protocol, + socket_type sv[2], asio::error_code& ec); + +ASIO_DECL bool sockatmark(socket_type s, asio::error_code& ec); + +ASIO_DECL size_t available(socket_type s, asio::error_code& ec); + +ASIO_DECL int listen(socket_type s, + int backlog, asio::error_code& ec); + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef WSABUF buf; +#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef iovec buf; +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +ASIO_DECL void init_buf(buf& b, void* data, size_t size); + +ASIO_DECL void init_buf(buf& b, const void* data, size_t size); + +ASIO_DECL signed_size_type recv(socket_type s, buf* bufs, + size_t count, int flags, asio::error_code& ec); + +ASIO_DECL signed_size_type recv1(socket_type s, + void* data, size_t size, int flags, asio::error_code& ec); + +ASIO_DECL size_t sync_recv(socket_type s, state_type state, buf* bufs, + size_t count, int flags, bool all_empty, asio::error_code& ec); + +ASIO_DECL size_t sync_recv1(socket_type s, state_type state, + void* data, size_t size, int flags, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_recv(state_type state, + const weak_cancel_token_type& cancel_token, bool all_empty, + asio::error_code& ec, size_t bytes_transferred); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_recv(socket_type s, + buf* bufs, size_t count, int flags, bool is_stream, + asio::error_code& ec, size_t& bytes_transferred); + +ASIO_DECL bool non_blocking_recv1(socket_type s, + void* data, size_t size, int flags, bool is_stream, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL signed_size_type recvfrom(socket_type s, buf* bufs, + size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +ASIO_DECL signed_size_type recvfrom1(socket_type s, void* data, + size_t size, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +ASIO_DECL size_t sync_recvfrom(socket_type s, state_type state, + buf* bufs, size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +ASIO_DECL size_t sync_recvfrom1(socket_type s, state_type state, + void* data, size_t size, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_recvfrom( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_recvfrom(socket_type s, + buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, size_t& bytes_transferred); + +ASIO_DECL bool non_blocking_recvfrom1(socket_type s, + void* data, size_t size, int flags, + socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL signed_size_type recvmsg(socket_type s, buf* bufs, + size_t count, int in_flags, int& out_flags, + asio::error_code& ec); + +ASIO_DECL size_t sync_recvmsg(socket_type s, state_type state, + buf* bufs, size_t count, int in_flags, int& out_flags, + asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_recvmsg( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_recvmsg(socket_type s, + buf* bufs, size_t count, int in_flags, int& out_flags, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL signed_size_type send(socket_type s, const buf* bufs, + size_t count, int flags, asio::error_code& ec); + +ASIO_DECL signed_size_type send1(socket_type s, + const void* data, size_t size, int flags, asio::error_code& ec); + +ASIO_DECL size_t sync_send(socket_type s, state_type state, + const buf* bufs, size_t count, int flags, + bool all_empty, asio::error_code& ec); + +ASIO_DECL size_t sync_send1(socket_type s, state_type state, + const void* data, size_t size, int flags, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_send( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_send(socket_type s, + const buf* bufs, size_t count, int flags, + asio::error_code& ec, size_t& bytes_transferred); + +ASIO_DECL bool non_blocking_send1(socket_type s, + const void* data, size_t size, int flags, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL signed_size_type sendto(socket_type s, const buf* bufs, + size_t count, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL signed_size_type sendto1(socket_type s, const void* data, + size_t size, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL size_t sync_sendto(socket_type s, state_type state, + const buf* bufs, size_t count, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL size_t sync_sendto1(socket_type s, state_type state, + const void* data, size_t size, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +#if !defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_sendto(socket_type s, + const buf* bufs, size_t count, int flags, + const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec, size_t& bytes_transferred); + +ASIO_DECL bool non_blocking_sendto1(socket_type s, + const void* data, size_t size, int flags, + const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // !defined(ASIO_HAS_IOCP) + +ASIO_DECL socket_type socket(int af, int type, int protocol, + asio::error_code& ec); + +ASIO_DECL int setsockopt(socket_type s, state_type& state, + int level, int optname, const void* optval, + std::size_t optlen, asio::error_code& ec); + +ASIO_DECL int getsockopt(socket_type s, state_type state, + int level, int optname, void* optval, + size_t* optlen, asio::error_code& ec); + +ASIO_DECL int getpeername(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, bool cached, asio::error_code& ec); + +ASIO_DECL int getsockname(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); + +ASIO_DECL int ioctl(socket_type s, state_type& state, + int cmd, ioctl_arg_type* arg, asio::error_code& ec); + +ASIO_DECL int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, timeval* timeout, asio::error_code& ec); + +ASIO_DECL int poll_read(socket_type s, + state_type state, int msec, asio::error_code& ec); + +ASIO_DECL int poll_write(socket_type s, + state_type state, int msec, asio::error_code& ec); + +ASIO_DECL int poll_error(socket_type s, + state_type state, int msec, asio::error_code& ec); + +ASIO_DECL int poll_connect(socket_type s, + int msec, asio::error_code& ec); + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +ASIO_DECL const char* inet_ntop(int af, const void* src, char* dest, + size_t length, unsigned long scope_id, asio::error_code& ec); + +ASIO_DECL int inet_pton(int af, const char* src, void* dest, + unsigned long* scope_id, asio::error_code& ec); + +ASIO_DECL int gethostname(char* name, + int namelen, asio::error_code& ec); + +#if !defined(ASIO_WINDOWS_RUNTIME) + +ASIO_DECL asio::error_code getaddrinfo(const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec); + +ASIO_DECL asio::error_code background_getaddrinfo( + const weak_cancel_token_type& cancel_token, const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec); + +ASIO_DECL void freeaddrinfo(addrinfo_type* ai); + +ASIO_DECL asio::error_code getnameinfo( + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int flags, asio::error_code& ec); + +ASIO_DECL asio::error_code sync_getnameinfo( + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec); + +ASIO_DECL asio::error_code background_getnameinfo( + const weak_cancel_token_type& cancel_token, + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec); + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +ASIO_DECL u_long_type network_to_host_long(u_long_type value); + +ASIO_DECL u_long_type host_to_network_long(u_long_type value); + +ASIO_DECL u_short_type network_to_host_short(u_short_type value); + +ASIO_DECL u_short_type host_to_network_short(u_short_type value); + +} // namespace socket_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/socket_ops.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_SOCKET_OPS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/socket_option.hpp b/third_party/asio/1.18.2/include/asio/detail/socket_option.hpp new file mode 100644 index 000000000..8d8c0d84f --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/socket_option.hpp @@ -0,0 +1,316 @@ +// +// detail/socket_option.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_OPTION_HPP +#define ASIO_DETAIL_SOCKET_OPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_exception.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace socket_option { + +// Helper template for implementing boolean-based options. +template +class boolean +{ +public: + // Default constructor. + boolean() + : value_(0) + { + } + + // Construct with a specific option value. + explicit boolean(bool v) + : value_(v ? 1 : 0) + { + } + + // Set the current value of the boolean. + boolean& operator=(bool v) + { + value_ = v ? 1 : 0; + return *this; + } + + // Get the current value of the boolean. + bool value() const + { + return !!value_; + } + + // Convert to bool. + operator bool() const + { + return !!value_; + } + + // Test for false. + bool operator!() const + { + return !value_; + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the boolean data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the boolean data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the boolean data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the boolean data. + template + void resize(const Protocol&, std::size_t s) + { + // On some platforms (e.g. Windows Vista), the getsockopt function will + // return the size of a boolean socket option as one byte, even though a + // four byte integer was passed in. + switch (s) + { + case sizeof(char): + value_ = *reinterpret_cast(&value_) ? 1 : 0; + break; + case sizeof(value_): + break; + default: + { + std::length_error ex("boolean socket option resize"); + asio::detail::throw_exception(ex); + } + } + } + +private: + int value_; +}; + +// Helper template for implementing integer options. +template +class integer +{ +public: + // Default constructor. + integer() + : value_(0) + { + } + + // Construct with a specific option value. + explicit integer(int v) + : value_(v) + { + } + + // Set the value of the int option. + integer& operator=(int v) + { + value_ = v; + return *this; + } + + // Get the current value of the int option. + int value() const + { + return value_; + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the int data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the int data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the int data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the int data. + template + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + { + std::length_error ex("integer socket option resize"); + asio::detail::throw_exception(ex); + } + } + +private: + int value_; +}; + +// Helper template for implementing linger options. +template +class linger +{ +public: + // Default constructor. + linger() + { + value_.l_onoff = 0; + value_.l_linger = 0; + } + + // Construct with specific option values. + linger(bool e, int t) + { + enabled(e); + timeout ASIO_PREVENT_MACRO_SUBSTITUTION(t); + } + + // Set the value for whether linger is enabled. + void enabled(bool value) + { + value_.l_onoff = value ? 1 : 0; + } + + // Get the value for whether linger is enabled. + bool enabled() const + { + return value_.l_onoff != 0; + } + + // Set the value for the linger timeout. + void timeout ASIO_PREVENT_MACRO_SUBSTITUTION(int value) + { +#if defined(WIN32) + value_.l_linger = static_cast(value); +#else + value_.l_linger = value; +#endif + } + + // Get the value for the linger timeout. + int timeout ASIO_PREVENT_MACRO_SUBSTITUTION() const + { + return static_cast(value_.l_linger); + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the linger data. + template + detail::linger_type* data(const Protocol&) + { + return &value_; + } + + // Get the address of the linger data. + template + const detail::linger_type* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the linger data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the int data. + template + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + { + std::length_error ex("linger socket option resize"); + asio::detail::throw_exception(ex); + } + } + +private: + detail::linger_type value_; +}; + +} // namespace socket_option +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPTION_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/socket_select_interrupter.hpp b/third_party/asio/1.18.2/include/asio/detail/socket_select_interrupter.hpp new file mode 100644 index 000000000..2e971d200 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/socket_select_interrupter.hpp @@ -0,0 +1,91 @@ +// +// detail/socket_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_WINDOWS_RUNTIME) + +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class socket_select_interrupter +{ +public: + // Constructor. + ASIO_DECL socket_select_interrupter(); + + // Destructor. + ASIO_DECL ~socket_select_interrupter(); + + // Recreate the interrupter's descriptors. Used after a fork. + ASIO_DECL void recreate(); + + // Interrupt the select call. + ASIO_DECL void interrupt(); + + // Reset the select interrupter. Returns true if the reset was successful. + ASIO_DECL bool reset(); + + // Get the read descriptor to be passed to select. + socket_type read_descriptor() const + { + return read_descriptor_; + } + +private: + // Open the descriptors. Throws on error. + ASIO_DECL void open_descriptors(); + + // Close the descriptors. + ASIO_DECL void close_descriptors(); + + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + socket_type read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + socket_type write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/socket_select_interrupter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + +#endif // !defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/socket_types.hpp b/third_party/asio/1.18.2/include/asio/detail/socket_types.hpp new file mode 100644 index 000000000..33ad63eec --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/socket_types.hpp @@ -0,0 +1,417 @@ +// +// detail/socket_types.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_TYPES_HPP +#define ASIO_DETAIL_SOCKET_TYPES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +// Empty. +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# error WinSock.h has already been included +# endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# if defined(__BORLANDC__) +# include // Needed for __errno +# if !defined(_WSPIAPI_H_) +# define _WSPIAPI_H_ +# define ASIO_WSPIAPI_H_DEFINED +# endif // !defined(_WSPIAPI_H_) +# endif // defined(__BORLANDC__) +# include +# include +# if defined(WINAPI_FAMILY) +# if ((WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) != 0) +# include +# endif // ((WINAPI_FAMILY & WINAPI_PARTITION_DESKTOP) != 0) +# endif // defined(WINAPI_FAMILY) +# if !defined(ASIO_WINDOWS_APP) +# include +# endif // !defined(ASIO_WINDOWS_APP) +# if defined(ASIO_WSPIAPI_H_DEFINED) +# undef _WSPIAPI_H_ +# undef ASIO_WSPIAPI_H_DEFINED +# endif // defined(ASIO_WSPIAPI_H_DEFINED) +# if !defined(ASIO_NO_DEFAULT_LINKED_LIBS) +# if defined(UNDER_CE) +# pragma comment(lib, "ws2.lib") +# elif defined(_MSC_VER) || defined(__BORLANDC__) +# pragma comment(lib, "ws2_32.lib") +# if !defined(ASIO_WINDOWS_APP) +# pragma comment(lib, "mswsock.lib") +# endif // !defined(ASIO_WINDOWS_APP) +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# endif // !defined(ASIO_NO_DEFAULT_LINKED_LIBS) +# include "asio/detail/old_win_sdk_compat.hpp" +#else +# include +# if (defined(__MACH__) && defined(__APPLE__)) \ + || defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__linux__) \ + || defined(__EMSCRIPTEN__) +# include +# elif !defined(__SYMBIAN32__) +# include +# endif +# include +# include +# include +# if defined(__hpux) +# include +# endif +# if !defined(__hpux) || defined(__SELECT) +# include +# endif +# include +# include +# include +# include +# if !defined(__SYMBIAN32__) +# include +# endif +# include +# include +# include +# include +# if defined(__sun) +# include +# include +# endif +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_WINDOWS_RUNTIME) +const int max_addr_v4_str_len = 256; +const int max_addr_v6_str_len = 256; +typedef unsigned __int32 u_long_type; +typedef unsigned __int16 u_short_type; +struct in4_addr_type { u_long_type s_addr; }; +struct in4_mreq_type { in4_addr_type imr_multiaddr, imr_interface; }; +struct in6_addr_type { unsigned char s6_addr[16]; }; +struct in6_mreq_type { in6_addr_type ipv6mr_multiaddr; + unsigned long ipv6mr_interface; }; +struct socket_addr_type { int sa_family; }; +struct sockaddr_in4_type { int sin_family; + in4_addr_type sin_addr; u_short_type sin_port; }; +struct sockaddr_in6_type { int sin6_family; + in6_addr_type sin6_addr; u_short_type sin6_port; + u_long_type sin6_flowinfo; u_long_type sin6_scope_id; }; +struct sockaddr_storage_type { int ss_family; + unsigned char ss_bytes[128 - sizeof(int)]; }; +struct addrinfo_type { int ai_flags; + int ai_family, ai_socktype, ai_protocol; + int ai_addrlen; const void* ai_addr; + const char* ai_canonname; addrinfo_type* ai_next; }; +struct linger_type { u_short_type l_onoff, l_linger; }; +typedef u_long_type ioctl_arg_type; +typedef int signed_size_type; +# define ASIO_OS_DEF(c) ASIO_OS_DEF_##c +# define ASIO_OS_DEF_AF_UNSPEC 0 +# define ASIO_OS_DEF_AF_INET 2 +# define ASIO_OS_DEF_AF_INET6 23 +# define ASIO_OS_DEF_SOCK_STREAM 1 +# define ASIO_OS_DEF_SOCK_DGRAM 2 +# define ASIO_OS_DEF_SOCK_RAW 3 +# define ASIO_OS_DEF_SOCK_SEQPACKET 5 +# define ASIO_OS_DEF_IPPROTO_IP 0 +# define ASIO_OS_DEF_IPPROTO_IPV6 41 +# define ASIO_OS_DEF_IPPROTO_TCP 6 +# define ASIO_OS_DEF_IPPROTO_UDP 17 +# define ASIO_OS_DEF_IPPROTO_ICMP 1 +# define ASIO_OS_DEF_IPPROTO_ICMPV6 58 +# define ASIO_OS_DEF_FIONBIO 1 +# define ASIO_OS_DEF_FIONREAD 2 +# define ASIO_OS_DEF_INADDR_ANY 0 +# define ASIO_OS_DEF_MSG_OOB 0x1 +# define ASIO_OS_DEF_MSG_PEEK 0x2 +# define ASIO_OS_DEF_MSG_DONTROUTE 0x4 +# define ASIO_OS_DEF_MSG_EOR 0 // Not supported. +# define ASIO_OS_DEF_SHUT_RD 0x0 +# define ASIO_OS_DEF_SHUT_WR 0x1 +# define ASIO_OS_DEF_SHUT_RDWR 0x2 +# define ASIO_OS_DEF_SOMAXCONN 0x7fffffff +# define ASIO_OS_DEF_SOL_SOCKET 0xffff +# define ASIO_OS_DEF_SO_BROADCAST 0x20 +# define ASIO_OS_DEF_SO_DEBUG 0x1 +# define ASIO_OS_DEF_SO_DONTROUTE 0x10 +# define ASIO_OS_DEF_SO_KEEPALIVE 0x8 +# define ASIO_OS_DEF_SO_LINGER 0x80 +# define ASIO_OS_DEF_SO_OOBINLINE 0x100 +# define ASIO_OS_DEF_SO_SNDBUF 0x1001 +# define ASIO_OS_DEF_SO_RCVBUF 0x1002 +# define ASIO_OS_DEF_SO_SNDLOWAT 0x1003 +# define ASIO_OS_DEF_SO_RCVLOWAT 0x1004 +# define ASIO_OS_DEF_SO_REUSEADDR 0x4 +# define ASIO_OS_DEF_TCP_NODELAY 0x1 +# define ASIO_OS_DEF_IP_MULTICAST_IF 2 +# define ASIO_OS_DEF_IP_MULTICAST_TTL 3 +# define ASIO_OS_DEF_IP_MULTICAST_LOOP 4 +# define ASIO_OS_DEF_IP_ADD_MEMBERSHIP 5 +# define ASIO_OS_DEF_IP_DROP_MEMBERSHIP 6 +# define ASIO_OS_DEF_IP_TTL 7 +# define ASIO_OS_DEF_IPV6_UNICAST_HOPS 4 +# define ASIO_OS_DEF_IPV6_MULTICAST_IF 9 +# define ASIO_OS_DEF_IPV6_MULTICAST_HOPS 10 +# define ASIO_OS_DEF_IPV6_MULTICAST_LOOP 11 +# define ASIO_OS_DEF_IPV6_JOIN_GROUP 12 +# define ASIO_OS_DEF_IPV6_LEAVE_GROUP 13 +# define ASIO_OS_DEF_AI_CANONNAME 0x2 +# define ASIO_OS_DEF_AI_PASSIVE 0x1 +# define ASIO_OS_DEF_AI_NUMERICHOST 0x4 +# define ASIO_OS_DEF_AI_NUMERICSERV 0x8 +# define ASIO_OS_DEF_AI_V4MAPPED 0x800 +# define ASIO_OS_DEF_AI_ALL 0x100 +# define ASIO_OS_DEF_AI_ADDRCONFIG 0x400 +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +typedef SOCKET socket_type; +const SOCKET invalid_socket = INVALID_SOCKET; +const int socket_error_retval = SOCKET_ERROR; +const int max_addr_v4_str_len = 256; +const int max_addr_v6_str_len = 256; +typedef sockaddr socket_addr_type; +typedef in_addr in4_addr_type; +typedef ip_mreq in4_mreq_type; +typedef sockaddr_in sockaddr_in4_type; +# if defined(ASIO_HAS_OLD_WIN_SDK) +typedef in6_addr_emulation in6_addr_type; +typedef ipv6_mreq_emulation in6_mreq_type; +typedef sockaddr_in6_emulation sockaddr_in6_type; +typedef sockaddr_storage_emulation sockaddr_storage_type; +typedef addrinfo_emulation addrinfo_type; +# else +typedef in6_addr in6_addr_type; +typedef ipv6_mreq in6_mreq_type; +typedef sockaddr_in6 sockaddr_in6_type; +typedef sockaddr_storage sockaddr_storage_type; +typedef addrinfo addrinfo_type; +# endif +typedef ::linger linger_type; +typedef unsigned long ioctl_arg_type; +typedef u_long u_long_type; +typedef u_short u_short_type; +typedef int signed_size_type; +struct sockaddr_un_type { u_short sun_family; char sun_path[108]; }; +# define ASIO_OS_DEF(c) ASIO_OS_DEF_##c +# define ASIO_OS_DEF_AF_UNSPEC AF_UNSPEC +# define ASIO_OS_DEF_AF_INET AF_INET +# define ASIO_OS_DEF_AF_INET6 AF_INET6 +# define ASIO_OS_DEF_SOCK_STREAM SOCK_STREAM +# define ASIO_OS_DEF_SOCK_DGRAM SOCK_DGRAM +# define ASIO_OS_DEF_SOCK_RAW SOCK_RAW +# define ASIO_OS_DEF_SOCK_SEQPACKET SOCK_SEQPACKET +# define ASIO_OS_DEF_IPPROTO_IP IPPROTO_IP +# define ASIO_OS_DEF_IPPROTO_IPV6 IPPROTO_IPV6 +# define ASIO_OS_DEF_IPPROTO_TCP IPPROTO_TCP +# define ASIO_OS_DEF_IPPROTO_UDP IPPROTO_UDP +# define ASIO_OS_DEF_IPPROTO_ICMP IPPROTO_ICMP +# define ASIO_OS_DEF_IPPROTO_ICMPV6 IPPROTO_ICMPV6 +# define ASIO_OS_DEF_FIONBIO FIONBIO +# define ASIO_OS_DEF_FIONREAD FIONREAD +# define ASIO_OS_DEF_INADDR_ANY INADDR_ANY +# define ASIO_OS_DEF_MSG_OOB MSG_OOB +# define ASIO_OS_DEF_MSG_PEEK MSG_PEEK +# define ASIO_OS_DEF_MSG_DONTROUTE MSG_DONTROUTE +# define ASIO_OS_DEF_MSG_EOR 0 // Not supported on Windows. +# define ASIO_OS_DEF_SHUT_RD SD_RECEIVE +# define ASIO_OS_DEF_SHUT_WR SD_SEND +# define ASIO_OS_DEF_SHUT_RDWR SD_BOTH +# define ASIO_OS_DEF_SOMAXCONN SOMAXCONN +# define ASIO_OS_DEF_SOL_SOCKET SOL_SOCKET +# define ASIO_OS_DEF_SO_BROADCAST SO_BROADCAST +# define ASIO_OS_DEF_SO_DEBUG SO_DEBUG +# define ASIO_OS_DEF_SO_DONTROUTE SO_DONTROUTE +# define ASIO_OS_DEF_SO_KEEPALIVE SO_KEEPALIVE +# define ASIO_OS_DEF_SO_LINGER SO_LINGER +# define ASIO_OS_DEF_SO_OOBINLINE SO_OOBINLINE +# define ASIO_OS_DEF_SO_SNDBUF SO_SNDBUF +# define ASIO_OS_DEF_SO_RCVBUF SO_RCVBUF +# define ASIO_OS_DEF_SO_SNDLOWAT SO_SNDLOWAT +# define ASIO_OS_DEF_SO_RCVLOWAT SO_RCVLOWAT +# define ASIO_OS_DEF_SO_REUSEADDR SO_REUSEADDR +# define ASIO_OS_DEF_TCP_NODELAY TCP_NODELAY +# define ASIO_OS_DEF_IP_MULTICAST_IF IP_MULTICAST_IF +# define ASIO_OS_DEF_IP_MULTICAST_TTL IP_MULTICAST_TTL +# define ASIO_OS_DEF_IP_MULTICAST_LOOP IP_MULTICAST_LOOP +# define ASIO_OS_DEF_IP_ADD_MEMBERSHIP IP_ADD_MEMBERSHIP +# define ASIO_OS_DEF_IP_DROP_MEMBERSHIP IP_DROP_MEMBERSHIP +# define ASIO_OS_DEF_IP_TTL IP_TTL +# define ASIO_OS_DEF_IPV6_UNICAST_HOPS IPV6_UNICAST_HOPS +# define ASIO_OS_DEF_IPV6_MULTICAST_IF IPV6_MULTICAST_IF +# define ASIO_OS_DEF_IPV6_MULTICAST_HOPS IPV6_MULTICAST_HOPS +# define ASIO_OS_DEF_IPV6_MULTICAST_LOOP IPV6_MULTICAST_LOOP +# define ASIO_OS_DEF_IPV6_JOIN_GROUP IPV6_JOIN_GROUP +# define ASIO_OS_DEF_IPV6_LEAVE_GROUP IPV6_LEAVE_GROUP +# define ASIO_OS_DEF_AI_CANONNAME AI_CANONNAME +# define ASIO_OS_DEF_AI_PASSIVE AI_PASSIVE +# define ASIO_OS_DEF_AI_NUMERICHOST AI_NUMERICHOST +# if defined(AI_NUMERICSERV) +# define ASIO_OS_DEF_AI_NUMERICSERV AI_NUMERICSERV +# else +# define ASIO_OS_DEF_AI_NUMERICSERV 0 +# endif +# if defined(AI_V4MAPPED) +# define ASIO_OS_DEF_AI_V4MAPPED AI_V4MAPPED +# else +# define ASIO_OS_DEF_AI_V4MAPPED 0 +# endif +# if defined(AI_ALL) +# define ASIO_OS_DEF_AI_ALL AI_ALL +# else +# define ASIO_OS_DEF_AI_ALL 0 +# endif +# if defined(AI_ADDRCONFIG) +# define ASIO_OS_DEF_AI_ADDRCONFIG AI_ADDRCONFIG +# else +# define ASIO_OS_DEF_AI_ADDRCONFIG 0 +# endif +# if defined (_WIN32_WINNT) +const int max_iov_len = 64; +# else +const int max_iov_len = 16; +# endif +#else +typedef int socket_type; +const int invalid_socket = -1; +const int socket_error_retval = -1; +const int max_addr_v4_str_len = INET_ADDRSTRLEN; +#if defined(INET6_ADDRSTRLEN) +const int max_addr_v6_str_len = INET6_ADDRSTRLEN + 1 + IF_NAMESIZE; +#else // defined(INET6_ADDRSTRLEN) +const int max_addr_v6_str_len = 256; +#endif // defined(INET6_ADDRSTRLEN) +typedef sockaddr socket_addr_type; +typedef in_addr in4_addr_type; +# if defined(__hpux) +// HP-UX doesn't provide ip_mreq when _XOPEN_SOURCE_EXTENDED is defined. +struct in4_mreq_type +{ + struct in_addr imr_multiaddr; + struct in_addr imr_interface; +}; +# else +typedef ip_mreq in4_mreq_type; +# endif +typedef sockaddr_in sockaddr_in4_type; +typedef in6_addr in6_addr_type; +typedef ipv6_mreq in6_mreq_type; +typedef sockaddr_in6 sockaddr_in6_type; +typedef sockaddr_storage sockaddr_storage_type; +typedef sockaddr_un sockaddr_un_type; +typedef addrinfo addrinfo_type; +typedef ::linger linger_type; +typedef int ioctl_arg_type; +typedef uint32_t u_long_type; +typedef uint16_t u_short_type; +#if defined(ASIO_HAS_SSIZE_T) +typedef ssize_t signed_size_type; +#else // defined(ASIO_HAS_SSIZE_T) +typedef int signed_size_type; +#endif // defined(ASIO_HAS_SSIZE_T) +# define ASIO_OS_DEF(c) ASIO_OS_DEF_##c +# define ASIO_OS_DEF_AF_UNSPEC AF_UNSPEC +# define ASIO_OS_DEF_AF_INET AF_INET +# define ASIO_OS_DEF_AF_INET6 AF_INET6 +# define ASIO_OS_DEF_SOCK_STREAM SOCK_STREAM +# define ASIO_OS_DEF_SOCK_DGRAM SOCK_DGRAM +# define ASIO_OS_DEF_SOCK_RAW SOCK_RAW +# define ASIO_OS_DEF_SOCK_SEQPACKET SOCK_SEQPACKET +# define ASIO_OS_DEF_IPPROTO_IP IPPROTO_IP +# define ASIO_OS_DEF_IPPROTO_IPV6 IPPROTO_IPV6 +# define ASIO_OS_DEF_IPPROTO_TCP IPPROTO_TCP +# define ASIO_OS_DEF_IPPROTO_UDP IPPROTO_UDP +# define ASIO_OS_DEF_IPPROTO_ICMP IPPROTO_ICMP +# define ASIO_OS_DEF_IPPROTO_ICMPV6 IPPROTO_ICMPV6 +# define ASIO_OS_DEF_FIONBIO FIONBIO +# define ASIO_OS_DEF_FIONREAD FIONREAD +# define ASIO_OS_DEF_INADDR_ANY INADDR_ANY +# define ASIO_OS_DEF_MSG_OOB MSG_OOB +# define ASIO_OS_DEF_MSG_PEEK MSG_PEEK +# define ASIO_OS_DEF_MSG_DONTROUTE MSG_DONTROUTE +# define ASIO_OS_DEF_MSG_EOR MSG_EOR +# define ASIO_OS_DEF_SHUT_RD SHUT_RD +# define ASIO_OS_DEF_SHUT_WR SHUT_WR +# define ASIO_OS_DEF_SHUT_RDWR SHUT_RDWR +# define ASIO_OS_DEF_SOMAXCONN SOMAXCONN +# define ASIO_OS_DEF_SOL_SOCKET SOL_SOCKET +# define ASIO_OS_DEF_SO_BROADCAST SO_BROADCAST +# define ASIO_OS_DEF_SO_DEBUG SO_DEBUG +# define ASIO_OS_DEF_SO_DONTROUTE SO_DONTROUTE +# define ASIO_OS_DEF_SO_KEEPALIVE SO_KEEPALIVE +# define ASIO_OS_DEF_SO_LINGER SO_LINGER +# define ASIO_OS_DEF_SO_OOBINLINE SO_OOBINLINE +# define ASIO_OS_DEF_SO_SNDBUF SO_SNDBUF +# define ASIO_OS_DEF_SO_RCVBUF SO_RCVBUF +# define ASIO_OS_DEF_SO_SNDLOWAT SO_SNDLOWAT +# define ASIO_OS_DEF_SO_RCVLOWAT SO_RCVLOWAT +# define ASIO_OS_DEF_SO_REUSEADDR SO_REUSEADDR +# define ASIO_OS_DEF_TCP_NODELAY TCP_NODELAY +# define ASIO_OS_DEF_IP_MULTICAST_IF IP_MULTICAST_IF +# define ASIO_OS_DEF_IP_MULTICAST_TTL IP_MULTICAST_TTL +# define ASIO_OS_DEF_IP_MULTICAST_LOOP IP_MULTICAST_LOOP +# define ASIO_OS_DEF_IP_ADD_MEMBERSHIP IP_ADD_MEMBERSHIP +# define ASIO_OS_DEF_IP_DROP_MEMBERSHIP IP_DROP_MEMBERSHIP +# define ASIO_OS_DEF_IP_TTL IP_TTL +# define ASIO_OS_DEF_IPV6_UNICAST_HOPS IPV6_UNICAST_HOPS +# define ASIO_OS_DEF_IPV6_MULTICAST_IF IPV6_MULTICAST_IF +# define ASIO_OS_DEF_IPV6_MULTICAST_HOPS IPV6_MULTICAST_HOPS +# define ASIO_OS_DEF_IPV6_MULTICAST_LOOP IPV6_MULTICAST_LOOP +# define ASIO_OS_DEF_IPV6_JOIN_GROUP IPV6_JOIN_GROUP +# define ASIO_OS_DEF_IPV6_LEAVE_GROUP IPV6_LEAVE_GROUP +# define ASIO_OS_DEF_AI_CANONNAME AI_CANONNAME +# define ASIO_OS_DEF_AI_PASSIVE AI_PASSIVE +# define ASIO_OS_DEF_AI_NUMERICHOST AI_NUMERICHOST +# if defined(AI_NUMERICSERV) +# define ASIO_OS_DEF_AI_NUMERICSERV AI_NUMERICSERV +# else +# define ASIO_OS_DEF_AI_NUMERICSERV 0 +# endif +// Note: QNX Neutrino 6.3 defines AI_V4MAPPED, AI_ALL and AI_ADDRCONFIG but +// does not implement them. Therefore they are specifically excluded here. +# if defined(AI_V4MAPPED) && !defined(__QNXNTO__) +# define ASIO_OS_DEF_AI_V4MAPPED AI_V4MAPPED +# else +# define ASIO_OS_DEF_AI_V4MAPPED 0 +# endif +# if defined(AI_ALL) && !defined(__QNXNTO__) +# define ASIO_OS_DEF_AI_ALL AI_ALL +# else +# define ASIO_OS_DEF_AI_ALL 0 +# endif +# if defined(AI_ADDRCONFIG) && !defined(__QNXNTO__) +# define ASIO_OS_DEF_AI_ADDRCONFIG AI_ADDRCONFIG +# else +# define ASIO_OS_DEF_AI_ADDRCONFIG 0 +# endif +# if defined(IOV_MAX) +const int max_iov_len = IOV_MAX; +# else +// POSIX platforms are not required to define IOV_MAX. +const int max_iov_len = 16; +# endif +#endif +const int custom_socket_option_level = 0xA5100000; +const int enable_connection_aborted_option = 1; +const int always_fail_option = 2; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_TYPES_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/solaris_fenced_block.hpp b/third_party/asio/1.18.2/include/asio/detail/solaris_fenced_block.hpp new file mode 100644 index 000000000..4f6640b3d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/solaris_fenced_block.hpp @@ -0,0 +1,62 @@ +// +// detail/solaris_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP +#define ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__sun) + +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class solaris_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit solaris_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit solaris_fenced_block(full_t) + { + membar_consumer(); + } + + // Destructor. + ~solaris_fenced_block() + { + membar_producer(); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__sun) + +#endif // ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/source_location.hpp b/third_party/asio/1.18.2/include/asio/detail/source_location.hpp new file mode 100644 index 000000000..a9ac8e835 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/source_location.hpp @@ -0,0 +1,45 @@ +// +// detail/source_location.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOURCE_LOCATION_HPP +#define ASIO_DETAIL_SOURCE_LOCATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SOURCE_LOCATION) + +#if defined(ASIO_HAS_STD_SOURCE_LOCATION) +# include +#elif defined(ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION) +# include +#else // defined(ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION) +# error ASIO_HAS_SOURCE_LOCATION is set \ + but no source_location is available +#endif // defined(ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION) + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_STD_SOURCE_LOCATION) +using std::source_location; +#elif defined(ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION) +using std::experimental::source_location; +#endif // defined(ASIO_HAS_STD_EXPERIMENTAL_SOURCE_LOCATION) + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_SOURCE_LOCATION) + +#endif // ASIO_DETAIL_SOURCE_LOCATION_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/static_mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/static_mutex.hpp new file mode 100644 index 000000000..5876a99a8 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/static_mutex.hpp @@ -0,0 +1,52 @@ +// +// detail/static_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STATIC_MUTEX_HPP +#define ASIO_DETAIL_STATIC_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_static_mutex.hpp" +#elif defined(ASIO_WINDOWS) +# include "asio/detail/win_static_mutex.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_static_mutex.hpp" +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +# include "asio/detail/std_static_mutex.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) +typedef null_static_mutex static_mutex; +# define ASIO_STATIC_MUTEX_INIT ASIO_NULL_STATIC_MUTEX_INIT +#elif defined(ASIO_WINDOWS) +typedef win_static_mutex static_mutex; +# define ASIO_STATIC_MUTEX_INIT ASIO_WIN_STATIC_MUTEX_INIT +#elif defined(ASIO_HAS_PTHREADS) +typedef posix_static_mutex static_mutex; +# define ASIO_STATIC_MUTEX_INIT ASIO_POSIX_STATIC_MUTEX_INIT +#elif defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) +typedef std_static_mutex static_mutex; +# define ASIO_STATIC_MUTEX_INIT ASIO_STD_STATIC_MUTEX_INIT +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_STATIC_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/std_event.hpp b/third_party/asio/1.18.2/include/asio/detail/std_event.hpp new file mode 100644 index 000000000..1fc5177ed --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/std_event.hpp @@ -0,0 +1,188 @@ +// +// detail/std_event.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_EVENT_HPP +#define ASIO_DETAIL_STD_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#include +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class std_event + : private noncopyable +{ +public: + // Constructor. + std_event() + : state_(0) + { + } + + // Destructor. + ~std_event() + { + } + + // Signal the event. (Retained for backward compatibility.) + template + void signal(Lock& lock) + { + this->signal_all(lock); + } + + // Signal all waiters. + template + void signal_all(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + state_ |= 1; + cond_.notify_all(); + } + + // Unlock the mutex and signal one waiter. + template + void unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + bool have_waiters = (state_ > 1); + lock.unlock(); + if (have_waiters) + cond_.notify_one(); + } + + // Unlock the mutex and signal one waiter who may destroy us. + template + void unlock_and_signal_one_for_destruction(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + bool have_waiters = (state_ > 1); + if (have_waiters) + cond_.notify_one(); + lock.unlock(); + } + + // If there's a waiter, unlock the mutex and signal it. + template + bool maybe_unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + if (state_ > 1) + { + lock.unlock(); + cond_.notify_one(); + return true; + } + return false; + } + + // Reset the event. + template + void clear(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + state_ &= ~std::size_t(1); + } + + // Wait for the event to become signalled. + template + void wait(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + unique_lock_adapter u_lock(lock); + while ((state_ & 1) == 0) + { + waiter w(state_); + cond_.wait(u_lock.unique_lock_); + } + } + + // Timed wait for the event to become signalled. + template + bool wait_for_usec(Lock& lock, long usec) + { + ASIO_ASSERT(lock.locked()); + unique_lock_adapter u_lock(lock); + if ((state_ & 1) == 0) + { + waiter w(state_); + cond_.wait_for(u_lock.unique_lock_, std::chrono::microseconds(usec)); + } + return (state_ & 1) != 0; + } + +private: + // Helper class to temporarily adapt a scoped_lock into a unique_lock so that + // it can be passed to std::condition_variable::wait(). + struct unique_lock_adapter + { + template + explicit unique_lock_adapter(Lock& lock) + : unique_lock_(lock.mutex().mutex_, std::adopt_lock) + { + } + + ~unique_lock_adapter() + { + unique_lock_.release(); + } + + std::unique_lock unique_lock_; + }; + + // Helper to increment and decrement the state to track outstanding waiters. + class waiter + { + public: + explicit waiter(std::size_t& state) + : state_(state) + { + state_ += 2; + } + + ~waiter() + { + state_ -= 2; + } + + private: + std::size_t& state_; + }; + + std::condition_variable cond_; + std::size_t state_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#endif // ASIO_DETAIL_STD_EVENT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/std_fenced_block.hpp b/third_party/asio/1.18.2/include/asio/detail/std_fenced_block.hpp new file mode 100644 index 000000000..2361b17fd --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/std_fenced_block.hpp @@ -0,0 +1,62 @@ +// +// detail/std_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_FENCED_BLOCK_HPP +#define ASIO_DETAIL_STD_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_ATOMIC) + +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class std_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit std_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit std_fenced_block(full_t) + { + std::atomic_thread_fence(std::memory_order_acquire); + } + + // Destructor. + ~std_fenced_block() + { + std::atomic_thread_fence(std::memory_order_release); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_ATOMIC) + +#endif // ASIO_DETAIL_STD_FENCED_BLOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/std_global.hpp b/third_party/asio/1.18.2/include/asio/detail/std_global.hpp new file mode 100644 index 000000000..cdb306207 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/std_global.hpp @@ -0,0 +1,70 @@ +// +// detail/std_global.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_GLOBAL_HPP +#define ASIO_DETAIL_STD_GLOBAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_CALL_ONCE) + +#include +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct std_global_impl +{ + // Helper function to perform initialisation. + static void do_init() + { + instance_.ptr_ = new T; + } + + // Destructor automatically cleans up the global. + ~std_global_impl() + { + delete ptr_; + } + + static std::once_flag init_once_; + static std_global_impl instance_; + T* ptr_; +}; + +template +std::once_flag std_global_impl::init_once_; + +template +std_global_impl std_global_impl::instance_; + +template +T& std_global() +{ + std::call_once(std_global_impl::init_once_, &std_global_impl::do_init); + return *std_global_impl::instance_.ptr_; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_CALL_ONCE) + +#endif // ASIO_DETAIL_STD_GLOBAL_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/std_mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/std_mutex.hpp new file mode 100644 index 000000000..37471beba --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/std_mutex.hpp @@ -0,0 +1,73 @@ +// +// detail/std_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_MUTEX_HPP +#define ASIO_DETAIL_STD_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class std_event; + +class std_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + std_mutex() + { + } + + // Destructor. + ~std_mutex() + { + } + + // Lock the mutex. + void lock() + { + mutex_.lock(); + } + + // Unlock the mutex. + void unlock() + { + mutex_.unlock(); + } + +private: + friend class std_event; + std::mutex mutex_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#endif // ASIO_DETAIL_STD_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/std_static_mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/std_static_mutex.hpp new file mode 100644 index 000000000..ce14133f5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/std_static_mutex.hpp @@ -0,0 +1,81 @@ +// +// detail/std_static_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_STATIC_MUTEX_HPP +#define ASIO_DETAIL_STD_STATIC_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class std_event; + +class std_static_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + std_static_mutex(int) + { + } + + // Destructor. + ~std_static_mutex() + { + } + + // Initialise the mutex. + void init() + { + // Nothing to do. + } + + // Lock the mutex. + void lock() + { + mutex_.lock(); + } + + // Unlock the mutex. + void unlock() + { + mutex_.unlock(); + } + +private: + friend class std_event; + std::mutex mutex_; +}; + +#define ASIO_STD_STATIC_MUTEX_INIT 0 + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_MUTEX_AND_CONDVAR) + +#endif // ASIO_DETAIL_STD_STATIC_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/std_thread.hpp b/third_party/asio/1.18.2/include/asio/detail/std_thread.hpp new file mode 100644 index 000000000..00cd4c902 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/std_thread.hpp @@ -0,0 +1,71 @@ +// +// detail/std_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STD_THREAD_HPP +#define ASIO_DETAIL_STD_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_THREAD) + +#include +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class std_thread + : private noncopyable +{ +public: + // Constructor. + template + std_thread(Function f, unsigned int = 0) + : thread_(f) + { + } + + // Destructor. + ~std_thread() + { + join(); + } + + // Wait for the thread to exit. + void join() + { + if (thread_.joinable()) + thread_.join(); + } + + // Get number of CPUs. + static std::size_t hardware_concurrency() + { + return std::thread::hardware_concurrency(); + } + +private: + std::thread thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_STD_THREAD) + +#endif // ASIO_DETAIL_STD_THREAD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/strand_executor_service.hpp b/third_party/asio/1.18.2/include/asio/detail/strand_executor_service.hpp new file mode 100644 index 000000000..cdbea6ba5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/strand_executor_service.hpp @@ -0,0 +1,173 @@ +// +// detail/strand_executor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STRAND_EXECUTOR_SERVICE_HPP +#define ASIO_DETAIL_STRAND_EXECUTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/executor_op.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/scheduler_operation.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Default service implementation for a strand. +class strand_executor_service + : public execution_context_service_base +{ +public: + // The underlying implementation of a strand. + class strand_impl + { + public: + ASIO_DECL ~strand_impl(); + + private: + friend class strand_executor_service; + + // Mutex to protect access to internal data. + mutex* mutex_; + + // Indicates whether the strand is currently "locked" by a handler. This + // means that there is a handler upcall in progress, or that the strand + // itself has been scheduled in order to invoke some pending handlers. + bool locked_; + + // Indicates that the strand has been shut down and will accept no further + // handlers. + bool shutdown_; + + // The handlers that are waiting on the strand but should not be run until + // after the next time the strand is scheduled. This queue must only be + // modified while the mutex is locked. + op_queue waiting_queue_; + + // The handlers that are ready to be run. Logically speaking, these are the + // handlers that hold the strand's lock. The ready queue is only modified + // from within the strand and so may be accessed without locking the mutex. + op_queue ready_queue_; + + // Pointers to adjacent handle implementations in linked list. + strand_impl* next_; + strand_impl* prev_; + + // The strand service in where the implementation is held. + strand_executor_service* service_; + }; + + typedef shared_ptr implementation_type; + + // Construct a new strand service for the specified context. + ASIO_DECL explicit strand_executor_service(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Create a new strand_executor implementation. + ASIO_DECL implementation_type create_implementation(); + + // Request invocation of the given function. + template + static void execute(const implementation_type& impl, Executor& ex, + ASIO_MOVE_ARG(Function) function, + typename enable_if< + can_query >::value + >::type* = 0); + + // Request invocation of the given function. + template + static void execute(const implementation_type& impl, Executor& ex, + ASIO_MOVE_ARG(Function) function, + typename enable_if< + !can_query >::value + >::type* = 0); + + // Request invocation of the given function. + template + static void dispatch(const implementation_type& impl, Executor& ex, + ASIO_MOVE_ARG(Function) function, const Allocator& a); + + // Request invocation of the given function and return immediately. + template + static void post(const implementation_type& impl, Executor& ex, + ASIO_MOVE_ARG(Function) function, const Allocator& a); + + // Request invocation of the given function and return immediately. + template + static void defer(const implementation_type& impl, Executor& ex, + ASIO_MOVE_ARG(Function) function, const Allocator& a); + + // Determine whether the strand is running in the current thread. + ASIO_DECL static bool running_in_this_thread( + const implementation_type& impl); + +private: + friend class strand_impl; + template class allocator_binder; + template class invoker; + + // Adds a function to the strand. Returns true if it acquires the lock. + ASIO_DECL static bool enqueue(const implementation_type& impl, + scheduler_operation* op); + + // Transfers waiting handlers to the ready queue. Returns true if one or more + // handlers were transferred. + ASIO_DECL static bool push_waiting_to_ready(implementation_type& impl); + + // Invokes all ready-to-run handlers. + ASIO_DECL static void run_ready_handlers(implementation_type& impl); + + // Helper function to request invocation of the given function. + template + static void do_execute(const implementation_type& impl, Executor& ex, + ASIO_MOVE_ARG(Function) function, const Allocator& a); + + // Mutex to protect access to the service-wide state. + mutex mutex_; + + // Number of mutexes shared between all strand objects. + enum { num_mutexes = 193 }; + + // Pool of mutexes. + scoped_ptr mutexes_[num_mutexes]; + + // Extra value used when hashing to prevent recycled memory locations from + // getting the same mutex. + std::size_t salt_; + + // The head of a linked list of all implementations. + strand_impl* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/strand_executor_service.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/strand_executor_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_STRAND_EXECUTOR_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/strand_service.hpp b/third_party/asio/1.18.2/include/asio/detail/strand_service.hpp new file mode 100644 index 000000000..2aec8e5d4 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/strand_service.hpp @@ -0,0 +1,144 @@ +// +// detail/strand_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STRAND_SERVICE_HPP +#define ASIO_DETAIL_STRAND_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/io_context.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/scoped_ptr.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Default service implementation for a strand. +class strand_service + : public asio::detail::service_base +{ +private: + // Helper class to re-post the strand on exit. + struct on_do_complete_exit; + + // Helper class to re-post the strand on exit. + struct on_dispatch_exit; + +public: + + // The underlying implementation of a strand. + class strand_impl + : public operation + { + public: + strand_impl(); + + private: + // Only this service will have access to the internal values. + friend class strand_service; + friend struct on_do_complete_exit; + friend struct on_dispatch_exit; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // Indicates whether the strand is currently "locked" by a handler. This + // means that there is a handler upcall in progress, or that the strand + // itself has been scheduled in order to invoke some pending handlers. + bool locked_; + + // The handlers that are waiting on the strand but should not be run until + // after the next time the strand is scheduled. This queue must only be + // modified while the mutex is locked. + op_queue waiting_queue_; + + // The handlers that are ready to be run. Logically speaking, these are the + // handlers that hold the strand's lock. The ready queue is only modified + // from within the strand and so may be accessed without locking the mutex. + op_queue ready_queue_; + }; + + typedef strand_impl* implementation_type; + + // Construct a new strand service for the specified io_context. + ASIO_DECL explicit strand_service(asio::io_context& io_context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new strand implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Request the io_context to invoke the given handler. + template + void dispatch(implementation_type& impl, Handler& handler); + + // Request the io_context to invoke the given handler and return immediately. + template + void post(implementation_type& impl, Handler& handler); + + // Determine whether the strand is running in the current thread. + ASIO_DECL bool running_in_this_thread( + const implementation_type& impl) const; + +private: + // Helper function to dispatch a handler. + ASIO_DECL void do_dispatch(implementation_type& impl, operation* op); + + // Helper function to post a handler. + ASIO_DECL void do_post(implementation_type& impl, + operation* op, bool is_continuation); + + ASIO_DECL static void do_complete(void* owner, + operation* base, const asio::error_code& ec, + std::size_t bytes_transferred); + + // The io_context used to obtain an I/O executor. + io_context& io_context_; + + // The io_context implementation used to post completions. + io_context_impl& io_context_impl_; + + // Mutex to protect access to the array of implementations. + asio::detail::mutex mutex_; + + // Number of implementations shared between all strand objects. +#if defined(ASIO_STRAND_IMPLEMENTATIONS) + enum { num_implementations = ASIO_STRAND_IMPLEMENTATIONS }; +#else // defined(ASIO_STRAND_IMPLEMENTATIONS) + enum { num_implementations = 193 }; +#endif // defined(ASIO_STRAND_IMPLEMENTATIONS) + + // Pool of implementations. + scoped_ptr implementations_[num_implementations]; + + // Extra value used when hashing to prevent recycled memory locations from + // getting the same strand implementation. + std::size_t salt_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/strand_service.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/strand_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_STRAND_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/string_view.hpp b/third_party/asio/1.18.2/include/asio/detail/string_view.hpp new file mode 100644 index 000000000..488e9aa5a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/string_view.hpp @@ -0,0 +1,47 @@ +// +// detail/string_view.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STRING_VIEW_HPP +#define ASIO_DETAIL_STRING_VIEW_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STRING_VIEW) + +#if defined(ASIO_HAS_STD_STRING_VIEW) +# include +#elif defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +# include +#else // defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +# error ASIO_HAS_STRING_VIEW is set but no string_view is available +#endif // defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) + +namespace asio { + +#if defined(ASIO_HAS_STD_STRING_VIEW) +using std::basic_string_view; +using std::string_view; +#elif defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) +using std::experimental::basic_string_view; +using std::experimental::string_view; +#endif // defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW) + +} // namespace asio + +# define ASIO_STRING_VIEW_PARAM asio::string_view +#else // defined(ASIO_HAS_STRING_VIEW) +# define ASIO_STRING_VIEW_PARAM const std::string& +#endif // defined(ASIO_HAS_STRING_VIEW) + +#endif // ASIO_DETAIL_STRING_VIEW_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/thread.hpp b/third_party/asio/1.18.2/include/asio/detail/thread.hpp new file mode 100644 index 000000000..8d1b66283 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/thread.hpp @@ -0,0 +1,60 @@ +// +// detail/thread.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THREAD_HPP +#define ASIO_DETAIL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_thread.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_thread.hpp" +#elif defined(ASIO_WINDOWS) +# if defined(UNDER_CE) +# include "asio/detail/wince_thread.hpp" +# elif defined(ASIO_WINDOWS_APP) +# include "asio/detail/winapp_thread.hpp" +# else +# include "asio/detail/win_thread.hpp" +# endif +#elif defined(ASIO_HAS_STD_THREAD) +# include "asio/detail/std_thread.hpp" +#else +# error Only Windows, POSIX and std::thread are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(ASIO_HAS_THREADS) +typedef null_thread thread; +#elif defined(ASIO_HAS_PTHREADS) +typedef posix_thread thread; +#elif defined(ASIO_WINDOWS) +# if defined(UNDER_CE) +typedef wince_thread thread; +# elif defined(ASIO_WINDOWS_APP) +typedef winapp_thread thread; +# else +typedef win_thread thread; +# endif +#elif defined(ASIO_HAS_STD_THREAD) +typedef std_thread thread; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_THREAD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/thread_context.hpp b/third_party/asio/1.18.2/include/asio/detail/thread_context.hpp new file mode 100644 index 000000000..f17824182 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/thread_context.hpp @@ -0,0 +1,51 @@ +// +// detail/thread_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THREAD_CONTEXT_HPP +#define ASIO_DETAIL_THREAD_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include "asio/detail/call_stack.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class thread_info_base; + +// Base class for things that manage threads (scheduler, win_iocp_io_context). +class thread_context +{ +public: + // Obtain a pointer to the top of the thread call stack. Returns null when + // not running inside a thread context. + ASIO_DECL static thread_info_base* top_of_thread_call_stack(); + +protected: + // Per-thread call stack to track the state of each thread in the context. + typedef call_stack thread_call_stack; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/thread_context.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_THREAD_CONTEXT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/thread_group.hpp b/third_party/asio/1.18.2/include/asio/detail/thread_group.hpp new file mode 100644 index 000000000..11965f5a7 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/thread_group.hpp @@ -0,0 +1,99 @@ +// +// detail/thread_group.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THREAD_GROUP_HPP +#define ASIO_DETAIL_THREAD_GROUP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/thread.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class thread_group +{ +public: + // Constructor initialises an empty thread group. + thread_group() + : first_(0) + { + } + + // Destructor joins any remaining threads in the group. + ~thread_group() + { + join(); + } + + // Create a new thread in the group. + template + void create_thread(Function f) + { + first_ = new item(f, first_); + } + + // Create new threads in the group. + template + void create_threads(Function f, std::size_t num_threads) + { + for (std::size_t i = 0; i < num_threads; ++i) + create_thread(f); + } + + // Wait for all threads in the group to exit. + void join() + { + while (first_) + { + first_->thread_.join(); + item* tmp = first_; + first_ = first_->next_; + delete tmp; + } + } + + // Test whether the group is empty. + bool empty() const + { + return first_ == 0; + } + +private: + // Structure used to track a single thread in the group. + struct item + { + template + explicit item(Function f, item* next) + : thread_(f), + next_(next) + { + } + + asio::detail::thread thread_; + item* next_; + }; + + // The first thread in the group. + item* first_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_THREAD_GROUP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/thread_info_base.hpp b/third_party/asio/1.18.2/include/asio/detail/thread_info_base.hpp new file mode 100644 index 000000000..6431b6434 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/thread_info_base.hpp @@ -0,0 +1,190 @@ +// +// detail/thread_info_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THREAD_INFO_BASE_HPP +#define ASIO_DETAIL_THREAD_INFO_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/noncopyable.hpp" + +#if defined(ASIO_HAS_STD_EXCEPTION_PTR) \ + && !defined(ASIO_NO_EXCEPTIONS) +# include +# include "asio/multiple_exceptions.hpp" +#endif // defined(ASIO_HAS_STD_EXCEPTION_PTR) + // && !defined(ASIO_NO_EXCEPTIONS) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class thread_info_base + : private noncopyable +{ +public: + struct default_tag + { + enum { mem_index = 0 }; + }; + + struct awaitable_frame_tag + { + enum { mem_index = 1 }; + }; + + struct executor_function_tag + { + enum { mem_index = 2 }; + }; + + thread_info_base() +#if defined(ASIO_HAS_STD_EXCEPTION_PTR) \ + && !defined(ASIO_NO_EXCEPTIONS) + : has_pending_exception_(0) +#endif // defined(ASIO_HAS_STD_EXCEPTION_PTR) + // && !defined(ASIO_NO_EXCEPTIONS) + { + for (int i = 0; i < max_mem_index; ++i) + reusable_memory_[i] = 0; + } + + ~thread_info_base() + { + for (int i = 0; i < max_mem_index; ++i) + { + // The following test for non-null pointers is technically redundant, but + // it is significantly faster when using a tight io_context::poll() loop + // in latency sensitive applications. + if (reusable_memory_[i]) + ::operator delete(reusable_memory_[i]); + } + } + + static void* allocate(thread_info_base* this_thread, std::size_t size) + { + return allocate(default_tag(), this_thread, size); + } + + static void deallocate(thread_info_base* this_thread, + void* pointer, std::size_t size) + { + deallocate(default_tag(), this_thread, pointer, size); + } + + template + static void* allocate(Purpose, thread_info_base* this_thread, + std::size_t size) + { + std::size_t chunks = (size + chunk_size - 1) / chunk_size; + + if (this_thread && this_thread->reusable_memory_[Purpose::mem_index]) + { + void* const pointer = this_thread->reusable_memory_[Purpose::mem_index]; + this_thread->reusable_memory_[Purpose::mem_index] = 0; + + unsigned char* const mem = static_cast(pointer); + if (static_cast(mem[0]) >= chunks) + { + mem[size] = mem[0]; + return pointer; + } + + ::operator delete(pointer); + } + + void* const pointer = ::operator new(chunks * chunk_size + 1); + unsigned char* const mem = static_cast(pointer); + mem[size] = (chunks <= UCHAR_MAX) ? static_cast(chunks) : 0; + return pointer; + } + + template + static void deallocate(Purpose, thread_info_base* this_thread, + void* pointer, std::size_t size) + { + if (size <= chunk_size * UCHAR_MAX) + { + if (this_thread && this_thread->reusable_memory_[Purpose::mem_index] == 0) + { + unsigned char* const mem = static_cast(pointer); + mem[0] = mem[size]; + this_thread->reusable_memory_[Purpose::mem_index] = pointer; + return; + } + } + + ::operator delete(pointer); + } + + void capture_current_exception() + { +#if defined(ASIO_HAS_STD_EXCEPTION_PTR) \ + && !defined(ASIO_NO_EXCEPTIONS) + switch (has_pending_exception_) + { + case 0: + has_pending_exception_ = 1; + pending_exception_ = std::current_exception(); + break; + case 1: + has_pending_exception_ = 2; + pending_exception_ = + std::make_exception_ptr( + multiple_exceptions(pending_exception_)); + break; + default: + break; + } +#endif // defined(ASIO_HAS_STD_EXCEPTION_PTR) + // && !defined(ASIO_NO_EXCEPTIONS) + } + + void rethrow_pending_exception() + { +#if defined(ASIO_HAS_STD_EXCEPTION_PTR) \ + && !defined(ASIO_NO_EXCEPTIONS) + if (has_pending_exception_ > 0) + { + has_pending_exception_ = 0; + std::exception_ptr ex( + ASIO_MOVE_CAST(std::exception_ptr)( + pending_exception_)); + std::rethrow_exception(ex); + } +#endif // defined(ASIO_HAS_STD_EXCEPTION_PTR) + // && !defined(ASIO_NO_EXCEPTIONS) + } + +private: + enum { chunk_size = 4 }; + enum { max_mem_index = 3 }; + void* reusable_memory_[max_mem_index]; + +#if defined(ASIO_HAS_STD_EXCEPTION_PTR) \ + && !defined(ASIO_NO_EXCEPTIONS) + int has_pending_exception_; + std::exception_ptr pending_exception_; +#endif // defined(ASIO_HAS_STD_EXCEPTION_PTR) + // && !defined(ASIO_NO_EXCEPTIONS) +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_THREAD_INFO_BASE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/throw_error.hpp b/third_party/asio/1.18.2/include/asio/detail/throw_error.hpp new file mode 100644 index 000000000..d03cad54a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/throw_error.hpp @@ -0,0 +1,53 @@ +// +// detail/throw_error.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THROW_ERROR_HPP +#define ASIO_DETAIL_THROW_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error_code.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +ASIO_DECL void do_throw_error(const asio::error_code& err); + +ASIO_DECL void do_throw_error(const asio::error_code& err, + const char* location); + +inline void throw_error(const asio::error_code& err) +{ + if (err) + do_throw_error(err); +} + +inline void throw_error(const asio::error_code& err, + const char* location) +{ + if (err) + do_throw_error(err, location); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/throw_error.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_THROW_ERROR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/throw_exception.hpp b/third_party/asio/1.18.2/include/asio/detail/throw_exception.hpp new file mode 100644 index 000000000..bbee22ae9 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/throw_exception.hpp @@ -0,0 +1,51 @@ +// +// detail/throw_exception.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THROW_EXCEPTION_HPP +#define ASIO_DETAIL_THROW_EXCEPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_THROW_EXCEPTION) +# include +#endif // defined(ASIO_BOOST_THROW_EXCEPTION) + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_BOOST_THROW_EXCEPTION) +using boost::throw_exception; +#else // defined(ASIO_HAS_BOOST_THROW_EXCEPTION) + +// Declare the throw_exception function for all targets. +template +void throw_exception(const Exception& e); + +// Only define the throw_exception function when exceptions are enabled. +// Otherwise, it is up to the application to provide a definition of this +// function. +# if !defined(ASIO_NO_EXCEPTIONS) +template +void throw_exception(const Exception& e) +{ + throw e; +} +# endif // !defined(ASIO_NO_EXCEPTIONS) + +#endif // defined(ASIO_HAS_BOOST_THROW_EXCEPTION) + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_THROW_EXCEPTION_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/timer_queue.hpp b/third_party/asio/1.18.2/include/asio/detail/timer_queue.hpp new file mode 100644 index 000000000..d336869c9 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/timer_queue.hpp @@ -0,0 +1,360 @@ +// +// detail/timer_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_QUEUE_HPP +#define ASIO_DETAIL_TIMER_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/cstdint.hpp" +#include "asio/detail/date_time_fwd.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class timer_queue + : public timer_queue_base +{ +public: + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // Per-timer data. + class per_timer_data + { + public: + per_timer_data() : + heap_index_((std::numeric_limits::max)()), + next_(0), prev_(0) + { + } + + private: + friend class timer_queue; + + // The operations waiting on the timer. + op_queue op_queue_; + + // The index of the timer in the heap. + std::size_t heap_index_; + + // Pointers to adjacent timers in a linked list. + per_timer_data* next_; + per_timer_data* prev_; + }; + + // Constructor. + timer_queue() + : timers_(), + heap_() + { + } + + // Add a new timer to the queue. Returns true if this is the timer that is + // earliest in the queue, in which case the reactor's event demultiplexing + // function call may need to be interrupted and restarted. + bool enqueue_timer(const time_type& time, per_timer_data& timer, wait_op* op) + { + // Enqueue the timer object. + if (timer.prev_ == 0 && &timer != timers_) + { + if (this->is_positive_infinity(time)) + { + // No heap entry is required for timers that never expire. + timer.heap_index_ = (std::numeric_limits::max)(); + } + else + { + // Put the new timer at the correct position in the heap. This is done + // first since push_back() can throw due to allocation failure. + timer.heap_index_ = heap_.size(); + heap_entry entry = { time, &timer }; + heap_.push_back(entry); + up_heap(heap_.size() - 1); + } + + // Insert the new timer into the linked list of active timers. + timer.next_ = timers_; + timer.prev_ = 0; + if (timers_) + timers_->prev_ = &timer; + timers_ = &timer; + } + + // Enqueue the individual timer operation. + timer.op_queue_.push(op); + + // Interrupt reactor only if newly added timer is first to expire. + return timer.heap_index_ == 0 && timer.op_queue_.front() == op; + } + + // Whether there are no timers in the queue. + virtual bool empty() const + { + return timers_ == 0; + } + + // Get the time for the timer that is earliest in the queue. + virtual long wait_duration_msec(long max_duration) const + { + if (heap_.empty()) + return max_duration; + + return this->to_msec( + Time_Traits::to_posix_duration( + Time_Traits::subtract(heap_[0].time_, Time_Traits::now())), + max_duration); + } + + // Get the time for the timer that is earliest in the queue. + virtual long wait_duration_usec(long max_duration) const + { + if (heap_.empty()) + return max_duration; + + return this->to_usec( + Time_Traits::to_posix_duration( + Time_Traits::subtract(heap_[0].time_, Time_Traits::now())), + max_duration); + } + + // Dequeue all timers not later than the current time. + virtual void get_ready_timers(op_queue& ops) + { + if (!heap_.empty()) + { + const time_type now = Time_Traits::now(); + while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0].time_)) + { + per_timer_data* timer = heap_[0].timer_; + ops.push(timer->op_queue_); + remove_timer(*timer); + } + } + } + + // Dequeue all timers. + virtual void get_all_timers(op_queue& ops) + { + while (timers_) + { + per_timer_data* timer = timers_; + timers_ = timers_->next_; + ops.push(timer->op_queue_); + timer->next_ = 0; + timer->prev_ = 0; + } + + heap_.clear(); + } + + // Cancel and dequeue operations for the given timer. + std::size_t cancel_timer(per_timer_data& timer, op_queue& ops, + std::size_t max_cancelled = (std::numeric_limits::max)()) + { + std::size_t num_cancelled = 0; + if (timer.prev_ != 0 || &timer == timers_) + { + while (wait_op* op = (num_cancelled != max_cancelled) + ? timer.op_queue_.front() : 0) + { + op->ec_ = asio::error::operation_aborted; + timer.op_queue_.pop(); + ops.push(op); + ++num_cancelled; + } + if (timer.op_queue_.empty()) + remove_timer(timer); + } + return num_cancelled; + } + + // Move operations from one timer to another, empty timer. + void move_timer(per_timer_data& target, per_timer_data& source) + { + target.op_queue_.push(source.op_queue_); + + target.heap_index_ = source.heap_index_; + source.heap_index_ = (std::numeric_limits::max)(); + + if (target.heap_index_ < heap_.size()) + heap_[target.heap_index_].timer_ = ⌖ + + if (timers_ == &source) + timers_ = ⌖ + if (source.prev_) + source.prev_->next_ = ⌖ + if (source.next_) + source.next_->prev_= ⌖ + target.next_ = source.next_; + target.prev_ = source.prev_; + source.next_ = 0; + source.prev_ = 0; + } + +private: + // Move the item at the given index up the heap to its correct position. + void up_heap(std::size_t index) + { + while (index > 0) + { + std::size_t parent = (index - 1) / 2; + if (!Time_Traits::less_than(heap_[index].time_, heap_[parent].time_)) + break; + swap_heap(index, parent); + index = parent; + } + } + + // Move the item at the given index down the heap to its correct position. + void down_heap(std::size_t index) + { + std::size_t child = index * 2 + 1; + while (child < heap_.size()) + { + std::size_t min_child = (child + 1 == heap_.size() + || Time_Traits::less_than( + heap_[child].time_, heap_[child + 1].time_)) + ? child : child + 1; + if (Time_Traits::less_than(heap_[index].time_, heap_[min_child].time_)) + break; + swap_heap(index, min_child); + index = min_child; + child = index * 2 + 1; + } + } + + // Swap two entries in the heap. + void swap_heap(std::size_t index1, std::size_t index2) + { + heap_entry tmp = heap_[index1]; + heap_[index1] = heap_[index2]; + heap_[index2] = tmp; + heap_[index1].timer_->heap_index_ = index1; + heap_[index2].timer_->heap_index_ = index2; + } + + // Remove a timer from the heap and list of timers. + void remove_timer(per_timer_data& timer) + { + // Remove the timer from the heap. + std::size_t index = timer.heap_index_; + if (!heap_.empty() && index < heap_.size()) + { + if (index == heap_.size() - 1) + { + timer.heap_index_ = (std::numeric_limits::max)(); + heap_.pop_back(); + } + else + { + swap_heap(index, heap_.size() - 1); + timer.heap_index_ = (std::numeric_limits::max)(); + heap_.pop_back(); + if (index > 0 && Time_Traits::less_than( + heap_[index].time_, heap_[(index - 1) / 2].time_)) + up_heap(index); + else + down_heap(index); + } + } + + // Remove the timer from the linked list of active timers. + if (timers_ == &timer) + timers_ = timer.next_; + if (timer.prev_) + timer.prev_->next_ = timer.next_; + if (timer.next_) + timer.next_->prev_= timer.prev_; + timer.next_ = 0; + timer.prev_ = 0; + } + + // Determine if the specified absolute time is positive infinity. + template + static bool is_positive_infinity(const Time_Type&) + { + return false; + } + + // Determine if the specified absolute time is positive infinity. + template + static bool is_positive_infinity( + const boost::date_time::base_time& time) + { + return time.is_pos_infinity(); + } + + // Helper function to convert a duration into milliseconds. + template + long to_msec(const Duration& d, long max_duration) const + { + if (d.ticks() <= 0) + return 0; + int64_t msec = d.total_milliseconds(); + if (msec == 0) + return 1; + if (msec > max_duration) + return max_duration; + return static_cast(msec); + } + + // Helper function to convert a duration into microseconds. + template + long to_usec(const Duration& d, long max_duration) const + { + if (d.ticks() <= 0) + return 0; + int64_t usec = d.total_microseconds(); + if (usec == 0) + return 1; + if (usec > max_duration) + return max_duration; + return static_cast(usec); + } + + // The head of a linked list of all active timers. + per_timer_data* timers_; + + struct heap_entry + { + // The time when the timer should fire. + time_type time_; + + // The associated timer with enqueued operations. + per_timer_data* timer_; + }; + + // The heap of timers, with the earliest timer at the front. + std::vector heap_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/timer_queue_base.hpp b/third_party/asio/1.18.2/include/asio/detail/timer_queue_base.hpp new file mode 100644 index 000000000..7ab2e6d40 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/timer_queue_base.hpp @@ -0,0 +1,68 @@ +// +// detail/timer_queue_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_QUEUE_BASE_HPP +#define ASIO_DETAIL_TIMER_QUEUE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class timer_queue_base + : private noncopyable +{ +public: + // Constructor. + timer_queue_base() : next_(0) {} + + // Destructor. + virtual ~timer_queue_base() {} + + // Whether there are no timers in the queue. + virtual bool empty() const = 0; + + // Get the time to wait until the next timer. + virtual long wait_duration_msec(long max_duration) const = 0; + + // Get the time to wait until the next timer. + virtual long wait_duration_usec(long max_duration) const = 0; + + // Dequeue all ready timers. + virtual void get_ready_timers(op_queue& ops) = 0; + + // Dequeue all timers. + virtual void get_all_timers(op_queue& ops) = 0; + +private: + friend class timer_queue_set; + + // Next timer queue in the set. + timer_queue_base* next_; +}; + +template +class timer_queue; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_BASE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/timer_queue_ptime.hpp b/third_party/asio/1.18.2/include/asio/detail/timer_queue_ptime.hpp new file mode 100644 index 000000000..dfdacd1ed --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/timer_queue_ptime.hpp @@ -0,0 +1,99 @@ +// +// detail/timer_queue_ptime.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_QUEUE_PTIME_HPP +#define ASIO_DETAIL_TIMER_QUEUE_PTIME_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_BOOST_DATE_TIME) + +#include "asio/time_traits.hpp" +#include "asio/detail/timer_queue.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct forwarding_posix_time_traits : time_traits {}; + +// Template specialisation for the commonly used instantation. +template <> +class timer_queue > + : public timer_queue_base +{ +public: + // The time type. + typedef boost::posix_time::ptime time_type; + + // The duration type. + typedef boost::posix_time::time_duration duration_type; + + // Per-timer data. + typedef timer_queue::per_timer_data + per_timer_data; + + // Constructor. + ASIO_DECL timer_queue(); + + // Destructor. + ASIO_DECL virtual ~timer_queue(); + + // Add a new timer to the queue. Returns true if this is the timer that is + // earliest in the queue, in which case the reactor's event demultiplexing + // function call may need to be interrupted and restarted. + ASIO_DECL bool enqueue_timer(const time_type& time, + per_timer_data& timer, wait_op* op); + + // Whether there are no timers in the queue. + ASIO_DECL virtual bool empty() const; + + // Get the time for the timer that is earliest in the queue. + ASIO_DECL virtual long wait_duration_msec(long max_duration) const; + + // Get the time for the timer that is earliest in the queue. + ASIO_DECL virtual long wait_duration_usec(long max_duration) const; + + // Dequeue all timers not later than the current time. + ASIO_DECL virtual void get_ready_timers(op_queue& ops); + + // Dequeue all timers. + ASIO_DECL virtual void get_all_timers(op_queue& ops); + + // Cancel and dequeue operations for the given timer. + ASIO_DECL std::size_t cancel_timer( + per_timer_data& timer, op_queue& ops, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move operations from one timer to another, empty timer. + ASIO_DECL void move_timer(per_timer_data& target, + per_timer_data& source); + +private: + timer_queue impl_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/timer_queue_ptime.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_BOOST_DATE_TIME) + +#endif // ASIO_DETAIL_TIMER_QUEUE_PTIME_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/timer_queue_set.hpp b/third_party/asio/1.18.2/include/asio/detail/timer_queue_set.hpp new file mode 100644 index 000000000..2cd3826c7 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/timer_queue_set.hpp @@ -0,0 +1,66 @@ +// +// detail/timer_queue_set.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_QUEUE_SET_HPP +#define ASIO_DETAIL_TIMER_QUEUE_SET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/timer_queue_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class timer_queue_set +{ +public: + // Constructor. + ASIO_DECL timer_queue_set(); + + // Add a timer queue to the set. + ASIO_DECL void insert(timer_queue_base* q); + + // Remove a timer queue from the set. + ASIO_DECL void erase(timer_queue_base* q); + + // Determine whether all queues are empty. + ASIO_DECL bool all_empty() const; + + // Get the wait duration in milliseconds. + ASIO_DECL long wait_duration_msec(long max_duration) const; + + // Get the wait duration in microseconds. + ASIO_DECL long wait_duration_usec(long max_duration) const; + + // Dequeue all ready timers. + ASIO_DECL void get_ready_timers(op_queue& ops); + + // Dequeue all timers. + ASIO_DECL void get_all_timers(op_queue& ops); + +private: + timer_queue_base* first_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/timer_queue_set.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_TIMER_QUEUE_SET_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/timer_scheduler.hpp b/third_party/asio/1.18.2/include/asio/detail/timer_scheduler.hpp new file mode 100644 index 000000000..8d9b4ed7c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/timer_scheduler.hpp @@ -0,0 +1,35 @@ +// +// detail/timer_scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_SCHEDULER_HPP +#define ASIO_DETAIL_TIMER_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/timer_scheduler_fwd.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/winrt_timer_scheduler.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#elif defined(ASIO_HAS_EPOLL) +# include "asio/detail/epoll_reactor.hpp" +#elif defined(ASIO_HAS_KQUEUE) +# include "asio/detail/kqueue_reactor.hpp" +#elif defined(ASIO_HAS_DEV_POLL) +# include "asio/detail/dev_poll_reactor.hpp" +#else +# include "asio/detail/select_reactor.hpp" +#endif + +#endif // ASIO_DETAIL_TIMER_SCHEDULER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/timer_scheduler_fwd.hpp b/third_party/asio/1.18.2/include/asio/detail/timer_scheduler_fwd.hpp new file mode 100644 index 000000000..15b5fbdff --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/timer_scheduler_fwd.hpp @@ -0,0 +1,40 @@ +// +// detail/timer_scheduler_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP +#define ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_WINDOWS_RUNTIME) +typedef class winrt_timer_scheduler timer_scheduler; +#elif defined(ASIO_HAS_IOCP) +typedef class win_iocp_io_context timer_scheduler; +#elif defined(ASIO_HAS_EPOLL) +typedef class epoll_reactor timer_scheduler; +#elif defined(ASIO_HAS_KQUEUE) +typedef class kqueue_reactor timer_scheduler; +#elif defined(ASIO_HAS_DEV_POLL) +typedef class dev_poll_reactor timer_scheduler; +#else +typedef class select_reactor timer_scheduler; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/tss_ptr.hpp b/third_party/asio/1.18.2/include/asio/detail/tss_ptr.hpp new file mode 100644 index 000000000..25e4d3d84 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/tss_ptr.hpp @@ -0,0 +1,69 @@ +// +// detail/tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TSS_PTR_HPP +#define ASIO_DETAIL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_THREADS) +# include "asio/detail/null_tss_ptr.hpp" +#elif defined(ASIO_HAS_THREAD_KEYWORD_EXTENSION) +# include "asio/detail/keyword_tss_ptr.hpp" +#elif defined(ASIO_WINDOWS) +# include "asio/detail/win_tss_ptr.hpp" +#elif defined(ASIO_HAS_PTHREADS) +# include "asio/detail/posix_tss_ptr.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class tss_ptr +#if !defined(ASIO_HAS_THREADS) + : public null_tss_ptr +#elif defined(ASIO_HAS_THREAD_KEYWORD_EXTENSION) + : public keyword_tss_ptr +#elif defined(ASIO_WINDOWS) + : public win_tss_ptr +#elif defined(ASIO_HAS_PTHREADS) + : public posix_tss_ptr +#endif +{ +public: + void operator=(T* value) + { +#if !defined(ASIO_HAS_THREADS) + null_tss_ptr::operator=(value); +#elif defined(ASIO_HAS_THREAD_KEYWORD_EXTENSION) + keyword_tss_ptr::operator=(value); +#elif defined(ASIO_WINDOWS) + win_tss_ptr::operator=(value); +#elif defined(ASIO_HAS_PTHREADS) + posix_tss_ptr::operator=(value); +#endif + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TSS_PTR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/type_traits.hpp b/third_party/asio/1.18.2/include/asio/detail/type_traits.hpp new file mode 100644 index 000000000..968f19a80 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/type_traits.hpp @@ -0,0 +1,156 @@ +// +// detail/type_traits.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TYPE_TRAITS_HPP +#define ASIO_DETAIL_TYPE_TRAITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_TYPE_TRAITS) +# include +#else // defined(ASIO_HAS_STD_TYPE_TRAITS) +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ASIO_HAS_STD_TYPE_TRAITS) + +namespace asio { + +#if defined(ASIO_HAS_STD_TYPE_TRAITS) +using std::add_const; +using std::add_lvalue_reference; +using std::aligned_storage; +using std::alignment_of; +using std::conditional; +using std::decay; +using std::declval; +using std::enable_if; +using std::false_type; +using std::integral_constant; +using std::is_base_of; +using std::is_class; +using std::is_const; +using std::is_constructible; +using std::is_convertible; +using std::is_copy_constructible; +using std::is_destructible; +using std::is_function; +using std::is_move_constructible; +using std::is_nothrow_copy_constructible; +using std::is_nothrow_destructible; +using std::is_object; +using std::is_reference; +using std::is_same; +using std::is_scalar; +using std::remove_cv; +template +struct remove_cvref : remove_cv::type> {}; +using std::remove_pointer; +using std::remove_reference; +#if defined(ASIO_HAS_STD_INVOKE_RESULT) +template struct result_of; +template +struct result_of : std::invoke_result {}; +#else // defined(ASIO_HAS_STD_INVOKE_RESULT) +using std::result_of; +#endif // defined(ASIO_HAS_STD_INVOKE_RESULT) +using std::true_type; +#else // defined(ASIO_HAS_STD_TYPE_TRAITS) +using boost::add_const; +using boost::add_lvalue_reference; +using boost::aligned_storage; +using boost::alignment_of; +template +struct enable_if : boost::enable_if_c {}; +using boost::conditional; +using boost::decay; +using boost::declval; +using boost::false_type; +using boost::integral_constant; +using boost::is_base_of; +using boost::is_class; +using boost::is_const; +using boost::is_constructible; +using boost::is_convertible; +using boost::is_copy_constructible; +using boost::is_destructible; +using boost::is_function; +#if defined(ASIO_HAS_MOVE) +template +struct is_move_constructible : false_type {}; +#else // defined(ASIO_HAS_MOVE) +template +struct is_move_constructible : is_copy_constructible {}; +#endif // defined(ASIO_HAS_MOVE) +template +struct is_nothrow_copy_constructible : boost::has_nothrow_copy {}; +template +struct is_nothrow_destructible : boost::has_nothrow_destructor {}; +using boost::is_object; +using boost::is_reference; +using boost::is_same; +using boost::is_scalar; +using boost::remove_cv; +template +struct remove_cvref : remove_cv::type> {}; +using boost::remove_pointer; +using boost::remove_reference; +using boost::result_of; +using boost::true_type; +#endif // defined(ASIO_HAS_STD_TYPE_TRAITS) + +template struct void_type { typedef void type; }; + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template struct conjunction : true_type {}; +template struct conjunction : T {}; +template struct conjunction : + conditional, Head>::type {}; + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +struct defaulted_constraint +{ + ASIO_CONSTEXPR defaulted_constraint() {} +}; + +template +struct constraint : enable_if {}; + +} // namespace asio + +#endif // ASIO_DETAIL_TYPE_TRAITS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/variadic_templates.hpp b/third_party/asio/1.18.2/include/asio/detail/variadic_templates.hpp new file mode 100644 index 000000000..3bcc8d276 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/variadic_templates.hpp @@ -0,0 +1,294 @@ +// +// detail/variadic_templates.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_VARIADIC_TEMPLATES_HPP +#define ASIO_DETAIL_VARIADIC_TEMPLATES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +# define ASIO_VARIADIC_TPARAMS(n) ASIO_VARIADIC_TPARAMS_##n + +# define ASIO_VARIADIC_TPARAMS_1 \ + typename T1 +# define ASIO_VARIADIC_TPARAMS_2 \ + typename T1, typename T2 +# define ASIO_VARIADIC_TPARAMS_3 \ + typename T1, typename T2, typename T3 +# define ASIO_VARIADIC_TPARAMS_4 \ + typename T1, typename T2, typename T3, typename T4 +# define ASIO_VARIADIC_TPARAMS_5 \ + typename T1, typename T2, typename T3, typename T4, typename T5 +# define ASIO_VARIADIC_TPARAMS_6 \ + typename T1, typename T2, typename T3, typename T4, typename T5, \ + typename T6 +# define ASIO_VARIADIC_TPARAMS_7 \ + typename T1, typename T2, typename T3, typename T4, typename T5, \ + typename T6, typename T7 +# define ASIO_VARIADIC_TPARAMS_8 \ + typename T1, typename T2, typename T3, typename T4, typename T5, \ + typename T6, typename T7, typename T8 + +# define ASIO_VARIADIC_TARGS(n) ASIO_VARIADIC_TARGS_##n + +# define ASIO_VARIADIC_TARGS_1 T1 +# define ASIO_VARIADIC_TARGS_2 T1, T2 +# define ASIO_VARIADIC_TARGS_3 T1, T2, T3 +# define ASIO_VARIADIC_TARGS_4 T1, T2, T3, T4 +# define ASIO_VARIADIC_TARGS_5 T1, T2, T3, T4, T5 +# define ASIO_VARIADIC_TARGS_6 T1, T2, T3, T4, T5, T6 +# define ASIO_VARIADIC_TARGS_7 T1, T2, T3, T4, T5, T6, T7 +# define ASIO_VARIADIC_TARGS_8 T1, T2, T3, T4, T5, T6, T7, T8 + +# define ASIO_VARIADIC_BYVAL_PARAMS(n) \ + ASIO_VARIADIC_BYVAL_PARAMS_##n + +# define ASIO_VARIADIC_BYVAL_PARAMS_1 T1 x1 +# define ASIO_VARIADIC_BYVAL_PARAMS_2 T1 x1, T2 x2 +# define ASIO_VARIADIC_BYVAL_PARAMS_3 T1 x1, T2 x2, T3 x3 +# define ASIO_VARIADIC_BYVAL_PARAMS_4 T1 x1, T2 x2, T3 x3, T4 x4 +# define ASIO_VARIADIC_BYVAL_PARAMS_5 T1 x1, T2 x2, T3 x3, T4 x4, T5 x5 +# define ASIO_VARIADIC_BYVAL_PARAMS_6 T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, \ + T6 x6 +# define ASIO_VARIADIC_BYVAL_PARAMS_7 T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, \ + T6 x6, T7 x7 +# define ASIO_VARIADIC_BYVAL_PARAMS_8 T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, \ + T6 x6, T7 x7, T8 x8 + +# define ASIO_VARIADIC_BYVAL_ARGS(n) \ + ASIO_VARIADIC_BYVAL_ARGS_##n + +# define ASIO_VARIADIC_BYVAL_ARGS_1 x1 +# define ASIO_VARIADIC_BYVAL_ARGS_2 x1, x2 +# define ASIO_VARIADIC_BYVAL_ARGS_3 x1, x2, x3 +# define ASIO_VARIADIC_BYVAL_ARGS_4 x1, x2, x3, x4 +# define ASIO_VARIADIC_BYVAL_ARGS_5 x1, x2, x3, x4, x5 +# define ASIO_VARIADIC_BYVAL_ARGS_6 x1, x2, x3, x4, x5, x6 +# define ASIO_VARIADIC_BYVAL_ARGS_7 x1, x2, x3, x4, x5, x6, x7 +# define ASIO_VARIADIC_BYVAL_ARGS_8 x1, x2, x3, x4, x5, x6, x7, x8 + +# define ASIO_VARIADIC_CONSTREF_PARAMS(n) \ + ASIO_VARIADIC_CONSTREF_PARAMS_##n + +# define ASIO_VARIADIC_CONSTREF_PARAMS_1 \ + const T1& x1 +# define ASIO_VARIADIC_CONSTREF_PARAMS_2 \ + const T1& x1, const T2& x2 +# define ASIO_VARIADIC_CONSTREF_PARAMS_3 \ + const T1& x1, const T2& x2, const T3& x3 +# define ASIO_VARIADIC_CONSTREF_PARAMS_4 \ + const T1& x1, const T2& x2, const T3& x3, const T4& x4 +# define ASIO_VARIADIC_CONSTREF_PARAMS_5 \ + const T1& x1, const T2& x2, const T3& x3, const T4& x4, const T5& x5 +# define ASIO_VARIADIC_CONSTREF_PARAMS_6 \ + const T1& x1, const T2& x2, const T3& x3, const T4& x4, const T5& x5, \ + const T6& x6 +# define ASIO_VARIADIC_CONSTREF_PARAMS_7 \ + const T1& x1, const T2& x2, const T3& x3, const T4& x4, const T5& x5, \ + const T6& x6, const T7& x7 +# define ASIO_VARIADIC_CONSTREF_PARAMS_8 \ + const T1& x1, const T2& x2, const T3& x3, const T4& x4, const T5& x5, \ + const T6& x6, const T7& x7, const T8& x8 + +# define ASIO_VARIADIC_MOVE_PARAMS(n) \ + ASIO_VARIADIC_MOVE_PARAMS_##n + +# define ASIO_VARIADIC_MOVE_PARAMS_1 \ + ASIO_MOVE_ARG(T1) x1 +# define ASIO_VARIADIC_MOVE_PARAMS_2 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2 +# define ASIO_VARIADIC_MOVE_PARAMS_3 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2, \ + ASIO_MOVE_ARG(T3) x3 +# define ASIO_VARIADIC_MOVE_PARAMS_4 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2, \ + ASIO_MOVE_ARG(T3) x3, ASIO_MOVE_ARG(T4) x4 +# define ASIO_VARIADIC_MOVE_PARAMS_5 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2, \ + ASIO_MOVE_ARG(T3) x3, ASIO_MOVE_ARG(T4) x4, \ + ASIO_MOVE_ARG(T5) x5 +# define ASIO_VARIADIC_MOVE_PARAMS_6 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2, \ + ASIO_MOVE_ARG(T3) x3, ASIO_MOVE_ARG(T4) x4, \ + ASIO_MOVE_ARG(T5) x5, ASIO_MOVE_ARG(T6) x6 +# define ASIO_VARIADIC_MOVE_PARAMS_7 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2, \ + ASIO_MOVE_ARG(T3) x3, ASIO_MOVE_ARG(T4) x4, \ + ASIO_MOVE_ARG(T5) x5, ASIO_MOVE_ARG(T6) x6, \ + ASIO_MOVE_ARG(T7) x7 +# define ASIO_VARIADIC_MOVE_PARAMS_8 \ + ASIO_MOVE_ARG(T1) x1, ASIO_MOVE_ARG(T2) x2, \ + ASIO_MOVE_ARG(T3) x3, ASIO_MOVE_ARG(T4) x4, \ + ASIO_MOVE_ARG(T5) x5, ASIO_MOVE_ARG(T6) x6, \ + ASIO_MOVE_ARG(T7) x7, ASIO_MOVE_ARG(T8) x8 + +# define ASIO_VARIADIC_UNNAMED_MOVE_PARAMS(n) \ + ASIO_VARIADIC_UNNAMED_MOVE_PARAMS_##n + +# define ASIO_VARIADIC_UNNAMED_MOVE_PARAMS_1 \ + ASIO_MOVE_ARG(T1) +# define ASIO_VARIADIC_UNNAMED_MOVE_PARAMS_2 \ + ASIO_MOVE_ARG(T1), ASIO_MOVE_ARG(T2) +# define ASIO_VARIADIC_UNNAMED_MOVE_PARAMS_3 \ + ASIO_MOVE_ARG(T1), ASIO_MOVE_ARG(T2), \ + ASIO_MOVE_ARG(T3) +# define ASIO_VARIADIC_UNNAMED_MOVE_PARAMS_4 \ + ASIO_MOVE_ARG(T1), ASIO_MOVE_ARG(T2), \ + ASIO_MOVE_ARG(T3), ASIO_MOVE_ARG(T4) +# define ASIO_VARIADIC_UNNAMED_MOVE_PARAMS_5 \ + ASIO_MOVE_ARG(T1), ASIO_MOVE_ARG(T2), \ + ASIO_MOVE_ARG(T3), ASIO_MOVE_ARG(T4), \ + ASIO_MOVE_ARG(T5) +# define ASIO_VARIADIC_UNNAMED_MOVE_PARAMS_6 \ + ASIO_MOVE_ARG(T1), ASIO_MOVE_ARG(T2), \ + ASIO_MOVE_ARG(T3), ASIO_MOVE_ARG(T4), \ + ASIO_MOVE_ARG(T5), ASIO_MOVE_ARG(T6) +# define ASIO_VARIADIC_UNNAMED_MOVE_PARAMS_7 \ + ASIO_MOVE_ARG(T1), ASIO_MOVE_ARG(T2), \ + ASIO_MOVE_ARG(T3), ASIO_MOVE_ARG(T4), \ + ASIO_MOVE_ARG(T5), ASIO_MOVE_ARG(T6), \ + ASIO_MOVE_ARG(T7) +# define ASIO_VARIADIC_UNNAMED_MOVE_PARAMS_8 \ + ASIO_MOVE_ARG(T1), ASIO_MOVE_ARG(T2), \ + ASIO_MOVE_ARG(T3), ASIO_MOVE_ARG(T4), \ + ASIO_MOVE_ARG(T5), ASIO_MOVE_ARG(T6), \ + ASIO_MOVE_ARG(T7), ASIO_MOVE_ARG(T8) + +# define ASIO_VARIADIC_MOVE_ARGS(n) \ + ASIO_VARIADIC_MOVE_ARGS_##n + +# define ASIO_VARIADIC_MOVE_ARGS_1 \ + ASIO_MOVE_CAST(T1)(x1) +# define ASIO_VARIADIC_MOVE_ARGS_2 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2) +# define ASIO_VARIADIC_MOVE_ARGS_3 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2), \ + ASIO_MOVE_CAST(T3)(x3) +# define ASIO_VARIADIC_MOVE_ARGS_4 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2), \ + ASIO_MOVE_CAST(T3)(x3), ASIO_MOVE_CAST(T4)(x4) +# define ASIO_VARIADIC_MOVE_ARGS_5 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2), \ + ASIO_MOVE_CAST(T3)(x3), ASIO_MOVE_CAST(T4)(x4), \ + ASIO_MOVE_CAST(T5)(x5) +# define ASIO_VARIADIC_MOVE_ARGS_6 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2), \ + ASIO_MOVE_CAST(T3)(x3), ASIO_MOVE_CAST(T4)(x4), \ + ASIO_MOVE_CAST(T5)(x5), ASIO_MOVE_CAST(T6)(x6) +# define ASIO_VARIADIC_MOVE_ARGS_7 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2), \ + ASIO_MOVE_CAST(T3)(x3), ASIO_MOVE_CAST(T4)(x4), \ + ASIO_MOVE_CAST(T5)(x5), ASIO_MOVE_CAST(T6)(x6), \ + ASIO_MOVE_CAST(T7)(x7) +# define ASIO_VARIADIC_MOVE_ARGS_8 \ + ASIO_MOVE_CAST(T1)(x1), ASIO_MOVE_CAST(T2)(x2), \ + ASIO_MOVE_CAST(T3)(x3), ASIO_MOVE_CAST(T4)(x4), \ + ASIO_MOVE_CAST(T5)(x5), ASIO_MOVE_CAST(T6)(x6), \ + ASIO_MOVE_CAST(T7)(x7), ASIO_MOVE_CAST(T8)(x8) + +# define ASIO_VARIADIC_DECLVAL(n) \ + ASIO_VARIADIC_DECLVAL_##n + +# define ASIO_VARIADIC_DECLVAL_1 \ + declval() +# define ASIO_VARIADIC_DECLVAL_2 \ + declval(), declval() +# define ASIO_VARIADIC_DECLVAL_3 \ + declval(), declval(), declval() +# define ASIO_VARIADIC_DECLVAL_4 \ + declval(), declval(), declval(), declval() +# define ASIO_VARIADIC_DECLVAL_5 \ + declval(), declval(), declval(), declval(), \ + declval() +# define ASIO_VARIADIC_DECLVAL_6 \ + declval(), declval(), declval(), declval(), \ + declval(), declval() +# define ASIO_VARIADIC_DECLVAL_7 \ + declval(), declval(), declval(), declval(), \ + declval(), declval(), declval() +# define ASIO_VARIADIC_DECLVAL_8 \ + declval(), declval(), declval(), declval(), \ + declval(), declval(), declval(), declval() + +# define ASIO_VARIADIC_MOVE_DECLVAL(n) \ + ASIO_VARIADIC_MOVE_DECLVAL_##n + +# define ASIO_VARIADIC_MOVE_DECLVAL_1 \ + declval() +# define ASIO_VARIADIC_MOVE_DECLVAL_2 \ + declval(), declval() +# define ASIO_VARIADIC_MOVE_DECLVAL_3 \ + declval(), declval(), \ + declval() +# define ASIO_VARIADIC_MOVE_DECLVAL_4 \ + declval(), declval(), \ + declval(), declval() +# define ASIO_VARIADIC_MOVE_DECLVAL_5 \ + declval(), declval(), \ + declval(), declval(), \ + declval() +# define ASIO_VARIADIC_MOVE_DECLVAL_6 \ + declval(), declval(), \ + declval(), declval(), \ + declval(), declval() +# define ASIO_VARIADIC_MOVE_DECLVAL_7 \ + declval(), declval(), \ + declval(), declval(), \ + declval(), declval(), \ + declval() +# define ASIO_VARIADIC_MOVE_DECLVAL_8 \ + declval(), declval(), \ + declval(), declval(), \ + declval(), declval(), \ + declval(), declval() + +# define ASIO_VARIADIC_DECAY(n) \ + ASIO_VARIADIC_DECAY_##n + +# define ASIO_VARIADIC_DECAY_1 \ + typename decay::type +# define ASIO_VARIADIC_DECAY_2 \ + typename decay::type, typename decay::type +# define ASIO_VARIADIC_DECAY_3 \ + typename decay::type, typename decay::type, \ + typename decay::type +# define ASIO_VARIADIC_DECAY_4 \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type +# define ASIO_VARIADIC_DECAY_5 \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type, \ + typename decay::type +# define ASIO_VARIADIC_DECAY_6 \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type +# define ASIO_VARIADIC_DECAY_7 \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type, \ + typename decay::type +# define ASIO_VARIADIC_DECAY_8 \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type, \ + typename decay::type, typename decay::type + +# define ASIO_VARIADIC_GENERATE(m) m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) +# define ASIO_VARIADIC_GENERATE_5(m) m(1) m(2) m(3) m(4) m(5) + +#endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#endif // ASIO_DETAIL_VARIADIC_TEMPLATES_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/wait_handler.hpp b/third_party/asio/1.18.2/include/asio/detail/wait_handler.hpp new file mode 100644 index 000000000..6a87a5385 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/wait_handler.hpp @@ -0,0 +1,90 @@ +// +// detail/wait_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WAIT_HANDLER_HPP +#define ASIO_DETAIL_WAIT_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/wait_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class wait_handler : public wait_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(wait_handler); + + wait_handler(Handler& h, const IoExecutor& io_ex) + : wait_op(&wait_handler::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(h)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + wait_handler* h(static_cast(base)); + ptr p = { asio::detail::addressof(h->handler_), h, h }; + + ASIO_HANDLER_COMPLETION((*h)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + h->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(h->handler_, h->ec_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WAIT_HANDLER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/wait_op.hpp b/third_party/asio/1.18.2/include/asio/detail/wait_op.hpp new file mode 100644 index 000000000..a13286a7a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/wait_op.hpp @@ -0,0 +1,45 @@ +// +// detail/wait_op.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WAIT_OP_HPP +#define ASIO_DETAIL_WAIT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class wait_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + +protected: + wait_op(func_type func) + : operation(func) + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WAIT_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_event.hpp b/third_party/asio/1.18.2/include/asio/detail/win_event.hpp new file mode 100644 index 000000000..3f00e0032 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_event.hpp @@ -0,0 +1,164 @@ +// +// detail/win_event.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_EVENT_HPP +#define ASIO_DETAIL_WIN_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_event + : private noncopyable +{ +public: + // Constructor. + ASIO_DECL win_event(); + + // Destructor. + ASIO_DECL ~win_event(); + + // Signal the event. (Retained for backward compatibility.) + template + void signal(Lock& lock) + { + this->signal_all(lock); + } + + // Signal all waiters. + template + void signal_all(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + state_ |= 1; + ::SetEvent(events_[0]); + } + + // Unlock the mutex and signal one waiter. + template + void unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + bool have_waiters = (state_ > 1); + lock.unlock(); + if (have_waiters) + ::SetEvent(events_[1]); + } + + // Unlock the mutex and signal one waiter who may destroy us. + template + void unlock_and_signal_one_for_destruction(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + bool have_waiters = (state_ > 1); + if (have_waiters) + ::SetEvent(events_[1]); + lock.unlock(); + } + + // If there's a waiter, unlock the mutex and signal it. + template + bool maybe_unlock_and_signal_one(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + state_ |= 1; + if (state_ > 1) + { + lock.unlock(); + ::SetEvent(events_[1]); + return true; + } + return false; + } + + // Reset the event. + template + void clear(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + (void)lock; + ::ResetEvent(events_[0]); + state_ &= ~std::size_t(1); + } + + // Wait for the event to become signalled. + template + void wait(Lock& lock) + { + ASIO_ASSERT(lock.locked()); + while ((state_ & 1) == 0) + { + state_ += 2; + lock.unlock(); +#if defined(ASIO_WINDOWS_APP) + ::WaitForMultipleObjectsEx(2, events_, false, INFINITE, false); +#else // defined(ASIO_WINDOWS_APP) + ::WaitForMultipleObjects(2, events_, false, INFINITE); +#endif // defined(ASIO_WINDOWS_APP) + lock.lock(); + state_ -= 2; + } + } + + // Timed wait for the event to become signalled. + template + bool wait_for_usec(Lock& lock, long usec) + { + ASIO_ASSERT(lock.locked()); + if ((state_ & 1) == 0) + { + state_ += 2; + lock.unlock(); + DWORD msec = usec > 0 ? (usec < 1000 ? 1 : usec / 1000) : 0; +#if defined(ASIO_WINDOWS_APP) + ::WaitForMultipleObjectsEx(2, events_, false, msec, false); +#else // defined(ASIO_WINDOWS_APP) + ::WaitForMultipleObjects(2, events_, false, msec); +#endif // defined(ASIO_WINDOWS_APP) + lock.lock(); + state_ -= 2; + } + return (state_ & 1) != 0; + } + +private: + HANDLE events_[2]; + std::size_t state_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_event.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_WIN_EVENT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_fd_set_adapter.hpp b/third_party/asio/1.18.2/include/asio/detail/win_fd_set_adapter.hpp new file mode 100644 index 000000000..f5e19f011 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_fd_set_adapter.hpp @@ -0,0 +1,149 @@ +// +// detail/win_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class win_fd_set_adapter : noncopyable +{ +public: + enum { default_fd_set_size = 1024 }; + + win_fd_set_adapter() + : capacity_(default_fd_set_size), + max_descriptor_(invalid_socket) + { + fd_set_ = static_cast(::operator new( + sizeof(win_fd_set) - sizeof(SOCKET) + + sizeof(SOCKET) * (capacity_))); + fd_set_->fd_count = 0; + } + + ~win_fd_set_adapter() + { + ::operator delete(fd_set_); + } + + void reset() + { + fd_set_->fd_count = 0; + max_descriptor_ = invalid_socket; + } + + bool set(socket_type descriptor) + { + for (u_int i = 0; i < fd_set_->fd_count; ++i) + if (fd_set_->fd_array[i] == descriptor) + return true; + + reserve(fd_set_->fd_count + 1); + fd_set_->fd_array[fd_set_->fd_count++] = descriptor; + return true; + } + + void set(reactor_op_queue& operations, op_queue&) + { + reactor_op_queue::iterator i = operations.begin(); + while (i != operations.end()) + { + reactor_op_queue::iterator op_iter = i++; + reserve(fd_set_->fd_count + 1); + fd_set_->fd_array[fd_set_->fd_count++] = op_iter->first; + } + } + + bool is_set(socket_type descriptor) const + { + return !!__WSAFDIsSet(descriptor, + const_cast(reinterpret_cast(fd_set_))); + } + + operator fd_set*() + { + return reinterpret_cast(fd_set_); + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + + void perform(reactor_op_queue& operations, + op_queue& ops) const + { + for (u_int i = 0; i < fd_set_->fd_count; ++i) + operations.perform_operations(fd_set_->fd_array[i], ops); + } + +private: + // This structure is defined to be compatible with the Windows API fd_set + // structure, but without being dependent on the value of FD_SETSIZE. We use + // the "struct hack" to allow the number of descriptors to be varied at + // runtime. + struct win_fd_set + { + u_int fd_count; + SOCKET fd_array[1]; + }; + + // Increase the fd_set_ capacity to at least the specified number of elements. + void reserve(u_int n) + { + if (n <= capacity_) + return; + + u_int new_capacity = capacity_ + capacity_ / 2; + if (new_capacity < n) + new_capacity = n; + + win_fd_set* new_fd_set = static_cast(::operator new( + sizeof(win_fd_set) - sizeof(SOCKET) + + sizeof(SOCKET) * (new_capacity))); + + new_fd_set->fd_count = fd_set_->fd_count; + for (u_int i = 0; i < fd_set_->fd_count; ++i) + new_fd_set->fd_array[i] = fd_set_->fd_array[i]; + + ::operator delete(fd_set_); + fd_set_ = new_fd_set; + capacity_ = new_capacity; + } + + win_fd_set* fd_set_; + u_int capacity_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_fenced_block.hpp b/third_party/asio/1.18.2/include/asio/detail/win_fenced_block.hpp new file mode 100644 index 000000000..77d1eb150 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_fenced_block.hpp @@ -0,0 +1,90 @@ +// +// detail/win_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_FENCED_BLOCK_HPP +#define ASIO_DETAIL_WIN_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) && !defined(UNDER_CE) + +#include "asio/detail/socket_types.hpp" +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_fenced_block + : private noncopyable +{ +public: + enum half_t { half }; + enum full_t { full }; + + // Constructor for a half fenced block. + explicit win_fenced_block(half_t) + { + } + + // Constructor for a full fenced block. + explicit win_fenced_block(full_t) + { +#if defined(__BORLANDC__) + LONG barrier = 0; + ::InterlockedExchange(&barrier, 1); +#elif defined(ASIO_MSVC) \ + && ((ASIO_MSVC < 1400) || !defined(MemoryBarrier)) +# if defined(_M_IX86) +# pragma warning(push) +# pragma warning(disable:4793) + LONG barrier; + __asm { xchg barrier, eax } +# pragma warning(pop) +# endif // defined(_M_IX86) +#else + MemoryBarrier(); +#endif + } + + // Destructor. + ~win_fenced_block() + { +#if defined(__BORLANDC__) + LONG barrier = 0; + ::InterlockedExchange(&barrier, 1); +#elif defined(ASIO_MSVC) \ + && ((ASIO_MSVC < 1400) || !defined(MemoryBarrier)) +# if defined(_M_IX86) +# pragma warning(push) +# pragma warning(disable:4793) + LONG barrier; + __asm { xchg barrier, eax } +# pragma warning(pop) +# endif // defined(_M_IX86) +#else + MemoryBarrier(); +#endif + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) && !defined(UNDER_CE) + +#endif // ASIO_DETAIL_WIN_FENCED_BLOCK_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_global.hpp b/third_party/asio/1.18.2/include/asio/detail/win_global.hpp new file mode 100644 index 000000000..f77976e21 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_global.hpp @@ -0,0 +1,71 @@ +// +// detail/win_global.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_GLOBAL_HPP +#define ASIO_DETAIL_WIN_GLOBAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/static_mutex.hpp" +#include "asio/detail/tss_ptr.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct win_global_impl +{ + // Destructor automatically cleans up the global. + ~win_global_impl() + { + delete ptr_; + } + + static win_global_impl instance_; + static static_mutex mutex_; + T* ptr_; + static tss_ptr tss_ptr_; +}; + +template +win_global_impl win_global_impl::instance_ = { 0 }; + +template +static_mutex win_global_impl::mutex_ = ASIO_STATIC_MUTEX_INIT; + +template +tss_ptr win_global_impl::tss_ptr_; + +template +T& win_global() +{ + if (static_cast(win_global_impl::tss_ptr_) == 0) + { + win_global_impl::mutex_.init(); + static_mutex::scoped_lock lock(win_global_impl::mutex_); + if (win_global_impl::instance_.ptr_ == 0) + win_global_impl::instance_.ptr_ = new T; + win_global_impl::tss_ptr_ = win_global_impl::instance_.ptr_; + } + + return *win_global_impl::tss_ptr_; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_GLOBAL_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_handle_read_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_handle_read_op.hpp new file mode 100644 index 000000000..f11abb122 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_handle_read_op.hpp @@ -0,0 +1,117 @@ +// +// detail/win_iocp_handle_read_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_handle_read_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_handle_read_op); + + win_iocp_handle_read_op(const MutableBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + : operation(&win_iocp_handle_read_op::do_complete), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_handle_read_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (owner) + { + // Check whether buffers are still valid. + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_HANDLE_EOF) + ec = asio::error::eof; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + MutableBufferSequence buffers_; + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_handle_service.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_handle_service.hpp new file mode 100644 index 000000000..a9bee45ff --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_handle_service.hpp @@ -0,0 +1,335 @@ +// +// detail/win_iocp_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/cstdint.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/win_iocp_handle_read_op.hpp" +#include "asio/detail/win_iocp_handle_write_op.hpp" +#include "asio/detail/win_iocp_io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_handle_service : + public execution_context_service_base +{ +public: + // The native type of a stream handle. + typedef HANDLE native_handle_type; + + // The implementation type of the stream handle. + class implementation_type + { + public: + // Default constructor. + implementation_type() + : handle_(INVALID_HANDLE_VALUE), + safe_cancellation_thread_id_(0), + next_(0), + prev_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class win_iocp_handle_service; + + // The native stream handle representation. + native_handle_type handle_; + + // The ID of the thread from which it is safe to cancel asynchronous + // operations. 0 means no asynchronous operations have been started yet. + // ~0 means asynchronous operations have been started from more than one + // thread, and cancellation is not supported for the handle. + DWORD safe_cancellation_thread_id_; + + // Pointers to adjacent handle implementations in linked list. + implementation_type* next_; + implementation_type* prev_; + }; + + ASIO_DECL win_iocp_handle_service(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new handle implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Move-construct a new handle implementation. + ASIO_DECL void move_construct(implementation_type& impl, + implementation_type& other_impl); + + // Move-assign from another handle implementation. + ASIO_DECL void move_assign(implementation_type& impl, + win_iocp_handle_service& other_service, + implementation_type& other_impl); + + // Destroy a handle implementation. + ASIO_DECL void destroy(implementation_type& impl); + + // Assign a native handle to a handle implementation. + ASIO_DECL asio::error_code assign(implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec); + + // Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return impl.handle_ != INVALID_HANDLE_VALUE; + } + + // Destroy a handle implementation. + ASIO_DECL asio::error_code close(implementation_type& impl, + asio::error_code& ec); + + // Get the native handle representation. + native_handle_type native_handle(const implementation_type& impl) const + { + return impl.handle_; + } + + // Cancel all operations associated with the handle. + ASIO_DECL asio::error_code cancel(implementation_type& impl, + asio::error_code& ec); + + // Write the given data. Returns the number of bytes written. + template + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return write_some_at(impl, 0, buffers, ec); + } + + // Write the given data at the specified offset. Returns the number of bytes + // written. + template + size_t write_some_at(implementation_type& impl, uint64_t offset, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + asio::const_buffer buffer = + buffer_sequence_adapter::first(buffers); + + return do_write(impl, offset, buffer, ec); + } + + // Start an asynchronous write. The data being written must be valid for the + // lifetime of the asynchronous operation. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_handle_write_op< + ConstBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((iocp_service_.context(), *p.p, "handle", &impl, + reinterpret_cast(impl.handle_), "async_write_some")); + + start_write_op(impl, 0, + buffer_sequence_adapter::first(buffers), p.p); + p.v = p.p = 0; + } + + // Start an asynchronous write at a specified offset. The data being written + // must be valid for the lifetime of the asynchronous operation. + template + void async_write_some_at(implementation_type& impl, + uint64_t offset, const ConstBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_handle_write_op< + ConstBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((iocp_service_.context(), *p.p, "handle", &impl, + reinterpret_cast(impl.handle_), "async_write_some_at")); + + start_write_op(impl, offset, + buffer_sequence_adapter::first(buffers), p.p); + p.v = p.p = 0; + } + + // Read some data. Returns the number of bytes received. + template + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return read_some_at(impl, 0, buffers, ec); + } + + // Read some data at a specified offset. Returns the number of bytes received. + template + size_t read_some_at(implementation_type& impl, uint64_t offset, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + asio::mutable_buffer buffer = + buffer_sequence_adapter::first(buffers); + + return do_read(impl, offset, buffer, ec); + } + + // Start an asynchronous read. The buffer for the data being received must be + // valid for the lifetime of the asynchronous operation. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_handle_read_op< + MutableBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((iocp_service_.context(), *p.p, "handle", &impl, + reinterpret_cast(impl.handle_), "async_read_some")); + + start_read_op(impl, 0, + buffer_sequence_adapter::first(buffers), p.p); + p.v = p.p = 0; + } + + // Start an asynchronous read at a specified offset. The buffer for the data + // being received must be valid for the lifetime of the asynchronous + // operation. + template + void async_read_some_at(implementation_type& impl, + uint64_t offset, const MutableBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_handle_read_op< + MutableBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((iocp_service_.context(), *p.p, "handle", &impl, + reinterpret_cast(impl.handle_), "async_read_some_at")); + + start_read_op(impl, offset, + buffer_sequence_adapter::first(buffers), p.p); + p.v = p.p = 0; + } + +private: + // Prevent the use of the null_buffers type with this service. + size_t write_some(implementation_type& impl, + const null_buffers& buffers, asio::error_code& ec); + size_t write_some_at(implementation_type& impl, uint64_t offset, + const null_buffers& buffers, asio::error_code& ec); + template + void async_write_some(implementation_type& impl, + const null_buffers& buffers, Handler& handler, + const IoExecutor& io_ex); + template + void async_write_some_at(implementation_type& impl, uint64_t offset, + const null_buffers& buffers, Handler& handler, const IoExecutor& io_ex); + size_t read_some(implementation_type& impl, + const null_buffers& buffers, asio::error_code& ec); + size_t read_some_at(implementation_type& impl, uint64_t offset, + const null_buffers& buffers, asio::error_code& ec); + template + void async_read_some(implementation_type& impl, + const null_buffers& buffers, Handler& handler, + const IoExecutor& io_ex); + template + void async_read_some_at(implementation_type& impl, uint64_t offset, + const null_buffers& buffers, Handler& handler, const IoExecutor& io_ex); + + // Helper class for waiting for synchronous operations to complete. + class overlapped_wrapper; + + // Helper function to perform a synchronous write operation. + ASIO_DECL size_t do_write(implementation_type& impl, + uint64_t offset, const asio::const_buffer& buffer, + asio::error_code& ec); + + // Helper function to start a write operation. + ASIO_DECL void start_write_op(implementation_type& impl, + uint64_t offset, const asio::const_buffer& buffer, + operation* op); + + // Helper function to perform a synchronous write operation. + ASIO_DECL size_t do_read(implementation_type& impl, + uint64_t offset, const asio::mutable_buffer& buffer, + asio::error_code& ec); + + // Helper function to start a read operation. + ASIO_DECL void start_read_op(implementation_type& impl, + uint64_t offset, const asio::mutable_buffer& buffer, + operation* op); + + // Update the ID of the thread from which cancellation is safe. + ASIO_DECL void update_cancellation_thread_id(implementation_type& impl); + + // Helper function to close a handle when the associated object is being + // destroyed. + ASIO_DECL void close_for_destruction(implementation_type& impl); + + // The IOCP service used for running asynchronous operations and dispatching + // handlers. + win_iocp_io_context& iocp_service_; + + // Mutex to protect access to the linked list of implementations. + mutex mutex_; + + // The head of a linked list of all implementations. + implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_handle_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_handle_write_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_handle_write_op.hpp new file mode 100644 index 000000000..a6862051d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_handle_write_op.hpp @@ -0,0 +1,110 @@ +// +// detail/win_iocp_handle_write_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_handle_write_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_handle_write_op); + + win_iocp_handle_write_op(const ConstBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + : operation(&win_iocp_handle_write_op::do_complete), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_handle_write_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (owner) + { + // Check whether buffers are still valid. + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + ConstBufferSequence buffers_; + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_io_context.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_io_context.hpp new file mode 100644 index 000000000..9cc1b2e53 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_io_context.hpp @@ -0,0 +1,339 @@ +// +// detail/win_iocp_io_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_IO_CONTEXT_HPP +#define ASIO_DETAIL_WIN_IOCP_IO_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/limits.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/thread_context.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/detail/win_iocp_operation.hpp" +#include "asio/detail/win_iocp_thread_info.hpp" +#include "asio/execution_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class wait_op; + +class win_iocp_io_context + : public execution_context_service_base, + public thread_context +{ +public: + // Constructor. Specifies a concurrency hint that is passed through to the + // underlying I/O completion port. + ASIO_DECL win_iocp_io_context(asio::execution_context& ctx, + int concurrency_hint = -1, bool own_thread = true); + + // Destructor. + ASIO_DECL ~win_iocp_io_context(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Initialise the task. Nothing to do here. + void init_task() + { + } + + // Register a handle with the IO completion port. + ASIO_DECL asio::error_code register_handle( + HANDLE handle, asio::error_code& ec); + + // Run the event loop until stopped or no more work. + ASIO_DECL size_t run(asio::error_code& ec); + + // Run until stopped or one operation is performed. + ASIO_DECL size_t run_one(asio::error_code& ec); + + // Run until timeout, interrupted, or one operation is performed. + ASIO_DECL size_t wait_one(long usec, asio::error_code& ec); + + // Poll for operations without blocking. + ASIO_DECL size_t poll(asio::error_code& ec); + + // Poll for one operation without blocking. + ASIO_DECL size_t poll_one(asio::error_code& ec); + + // Stop the event processing loop. + ASIO_DECL void stop(); + + // Determine whether the io_context is stopped. + bool stopped() const + { + return ::InterlockedExchangeAdd(&stopped_, 0) != 0; + } + + // Restart in preparation for a subsequent run invocation. + void restart() + { + ::InterlockedExchange(&stopped_, 0); + } + + // Notify that some work has started. + void work_started() + { + ::InterlockedIncrement(&outstanding_work_); + } + + // Notify that some work has finished. + void work_finished() + { + if (::InterlockedDecrement(&outstanding_work_) == 0) + stop(); + } + + // Return whether a handler can be dispatched immediately. + ASIO_DECL bool can_dispatch(); + + /// Capture the current exception so it can be rethrown from a run function. + ASIO_DECL void capture_current_exception(); + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() has not yet been called for the operation. + void post_immediate_completion(win_iocp_operation* op, bool) + { + work_started(); + post_deferred_completion(op); + } + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() was previously called for the operation. + ASIO_DECL void post_deferred_completion(win_iocp_operation* op); + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() was previously called for the operations. + ASIO_DECL void post_deferred_completions( + op_queue& ops); + + // Request invocation of the given operation using the thread-private queue + // and return immediately. Assumes that work_started() has not yet been + // called for the operation. + void post_private_immediate_completion(win_iocp_operation* op) + { + post_immediate_completion(op, false); + } + + // Request invocation of the given operation using the thread-private queue + // and return immediately. Assumes that work_started() was previously called + // for the operation. + void post_private_deferred_completion(win_iocp_operation* op) + { + post_deferred_completion(op); + } + + // Enqueue the given operation following a failed attempt to dispatch the + // operation for immediate invocation. + void do_dispatch(operation* op) + { + post_immediate_completion(op, false); + } + + // Process unfinished operations as part of a shutdown operation. Assumes + // that work_started() was previously called for the operations. + ASIO_DECL void abandon_operations(op_queue& ops); + + // Called after starting an overlapped I/O operation that did not complete + // immediately. The caller must have already called work_started() prior to + // starting the operation. + ASIO_DECL void on_pending(win_iocp_operation* op); + + // Called after starting an overlapped I/O operation that completed + // immediately. The caller must have already called work_started() prior to + // starting the operation. + ASIO_DECL void on_completion(win_iocp_operation* op, + DWORD last_error = 0, DWORD bytes_transferred = 0); + + // Called after starting an overlapped I/O operation that completed + // immediately. The caller must have already called work_started() prior to + // starting the operation. + ASIO_DECL void on_completion(win_iocp_operation* op, + const asio::error_code& ec, DWORD bytes_transferred = 0); + + // Add a new timer queue to the service. + template + void add_timer_queue(timer_queue& timer_queue); + + // Remove a timer queue from the service. + template + void remove_timer_queue(timer_queue& timer_queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& to, + typename timer_queue::per_timer_data& from); + + // Get the concurrency hint that was used to initialise the io_context. + int concurrency_hint() const + { + return concurrency_hint_; + } + +private: +#if defined(WINVER) && (WINVER < 0x0500) + typedef DWORD dword_ptr_t; + typedef ULONG ulong_ptr_t; +#else // defined(WINVER) && (WINVER < 0x0500) + typedef DWORD_PTR dword_ptr_t; + typedef ULONG_PTR ulong_ptr_t; +#endif // defined(WINVER) && (WINVER < 0x0500) + + // Dequeues at most one operation from the I/O completion port, and then + // executes it. Returns the number of operations that were dequeued (i.e. + // either 0 or 1). + ASIO_DECL size_t do_one(DWORD msec, + win_iocp_thread_info& this_thread, asio::error_code& ec); + + // Helper to calculate the GetQueuedCompletionStatus timeout. + ASIO_DECL static DWORD get_gqcs_timeout(); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Called to recalculate and update the timeout. + ASIO_DECL void update_timeout(); + + // Helper class to call work_finished() on block exit. + struct work_finished_on_block_exit; + + // Helper class for managing a HANDLE. + struct auto_handle + { + HANDLE handle; + auto_handle() : handle(0) {} + ~auto_handle() { if (handle) ::CloseHandle(handle); } + }; + + // The IO completion port used for queueing operations. + auto_handle iocp_; + + // The count of unfinished work. + long outstanding_work_; + + // Flag to indicate whether the event loop has been stopped. + mutable long stopped_; + + // Flag to indicate whether there is an in-flight stop event. Every event + // posted using PostQueuedCompletionStatus consumes non-paged pool, so to + // avoid exhausting this resouce we limit the number of outstanding events. + long stop_event_posted_; + + // Flag to indicate whether the service has been shut down. + long shutdown_; + + enum + { + // Timeout to use with GetQueuedCompletionStatus on older versions of + // Windows. Some versions of windows have a "bug" where a call to + // GetQueuedCompletionStatus can appear stuck even though there are events + // waiting on the queue. Using a timeout helps to work around the issue. + default_gqcs_timeout = 500, + + // Maximum waitable timer timeout, in milliseconds. + max_timeout_msec = 5 * 60 * 1000, + + // Maximum waitable timer timeout, in microseconds. + max_timeout_usec = max_timeout_msec * 1000, + + // Completion key value used to wake up a thread to dispatch timers or + // completed operations. + wake_for_dispatch = 1, + + // Completion key value to indicate that an operation has posted with the + // original last_error and bytes_transferred values stored in the fields of + // the OVERLAPPED structure. + overlapped_contains_result = 2 + }; + + // Timeout to use with GetQueuedCompletionStatus. + const DWORD gqcs_timeout_; + + // Helper class to run the scheduler in its own thread. + struct thread_function; + friend struct thread_function; + + // Function object for processing timeouts in a background thread. + struct timer_thread_function; + friend struct timer_thread_function; + + // Background thread used for processing timeouts. + scoped_ptr timer_thread_; + + // A waitable timer object used for waiting for timeouts. + auto_handle waitable_timer_; + + // Non-zero if timers or completed operations need to be dispatched. + long dispatch_required_; + + // Mutex for protecting access to the timer queues and completed operations. + mutex dispatch_mutex_; + + // The timer queues. + timer_queue_set timer_queues_; + + // The operations that are ready to dispatch. + op_queue completed_ops_; + + // The concurrency hint used to initialise the io_context. + const int concurrency_hint_; + + // The thread that is running the io_context. + scoped_ptr thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/win_iocp_io_context.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_io_context.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_IO_CONTEXT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_null_buffers_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_null_buffers_op.hpp new file mode 100644 index 000000000..5aedd7aed --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_null_buffers_op.hpp @@ -0,0 +1,127 @@ +// +// detail/win_iocp_null_buffers_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_null_buffers_op : public reactor_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_null_buffers_op); + + win_iocp_null_buffers_op(socket_ops::weak_cancel_token_type cancel_token, + Handler& handler, const IoExecutor& io_ex) + : reactor_op(asio::error_code(), + &win_iocp_null_buffers_op::do_perform, + &win_iocp_null_buffers_op::do_complete), + cancel_token_(cancel_token), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static status do_perform(reactor_op*) + { + return done; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_null_buffers_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // The reactor may have stored a result in the operation object. + if (o->ec_) + ec = o->ec_; + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (o->cancel_token_.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_operation.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_operation.hpp new file mode 100644 index 000000000..1349192d5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_operation.hpp @@ -0,0 +1,96 @@ +// +// detail/win_iocp_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_OPERATION_HPP +#define ASIO_DETAIL_WIN_IOCP_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/handler_tracking.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/error_code.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_io_context; + +// Base class for all operations. A function pointer is used instead of virtual +// functions to avoid the associated overhead. +class win_iocp_operation + : public OVERLAPPED + ASIO_ALSO_INHERIT_TRACKED_HANDLER +{ +public: + typedef win_iocp_operation operation_type; + + void complete(void* owner, const asio::error_code& ec, + std::size_t bytes_transferred) + { + func_(owner, this, ec, bytes_transferred); + } + + void destroy() + { + func_(0, this, asio::error_code(), 0); + } + +protected: + typedef void (*func_type)( + void*, win_iocp_operation*, + const asio::error_code&, std::size_t); + + win_iocp_operation(func_type func) + : next_(0), + func_(func) + { + reset(); + } + + // Prevents deletion through this type. + ~win_iocp_operation() + { + } + + void reset() + { + Internal = 0; + InternalHigh = 0; + Offset = 0; + OffsetHigh = 0; + hEvent = 0; + ready_ = 0; + } + +private: + friend class op_queue_access; + friend class win_iocp_io_context; + win_iocp_operation* next_; + func_type func_; + long ready_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_OPERATION_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_overlapped_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_overlapped_op.hpp new file mode 100644 index 000000000..247375ba2 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_overlapped_op.hpp @@ -0,0 +1,96 @@ +// +// detail/win_iocp_overlapped_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_overlapped_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_overlapped_op); + + win_iocp_overlapped_op(Handler& handler, const IoExecutor& io_ex) + : operation(&win_iocp_overlapped_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_overlapped_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_overlapped_ptr.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_overlapped_ptr.hpp new file mode 100644 index 000000000..5249abb66 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_overlapped_ptr.hpp @@ -0,0 +1,171 @@ +// +// detail/win_iocp_overlapped_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP +#define ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/io_context.hpp" +#include "asio/query.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/win_iocp_overlapped_op.hpp" +#include "asio/detail/win_iocp_io_context.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Wraps a handler to create an OVERLAPPED object for use with overlapped I/O. +class win_iocp_overlapped_ptr + : private noncopyable +{ +public: + // Construct an empty win_iocp_overlapped_ptr. + win_iocp_overlapped_ptr() + : ptr_(0), + iocp_service_(0) + { + } + + // Construct an win_iocp_overlapped_ptr to contain the specified handler. + template + explicit win_iocp_overlapped_ptr(const Executor& ex, + ASIO_MOVE_ARG(Handler) handler) + : ptr_(0), + iocp_service_(0) + { + this->reset(ex, ASIO_MOVE_CAST(Handler)(handler)); + } + + // Destructor automatically frees the OVERLAPPED object unless released. + ~win_iocp_overlapped_ptr() + { + reset(); + } + + // Reset to empty. + void reset() + { + if (ptr_) + { + ptr_->destroy(); + ptr_ = 0; + iocp_service_->work_finished(); + iocp_service_ = 0; + } + } + + // Reset to contain the specified handler, freeing any current OVERLAPPED + // object. + template + void reset(const Executor& ex, Handler handler) + { + win_iocp_io_context* iocp_service = this->get_iocp_service(ex); + + typedef win_iocp_overlapped_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler, ex); + + ASIO_HANDLER_CREATION((ex.context(), *p.p, + "iocp_service", iocp_service, 0, "overlapped")); + + iocp_service->work_started(); + reset(); + ptr_ = p.p; + p.v = p.p = 0; + iocp_service_ = iocp_service; + } + + // Get the contained OVERLAPPED object. + OVERLAPPED* get() + { + return ptr_; + } + + // Get the contained OVERLAPPED object. + const OVERLAPPED* get() const + { + return ptr_; + } + + // Release ownership of the OVERLAPPED object. + OVERLAPPED* release() + { + if (ptr_) + iocp_service_->on_pending(ptr_); + + OVERLAPPED* tmp = ptr_; + ptr_ = 0; + iocp_service_ = 0; + return tmp; + } + + // Post completion notification for overlapped operation. Releases ownership. + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + if (ptr_) + { + iocp_service_->on_completion(ptr_, ec, + static_cast(bytes_transferred)); + ptr_ = 0; + iocp_service_ = 0; + } + } + +private: + template + static win_iocp_io_context* get_iocp_service(const Executor& ex, + typename enable_if< + can_query::value + >::type* = 0) + { + return &use_service( + asio::query(ex, execution::context)); + } + + template + static win_iocp_io_context* get_iocp_service(const Executor& ex, + typename enable_if< + !can_query::value + >::type* = 0) + { + return &use_service(ex.context()); + } + + static win_iocp_io_context* get_iocp_service( + const io_context::executor_type& ex) + { + return &asio::query(ex, execution::context).impl_; + } + + win_iocp_operation* ptr_; + win_iocp_io_context* iocp_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_serial_port_service.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_serial_port_service.hpp new file mode 100644 index 000000000..d1d9d8281 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_serial_port_service.hpp @@ -0,0 +1,232 @@ +// +// detail/win_iocp_serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + +#include +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/detail/win_iocp_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Extend win_iocp_handle_service to provide serial port support. +class win_iocp_serial_port_service : + public execution_context_service_base +{ +public: + // The native type of a serial port. + typedef win_iocp_handle_service::native_handle_type native_handle_type; + + // The implementation type of the serial port. + typedef win_iocp_handle_service::implementation_type implementation_type; + + // Constructor. + ASIO_DECL win_iocp_serial_port_service(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new serial port implementation. + void construct(implementation_type& impl) + { + handle_service_.construct(impl); + } + + // Move-construct a new serial port implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + handle_service_.move_construct(impl, other_impl); + } + + // Move-assign from another serial port implementation. + void move_assign(implementation_type& impl, + win_iocp_serial_port_service& other_service, + implementation_type& other_impl) + { + handle_service_.move_assign(impl, + other_service.handle_service_, other_impl); + } + + // Destroy a serial port implementation. + void destroy(implementation_type& impl) + { + handle_service_.destroy(impl); + } + + // Open the serial port using the specified device name. + ASIO_DECL asio::error_code open(implementation_type& impl, + const std::string& device, asio::error_code& ec); + + // Assign a native handle to a serial port implementation. + asio::error_code assign(implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec) + { + return handle_service_.assign(impl, handle, ec); + } + + // Determine whether the serial port is open. + bool is_open(const implementation_type& impl) const + { + return handle_service_.is_open(impl); + } + + // Destroy a serial port implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return handle_service_.close(impl, ec); + } + + // Get the native serial port representation. + native_handle_type native_handle(implementation_type& impl) + { + return handle_service_.native_handle(impl); + } + + // Cancel all operations associated with the handle. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return handle_service_.cancel(impl, ec); + } + + // Set an option on the serial port. + template + asio::error_code set_option(implementation_type& impl, + const SettableSerialPortOption& option, asio::error_code& ec) + { + return do_set_option(impl, + &win_iocp_serial_port_service::store_option, + &option, ec); + } + + // Get an option from the serial port. + template + asio::error_code get_option(const implementation_type& impl, + GettableSerialPortOption& option, asio::error_code& ec) const + { + return do_get_option(impl, + &win_iocp_serial_port_service::load_option, + &option, ec); + } + + // Send a break sequence to the serial port. + asio::error_code send_break(implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Write the given data. Returns the number of bytes sent. + template + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return handle_service_.write_some(impl, buffers, ec); + } + + // Start an asynchronous write. The data being written must be valid for the + // lifetime of the asynchronous operation. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + { + handle_service_.async_write_some(impl, buffers, handler, io_ex); + } + + // Read some data. Returns the number of bytes received. + template + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return handle_service_.read_some(impl, buffers, ec); + } + + // Start an asynchronous read. The buffer for the data being received must be + // valid for the lifetime of the asynchronous operation. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + { + handle_service_.async_read_some(impl, buffers, handler, io_ex); + } + +private: + // Function pointer type for storing a serial port option. + typedef asio::error_code (*store_function_type)( + const void*, ::DCB&, asio::error_code&); + + // Helper function template to store a serial port option. + template + static asio::error_code store_option(const void* option, + ::DCB& storage, asio::error_code& ec) + { + static_cast(option)->store(storage, ec); + return ec; + } + + // Helper function to set a serial port option. + ASIO_DECL asio::error_code do_set_option( + implementation_type& impl, store_function_type store, + const void* option, asio::error_code& ec); + + // Function pointer type for loading a serial port option. + typedef asio::error_code (*load_function_type)( + void*, const ::DCB&, asio::error_code&); + + // Helper function template to load a serial port option. + template + static asio::error_code load_option(void* option, + const ::DCB& storage, asio::error_code& ec) + { + static_cast(option)->load(storage, ec); + return ec; + } + + // Helper function to get a serial port option. + ASIO_DECL asio::error_code do_get_option( + const implementation_type& impl, load_function_type load, + void* option, asio::error_code& ec) const; + + // The implementation used for initiating asynchronous operations. + win_iocp_handle_service handle_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_serial_port_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_accept_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_accept_op.hpp new file mode 100644 index 000000000..1f21685ec --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_accept_op.hpp @@ -0,0 +1,312 @@ +// +// detail/win_iocp_socket_accept_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/win_iocp_socket_service_base.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_accept_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_accept_op); + + win_iocp_socket_accept_op(win_iocp_socket_service_base& socket_service, + socket_type socket, Socket& peer, const Protocol& protocol, + typename Protocol::endpoint* peer_endpoint, + bool enable_connection_aborted, Handler& handler, const IoExecutor& io_ex) + : operation(&win_iocp_socket_accept_op::do_complete), + socket_service_(socket_service), + socket_(socket), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + enable_connection_aborted_(enable_connection_aborted), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + socket_holder& new_socket() + { + return new_socket_; + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage_type) + 16; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t /*bytes_transferred*/) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_accept_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + if (owner) + { + typename Protocol::endpoint peer_endpoint; + std::size_t addr_len = peer_endpoint.capacity(); + socket_ops::complete_iocp_accept(o->socket_, + o->output_buffer(), o->address_length(), + peer_endpoint.data(), &addr_len, + o->new_socket_.get(), ec); + + // Restart the accept operation if we got the connection_aborted error + // and the enable_connection_aborted socket option is not set. + if (ec == asio::error::connection_aborted + && !o->enable_connection_aborted_) + { + o->reset(); + o->socket_service_.restart_accept_op(o->socket_, + o->new_socket_, o->protocol_.family(), + o->protocol_.type(), o->protocol_.protocol(), + o->output_buffer(), o->address_length(), o); + p.v = p.p = 0; + return; + } + + // If the socket was successfully accepted, transfer ownership of the + // socket to the peer object. + if (!ec) + { + o->peer_.assign(o->protocol_, + typename Socket::native_handle_type( + o->new_socket_.get(), peer_endpoint), ec); + if (!ec) + o->new_socket_.release(); + } + + // Pass endpoint back to caller. + if (o->peer_endpoint_) + *o->peer_endpoint_ = peer_endpoint; + } + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, ec); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + win_iocp_socket_service_base& socket_service_; + socket_type socket_; + socket_holder new_socket_; + Socket& peer_; + Protocol protocol_; + typename Protocol::endpoint* peer_endpoint_; + unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; + bool enable_connection_aborted_; + Handler handler_; + handler_work work_; +}; + +#if defined(ASIO_HAS_MOVE) + +template +class win_iocp_socket_move_accept_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_move_accept_op); + + win_iocp_socket_move_accept_op( + win_iocp_socket_service_base& socket_service, socket_type socket, + const Protocol& protocol, const PeerIoExecutor& peer_io_ex, + typename Protocol::endpoint* peer_endpoint, + bool enable_connection_aborted, Handler& handler, const IoExecutor& io_ex) + : operation(&win_iocp_socket_move_accept_op::do_complete), + socket_service_(socket_service), + socket_(socket), + peer_(peer_io_ex), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + enable_connection_aborted_(enable_connection_aborted), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + socket_holder& new_socket() + { + return new_socket_; + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage_type) + 16; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t /*bytes_transferred*/) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_move_accept_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + if (owner) + { + typename Protocol::endpoint peer_endpoint; + std::size_t addr_len = peer_endpoint.capacity(); + socket_ops::complete_iocp_accept(o->socket_, + o->output_buffer(), o->address_length(), + peer_endpoint.data(), &addr_len, + o->new_socket_.get(), ec); + + // Restart the accept operation if we got the connection_aborted error + // and the enable_connection_aborted socket option is not set. + if (ec == asio::error::connection_aborted + && !o->enable_connection_aborted_) + { + o->reset(); + o->socket_service_.restart_accept_op(o->socket_, + o->new_socket_, o->protocol_.family(), + o->protocol_.type(), o->protocol_.protocol(), + o->output_buffer(), o->address_length(), o); + p.v = p.p = 0; + return; + } + + // If the socket was successfully accepted, transfer ownership of the + // socket to the peer object. + if (!ec) + { + o->peer_.assign(o->protocol_, + typename Protocol::socket::native_handle_type( + o->new_socket_.get(), peer_endpoint), ec); + if (!ec) + o->new_socket_.release(); + } + + // Pass endpoint back to caller. + if (o->peer_endpoint_) + *o->peer_endpoint_ = peer_endpoint; + } + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::move_binder2 + handler(0, ASIO_MOVE_CAST(Handler)(o->handler_), ec, + ASIO_MOVE_CAST(peer_socket_type)(o->peer_)); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + typedef typename Protocol::socket::template + rebind_executor::other peer_socket_type; + + win_iocp_socket_service_base& socket_service_; + socket_type socket_; + socket_holder new_socket_; + peer_socket_type peer_; + Protocol protocol_; + typename Protocol::endpoint* peer_endpoint_; + unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; + bool enable_connection_aborted_; + Handler handler_; + handler_work work_; +}; + +#endif // defined(ASIO_HAS_MOVE) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_connect_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_connect_op.hpp new file mode 100644 index 000000000..f7be04d5c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_connect_op.hpp @@ -0,0 +1,135 @@ +// +// detail/win_iocp_socket_connect_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_CONNECT_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_CONNECT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_socket_connect_op_base : public reactor_op +{ +public: + win_iocp_socket_connect_op_base(socket_type socket, func_type complete_func) + : reactor_op(asio::error_code(), + &win_iocp_socket_connect_op_base::do_perform, complete_func), + socket_(socket), + connect_ex_(false) + { + } + + static status do_perform(reactor_op* base) + { + win_iocp_socket_connect_op_base* o( + static_cast(base)); + + return socket_ops::non_blocking_connect( + o->socket_, o->ec_) ? done : not_done; + } + + socket_type socket_; + bool connect_ex_; +}; + +template +class win_iocp_socket_connect_op : public win_iocp_socket_connect_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_connect_op); + + win_iocp_socket_connect_op(socket_type socket, + Handler& handler, const IoExecutor& io_ex) + : win_iocp_socket_connect_op_base(socket, + &win_iocp_socket_connect_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t /*bytes_transferred*/) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_connect_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + if (owner) + { + if (o->connect_ex_) + socket_ops::complete_iocp_connect(o->socket_, ec); + else + ec = o->ec_; + } + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, ec); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_CONNECT_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_recv_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_recv_op.hpp new file mode 100644 index 000000000..0da29825d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_recv_op.hpp @@ -0,0 +1,124 @@ +// +// detail/win_iocp_socket_recv_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_recv_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recv_op); + + win_iocp_socket_recv_op(socket_ops::state_type state, + socket_ops::weak_cancel_token_type cancel_token, + const MutableBufferSequence& buffers, Handler& handler, + const IoExecutor& io_ex) + : operation(&win_iocp_socket_recv_op::do_complete), + state_(state), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_recv_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_recv(o->state_, o->cancel_token_, + buffer_sequence_adapter::all_empty(o->buffers_), + ec, bytes_transferred); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + socket_ops::state_type state_; + socket_ops::weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_recvfrom_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_recvfrom_op.hpp new file mode 100644 index 000000000..40c91144c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_recvfrom_op.hpp @@ -0,0 +1,133 @@ +// +// detail/win_iocp_socket_recvfrom_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_recvfrom_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recvfrom_op); + + win_iocp_socket_recvfrom_op(Endpoint& endpoint, + socket_ops::weak_cancel_token_type cancel_token, + const MutableBufferSequence& buffers, Handler& handler, + const IoExecutor& io_ex) + : operation(&win_iocp_socket_recvfrom_op::do_complete), + endpoint_(endpoint), + endpoint_size_(static_cast(endpoint.capacity())), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + int& endpoint_size() + { + return endpoint_size_; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_recvfrom_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_recvfrom(o->cancel_token_, ec); + + // Record the size of the endpoint returned by the operation. + o->endpoint_.resize(o->endpoint_size_); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Endpoint& endpoint_; + int endpoint_size_; + socket_ops::weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_recvmsg_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_recvmsg_op.hpp new file mode 100644 index 000000000..a14a4b5cb --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_recvmsg_op.hpp @@ -0,0 +1,125 @@ +// +// detail/win_iocp_socket_recvmsg_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECVMSG_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_RECVMSG_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" +#include "asio/socket_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_recvmsg_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recvmsg_op); + + win_iocp_socket_recvmsg_op( + socket_ops::weak_cancel_token_type cancel_token, + const MutableBufferSequence& buffers, + socket_base::message_flags& out_flags, + Handler& handler, const IoExecutor& io_ex) + : operation(&win_iocp_socket_recvmsg_op::do_complete), + cancel_token_(cancel_token), + buffers_(buffers), + out_flags_(out_flags), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_recvmsg_op* o( + static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_recvmsg(o->cancel_token_, ec); + o->out_flags_ = 0; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + socket_base::message_flags& out_flags_; + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECVMSG_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_send_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_send_op.hpp new file mode 100644 index 000000000..3a40abefa --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_send_op.hpp @@ -0,0 +1,118 @@ +// +// detail/win_iocp_socket_send_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_send_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_send_op); + + win_iocp_socket_send_op(socket_ops::weak_cancel_token_type cancel_token, + const ConstBufferSequence& buffers, Handler& handler, + const IoExecutor& io_ex) + : operation(&win_iocp_socket_send_op::do_complete), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t bytes_transferred) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_socket_send_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_send(o->cancel_token_, ec); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + ConstBufferSequence buffers_; + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_service.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_service.hpp new file mode 100644 index 000000000..e2c42c213 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_service.hpp @@ -0,0 +1,581 @@ +// +// detail/win_iocp_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_iocp_io_context.hpp" +#include "asio/detail/win_iocp_null_buffers_op.hpp" +#include "asio/detail/win_iocp_socket_accept_op.hpp" +#include "asio/detail/win_iocp_socket_connect_op.hpp" +#include "asio/detail/win_iocp_socket_recvfrom_op.hpp" +#include "asio/detail/win_iocp_socket_send_op.hpp" +#include "asio/detail/win_iocp_socket_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_service : + public execution_context_service_base >, + public win_iocp_socket_service_base +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + class native_handle_type + { + public: + native_handle_type(socket_type s) + : socket_(s), + have_remote_endpoint_(false) + { + } + + native_handle_type(socket_type s, const endpoint_type& ep) + : socket_(s), + have_remote_endpoint_(true), + remote_endpoint_(ep) + { + } + + void operator=(socket_type s) + { + socket_ = s; + have_remote_endpoint_ = false; + remote_endpoint_ = endpoint_type(); + } + + operator socket_type() const + { + return socket_; + } + + bool have_remote_endpoint() const + { + return have_remote_endpoint_; + } + + endpoint_type remote_endpoint() const + { + return remote_endpoint_; + } + + private: + socket_type socket_; + bool have_remote_endpoint_; + endpoint_type remote_endpoint_; + }; + + // The implementation type of the socket. + struct implementation_type : + win_iocp_socket_service_base::base_implementation_type + { + // Default constructor. + implementation_type() + : protocol_(endpoint_type().protocol()), + have_remote_endpoint_(false), + remote_endpoint_() + { + } + + // The protocol associated with the socket. + protocol_type protocol_; + + // Whether we have a cached remote endpoint. + bool have_remote_endpoint_; + + // A cached remote endpoint. + endpoint_type remote_endpoint_; + }; + + // Constructor. + win_iocp_socket_service(execution_context& context) + : execution_context_service_base< + win_iocp_socket_service >(context), + win_iocp_socket_service_base(context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + this->base_shutdown(); + } + + // Move-construct a new socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) ASIO_NOEXCEPT + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + + impl.have_remote_endpoint_ = other_impl.have_remote_endpoint_; + other_impl.have_remote_endpoint_ = false; + + impl.remote_endpoint_ = other_impl.remote_endpoint_; + other_impl.remote_endpoint_ = endpoint_type(); + } + + // Move-assign from another socket implementation. + void move_assign(implementation_type& impl, + win_iocp_socket_service_base& other_service, + implementation_type& other_impl) + { + this->base_move_assign(impl, other_service, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + + impl.have_remote_endpoint_ = other_impl.have_remote_endpoint_; + other_impl.have_remote_endpoint_ = false; + + impl.remote_endpoint_ = other_impl.remote_endpoint_; + other_impl.remote_endpoint_ = endpoint_type(); + } + + // Move-construct a new socket implementation from another protocol type. + template + void converting_move_construct(implementation_type& impl, + win_iocp_socket_service&, + typename win_iocp_socket_service< + Protocol1>::implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = protocol_type(other_impl.protocol_); + other_impl.protocol_ = typename Protocol1::endpoint().protocol(); + + impl.have_remote_endpoint_ = other_impl.have_remote_endpoint_; + other_impl.have_remote_endpoint_ = false; + + impl.remote_endpoint_ = other_impl.remote_endpoint_; + other_impl.remote_endpoint_ = typename Protocol1::endpoint(); + } + + // Open a new socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (!do_open(impl, protocol.family(), + protocol.type(), protocol.protocol(), ec)) + { + impl.protocol_ = protocol; + impl.have_remote_endpoint_ = false; + impl.remote_endpoint_ = endpoint_type(); + } + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + asio::error_code& ec) + { + if (!do_assign(impl, protocol.type(), native_socket, ec)) + { + impl.protocol_ = protocol; + impl.have_remote_endpoint_ = native_socket.have_remote_endpoint(); + impl.remote_endpoint_ = native_socket.remote_endpoint(); + } + return ec; + } + + // Get the native socket representation. + native_handle_type native_handle(implementation_type& impl) + { + if (impl.have_remote_endpoint_) + return native_handle_type(impl.socket_, impl.remote_endpoint_); + return native_handle_type(impl.socket_); + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); + return ec; + } + + // Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const Option& option, asio::error_code& ec) + { + socket_ops::setsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; + } + + // Set a socket option. + template + asio::error_code get_option(const implementation_type& impl, + Option& option, asio::error_code& ec) const + { + std::size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint = impl.remote_endpoint_; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, endpoint.data(), + &addr_len, impl.have_remote_endpoint_, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Disable sends or receives on the socket. + asio::error_code shutdown(base_implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_sendto(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, + destination.data(), destination.size(), ec); + } + + // Wait until data can be sent without blocking. + size_t send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, Handler& handler, + const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_send_op< + ConstBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_send_to")); + + buffer_sequence_adapter bufs(buffers); + + start_send_to_op(impl, bufs.buffers(), bufs.count(), + destination.data(), static_cast(destination.size()), + flags, p.p); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, Handler& handler, + const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_send_to(null_buffers)")); + + start_reactor_op(impl, select_reactor::write_op, p.p); + p.v = p.p = 0; + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + std::size_t addr_len = sender_endpoint.capacity(); + std::size_t bytes_recvd = socket_ops::sync_recvfrom( + impl.socket_, impl.state_, bufs.buffers(), bufs.count(), + flags, sender_endpoint.data(), &addr_len, ec); + + if (!ec) + sender_endpoint.resize(addr_len); + + return bytes_recvd; + } + + // Wait until data can be received without blocking. + size_t receive_from(implementation_type& impl, + const null_buffers&, endpoint_type& sender_endpoint, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endp, + socket_base::message_flags flags, Handler& handler, + const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_recvfrom_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(sender_endp, impl.cancel_token_, + buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_receive_from")); + + buffer_sequence_adapter bufs(buffers); + + start_receive_from_op(impl, bufs.buffers(), bufs.count(), + sender_endp.data(), flags, &p.p->endpoint_size(), p.p); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive_from(implementation_type& impl, const null_buffers&, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_receive_from(null_buffers)")); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + start_null_buffers_receive_op(impl, flags, p.p); + p.v = p.p = 0; + } + + // Accept a new connection. + template + asio::error_code accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, asio::error_code& ec) + { + // We cannot accept a socket that is already open. + if (peer.is_open()) + { + ec = asio::error::already_open; + return ec; + } + + std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; + socket_holder new_socket(socket_ops::sync_accept(impl.socket_, + impl.state_, peer_endpoint ? peer_endpoint->data() : 0, + peer_endpoint ? &addr_len : 0, ec)); + + // On success, assign new connection to peer socket object. + if (new_socket.get() != invalid_socket) + { + if (peer_endpoint) + peer_endpoint->resize(addr_len); + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + } + + return ec; + } + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_accept_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + bool enable_connection_aborted = + (impl.state_ & socket_ops::enable_connection_aborted) != 0; + p.p = new (p.v) op(*this, impl.socket_, peer, impl.protocol_, + peer_endpoint, enable_connection_aborted, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, peer.is_open(), p.p->new_socket(), + impl.protocol_.family(), impl.protocol_.type(), + impl.protocol_.protocol(), p.p->output_buffer(), + p.p->address_length(), p.p); + p.v = p.p = 0; + } + +#if defined(ASIO_HAS_MOVE) + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_move_accept(implementation_type& impl, + const PeerIoExecutor& peer_io_ex, endpoint_type* peer_endpoint, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_move_accept_op< + protocol_type, PeerIoExecutor, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + bool enable_connection_aborted = + (impl.state_ & socket_ops::enable_connection_aborted) != 0; + p.p = new (p.v) op(*this, impl.socket_, impl.protocol_, + peer_io_ex, peer_endpoint, enable_connection_aborted, + handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, false, p.p->new_socket(), + impl.protocol_.family(), impl.protocol_.type(), + impl.protocol_.protocol(), p.p->output_buffer(), + p.p->address_length(), p.p); + p.v = p.p = 0; + } +#endif // defined(ASIO_HAS_MOVE) + + // Connect the socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + socket_ops::sync_connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size(), ec); + return ec; + } + + // Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, Handler& handler, + const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_connect_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.socket_, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_connect")); + + start_connect_op(impl, impl.protocol_.family(), impl.protocol_.type(), + peer_endpoint.data(), static_cast(peer_endpoint.size()), p.p); + p.v = p.p = 0; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_service_base.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_service_base.hpp new file mode 100644 index 000000000..2a1f644a1 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_socket_service_base.hpp @@ -0,0 +1,600 @@ +// +// detail/win_iocp_socket_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_iocp_io_context.hpp" +#include "asio/detail/win_iocp_null_buffers_op.hpp" +#include "asio/detail/win_iocp_socket_connect_op.hpp" +#include "asio/detail/win_iocp_socket_send_op.hpp" +#include "asio/detail/win_iocp_socket_recv_op.hpp" +#include "asio/detail/win_iocp_socket_recvmsg_op.hpp" +#include "asio/detail/win_iocp_wait_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_socket_service_base +{ +public: + // The implementation type of the socket. + struct base_implementation_type + { + // The native socket representation. + socket_type socket_; + + // The current state of the socket. + socket_ops::state_type state_; + + // We use a shared pointer as a cancellation token here to work around the + // broken Windows support for cancellation. MSDN says that when you call + // closesocket any outstanding WSARecv or WSASend operations will complete + // with the error ERROR_OPERATION_ABORTED. In practice they complete with + // ERROR_NETNAME_DELETED, which means you can't tell the difference between + // a local cancellation and the socket being hard-closed by the peer. + socket_ops::shared_cancel_token_type cancel_token_; + + // Per-descriptor data used by the reactor. + select_reactor::per_descriptor_data reactor_data_; + +#if defined(ASIO_ENABLE_CANCELIO) + // The ID of the thread from which it is safe to cancel asynchronous + // operations. 0 means no asynchronous operations have been started yet. + // ~0 means asynchronous operations have been started from more than one + // thread, and cancellation is not supported for the socket. + DWORD safe_cancellation_thread_id_; +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Pointers to adjacent socket implementations in linked list. + base_implementation_type* next_; + base_implementation_type* prev_; + }; + + // Constructor. + ASIO_DECL win_iocp_socket_service_base(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void base_shutdown(); + + // Construct a new socket implementation. + ASIO_DECL void construct(base_implementation_type& impl); + + // Move-construct a new socket implementation. + ASIO_DECL void base_move_construct(base_implementation_type& impl, + base_implementation_type& other_impl) ASIO_NOEXCEPT; + + // Move-assign from another socket implementation. + ASIO_DECL void base_move_assign(base_implementation_type& impl, + win_iocp_socket_service_base& other_service, + base_implementation_type& other_impl); + + // Destroy a socket implementation. + ASIO_DECL void destroy(base_implementation_type& impl); + + // Determine whether the socket is open. + bool is_open(const base_implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + ASIO_DECL asio::error_code close( + base_implementation_type& impl, asio::error_code& ec); + + // Release ownership of the socket. + ASIO_DECL socket_type release( + base_implementation_type& impl, asio::error_code& ec); + + // Cancel all operations associated with the socket. + ASIO_DECL asio::error_code cancel( + base_implementation_type& impl, asio::error_code& ec); + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::sockatmark(impl.socket_, ec); + } + + // Determine the number of bytes available for reading. + std::size_t available(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::available(impl.socket_, ec); + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(base_implementation_type& impl, + int backlog, asio::error_code& ec) + { + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(base_implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + socket_ops::ioctl(impl.socket_, impl.state_, command.name(), + static_cast(command.data()), ec); + return ec; + } + + // Gets the non-blocking mode of the socket. + bool non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::user_set_non_blocking) != 0; + } + + // Sets the non-blocking mode of the socket. + asio::error_code non_blocking(base_implementation_type& impl, + bool mode, asio::error_code& ec) + { + socket_ops::set_user_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::internal_non_blocking) != 0; + } + + // Sets the non-blocking mode of the native socket implementation. + asio::error_code native_non_blocking(base_implementation_type& impl, + bool mode, asio::error_code& ec) + { + socket_ops::set_internal_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Wait for the socket to become ready to read, ready to write, or to have + // pending error conditions. + asio::error_code wait(base_implementation_type& impl, + socket_base::wait_type w, asio::error_code& ec) + { + switch (w) + { + case socket_base::wait_read: + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_write: + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_error: + socket_ops::poll_error(impl.socket_, impl.state_, -1, ec); + break; + default: + ec = asio::error::invalid_argument; + break; + } + + return ec; + } + + // Asynchronously wait for the socket to become ready to read, ready to + // write, or to have pending error conditions. + template + void async_wait(base_implementation_type& impl, + socket_base::wait_type w, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_wait_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_wait")); + + switch (w) + { + case socket_base::wait_read: + start_null_buffers_receive_op(impl, 0, p.p); + break; + case socket_base::wait_write: + start_reactor_op(impl, select_reactor::write_op, p.p); + break; + case socket_base::wait_error: + start_reactor_op(impl, select_reactor::except_op, p.p); + break; + default: + p.p->ec_ = asio::error::invalid_argument; + iocp_service_.post_immediate_completion(p.p, is_continuation); + break; + } + + p.v = p.p = 0; + } + + // Send the given data to the peer. Returns the number of bytes sent. + template + size_t send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_send(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be sent without blocking. + size_t send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(base_implementation_type& impl, + const ConstBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_send_op< + ConstBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_send")); + + buffer_sequence_adapter bufs(buffers); + + start_send_op(impl, bufs.buffers(), bufs.count(), flags, + (impl.state_ & socket_ops::stream_oriented) != 0 && bufs.all_empty(), + p.p); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_send(null_buffers)")); + + start_reactor_op(impl, select_reactor::write_op, p.p); + p.v = p.p = 0; + } + + // Receive some data from the peer. Returns the number of bytes received. + template + size_t receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_recv(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be received without blocking. + size_t receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_recv_op< + MutableBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.state_, impl.cancel_token_, + buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_receive")); + + buffer_sequence_adapter bufs(buffers); + + start_receive_op(impl, bufs.buffers(), bufs.count(), flags, + (impl.state_ & socket_ops::stream_oriented) != 0 && bufs.all_empty(), + p.p); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_receive(null_buffers)")); + + start_null_buffers_receive_op(impl, flags, p.p); + p.v = p.p = 0; + } + + // Receive some data with associated flags. Returns the number of bytes + // received. + template + size_t receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_recvmsg(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), in_flags, out_flags, ec); + } + + // Wait until data can be received without blocking. + size_t receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags, + socket_base::message_flags& out_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Clear out_flags, since we cannot give it any other sensible value when + // performing a null_buffers operation. + out_flags = 0; + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_recvmsg_op< + MutableBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, + buffers, out_flags, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags")); + + buffer_sequence_adapter bufs(buffers); + + start_receive_op(impl, bufs.buffers(), bufs.count(), in_flags, false, p.p); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler, io_ex); + + ASIO_HANDLER_CREATION((context_, *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags(null_buffers)")); + + // Reset out_flags since it can be given no sensible value at this time. + out_flags = 0; + + start_null_buffers_receive_op(impl, in_flags, p.p); + p.v = p.p = 0; + } + + // Helper function to restart an asynchronous accept operation. + ASIO_DECL void restart_accept_op(socket_type s, + socket_holder& new_socket, int family, int type, int protocol, + void* output_buffer, DWORD address_length, operation* op); + +protected: + // Open a new socket implementation. + ASIO_DECL asio::error_code do_open( + base_implementation_type& impl, int family, int type, + int protocol, asio::error_code& ec); + + // Assign a native socket to a socket implementation. + ASIO_DECL asio::error_code do_assign( + base_implementation_type& impl, int type, + socket_type native_socket, asio::error_code& ec); + + // Helper function to start an asynchronous send operation. + ASIO_DECL void start_send_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op); + + // Helper function to start an asynchronous send_to operation. + ASIO_DECL void start_send_to_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + const socket_addr_type* addr, int addrlen, + socket_base::message_flags flags, operation* op); + + // Helper function to start an asynchronous receive operation. + ASIO_DECL void start_receive_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op); + + // Helper function to start an asynchronous null_buffers receive operation. + ASIO_DECL void start_null_buffers_receive_op( + base_implementation_type& impl, + socket_base::message_flags flags, reactor_op* op); + + // Helper function to start an asynchronous receive_from operation. + ASIO_DECL void start_receive_from_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr, + socket_base::message_flags flags, int* addrlen, operation* op); + + // Helper function to start an asynchronous accept operation. + ASIO_DECL void start_accept_op(base_implementation_type& impl, + bool peer_is_open, socket_holder& new_socket, int family, int type, + int protocol, void* output_buffer, DWORD address_length, operation* op); + + // Start an asynchronous read or write operation using the reactor. + ASIO_DECL void start_reactor_op(base_implementation_type& impl, + int op_type, reactor_op* op); + + // Start the asynchronous connect operation using the reactor. + ASIO_DECL void start_connect_op(base_implementation_type& impl, + int family, int type, const socket_addr_type* remote_addr, + std::size_t remote_addrlen, win_iocp_socket_connect_op_base* op); + + // Helper function to close a socket when the associated object is being + // destroyed. + ASIO_DECL void close_for_destruction(base_implementation_type& impl); + + // Update the ID of the thread from which cancellation is safe. + ASIO_DECL void update_cancellation_thread_id( + base_implementation_type& impl); + + // Helper function to get the reactor. If no reactor has been created yet, a + // new one is obtained from the execution context and a pointer to it is + // cached in this service. + ASIO_DECL select_reactor& get_reactor(); + + // The type of a ConnectEx function pointer, as old SDKs may not provide it. + typedef BOOL (PASCAL *connect_ex_fn)(SOCKET, + const socket_addr_type*, int, void*, DWORD, DWORD*, OVERLAPPED*); + + // Helper function to get the ConnectEx pointer. If no ConnectEx pointer has + // been obtained yet, one is obtained using WSAIoctl and the pointer is + // cached. Returns a null pointer if ConnectEx is not available. + ASIO_DECL connect_ex_fn get_connect_ex( + base_implementation_type& impl, int type); + + // The type of a NtSetInformationFile function pointer. + typedef LONG (NTAPI *nt_set_info_fn)(HANDLE, ULONG_PTR*, void*, ULONG, ULONG); + + // Helper function to get the NtSetInformationFile function pointer. If no + // NtSetInformationFile pointer has been obtained yet, one is obtained using + // GetProcAddress and the pointer is cached. Returns a null pointer if + // NtSetInformationFile is not available. + ASIO_DECL nt_set_info_fn get_nt_set_info(); + + // Helper function to emulate InterlockedCompareExchangePointer functionality + // for: + // - very old Platform SDKs; and + // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. + ASIO_DECL void* interlocked_compare_exchange_pointer( + void** dest, void* exch, void* cmp); + + // Helper function to emulate InterlockedExchangePointer functionality for: + // - very old Platform SDKs; and + // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. + ASIO_DECL void* interlocked_exchange_pointer(void** dest, void* val); + + // The execution context used to obtain the reactor, if required. + execution_context& context_; + + // The IOCP service used for running asynchronous operations and dispatching + // handlers. + win_iocp_io_context& iocp_service_; + + // The reactor used for performing connect operations. This object is created + // only if needed. + select_reactor* reactor_; + + // Pointer to ConnectEx implementation. + void* connect_ex_; + + // Pointer to NtSetInformationFile implementation. + void* nt_set_info_; + + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + base_implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_socket_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_thread_info.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_thread_info.hpp new file mode 100644 index 000000000..2b9b1bf56 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_thread_info.hpp @@ -0,0 +1,34 @@ +// +// detail/win_iocp_thread_info.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_THREAD_INFO_HPP +#define ASIO_DETAIL_WIN_IOCP_THREAD_INFO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/thread_info_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct win_iocp_thread_info : public thread_info_base +{ +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_THREAD_INFO_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_iocp_wait_op.hpp b/third_party/asio/1.18.2/include/asio/detail/win_iocp_wait_op.hpp new file mode 100644 index 000000000..b17fe404b --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_iocp_wait_op.hpp @@ -0,0 +1,128 @@ +// +// detail/win_iocp_wait_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_WAIT_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_WAIT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_wait_op : public reactor_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_wait_op); + + win_iocp_wait_op(socket_ops::weak_cancel_token_type cancel_token, + Handler& handler, const IoExecutor& io_ex) + : reactor_op(asio::error_code(), + &win_iocp_wait_op::do_perform, + &win_iocp_wait_op::do_complete), + cancel_token_(cancel_token), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static status do_perform(reactor_op*) + { + return done; + } + + static void do_complete(void* owner, operation* base, + const asio::error_code& result_ec, + std::size_t /*bytes_transferred*/) + { + asio::error_code ec(result_ec); + + // Take ownership of the operation object. + win_iocp_wait_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // The reactor may have stored a result in the operation object. + if (o->ec_) + ec = o->ec_; + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (o->cancel_token_.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, ec); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + Handler handler_; + handler_work work_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_WAIT_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/win_mutex.hpp new file mode 100644 index 000000000..aed9ad91b --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_mutex.hpp @@ -0,0 +1,78 @@ +// +// detail/win_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_MUTEX_HPP +#define ASIO_DETAIL_WIN_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + ASIO_DECL win_mutex(); + + // Destructor. + ~win_mutex() + { + ::DeleteCriticalSection(&crit_section_); + } + + // Lock the mutex. + void lock() + { + ::EnterCriticalSection(&crit_section_); + } + + // Unlock the mutex. + void unlock() + { + ::LeaveCriticalSection(&crit_section_); + } + +private: + // Initialisation must be performed in a separate function to the constructor + // since the compiler does not support the use of structured exceptions and + // C++ exceptions in the same function. + ASIO_DECL int do_init(); + + ::CRITICAL_SECTION crit_section_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_mutex.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_WIN_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_object_handle_service.hpp b/third_party/asio/1.18.2/include/asio/detail/win_object_handle_service.hpp new file mode 100644 index 000000000..a80c27dfc --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_object_handle_service.hpp @@ -0,0 +1,195 @@ +// +// detail/win_object_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2011 Boris Schaeling (boris@highscore.de) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_OBJECT_HANDLE_SERVICE_HPP +#define ASIO_DETAIL_WIN_OBJECT_HANDLE_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/wait_handler.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else // defined(ASIO_HAS_IOCP) +# include "asio/detail/scheduler.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_object_handle_service : + public execution_context_service_base +{ +public: + // The native type of an object handle. + typedef HANDLE native_handle_type; + + // The implementation type of the object handle. + class implementation_type + { + public: + // Default constructor. + implementation_type() + : handle_(INVALID_HANDLE_VALUE), + wait_handle_(INVALID_HANDLE_VALUE), + owner_(0), + next_(0), + prev_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class win_object_handle_service; + + // The native object handle representation. May be accessed or modified + // without locking the mutex. + native_handle_type handle_; + + // The handle used to unregister the wait operation. The mutex must be + // locked when accessing or modifying this member. + HANDLE wait_handle_; + + // The operations waiting on the object handle. If there is a registered + // wait then the mutex must be locked when accessing or modifying this + // member + op_queue op_queue_; + + // The service instance that owns the object handle implementation. + win_object_handle_service* owner_; + + // Pointers to adjacent handle implementations in linked list. The mutex + // must be locked when accessing or modifying these members. + implementation_type* next_; + implementation_type* prev_; + }; + + // Constructor. + ASIO_DECL win_object_handle_service(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Construct a new handle implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Move-construct a new handle implementation. + ASIO_DECL void move_construct(implementation_type& impl, + implementation_type& other_impl); + + // Move-assign from another handle implementation. + ASIO_DECL void move_assign(implementation_type& impl, + win_object_handle_service& other_service, + implementation_type& other_impl); + + // Destroy a handle implementation. + ASIO_DECL void destroy(implementation_type& impl); + + // Assign a native handle to a handle implementation. + ASIO_DECL asio::error_code assign(implementation_type& impl, + const native_handle_type& handle, asio::error_code& ec); + + // Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return impl.handle_ != INVALID_HANDLE_VALUE && impl.handle_ != 0; + } + + // Destroy a handle implementation. + ASIO_DECL asio::error_code close(implementation_type& impl, + asio::error_code& ec); + + // Get the native handle representation. + native_handle_type native_handle(const implementation_type& impl) const + { + return impl.handle_; + } + + // Cancel all operations associated with the handle. + ASIO_DECL asio::error_code cancel(implementation_type& impl, + asio::error_code& ec); + + // Perform a synchronous wait for the object to enter a signalled state. + ASIO_DECL void wait(implementation_type& impl, + asio::error_code& ec); + + /// Start an asynchronous wait. + template + void async_wait(implementation_type& impl, + Handler& handler, const IoExecutor& io_ex) + { + // Allocate and construct an operation to wrap the handler. + typedef wait_handler op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler, io_ex); + + ASIO_HANDLER_CREATION((scheduler_.context(), *p.p, "object_handle", + &impl, reinterpret_cast(impl.wait_handle_), "async_wait")); + + start_wait_op(impl, p.p); + p.v = p.p = 0; + } + +private: + // Helper function to start an asynchronous wait operation. + ASIO_DECL void start_wait_op(implementation_type& impl, wait_op* op); + + // Helper function to register a wait operation. + ASIO_DECL void register_wait_callback( + implementation_type& impl, mutex::scoped_lock& lock); + + // Callback function invoked when the registered wait completes. + static ASIO_DECL VOID CALLBACK wait_callback( + PVOID param, BOOLEAN timeout); + + // The scheduler used to post completions. +#if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_impl; +#else + typedef class scheduler scheduler_impl; +#endif + scheduler_impl& scheduler_; + + // Mutex to protect access to internal state. + mutex mutex_; + + // The head of a linked list of all implementations. + implementation_type* impl_list_; + + // Flag to indicate that the dispatcher has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_object_handle_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_WINDOWS_OBJECT_HANDLE) + +#endif // ASIO_DETAIL_WIN_OBJECT_HANDLE_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_static_mutex.hpp b/third_party/asio/1.18.2/include/asio/detail/win_static_mutex.hpp new file mode 100644 index 000000000..4538c3d16 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_static_mutex.hpp @@ -0,0 +1,74 @@ +// +// detail/win_static_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_STATIC_MUTEX_HPP +#define ASIO_DETAIL_WIN_STATIC_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct win_static_mutex +{ + typedef asio::detail::scoped_lock scoped_lock; + + // Initialise the mutex. + ASIO_DECL void init(); + + // Initialisation must be performed in a separate function to the "public" + // init() function since the compiler does not support the use of structured + // exceptions and C++ exceptions in the same function. + ASIO_DECL int do_init(); + + // Lock the mutex. + void lock() + { + ::EnterCriticalSection(&crit_section_); + } + + // Unlock the mutex. + void unlock() + { + ::LeaveCriticalSection(&crit_section_); + } + + bool initialised_; + ::CRITICAL_SECTION crit_section_; +}; + +#if defined(UNDER_CE) +# define ASIO_WIN_STATIC_MUTEX_INIT { false, { 0, 0, 0, 0, 0 } } +#else // defined(UNDER_CE) +# define ASIO_WIN_STATIC_MUTEX_INIT { false, { 0, 0, 0, 0, 0, 0 } } +#endif // defined(UNDER_CE) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_static_mutex.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_WIN_STATIC_MUTEX_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_thread.hpp b/third_party/asio/1.18.2/include/asio/detail/win_thread.hpp new file mode 100644 index 000000000..0f6679d01 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_thread.hpp @@ -0,0 +1,147 @@ +// +// detail/win_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_THREAD_HPP +#define ASIO_DETAIL_WIN_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) \ + && !defined(ASIO_WINDOWS_APP) \ + && !defined(UNDER_CE) + +#include +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +ASIO_DECL unsigned int __stdcall win_thread_function(void* arg); + +#if defined(WINVER) && (WINVER < 0x0500) +ASIO_DECL void __stdcall apc_function(ULONG data); +#else +ASIO_DECL void __stdcall apc_function(ULONG_PTR data); +#endif + +template +class win_thread_base +{ +public: + static bool terminate_threads() + { + return ::InterlockedExchangeAdd(&terminate_threads_, 0) != 0; + } + + static void set_terminate_threads(bool b) + { + ::InterlockedExchange(&terminate_threads_, b ? 1 : 0); + } + +private: + static long terminate_threads_; +}; + +template +long win_thread_base::terminate_threads_ = 0; + +class win_thread + : private noncopyable, + public win_thread_base +{ +public: + // Constructor. + template + win_thread(Function f, unsigned int stack_size = 0) + : thread_(0), + exit_event_(0) + { + start_thread(new func(f), stack_size); + } + + // Destructor. + ASIO_DECL ~win_thread(); + + // Wait for the thread to exit. + ASIO_DECL void join(); + + // Get number of CPUs. + ASIO_DECL static std::size_t hardware_concurrency(); + +private: + friend ASIO_DECL unsigned int __stdcall win_thread_function(void* arg); + +#if defined(WINVER) && (WINVER < 0x0500) + friend ASIO_DECL void __stdcall apc_function(ULONG); +#else + friend ASIO_DECL void __stdcall apc_function(ULONG_PTR); +#endif + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + ::HANDLE entry_event_; + ::HANDLE exit_event_; + }; + + struct auto_func_base_ptr + { + func_base* ptr; + ~auto_func_base_ptr() { delete ptr; } + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ASIO_DECL void start_thread(func_base* arg, unsigned int stack_size); + + ::HANDLE thread_; + ::HANDLE exit_event_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_thread.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + // && !defined(ASIO_WINDOWS_APP) + // && !defined(UNDER_CE) + +#endif // ASIO_DETAIL_WIN_THREAD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/win_tss_ptr.hpp b/third_party/asio/1.18.2/include/asio/detail/win_tss_ptr.hpp new file mode 100644 index 000000000..fa137a8bc --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/win_tss_ptr.hpp @@ -0,0 +1,79 @@ +// +// detail/win_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_TSS_PTR_HPP +#define ASIO_DETAIL_WIN_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// Helper function to create thread-specific storage. +ASIO_DECL DWORD win_tss_ptr_create(); + +template +class win_tss_ptr + : private noncopyable +{ +public: + // Constructor. + win_tss_ptr() + : tss_key_(win_tss_ptr_create()) + { + } + + // Destructor. + ~win_tss_ptr() + { + ::TlsFree(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast(::TlsGetValue(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::TlsSetValue(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + DWORD tss_key_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_tss_ptr.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) + +#endif // ASIO_DETAIL_WIN_TSS_PTR_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winapp_thread.hpp b/third_party/asio/1.18.2/include/asio/detail/winapp_thread.hpp new file mode 100644 index 000000000..512aa231e --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winapp_thread.hpp @@ -0,0 +1,124 @@ +// +// detail/winapp_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINAPP_THREAD_HPP +#define ASIO_DETAIL_WINAPP_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) && defined(ASIO_WINDOWS_APP) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +DWORD WINAPI winapp_thread_function(LPVOID arg); + +class winapp_thread + : private noncopyable +{ +public: + // Constructor. + template + winapp_thread(Function f, unsigned int = 0) + { + scoped_ptr arg(new func(f)); + DWORD thread_id = 0; + thread_ = ::CreateThread(0, 0, winapp_thread_function, + arg.get(), 0, &thread_id); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); + } + arg.release(); + } + + // Destructor. + ~winapp_thread() + { + ::CloseHandle(thread_); + } + + // Wait for the thread to exit. + void join() + { + ::WaitForSingleObjectEx(thread_, INFINITE, false); + } + + // Get number of CPUs. + static std::size_t hardware_concurrency() + { + SYSTEM_INFO system_info; + ::GetNativeSystemInfo(&system_info); + return system_info.dwNumberOfProcessors; + } + +private: + friend DWORD WINAPI winapp_thread_function(LPVOID arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::HANDLE thread_; +}; + +inline DWORD WINAPI winapp_thread_function(LPVOID arg) +{ + scoped_ptr func( + static_cast(arg)); + func->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) && defined(ASIO_WINDOWS_APP) + +#endif // ASIO_DETAIL_WINAPP_THREAD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/wince_thread.hpp b/third_party/asio/1.18.2/include/asio/detail/wince_thread.hpp new file mode 100644 index 000000000..63030f254 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/wince_thread.hpp @@ -0,0 +1,124 @@ +// +// detail/wince_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINCE_THREAD_HPP +#define ASIO_DETAIL_WINCE_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) && defined(UNDER_CE) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +DWORD WINAPI wince_thread_function(LPVOID arg); + +class wince_thread + : private noncopyable +{ +public: + // Constructor. + template + wince_thread(Function f, unsigned int = 0) + { + scoped_ptr arg(new func(f)); + DWORD thread_id = 0; + thread_ = ::CreateThread(0, 0, wince_thread_function, + arg.get(), 0, &thread_id); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); + } + arg.release(); + } + + // Destructor. + ~wince_thread() + { + ::CloseHandle(thread_); + } + + // Wait for the thread to exit. + void join() + { + ::WaitForSingleObject(thread_, INFINITE); + } + + // Get number of CPUs. + static std::size_t hardware_concurrency() + { + SYSTEM_INFO system_info; + ::GetSystemInfo(&system_info); + return system_info.dwNumberOfProcessors; + } + +private: + friend DWORD WINAPI wince_thread_function(LPVOID arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::HANDLE thread_; +}; + +inline DWORD WINAPI wince_thread_function(LPVOID arg) +{ + scoped_ptr func( + static_cast(arg)); + func->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS) && defined(UNDER_CE) + +#endif // ASIO_DETAIL_WINCE_THREAD_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_async_manager.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_async_manager.hpp new file mode 100644 index 000000000..ee7d4f691 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_async_manager.hpp @@ -0,0 +1,305 @@ +// +// detail/winrt_async_manager.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_ASYNC_MANAGER_HPP +#define ASIO_DETAIL_WINRT_ASYNC_MANAGER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else // defined(ASIO_HAS_IOCP) +# include "asio/detail/scheduler.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class winrt_async_manager + : public execution_context_service_base +{ +public: + // Constructor. + winrt_async_manager(execution_context& context) + : execution_context_service_base(context), + scheduler_(use_service(context)), + outstanding_ops_(1) + { + } + + // Destructor. + ~winrt_async_manager() + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + if (--outstanding_ops_ > 0) + { + // Block until last operation is complete. + std::future f = promise_.get_future(); + f.wait(); + } + } + + void sync(Windows::Foundation::IAsyncAction^ action, + asio::error_code& ec) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto promise = std::make_shared>(); + auto future = promise->get_future(); + + action->Completed = ref new AsyncActionCompletedHandler( + [promise](IAsyncAction^ action, AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + promise->set_value(asio::error::operation_aborted); + break; + case AsyncStatus::Error: + case AsyncStatus::Completed: + default: + asio::error_code ec( + action->ErrorCode.Value, + asio::system_category()); + promise->set_value(ec); + break; + } + }); + + ec = future.get(); + } + + template + TResult sync(Windows::Foundation::IAsyncOperation^ operation, + asio::error_code& ec) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto promise = std::make_shared>(); + auto future = promise->get_future(); + + operation->Completed = ref new AsyncOperationCompletedHandler( + [promise](IAsyncOperation^ operation, AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + promise->set_value(asio::error::operation_aborted); + break; + case AsyncStatus::Error: + case AsyncStatus::Completed: + default: + asio::error_code ec( + operation->ErrorCode.Value, + asio::system_category()); + promise->set_value(ec); + break; + } + }); + + ec = future.get(); + return operation->GetResults(); + } + + template + TResult sync( + Windows::Foundation::IAsyncOperationWithProgress< + TResult, TProgress>^ operation, + asio::error_code& ec) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto promise = std::make_shared>(); + auto future = promise->get_future(); + + operation->Completed + = ref new AsyncOperationWithProgressCompletedHandler( + [promise](IAsyncOperationWithProgress^ operation, + AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + promise->set_value(asio::error::operation_aborted); + break; + case AsyncStatus::Started: + break; + case AsyncStatus::Error: + case AsyncStatus::Completed: + default: + asio::error_code ec( + operation->ErrorCode.Value, + asio::system_category()); + promise->set_value(ec); + break; + } + }); + + ec = future.get(); + return operation->GetResults(); + } + + void async(Windows::Foundation::IAsyncAction^ action, + winrt_async_op* handler) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto on_completed = ref new AsyncActionCompletedHandler( + [this, handler](IAsyncAction^ action, AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + handler->ec_ = asio::error::operation_aborted; + break; + case AsyncStatus::Started: + return; + case AsyncStatus::Completed: + case AsyncStatus::Error: + default: + handler->ec_ = asio::error_code( + action->ErrorCode.Value, + asio::system_category()); + break; + } + scheduler_.post_deferred_completion(handler); + if (--outstanding_ops_ == 0) + promise_.set_value(); + }); + + scheduler_.work_started(); + ++outstanding_ops_; + action->Completed = on_completed; + } + + template + void async(Windows::Foundation::IAsyncOperation^ operation, + winrt_async_op* handler) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto on_completed = ref new AsyncOperationCompletedHandler( + [this, handler](IAsyncOperation^ operation, AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + handler->ec_ = asio::error::operation_aborted; + break; + case AsyncStatus::Started: + return; + case AsyncStatus::Completed: + handler->result_ = operation->GetResults(); + // Fall through. + case AsyncStatus::Error: + default: + handler->ec_ = asio::error_code( + operation->ErrorCode.Value, + asio::system_category()); + break; + } + scheduler_.post_deferred_completion(handler); + if (--outstanding_ops_ == 0) + promise_.set_value(); + }); + + scheduler_.work_started(); + ++outstanding_ops_; + operation->Completed = on_completed; + } + + template + void async( + Windows::Foundation::IAsyncOperationWithProgress< + TResult, TProgress>^ operation, + winrt_async_op* handler) + { + using namespace Windows::Foundation; + using Windows::Foundation::AsyncStatus; + + auto on_completed + = ref new AsyncOperationWithProgressCompletedHandler( + [this, handler](IAsyncOperationWithProgress< + TResult, TProgress>^ operation, AsyncStatus status) + { + switch (status) + { + case AsyncStatus::Canceled: + handler->ec_ = asio::error::operation_aborted; + break; + case AsyncStatus::Started: + return; + case AsyncStatus::Completed: + handler->result_ = operation->GetResults(); + // Fall through. + case AsyncStatus::Error: + default: + handler->ec_ = asio::error_code( + operation->ErrorCode.Value, + asio::system_category()); + break; + } + scheduler_.post_deferred_completion(handler); + if (--outstanding_ops_ == 0) + promise_.set_value(); + }); + + scheduler_.work_started(); + ++outstanding_ops_; + operation->Completed = on_completed; + } + +private: + // The scheduler implementation used to post completed handlers. +#if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_impl; +#else + typedef class scheduler scheduler_impl; +#endif + scheduler_impl& scheduler_; + + // Count of outstanding operations. + atomic_count outstanding_ops_; + + // Used to keep wait for outstanding operations to complete. + std::promise promise_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_ASYNC_MANAGER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_async_op.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_async_op.hpp new file mode 100644 index 000000000..0751681fa --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_async_op.hpp @@ -0,0 +1,65 @@ +// +// detail/winrt_async_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_ASYNC_OP_HPP +#define ASIO_DETAIL_WINRT_ASYNC_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_async_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + + // The result of the operation, to be passed to the completion handler. + TResult result_; + +protected: + winrt_async_op(func_type complete_func) + : operation(complete_func), + result_() + { + } +}; + +template <> +class winrt_async_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + +protected: + winrt_async_op(func_type complete_func) + : operation(complete_func) + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WINRT_ASYNC_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_resolve_op.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_resolve_op.hpp new file mode 100644 index 000000000..b7cfcb203 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_resolve_op.hpp @@ -0,0 +1,125 @@ +// +// detail/winrt_resolve_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_RESOLVE_OP_HPP +#define ASIO_DETAIL_WINRT_RESOLVE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/ip/basic_resolver_results.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_resolve_op : + public winrt_async_op< + Windows::Foundation::Collections::IVectorView< + Windows::Networking::EndpointPair^>^> +{ +public: + ASIO_DEFINE_HANDLER_PTR(winrt_resolve_op); + + typedef typename Protocol::endpoint endpoint_type; + typedef asio::ip::basic_resolver_query query_type; + typedef asio::ip::basic_resolver_results results_type; + + winrt_resolve_op(const query_type& query, + Handler& handler, const IoExecutor& io_ex) + : winrt_async_op< + Windows::Foundation::Collections::IVectorView< + Windows::Networking::EndpointPair^>^>( + &winrt_resolve_op::do_complete), + query_(query), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code&, std::size_t) + { + // Take ownership of the operation object. + winrt_resolve_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + results_type results = results_type(); + if (!o->ec_) + { + try + { + results = results_type::create(o->result_, o->query_.hints(), + o->query_.host_name(), o->query_.service_name()); + } + catch (Platform::Exception^ e) + { + o->ec_ = asio::error_code(e->HResult, + asio::system_category()); + } + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, results); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + query_type query_; + Handler handler_; + handler_work executor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_RESOLVE_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_resolver_service.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_resolver_service.hpp new file mode 100644 index 000000000..81ecb7400 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_resolver_service.hpp @@ -0,0 +1,212 @@ +// +// detail/winrt_resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_RESOLVER_SERVICE_HPP +#define ASIO_DETAIL_WINRT_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/basic_resolver_results.hpp" +#include "asio/post.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/winrt_async_manager.hpp" +#include "asio/detail/winrt_resolve_op.hpp" +#include "asio/detail/winrt_utils.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else // defined(ASIO_HAS_IOCP) +# include "asio/detail/scheduler.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_resolver_service : + public execution_context_service_base > +{ +public: + // The implementation type of the resolver. A cancellation token is used to + // indicate to the asynchronous operation that the operation has been + // cancelled. + typedef socket_ops::shared_cancel_token_type implementation_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The query type. + typedef asio::ip::basic_resolver_query query_type; + + // The results type. + typedef asio::ip::basic_resolver_results results_type; + + // Constructor. + winrt_resolver_service(execution_context& context) + : execution_context_service_base< + winrt_resolver_service >(context), + scheduler_(use_service(context)), + async_manager_(use_service(context)) + { + } + + // Destructor. + ~winrt_resolver_service() + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + } + + // Perform any fork-related housekeeping. + void notify_fork(execution_context::fork_event) + { + } + + // Construct a new resolver implementation. + void construct(implementation_type&) + { + } + + // Move-construct a new resolver implementation. + void move_construct(implementation_type&, + implementation_type&) + { + } + + // Move-assign from another resolver implementation. + void move_assign(implementation_type&, + winrt_resolver_service&, implementation_type&) + { + } + + // Destroy a resolver implementation. + void destroy(implementation_type&) + { + } + + // Cancel pending asynchronous operations. + void cancel(implementation_type&) + { + } + + // Resolve a query to a list of entries. + results_type resolve(implementation_type&, + const query_type& query, asio::error_code& ec) + { + try + { + using namespace Windows::Networking::Sockets; + auto endpoint_pairs = async_manager_.sync( + DatagramSocket::GetEndpointPairsAsync( + winrt_utils::host_name(query.host_name()), + winrt_utils::string(query.service_name())), ec); + + if (ec) + return results_type(); + + return results_type::create( + endpoint_pairs, query.hints(), + query.host_name(), query.service_name()); + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + return results_type(); + } + } + + // Asynchronously resolve a query to a list of entries. + template + void async_resolve(implementation_type& impl, const query_type& query, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef winrt_resolve_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(query, handler, io_ex); + + ASIO_HANDLER_CREATION((scheduler_.context(), + *p.p, "resolver", &impl, 0, "async_resolve")); + (void)impl; + + try + { + using namespace Windows::Networking::Sockets; + async_manager_.async(DatagramSocket::GetEndpointPairsAsync( + winrt_utils::host_name(query.host_name()), + winrt_utils::string(query.service_name())), p.p); + p.v = p.p = 0; + } + catch (Platform::Exception^ e) + { + p.p->ec_ = asio::error_code( + e->HResult, asio::system_category()); + scheduler_.post_immediate_completion(p.p, is_continuation); + p.v = p.p = 0; + } + } + + // Resolve an endpoint to a list of entries. + results_type resolve(implementation_type&, + const endpoint_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return results_type(); + } + + // Asynchronously resolve an endpoint to a list of entries. + template + void async_resolve(implementation_type&, const endpoint_type&, + Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const results_type results; + asio::post(io_ex, detail::bind_handler(handler, ec, results)); + } + +private: + // The scheduler implementation used for delivering completions. +#if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_impl; +#else + typedef class scheduler scheduler_impl; +#endif + scheduler_impl& scheduler_; + + winrt_async_manager& async_manager_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_RESOLVER_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_socket_connect_op.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_socket_connect_op.hpp new file mode 100644 index 000000000..b0e0fbf29 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_socket_connect_op.hpp @@ -0,0 +1,98 @@ +// +// detail/winrt_socket_connect_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_SOCKET_CONNECT_OP_HPP +#define ASIO_DETAIL_WINRT_SOCKET_CONNECT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_socket_connect_op : + public winrt_async_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(winrt_socket_connect_op); + + winrt_socket_connect_op(Handler& handler, const IoExecutor& io_ex) + : winrt_async_op(&winrt_socket_connect_op::do_complete), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code&, std::size_t) + { + // Take ownership of the operation object. + winrt_socket_connect_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, o->ec_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work executor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_SOCKET_CONNECT_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_socket_recv_op.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_socket_recv_op.hpp new file mode 100644 index 000000000..722578ee9 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_socket_recv_op.hpp @@ -0,0 +1,119 @@ +// +// detail/winrt_socket_recv_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_SOCKET_RECV_OP_HPP +#define ASIO_DETAIL_WINRT_SOCKET_RECV_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_socket_recv_op : + public winrt_async_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(winrt_socket_recv_op); + + winrt_socket_recv_op(const MutableBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + : winrt_async_op( + &winrt_socket_recv_op::do_complete), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code&, std::size_t) + { + // Take ownership of the operation object. + winrt_socket_recv_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + std::size_t bytes_transferred = o->result_ ? o->result_->Length : 0; + if (bytes_transferred == 0 && !o->ec_ && + !buffer_sequence_adapter::all_empty(o->buffers_)) + { + o->ec_ = asio::error::eof; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, bytes_transferred); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + MutableBufferSequence buffers_; + Handler handler_; + handler_work executor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_SOCKET_RECV_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_socket_send_op.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_socket_send_op.hpp new file mode 100644 index 000000000..0f52d9718 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_socket_send_op.hpp @@ -0,0 +1,110 @@ +// +// detail/winrt_socket_send_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_SOCKET_SEND_OP_HPP +#define ASIO_DETAIL_WINRT_SOCKET_SEND_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/handler_work.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/winrt_async_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_socket_send_op : + public winrt_async_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(winrt_socket_send_op); + + winrt_socket_send_op(const ConstBufferSequence& buffers, + Handler& handler, const IoExecutor& io_ex) + : winrt_async_op(&winrt_socket_send_op::do_complete), + buffers_(buffers), + handler_(ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const asio::error_code&, std::size_t) + { + // Take ownership of the operation object. + winrt_socket_send_op* o(static_cast(base)); + ptr p = { asio::detail::addressof(o->handler_), o, o }; + + ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work w( + ASIO_MOVE_CAST2(handler_work)( + o->work_)); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->result_); + p.h = asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + w.complete(handler, handler.handler_); + ASIO_HANDLER_INVOCATION_END; + } + } + +private: + ConstBufferSequence buffers_; + Handler handler_; + handler_work executor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_SOCKET_SEND_OP_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_ssocket_service.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_ssocket_service.hpp new file mode 100644 index 000000000..b7727ff2d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_ssocket_service.hpp @@ -0,0 +1,250 @@ +// +// detail/winrt_ssocket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_SSOCKET_SERVICE_HPP +#define ASIO_DETAIL_WINRT_SSOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/winrt_socket_connect_op.hpp" +#include "asio/detail/winrt_ssocket_service_base.hpp" +#include "asio/detail/winrt_utils.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class winrt_ssocket_service : + public execution_context_service_base >, + public winrt_ssocket_service_base +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef Windows::Networking::Sockets::StreamSocket^ native_handle_type; + + // The implementation type of the socket. + struct implementation_type : base_implementation_type + { + // Default constructor. + implementation_type() + : base_implementation_type(), + protocol_(endpoint_type().protocol()) + { + } + + // The protocol associated with the socket. + protocol_type protocol_; + }; + + // Constructor. + winrt_ssocket_service(execution_context& context) + : execution_context_service_base >(context), + winrt_ssocket_service_base(context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + this->base_shutdown(); + } + + // Move-construct a new socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) ASIO_NOEXCEPT + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-assign from another socket implementation. + void move_assign(implementation_type& impl, + winrt_ssocket_service& other_service, + implementation_type& other_impl) + { + this->base_move_assign(impl, other_service, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-construct a new socket implementation from another protocol type. + template + void converting_move_construct(implementation_type& impl, + winrt_ssocket_service&, + typename winrt_ssocket_service< + Protocol1>::implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = protocol_type(other_impl.protocol_); + other_impl.protocol_ = typename Protocol1::endpoint().protocol(); + } + + // Open a new socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + try + { + impl.socket_ = ref new Windows::Networking::Sockets::StreamSocket; + impl.protocol_ = protocol; + ec = asio::error_code(); + } + catch (Platform::Exception^ e) + { + ec = asio::error_code(e->HResult, + asio::system_category()); + } + + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + impl.socket_ = native_socket; + impl.protocol_ = protocol; + ec = asio::error_code(); + + return ec; + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type&, + const endpoint_type&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint; + endpoint.resize(do_get_endpoint(impl, true, + endpoint.data(), endpoint.size(), ec)); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + endpoint_type endpoint; + endpoint.resize(do_get_endpoint(impl, false, + endpoint.data(), endpoint.size(), ec)); + return endpoint; + } + + // Disable sends or receives on the socket. + asio::error_code shutdown(implementation_type&, + socket_base::shutdown_type, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const Option& option, asio::error_code& ec) + { + return do_set_option(impl, option.level(impl.protocol_), + option.name(impl.protocol_), option.data(impl.protocol_), + option.size(impl.protocol_), ec); + } + + // Get a socket option. + template + asio::error_code get_option(const implementation_type& impl, + Option& option, asio::error_code& ec) const + { + std::size_t size = option.size(impl.protocol_); + do_get_option(impl, option.level(impl.protocol_), + option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + + // Connect the socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + return do_connect(impl, peer_endpoint.data(), ec); + } + + // Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef winrt_socket_connect_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(handler, io_ex); + + ASIO_HANDLER_CREATION((scheduler_.context(), + *p.p, "socket", &impl, 0, "async_connect")); + + start_connect_op(impl, peer_endpoint.data(), p.p, is_continuation); + p.v = p.p = 0; + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_SSOCKET_SERVICE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_ssocket_service_base.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_ssocket_service_base.hpp new file mode 100644 index 000000000..6668975e5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_ssocket_service_base.hpp @@ -0,0 +1,362 @@ +// +// detail/winrt_ssocket_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_SSOCKET_SERVICE_BASE_HPP +#define ASIO_DETAIL_WINRT_SSOCKET_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/winrt_async_manager.hpp" +#include "asio/detail/winrt_socket_recv_op.hpp" +#include "asio/detail/winrt_socket_send_op.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else // defined(ASIO_HAS_IOCP) +# include "asio/detail/scheduler.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class winrt_ssocket_service_base +{ +public: + // The native type of a socket. + typedef Windows::Networking::Sockets::StreamSocket^ native_handle_type; + + // The implementation type of the socket. + struct base_implementation_type + { + // Default constructor. + base_implementation_type() + : socket_(nullptr), + next_(0), + prev_(0) + { + } + + // The underlying native socket. + native_handle_type socket_; + + // Pointers to adjacent socket implementations in linked list. + base_implementation_type* next_; + base_implementation_type* prev_; + }; + + // Constructor. + ASIO_DECL winrt_ssocket_service_base(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void base_shutdown(); + + // Construct a new socket implementation. + ASIO_DECL void construct(base_implementation_type&); + + // Move-construct a new socket implementation. + ASIO_DECL void base_move_construct(base_implementation_type& impl, + base_implementation_type& other_impl) ASIO_NOEXCEPT; + + // Move-assign from another socket implementation. + ASIO_DECL void base_move_assign(base_implementation_type& impl, + winrt_ssocket_service_base& other_service, + base_implementation_type& other_impl); + + // Destroy a socket implementation. + ASIO_DECL void destroy(base_implementation_type& impl); + + // Determine whether the socket is open. + bool is_open(const base_implementation_type& impl) const + { + return impl.socket_ != nullptr; + } + + // Destroy a socket implementation. + ASIO_DECL asio::error_code close( + base_implementation_type& impl, asio::error_code& ec); + + // Release ownership of the socket. + ASIO_DECL native_handle_type release( + base_implementation_type& impl, asio::error_code& ec); + + // Get the native socket representation. + native_handle_type native_handle(base_implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + asio::error_code cancel(base_implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const base_implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return false; + } + + // Determine the number of bytes available for reading. + std::size_t available(const base_implementation_type&, + asio::error_code& ec) const + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(base_implementation_type&, + IO_Control_Command&, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Gets the non-blocking mode of the socket. + bool non_blocking(const base_implementation_type&) const + { + return false; + } + + // Sets the non-blocking mode of the socket. + asio::error_code non_blocking(base_implementation_type&, + bool, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const base_implementation_type&) const + { + return false; + } + + // Sets the non-blocking mode of the native socket implementation. + asio::error_code native_non_blocking(base_implementation_type&, + bool, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Send the given data to the peer. + template + std::size_t send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return do_send(impl, + buffer_sequence_adapter::first(buffers), flags, ec); + } + + // Wait until data can be sent without blocking. + std::size_t send(base_implementation_type&, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(base_implementation_type& impl, + const ConstBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef winrt_socket_send_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((scheduler_.context(), + *p.p, "socket", &impl, 0, "async_send")); + + start_send_op(impl, + buffer_sequence_adapter::first(buffers), + flags, p.p, is_continuation); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(base_implementation_type&, const null_buffers&, + socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, + detail::bind_handler(handler, ec, bytes_transferred)); + } + + // Receive some data from the peer. Returns the number of bytes received. + template + std::size_t receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return do_receive(impl, + buffer_sequence_adapter::first(buffers), flags, ec); + } + + // Wait until data can be received without blocking. + std::size_t receive(base_implementation_type&, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef winrt_socket_recv_op op; + typename op::ptr p = { asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(buffers, handler, io_ex); + + ASIO_HANDLER_CREATION((scheduler_.context(), + *p.p, "socket", &impl, 0, "async_receive")); + + start_receive_op(impl, + buffer_sequence_adapter::first(buffers), + flags, p.p, is_continuation); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive(base_implementation_type&, const null_buffers&, + socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) + { + asio::error_code ec = asio::error::operation_not_supported; + const std::size_t bytes_transferred = 0; + asio::post(io_ex, + detail::bind_handler(handler, ec, bytes_transferred)); + } + +protected: + // Helper function to obtain endpoints associated with the connection. + ASIO_DECL std::size_t do_get_endpoint( + const base_implementation_type& impl, bool local, + void* addr, std::size_t addr_len, asio::error_code& ec) const; + + // Helper function to set a socket option. + ASIO_DECL asio::error_code do_set_option( + base_implementation_type& impl, + int level, int optname, const void* optval, + std::size_t optlen, asio::error_code& ec); + + // Helper function to get a socket option. + ASIO_DECL void do_get_option( + const base_implementation_type& impl, + int level, int optname, void* optval, + std::size_t* optlen, asio::error_code& ec) const; + + // Helper function to perform a synchronous connect. + ASIO_DECL asio::error_code do_connect( + base_implementation_type& impl, + const void* addr, asio::error_code& ec); + + // Helper function to start an asynchronous connect. + ASIO_DECL void start_connect_op( + base_implementation_type& impl, const void* addr, + winrt_async_op* op, bool is_continuation); + + // Helper function to perform a synchronous send. + ASIO_DECL std::size_t do_send( + base_implementation_type& impl, const asio::const_buffer& data, + socket_base::message_flags flags, asio::error_code& ec); + + // Helper function to start an asynchronous send. + ASIO_DECL void start_send_op(base_implementation_type& impl, + const asio::const_buffer& data, socket_base::message_flags flags, + winrt_async_op* op, bool is_continuation); + + // Helper function to perform a synchronous receive. + ASIO_DECL std::size_t do_receive( + base_implementation_type& impl, const asio::mutable_buffer& data, + socket_base::message_flags flags, asio::error_code& ec); + + // Helper function to start an asynchronous receive. + ASIO_DECL void start_receive_op(base_implementation_type& impl, + const asio::mutable_buffer& data, socket_base::message_flags flags, + winrt_async_op* op, + bool is_continuation); + + // The scheduler implementation used for delivering completions. +#if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_impl; +#else + typedef class scheduler scheduler_impl; +#endif + scheduler_impl& scheduler_; + + // The manager that keeps track of outstanding operations. + winrt_async_manager& async_manager_; + + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + base_implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/winrt_ssocket_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_SSOCKET_SERVICE_BASE_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_timer_scheduler.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_timer_scheduler.hpp new file mode 100644 index 000000000..8fd9636bf --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_timer_scheduler.hpp @@ -0,0 +1,147 @@ +// +// detail/winrt_timer_scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_TIMER_SCHEDULER_HPP +#define ASIO_DETAIL_WINRT_TIMER_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include +#include "asio/detail/event.hpp" +#include "asio/detail/limits.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/wait_op.hpp" +#include "asio/execution_context.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_context.hpp" +#else // defined(ASIO_HAS_IOCP) +# include "asio/detail/scheduler.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/thread.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class winrt_timer_scheduler + : public execution_context_service_base +{ +public: + // Constructor. + ASIO_DECL winrt_timer_scheduler(execution_context& context); + + // Destructor. + ASIO_DECL ~winrt_timer_scheduler(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown(); + + // Recreate internal descriptors following a fork. + ASIO_DECL void notify_fork(execution_context::fork_event fork_ev); + + // Initialise the task. No effect as this class uses its own thread. + ASIO_DECL void init_task(); + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& queue); + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& queue); + + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, wait_op* op); + + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer, + std::size_t max_cancelled = (std::numeric_limits::max)()); + + // Move the timer operations associated with the given timer. + template + void move_timer(timer_queue& queue, + typename timer_queue::per_timer_data& to, + typename timer_queue::per_timer_data& from); + +private: + // Run the select loop in the thread. + ASIO_DECL void run_thread(); + + // Entry point for the select loop thread. + ASIO_DECL static void call_run_thread(winrt_timer_scheduler* reactor); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // The scheduler implementation used to post completions. +#if defined(ASIO_HAS_IOCP) + typedef class win_iocp_io_context scheduler_impl; +#else + typedef class scheduler scheduler_impl; +#endif + scheduler_impl& scheduler_; + + // Mutex used to protect internal variables. + asio::detail::mutex mutex_; + + // Event used to wake up background thread. + asio::detail::event event_; + + // The timer queues. + timer_queue_set timer_queues_; + + // The background thread that is waiting for timers to expire. + asio::detail::thread* thread_; + + // Does the background thread need to stop. + bool stop_thread_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/impl/winrt_timer_scheduler.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/winrt_timer_scheduler.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_TIMER_SCHEDULER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winrt_utils.hpp b/third_party/asio/1.18.2/include/asio/detail/winrt_utils.hpp new file mode 100644 index 000000000..d0e184098 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winrt_utils.hpp @@ -0,0 +1,106 @@ +// +// detail/winrt_utils.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINRT_UTILS_HPP +#define ASIO_DETAIL_WINRT_UTILS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS_RUNTIME) + +#include +#include +#include +#include +#include +#include +#include +#include "asio/buffer.hpp" +#include "asio/error_code.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace winrt_utils { + +inline Platform::String^ string(const char* from) +{ + std::wstring tmp(from, from + std::strlen(from)); + return ref new Platform::String(tmp.c_str()); +} + +inline Platform::String^ string(const std::string& from) +{ + std::wstring tmp(from.begin(), from.end()); + return ref new Platform::String(tmp.c_str()); +} + +inline std::string string(Platform::String^ from) +{ + std::wstring_convert> converter; + return converter.to_bytes(from->Data()); +} + +inline Platform::String^ string(unsigned short from) +{ + return string(std::to_string(from)); +} + +template +inline Platform::String^ string(const T& from) +{ + return string(from.to_string()); +} + +inline int integer(Platform::String^ from) +{ + return _wtoi(from->Data()); +} + +template +inline Windows::Networking::HostName^ host_name(const T& from) +{ + return ref new Windows::Networking::HostName((string)(from)); +} + +template +inline Windows::Storage::Streams::IBuffer^ buffer_dup( + const ConstBufferSequence& buffers) +{ + using Microsoft::WRL::ComPtr; + using asio::buffer_size; + std::size_t size = buffer_size(buffers); + auto b = ref new Windows::Storage::Streams::Buffer(size); + ComPtr insp = reinterpret_cast(b); + ComPtr bacc; + insp.As(&bacc); + byte* bytes = nullptr; + bacc->Buffer(&bytes); + asio::buffer_copy(asio::buffer(bytes, size), buffers); + b->Length = size; + return b; +} + +} // namespace winrt_utils +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_WINDOWS_RUNTIME) + +#endif // ASIO_DETAIL_WINRT_UTILS_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/winsock_init.hpp b/third_party/asio/1.18.2/include/asio/detail/winsock_init.hpp new file mode 100644 index 000000000..a9e361a1c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/winsock_init.hpp @@ -0,0 +1,128 @@ +// +// detail/winsock_init.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINSOCK_INIT_HPP +#define ASIO_DETAIL_WINSOCK_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class winsock_init_base +{ +protected: + // Structure to track result of initialisation and number of uses. POD is used + // to ensure that the values are zero-initialised prior to any code being run. + struct data + { + long init_count_; + long result_; + }; + + ASIO_DECL static void startup(data& d, + unsigned char major, unsigned char minor); + + ASIO_DECL static void manual_startup(data& d); + + ASIO_DECL static void cleanup(data& d); + + ASIO_DECL static void manual_cleanup(data& d); + + ASIO_DECL static void throw_on_error(data& d); +}; + +template +class winsock_init : private winsock_init_base +{ +public: + winsock_init(bool allow_throw = true) + { + startup(data_, Major, Minor); + if (allow_throw) + throw_on_error(data_); + } + + winsock_init(const winsock_init&) + { + startup(data_, Major, Minor); + throw_on_error(data_); + } + + ~winsock_init() + { + cleanup(data_); + } + + // This class may be used to indicate that user code will manage Winsock + // initialisation and cleanup. This may be required in the case of a DLL, for + // example, where it is not safe to initialise Winsock from global object + // constructors. + // + // To prevent asio from initialising Winsock, the object must be constructed + // before any Asio's own global objects. With MSVC, this may be accomplished + // by adding the following code to the DLL: + // + // #pragma warning(push) + // #pragma warning(disable:4073) + // #pragma init_seg(lib) + // asio::detail::winsock_init<>::manual manual_winsock_init; + // #pragma warning(pop) + class manual + { + public: + manual() + { + manual_startup(data_); + } + + manual(const manual&) + { + manual_startup(data_); + } + + ~manual() + { + manual_cleanup(data_); + } + }; + +private: + friend class manual; + static data data_; +}; + +template +winsock_init_base::data winsock_init::data_; + +// Static variable to ensure that winsock is initialised before main, and +// therefore before any other threads can get started. +static const winsock_init<>& winsock_init_instance = winsock_init<>(false); + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/winsock_init.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_WINSOCK_INIT_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/work_dispatcher.hpp b/third_party/asio/1.18.2/include/asio/detail/work_dispatcher.hpp new file mode 100644 index 000000000..836605f54 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/work_dispatcher.hpp @@ -0,0 +1,148 @@ +// +// detail/work_dispatcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WORK_DISPATCHER_HPP +#define ASIO_DETAIL_WORK_DISPATCHER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/associated_executor.hpp" +#include "asio/associated_allocator.hpp" +#include "asio/executor_work_guard.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/allocator.hpp" +#include "asio/execution/blocking.hpp" +#include "asio/execution/outstanding_work.hpp" +#include "asio/prefer.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct is_work_dispatcher_required : true_type +{ +}; + +template +struct is_work_dispatcher_required::asio_associated_executor_is_unspecialised, + void + >::value + >::type> : false_type +{ +}; + +template +class work_dispatcher +{ +public: + template + work_dispatcher(ASIO_MOVE_ARG(CompletionHandler) handler, + const Executor& handler_ex) + : handler_(ASIO_MOVE_CAST(CompletionHandler)(handler)), + executor_(asio::prefer(handler_ex, + execution::outstanding_work.tracked)) + { + } + +#if defined(ASIO_HAS_MOVE) + work_dispatcher(const work_dispatcher& other) + : handler_(other.handler_), + executor_(other.executor_) + { + } + + work_dispatcher(work_dispatcher&& other) + : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), + executor_(ASIO_MOVE_CAST(work_executor_type)(other.executor_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + execution::execute( + asio::prefer(executor_, + execution::blocking.possibly, + execution::allocator((get_associated_allocator)(handler_))), + ASIO_MOVE_CAST(Handler)(handler_)); + } + +private: + typedef typename decay< + typename prefer_result::type + >::type work_executor_type; + + Handler handler_; + work_executor_type executor_; +}; + +#if !defined(ASIO_NO_TS_EXECUTORS) + +template +class work_dispatcher::value>::type> +{ +public: + template + work_dispatcher(ASIO_MOVE_ARG(CompletionHandler) handler, + const Executor& handler_ex) + : work_(handler_ex), + handler_(ASIO_MOVE_CAST(CompletionHandler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + work_dispatcher(const work_dispatcher& other) + : work_(other.work_), + handler_(other.handler_) + { + } + + work_dispatcher(work_dispatcher&& other) + : work_(ASIO_MOVE_CAST(executor_work_guard)(other.work_)), + handler_(ASIO_MOVE_CAST(Handler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + typename associated_allocator::type alloc( + (get_associated_allocator)(handler_)); + work_.get_executor().dispatch( + ASIO_MOVE_CAST(Handler)(handler_), alloc); + work_.reset(); + } + +private: + executor_work_guard work_; + Handler handler_; +}; + +#endif // !defined(ASIO_NO_TS_EXECUTORS) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WORK_DISPATCHER_HPP diff --git a/third_party/asio/1.18.2/include/asio/detail/wrapped_handler.hpp b/third_party/asio/1.18.2/include/asio/detail/wrapped_handler.hpp new file mode 100644 index 000000000..18ca6d3b9 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/detail/wrapped_handler.hpp @@ -0,0 +1,327 @@ +// +// detail/wrapped_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WRAPPED_HANDLER_HPP +#define ASIO_DETAIL_WRAPPED_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_cont_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct is_continuation_delegated +{ + template + bool operator()(Dispatcher&, Handler& handler) const + { + return asio_handler_cont_helpers::is_continuation(handler); + } +}; + +struct is_continuation_if_running +{ + template + bool operator()(Dispatcher& dispatcher, Handler&) const + { + return dispatcher.running_in_this_thread(); + } +}; + +template +class wrapped_handler +{ +public: + typedef void result_type; + + wrapped_handler(Dispatcher dispatcher, Handler& handler) + : dispatcher_(dispatcher), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + } + +#if defined(ASIO_HAS_MOVE) + wrapped_handler(const wrapped_handler& other) + : dispatcher_(other.dispatcher_), + handler_(other.handler_) + { + } + + wrapped_handler(wrapped_handler&& other) + : dispatcher_(other.dispatcher_), + handler_(ASIO_MOVE_CAST(Handler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + dispatcher_.dispatch(ASIO_MOVE_CAST(Handler)(handler_)); + } + + void operator()() const + { + dispatcher_.dispatch(handler_); + } + + template + void operator()(const Arg1& arg1) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template + void operator()(const Arg1& arg1) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4, const Arg5& arg5) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4, const Arg5& arg5) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + +//private: + Dispatcher dispatcher_; + Handler handler_; +}; + +template +class rewrapped_handler +{ +public: + explicit rewrapped_handler(Handler& handler, const Context& context) + : context_(context), + handler_(ASIO_MOVE_CAST(Handler)(handler)) + { + } + + explicit rewrapped_handler(const Handler& handler, const Context& context) + : context_(context), + handler_(handler) + { + } + +#if defined(ASIO_HAS_MOVE) + rewrapped_handler(const rewrapped_handler& other) + : context_(other.context_), + handler_(other.handler_) + { + } + + rewrapped_handler(rewrapped_handler&& other) + : context_(ASIO_MOVE_CAST(Context)(other.context_)), + handler_(ASIO_MOVE_CAST(Handler)(other.handler_)) + { + } +#endif // defined(ASIO_HAS_MOVE) + + void operator()() + { + handler_(); + } + + void operator()() const + { + handler_(); + } + +//private: + Context context_; + Handler handler_; +}; + +template +inline asio_handler_allocate_is_deprecated +asio_handler_allocate(std::size_t size, + wrapped_handler* this_handler) +{ +#if defined(ASIO_NO_DEPRECATED) + asio_handler_alloc_helpers::allocate(size, this_handler->handler_); + return asio_handler_allocate_is_no_longer_used(); +#else // defined(ASIO_NO_DEPRECATED) + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_deallocate_is_deprecated +asio_handler_deallocate(void* pointer, std::size_t size, + wrapped_handler* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_deallocate_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline bool asio_handler_is_continuation( + wrapped_handler* this_handler) +{ + return IsContinuation()(this_handler->dispatcher_, this_handler->handler_); +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(Function& function, + wrapped_handler* this_handler) +{ + this_handler->dispatcher_.dispatch( + rewrapped_handler( + function, this_handler->handler_)); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(const Function& function, + wrapped_handler* this_handler) +{ + this_handler->dispatcher_.dispatch( + rewrapped_handler( + function, this_handler->handler_)); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_allocate_is_deprecated +asio_handler_allocate(std::size_t size, + rewrapped_handler* this_handler) +{ +#if defined(ASIO_NO_DEPRECATED) + asio_handler_alloc_helpers::allocate(size, this_handler->handler_); + return asio_handler_allocate_is_no_longer_used(); +#else // defined(ASIO_NO_DEPRECATED) + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_deallocate_is_deprecated +asio_handler_deallocate(void* pointer, std::size_t size, + rewrapped_handler* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->context_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_deallocate_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline bool asio_handler_is_continuation( + rewrapped_handler* this_handler) +{ + return asio_handler_cont_helpers::is_continuation( + this_handler->context_); +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(Function& function, + rewrapped_handler* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->context_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +template +inline asio_handler_invoke_is_deprecated +asio_handler_invoke(const Function& function, + rewrapped_handler* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->context_); +#if defined(ASIO_NO_DEPRECATED) + return asio_handler_invoke_is_no_longer_used(); +#endif // defined(ASIO_NO_DEPRECATED) +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WRAPPED_HANDLER_HPP diff --git a/third_party/asio/1.18.2/include/asio/dispatch.hpp b/third_party/asio/1.18.2/include/asio/dispatch.hpp new file mode 100644 index 000000000..64da41b7c --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/dispatch.hpp @@ -0,0 +1,121 @@ +// +// dispatch.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DISPATCH_HPP +#define ASIO_DISPATCH_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution_context.hpp" +#include "asio/execution/executor.hpp" +#include "asio/is_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Submits a completion token or function object for execution. +/** + * This function submits an object for execution using the object's associated + * executor. The function object may be called from the current thread prior to + * returning from dispatch(). Otherwise, it is queued for execution. + * + * This function has the following effects: + * + * @li Constructs a function object handler of type @c Handler, initialized + * with handler(forward(token)). + * + * @li Constructs an object @c result of type async_result, + * initializing the object as result(handler). + * + * @li Obtains the handler's associated executor object @c ex by performing + * get_associated_executor(handler). + * + * @li Obtains the handler's associated allocator object @c alloc by performing + * get_associated_allocator(handler). + * + * @li Performs ex.dispatch(std::move(handler), alloc). + * + * @li Returns result.get(). + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void()) dispatch( + ASIO_MOVE_ARG(CompletionToken) token); + +/// Submits a completion token or function object for execution. +/** + * This function submits an object for execution using the specified executor. + * The function object may be called from the current thread prior to returning + * from dispatch(). Otherwise, it is queued for execution. + * + * This function has the following effects: + * + * @li Constructs a function object handler of type @c Handler, initialized + * with handler(forward(token)). + * + * @li Constructs an object @c result of type async_result, + * initializing the object as result(handler). + * + * @li Obtains the handler's associated executor object @c ex1 by performing + * get_associated_executor(handler). + * + * @li Creates a work object @c w by performing make_work(ex1). + * + * @li Obtains the handler's associated allocator object @c alloc by performing + * get_associated_allocator(handler). + * + * @li Constructs a function object @c f with a function call operator that + * performs ex1.dispatch(std::move(handler), alloc) followed by + * w.reset(). + * + * @li Performs Executor(ex).dispatch(std::move(f), alloc). + * + * @li Returns result.get(). + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void()) dispatch( + const Executor& ex, + ASIO_MOVE_ARG(CompletionToken) token + ASIO_DEFAULT_COMPLETION_TOKEN(Executor), + typename constraint< + execution::is_executor::value || is_executor::value + >::type = 0); + +/// Submits a completion token or function object for execution. +/** + * @returns dispatch(ctx.get_executor(), + * forward(token)). + */ +template +ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void()) dispatch( + ExecutionContext& ctx, + ASIO_MOVE_ARG(CompletionToken) token + ASIO_DEFAULT_COMPLETION_TOKEN( + typename ExecutionContext::executor_type), + typename constraint::value>::type = 0); + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/dispatch.hpp" + +#endif // ASIO_DISPATCH_HPP diff --git a/third_party/asio/1.18.2/include/asio/error.hpp b/third_party/asio/1.18.2/include/asio/error.hpp new file mode 100644 index 000000000..8b4decd52 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/error.hpp @@ -0,0 +1,356 @@ +// +// error.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ERROR_HPP +#define ASIO_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error_code.hpp" +#include "asio/system_error.hpp" +#if defined(ASIO_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(ASIO_WINDOWS_RUNTIME) +# include +#else +# include +# include +#endif + +#if defined(GENERATING_DOCUMENTATION) +/// INTERNAL ONLY. +# define ASIO_NATIVE_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_SOCKET_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_NETDB_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_GETADDRINFO_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_WIN_OR_POSIX(e_win, e_posix) implementation_defined +#elif defined(ASIO_WINDOWS_RUNTIME) +# define ASIO_NATIVE_ERROR(e) __HRESULT_FROM_WIN32(e) +# define ASIO_SOCKET_ERROR(e) __HRESULT_FROM_WIN32(WSA ## e) +# define ASIO_NETDB_ERROR(e) __HRESULT_FROM_WIN32(WSA ## e) +# define ASIO_GETADDRINFO_ERROR(e) __HRESULT_FROM_WIN32(WSA ## e) +# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_win +#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) +# define ASIO_NATIVE_ERROR(e) e +# define ASIO_SOCKET_ERROR(e) WSA ## e +# define ASIO_NETDB_ERROR(e) WSA ## e +# define ASIO_GETADDRINFO_ERROR(e) WSA ## e +# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_win +#else +# define ASIO_NATIVE_ERROR(e) e +# define ASIO_SOCKET_ERROR(e) e +# define ASIO_NETDB_ERROR(e) e +# define ASIO_GETADDRINFO_ERROR(e) e +# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_posix +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace error { + +enum basic_errors +{ + /// Permission denied. + access_denied = ASIO_SOCKET_ERROR(EACCES), + + /// Address family not supported by protocol. + address_family_not_supported = ASIO_SOCKET_ERROR(EAFNOSUPPORT), + + /// Address already in use. + address_in_use = ASIO_SOCKET_ERROR(EADDRINUSE), + + /// Transport endpoint is already connected. + already_connected = ASIO_SOCKET_ERROR(EISCONN), + + /// Operation already in progress. + already_started = ASIO_SOCKET_ERROR(EALREADY), + + /// Broken pipe. + broken_pipe = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_BROKEN_PIPE), + ASIO_NATIVE_ERROR(EPIPE)), + + /// A connection has been aborted. + connection_aborted = ASIO_SOCKET_ERROR(ECONNABORTED), + + /// Connection refused. + connection_refused = ASIO_SOCKET_ERROR(ECONNREFUSED), + + /// Connection reset by peer. + connection_reset = ASIO_SOCKET_ERROR(ECONNRESET), + + /// Bad file descriptor. + bad_descriptor = ASIO_SOCKET_ERROR(EBADF), + + /// Bad address. + fault = ASIO_SOCKET_ERROR(EFAULT), + + /// No route to host. + host_unreachable = ASIO_SOCKET_ERROR(EHOSTUNREACH), + + /// Operation now in progress. + in_progress = ASIO_SOCKET_ERROR(EINPROGRESS), + + /// Interrupted system call. + interrupted = ASIO_SOCKET_ERROR(EINTR), + + /// Invalid argument. + invalid_argument = ASIO_SOCKET_ERROR(EINVAL), + + /// Message too long. + message_size = ASIO_SOCKET_ERROR(EMSGSIZE), + + /// The name was too long. + name_too_long = ASIO_SOCKET_ERROR(ENAMETOOLONG), + + /// Network is down. + network_down = ASIO_SOCKET_ERROR(ENETDOWN), + + /// Network dropped connection on reset. + network_reset = ASIO_SOCKET_ERROR(ENETRESET), + + /// Network is unreachable. + network_unreachable = ASIO_SOCKET_ERROR(ENETUNREACH), + + /// Too many open files. + no_descriptors = ASIO_SOCKET_ERROR(EMFILE), + + /// No buffer space available. + no_buffer_space = ASIO_SOCKET_ERROR(ENOBUFS), + + /// Cannot allocate memory. + no_memory = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_OUTOFMEMORY), + ASIO_NATIVE_ERROR(ENOMEM)), + + /// Operation not permitted. + no_permission = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_ACCESS_DENIED), + ASIO_NATIVE_ERROR(EPERM)), + + /// Protocol not available. + no_protocol_option = ASIO_SOCKET_ERROR(ENOPROTOOPT), + + /// No such device. + no_such_device = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_BAD_UNIT), + ASIO_NATIVE_ERROR(ENODEV)), + + /// Transport endpoint is not connected. + not_connected = ASIO_SOCKET_ERROR(ENOTCONN), + + /// Socket operation on non-socket. + not_socket = ASIO_SOCKET_ERROR(ENOTSOCK), + + /// Operation cancelled. + operation_aborted = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_OPERATION_ABORTED), + ASIO_NATIVE_ERROR(ECANCELED)), + + /// Operation not supported. + operation_not_supported = ASIO_SOCKET_ERROR(EOPNOTSUPP), + + /// Cannot send after transport endpoint shutdown. + shut_down = ASIO_SOCKET_ERROR(ESHUTDOWN), + + /// Connection timed out. + timed_out = ASIO_SOCKET_ERROR(ETIMEDOUT), + + /// Resource temporarily unavailable. + try_again = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_RETRY), + ASIO_NATIVE_ERROR(EAGAIN)), + + /// The socket is marked non-blocking and the requested operation would block. + would_block = ASIO_SOCKET_ERROR(EWOULDBLOCK) +}; + +enum netdb_errors +{ + /// Host not found (authoritative). + host_not_found = ASIO_NETDB_ERROR(HOST_NOT_FOUND), + + /// Host not found (non-authoritative). + host_not_found_try_again = ASIO_NETDB_ERROR(TRY_AGAIN), + + /// The query is valid but does not have associated address data. + no_data = ASIO_NETDB_ERROR(NO_DATA), + + /// A non-recoverable error occurred. + no_recovery = ASIO_NETDB_ERROR(NO_RECOVERY) +}; + +enum addrinfo_errors +{ + /// The service is not supported for the given socket type. + service_not_found = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(WSATYPE_NOT_FOUND), + ASIO_GETADDRINFO_ERROR(EAI_SERVICE)), + + /// The socket type is not supported. + socket_type_not_supported = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(WSAESOCKTNOSUPPORT), + ASIO_GETADDRINFO_ERROR(EAI_SOCKTYPE)) +}; + +enum misc_errors +{ + /// Already open. + already_open = 1, + + /// End of file or stream. + eof, + + /// Element not found. + not_found, + + /// The descriptor cannot fit into the select system call's fd_set. + fd_set_failure +}; + +inline const asio::error_category& get_system_category() +{ + return asio::system_category(); +} + +#if !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +extern ASIO_DECL +const asio::error_category& get_netdb_category(); + +extern ASIO_DECL +const asio::error_category& get_addrinfo_category(); + +#else // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +inline const asio::error_category& get_netdb_category() +{ + return get_system_category(); +} + +inline const asio::error_category& get_addrinfo_category() +{ + return get_system_category(); +} + +#endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__) + +extern ASIO_DECL +const asio::error_category& get_misc_category(); + +static const asio::error_category& + system_category ASIO_UNUSED_VARIABLE + = asio::error::get_system_category(); +static const asio::error_category& + netdb_category ASIO_UNUSED_VARIABLE + = asio::error::get_netdb_category(); +static const asio::error_category& + addrinfo_category ASIO_UNUSED_VARIABLE + = asio::error::get_addrinfo_category(); +static const asio::error_category& + misc_category ASIO_UNUSED_VARIABLE + = asio::error::get_misc_category(); + +} // namespace error +} // namespace asio + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) +namespace std { + +template<> struct is_error_code_enum +{ + static const bool value = true; +}; + +template<> struct is_error_code_enum +{ + static const bool value = true; +}; + +template<> struct is_error_code_enum +{ + static const bool value = true; +}; + +template<> struct is_error_code_enum +{ + static const bool value = true; +}; + +} // namespace std +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +namespace asio { +namespace error { + +inline asio::error_code make_error_code(basic_errors e) +{ + return asio::error_code( + static_cast(e), get_system_category()); +} + +inline asio::error_code make_error_code(netdb_errors e) +{ + return asio::error_code( + static_cast(e), get_netdb_category()); +} + +inline asio::error_code make_error_code(addrinfo_errors e) +{ + return asio::error_code( + static_cast(e), get_addrinfo_category()); +} + +inline asio::error_code make_error_code(misc_errors e) +{ + return asio::error_code( + static_cast(e), get_misc_category()); +} + +} // namespace error +namespace stream_errc { + // Simulates the proposed stream_errc scoped enum. + using error::eof; + using error::not_found; +} // namespace stream_errc +namespace socket_errc { + // Simulates the proposed socket_errc scoped enum. + using error::already_open; + using error::not_found; +} // namespace socket_errc +namespace resolver_errc { + // Simulates the proposed resolver_errc scoped enum. + using error::host_not_found; + const error::netdb_errors try_again = error::host_not_found_try_again; + using error::service_not_found; +} // namespace resolver_errc +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#undef ASIO_NATIVE_ERROR +#undef ASIO_SOCKET_ERROR +#undef ASIO_NETDB_ERROR +#undef ASIO_GETADDRINFO_ERROR +#undef ASIO_WIN_OR_POSIX + +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/error.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_ERROR_HPP diff --git a/third_party/asio/1.18.2/include/asio/error_code.hpp b/third_party/asio/1.18.2/include/asio/error_code.hpp new file mode 100644 index 000000000..4026b9ca5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/error_code.hpp @@ -0,0 +1,202 @@ +// +// error_code.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ERROR_CODE_HPP +#define ASIO_ERROR_CODE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) +# include +#else // defined(ASIO_HAS_STD_SYSTEM_ERROR) +# include +# include "asio/detail/noncopyable.hpp" +# if !defined(ASIO_NO_IOSTREAM) +# include +# endif // !defined(ASIO_NO_IOSTREAM) +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) + +typedef std::error_category error_category; + +#else // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +/// Base class for all error categories. +class error_category : private noncopyable +{ +public: + /// Destructor. + virtual ~error_category() + { + } + + /// Returns a string naming the error gategory. + virtual const char* name() const = 0; + + /// Returns a string describing the error denoted by @c value. + virtual std::string message(int value) const = 0; + + /// Equality operator to compare two error categories. + bool operator==(const error_category& rhs) const + { + return this == &rhs; + } + + /// Inequality operator to compare two error categories. + bool operator!=(const error_category& rhs) const + { + return !(*this == rhs); + } +}; + +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +/// Returns the error category used for the system errors produced by asio. +extern ASIO_DECL const error_category& system_category(); + +#if defined(ASIO_HAS_STD_SYSTEM_ERROR) + +typedef std::error_code error_code; + +#else // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +/// Class to represent an error code value. +class error_code +{ +public: + /// Default constructor. + error_code() + : value_(0), + category_(&system_category()) + { + } + + /// Construct with specific error code and category. + error_code(int v, const error_category& c) + : value_(v), + category_(&c) + { + } + + /// Construct from an error code enum. + template + error_code(ErrorEnum e) + { + *this = make_error_code(e); + } + + /// Clear the error value to the default. + void clear() + { + value_ = 0; + category_ = &system_category(); + } + + /// Assign a new error value. + void assign(int v, const error_category& c) + { + value_ = v; + category_ = &c; + } + + /// Get the error value. + int value() const + { + return value_; + } + + /// Get the error category. + const error_category& category() const + { + return *category_; + } + + /// Get the message associated with the error. + std::string message() const + { + return category_->message(value_); + } + + struct unspecified_bool_type_t + { + }; + + typedef void (*unspecified_bool_type)(unspecified_bool_type_t); + + static void unspecified_bool_true(unspecified_bool_type_t) {} + + /// Operator returns non-null if there is a non-success error code. + operator unspecified_bool_type() const + { + if (value_ == 0) + return 0; + else + return &error_code::unspecified_bool_true; + } + + /// Operator to test if the error represents success. + bool operator!() const + { + return value_ == 0; + } + + /// Equality operator to compare two error objects. + friend bool operator==(const error_code& e1, const error_code& e2) + { + return e1.value_ == e2.value_ && e1.category_ == e2.category_; + } + + /// Inequality operator to compare two error objects. + friend bool operator!=(const error_code& e1, const error_code& e2) + { + return e1.value_ != e2.value_ || e1.category_ != e2.category_; + } + +private: + // The value associated with the error code. + int value_; + + // The category associated with the error code. + const error_category* category_; +}; + +# if !defined(ASIO_NO_IOSTREAM) + +/// Output an error code. +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const error_code& ec) +{ + os << ec.category().name() << ':' << ec.value(); + return os; +} + +# endif // !defined(ASIO_NO_IOSTREAM) + +#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/error_code.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_ERROR_CODE_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution.hpp b/third_party/asio/1.18.2/include/asio/execution.hpp new file mode 100644 index 000000000..861b25bb4 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution.hpp @@ -0,0 +1,48 @@ +// +// execution.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_HPP +#define ASIO_EXECUTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/execution/allocator.hpp" +#include "asio/execution/any_executor.hpp" +#include "asio/execution/bad_executor.hpp" +#include "asio/execution/blocking.hpp" +#include "asio/execution/blocking_adaptation.hpp" +#include "asio/execution/bulk_execute.hpp" +#include "asio/execution/bulk_guarantee.hpp" +#include "asio/execution/connect.hpp" +#include "asio/execution/context.hpp" +#include "asio/execution/context_as.hpp" +#include "asio/execution/execute.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/invocable_archetype.hpp" +#include "asio/execution/mapping.hpp" +#include "asio/execution/occupancy.hpp" +#include "asio/execution/operation_state.hpp" +#include "asio/execution/outstanding_work.hpp" +#include "asio/execution/prefer_only.hpp" +#include "asio/execution/receiver.hpp" +#include "asio/execution/receiver_invocation_error.hpp" +#include "asio/execution/relationship.hpp" +#include "asio/execution/schedule.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/execution/set_done.hpp" +#include "asio/execution/set_error.hpp" +#include "asio/execution/set_value.hpp" +#include "asio/execution/start.hpp" +#include "asio/execution/submit.hpp" + +#endif // ASIO_EXECUTION_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/allocator.hpp b/third_party/asio/1.18.2/include/asio/execution/allocator.hpp new file mode 100644 index 000000000..ea4d56c38 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/allocator.hpp @@ -0,0 +1,337 @@ +// +// execution/allocator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_ALLOCATOR_HPP +#define ASIO_EXECUTION_ALLOCATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/traits/query_static_constexpr_member.hpp" +#include "asio/traits/static_query.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property to describe which allocator an executor will use to allocate the +/// memory required to store a submitted function object. +template +struct allocator_t +{ + /// The allocator_t property applies to executors, senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The allocator_t property can be required. + static constexpr bool is_requirable = true; + + /// The allocator_t property can be preferred. + static constexpr bool is_preferable = true; + + /// Default constructor. + constexpr allocator_t(); + + /// Obtain the allocator stored in the allocator_t property object. + /** + * Present only if @c ProtoAllocator is non-void. + */ + constexpr ProtoAllocator value() const; + + /// Create an allocator_t object with a different allocator. + /** + * Present only if @c ProtoAllocator is void. + */ + template + allocator_t allocator; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { + +template +struct allocator_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + + template + struct static_proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + struct type + { + template + static constexpr auto query(ASIO_MOVE_ARG(P) p) + noexcept( + noexcept( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + { + return T::query(ASIO_MOVE_CAST(P)(p)); + } + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + }; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename static_proxy::type, allocator_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = allocator_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + ASIO_CONSTEXPR ProtoAllocator value() const + { + return a_; + } + +private: + friend struct allocator_t; + + explicit ASIO_CONSTEXPR allocator_t(const ProtoAllocator& a) + : a_(a) + { + } + + ProtoAllocator a_; +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T allocator_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template <> +struct allocator_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + + ASIO_CONSTEXPR allocator_t() + { + } + + template + struct static_proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + struct type + { + template + static constexpr auto query(ASIO_MOVE_ARG(P) p) + noexcept( + noexcept( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + { + return T::query(ASIO_MOVE_CAST(P)(p)); + } + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + }; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename static_proxy::type, allocator_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = allocator_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + template + ASIO_CONSTEXPR allocator_t operator()( + const OtherProtoAllocator& a) const + { + return allocator_t(a); + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template +const T allocator_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr allocator_t allocator; +#else // defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +template +struct allocator_instance +{ + static allocator_t instance; +}; + +template +allocator_t allocator_instance::instance; + +namespace { +static const allocator_t& allocator = allocator_instance::instance; +} // namespace +#endif + +} // namespace execution + +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_applicable_property > + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query, + typename enable_if< + execution::allocator_t::template + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::allocator_t::template + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::allocator_t::template + query_static_constexpr_member::value(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_ALLOCATOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/any_executor.hpp b/third_party/asio/1.18.2/include/asio/execution/any_executor.hpp new file mode 100644 index 000000000..cce164688 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/any_executor.hpp @@ -0,0 +1,2346 @@ +// +// execution/any_executor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_ANY_EXECUTOR_HPP +#define ASIO_EXECUTION_ANY_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/assert.hpp" +#include "asio/detail/cstddef.hpp" +#include "asio/detail/executor_function.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/scoped_ptr.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/detail/variadic_templates.hpp" +#include "asio/execution/bad_executor.hpp" +#include "asio/execution/blocking.hpp" +#include "asio/execution/execute.hpp" +#include "asio/execution/executor.hpp" +#include "asio/prefer.hpp" +#include "asio/query.hpp" +#include "asio/require.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// Polymorphic executor wrapper. +template +class any_executor +{ +public: + /// Default constructor. + any_executor() noexcept; + + /// Construct in an empty state. Equivalent effects to default constructor. + any_executor(nullptr_t) noexcept; + + /// Copy constructor. + any_executor(const any_executor& e) noexcept; + + /// Move constructor. + any_executor(any_executor&& e) noexcept; + + /// Construct to point to the same target as another any_executor. + template + any_executor(any_executor e); + + /// Construct a polymorphic wrapper for the specified executor. + template + any_executor(Executor e); + + /// Assignment operator. + any_executor& operator=(const any_executor& e) noexcept; + + /// Move assignment operator. + any_executor& operator=(any_executor&& e) noexcept; + + /// Assignment operator that sets the polymorphic wrapper to the empty state. + any_executor& operator=(nullptr_t); + + /// Assignment operator to create a polymorphic wrapper for the specified + /// executor. + template + any_executor& operator=(Executor e); + + /// Destructor. + ~any_executor(); + + /// Swap targets with another polymorphic wrapper. + void swap(any_executor& other) noexcept; + + /// Obtain a polymorphic wrapper with the specified property. + /** + * Do not call this function directly. It is intended for use with the + * asio::require and asio::prefer customisation points. + * + * For example: + * @code execution::any_executor ex = ...; + * auto ex2 = asio::requre(ex, execution::blocking.possibly); @endcode + */ + template + any_executor require(Property) const; + + /// Obtain a polymorphic wrapper with the specified property. + /** + * Do not call this function directly. It is intended for use with the + * asio::prefer customisation point. + * + * For example: + * @code execution::any_executor ex = ...; + * auto ex2 = asio::prefer(ex, execution::blocking.possibly); @endcode + */ + template + any_executor prefer(Property) const; + + /// Obtain the value associated with the specified property. + /** + * Do not call this function directly. It is intended for use with the + * asio::query customisation point. + * + * For example: + * @code execution::any_executor ex = ...; + * size_t n = asio::query(ex, execution::occupancy); @endcode + */ + template + typename Property::polymorphic_query_result_type query(Property) const; + + /// Execute the function on the target executor. + /** + * Do not call this function directly. It is intended for use with the + * execution::execute customisation point. + * + * For example: + * @code execution::any_executor<> ex = ...; + * execution::execute(ex, my_function_object); @endcode + * + * Throws asio::bad_executor if the polymorphic wrapper has no target. + */ + template + void execute(Function&& f) const; + + /// Obtain the underlying execution context. + /** + * This function is provided for backward compatibility. It is automatically + * defined when the @c SupportableProperties... list includes a property of + * type execution::context_as, for some type U. + */ + automatically_determined context() const; + + /// Determine whether the wrapper has a target executor. + /** + * @returns @c true if the polymorphic wrapper has a target executor, + * otherwise false. + */ + explicit operator bool() const noexcept; + + /// Get the type of the target executor. + const type_info& target_type() const noexcept; + + /// Get a pointer to the target executor. + template Executor* target() noexcept; + + /// Get a pointer to the target executor. + template const Executor* target() const noexcept; +}; + +/// Equality operator. +/** + * @relates any_executor + */ +template +bool operator==(const any_executor& a, + const any_executor& b) noexcept; + +/// Equality operator. +/** + * @relates any_executor + */ +template +bool operator==(const any_executor& a, + nullptr_t) noexcept; + +/// Equality operator. +/** + * @relates any_executor + */ +template +bool operator==(nullptr_t, + const any_executor& b) noexcept; + +/// Inequality operator. +/** + * @relates any_executor + */ +template +bool operator!=(const any_executor& a, + const any_executor& b) noexcept; + +/// Inequality operator. +/** + * @relates any_executor + */ +template +bool operator!=(const any_executor& a, + nullptr_t) noexcept; + +/// Inequality operator. +/** + * @relates any_executor + */ +template +bool operator!=(nullptr_t, + const any_executor& b) noexcept; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { + +#if !defined(ASIO_EXECUTION_ANY_EXECUTOR_FWD_DECL) +#define ASIO_EXECUTION_ANY_EXECUTOR_FWD_DECL + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +class any_executor; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +class any_executor; + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#endif // !defined(ASIO_EXECUTION_ANY_EXECUTOR_FWD_DECL) + +template +struct context_as_t; + +namespace detail { + +// Traits used to detect whether a property is requirable or preferable, taking +// into account that T::is_requirable or T::is_preferable may not not be well +// formed. + +template +struct is_requirable : false_type {}; + +template +struct is_requirable::type> : + true_type {}; + +template +struct is_preferable : false_type {}; + +template +struct is_preferable::type> : + true_type {}; + +// Trait used to detect context_as property, for backward compatibility. + +template +struct is_context_as : false_type {}; + +template +struct is_context_as > : true_type {}; + +// Helper template to: +// - Check if a target can supply the supportable properties. +// - Find the first convertible-from-T property in the list. + +template +struct supportable_properties; + +template +struct supportable_properties +{ + template + struct is_valid_target : integral_constant::value + ? can_require::value + : true + ) + && + ( + is_preferable::value + ? can_prefer::value + : true + ) + && + ( + !is_requirable::value && !is_preferable::value + ? can_query::value + : true + ) + > + { + }; + + struct found + { + ASIO_STATIC_CONSTEXPR(bool, value = true); + typedef Prop type; + typedef typename Prop::polymorphic_query_result_type query_result_type; + ASIO_STATIC_CONSTEXPR(std::size_t, index = I); + }; + + struct not_found + { + ASIO_STATIC_CONSTEXPR(bool, value = false); + }; + + template + struct find_convertible_property : + conditional< + is_same::value || is_convertible::value, + found, + not_found + >::type {}; + + template + struct find_convertible_requirable_property : + conditional< + is_requirable::value + && (is_same::value || is_convertible::value), + found, + not_found + >::type {}; + + template + struct find_convertible_preferable_property : + conditional< + is_preferable::value + && (is_same::value || is_convertible::value), + found, + not_found + >::type {}; + + struct find_context_as_property : + conditional< + is_context_as::value, + found, + not_found + >::type {}; +}; + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct supportable_properties +{ + template + struct is_valid_target : integral_constant::template is_valid_target::value + && + supportable_properties::template is_valid_target::value + ) + > + { + }; + + template + struct find_convertible_property : + conditional< + is_convertible::value, + typename supportable_properties::found, + typename supportable_properties::template find_convertible_property + >::type {}; + + template + struct find_convertible_requirable_property : + conditional< + is_requirable::value + && is_convertible::value, + typename supportable_properties::found, + typename supportable_properties::template find_convertible_requirable_property + >::type {}; + + template + struct find_convertible_preferable_property : + conditional< + is_preferable::value + && is_convertible::value, + typename supportable_properties::found, + typename supportable_properties::template find_convertible_preferable_property + >::type {}; + + struct find_context_as_property : + conditional< + is_context_as::value, + typename supportable_properties::found, + typename supportable_properties::find_context_as_property + >::type {}; +}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#define ASIO_PRIVATE_ANY_EXECUTOR_PROPS_BASE_DEF(n) \ + template \ + struct supportable_properties \ + { \ + template \ + struct is_valid_target : integral_constant::template is_valid_target::value \ + && \ + supportable_properties::template \ + is_valid_target::value \ + ) \ + > \ + { \ + }; \ + \ + template \ + struct find_convertible_property : \ + conditional< \ + is_convertible::value, \ + typename supportable_properties::found, \ + typename supportable_properties::template \ + find_convertible_property \ + >::type {}; \ + \ + template \ + struct find_convertible_requirable_property : \ + conditional< \ + is_requirable::value \ + && is_convertible::value, \ + typename supportable_properties::found, \ + typename supportable_properties::template \ + find_convertible_requirable_property \ + >::type {}; \ + \ + template \ + struct find_convertible_preferable_property : \ + conditional< \ + is_preferable::value \ + && is_convertible::value, \ + typename supportable_properties::found, \ + typename supportable_properties::template \ + find_convertible_preferable_property \ + >::type {}; \ + \ + struct find_context_as_property : \ + conditional< \ + is_context_as::value, \ + typename supportable_properties::found, \ + typename supportable_properties::find_context_as_property \ + >::type {}; \ + }; \ + /**/ +ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_ANY_EXECUTOR_PROPS_BASE_DEF) +#undef ASIO_PRIVATE_ANY_EXECUTOR_PROPS_BASE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct is_valid_target_executor : + conditional< + is_executor::value, + typename supportable_properties<0, Props>::template is_valid_target, + false_type + >::type +{ +}; + +class any_executor_base +{ +public: + any_executor_base() ASIO_NOEXCEPT + : object_fns_(object_fns_table()), + target_(0), + target_fns_(target_fns_table()) + { + } + + template + any_executor_base(Executor ex, false_type) + : target_fns_(target_fns_table( + any_executor_base::query_blocking(ex, + can_query()) + == execution::blocking.always)) + { + any_executor_base::construct_object(ex, + integral_constant::value <= alignment_of::value + >()); + } + + template + any_executor_base(Executor other, true_type) + : object_fns_(object_fns_table >()), + target_fns_(other.target_fns_) + { + asio::detail::shared_ptr p = + asio::detail::make_shared( + ASIO_MOVE_CAST(Executor)(other)); + target_ = p->template target(); + new (&object_) asio::detail::shared_ptr( + ASIO_MOVE_CAST(asio::detail::shared_ptr)(p)); + } + + any_executor_base(const any_executor_base& other) ASIO_NOEXCEPT + : object_fns_(other.object_fns_), + target_fns_(other.target_fns_) + { + object_fns_->copy(*this, other); + } + + ~any_executor_base() ASIO_NOEXCEPT + { + object_fns_->destroy(*this); + } + + any_executor_base& operator=( + const any_executor_base& other) ASIO_NOEXCEPT + { + if (this != &other) + { + object_fns_->destroy(*this); + object_fns_ = other.object_fns_; + target_fns_ = other.target_fns_; + object_fns_->copy(*this, other); + } + return *this; + } + + any_executor_base& operator=(nullptr_t) ASIO_NOEXCEPT + { + object_fns_->destroy(*this); + target_ = 0; + object_fns_ = object_fns_table(); + target_fns_ = target_fns_table(); + return *this; + } + +#if defined(ASIO_HAS_MOVE) + + any_executor_base(any_executor_base&& other) ASIO_NOEXCEPT + : object_fns_(other.object_fns_), + target_fns_(other.target_fns_) + { + other.object_fns_ = object_fns_table(); + other.target_fns_ = target_fns_table(); + object_fns_->move(*this, other); + other.target_ = 0; + } + + any_executor_base& operator=( + any_executor_base&& other) ASIO_NOEXCEPT + { + if (this != &other) + { + object_fns_->destroy(*this); + object_fns_ = other.object_fns_; + other.object_fns_ = object_fns_table(); + target_fns_ = other.target_fns_; + other.target_fns_ = target_fns_table(); + object_fns_->move(*this, other); + other.target_ = 0; + } + return *this; + } + +#endif // defined(ASIO_HAS_MOVE) + + void swap(any_executor_base& other) ASIO_NOEXCEPT + { + if (this != &other) + { + any_executor_base tmp(ASIO_MOVE_CAST(any_executor_base)(other)); + other = ASIO_MOVE_CAST(any_executor_base)(*this); + *this = ASIO_MOVE_CAST(any_executor_base)(tmp); + } + } + + template + void execute(ASIO_MOVE_ARG(F) f) const + { + if (target_fns_->blocking_execute != 0) + { + asio::detail::non_const_lvalue f2(f); + target_fns_->blocking_execute(*this, function_view(f2.value)); + } + else + { + target_fns_->execute(*this, + function(ASIO_MOVE_CAST(F)(f), std::allocator())); + } + } + + template + Executor* target() + { + return static_cast(target_); + } + + template + const Executor* target() const + { + return static_cast(target_); + } + +#if !defined(ASIO_NO_TYPEID) + const std::type_info& target_type() const +#else // !defined(ASIO_NO_TYPEID) + const void* target_type() const +#endif // !defined(ASIO_NO_TYPEID) + { + return target_fns_->target_type(); + } + + struct unspecified_bool_type_t {}; + typedef void (*unspecified_bool_type)(unspecified_bool_type_t); + static void unspecified_bool_true(unspecified_bool_type_t) {} + + operator unspecified_bool_type() const ASIO_NOEXCEPT + { + return target_ ? &any_executor_base::unspecified_bool_true : 0; + } + + bool operator!() const ASIO_NOEXCEPT + { + return target_ == 0; + } + +protected: + bool equality_helper(const any_executor_base& other) const ASIO_NOEXCEPT + { + if (target_ == other.target_) + return true; + if (target_ && !other.target_) + return false; + if (!target_ && other.target_) + return false; + if (target_fns_ != other.target_fns_) + return false; + return target_fns_->equal(*this, other); + } + + template + Ex& object() + { + return *static_cast(static_cast(&object_)); + } + + template + const Ex& object() const + { + return *static_cast(static_cast(&object_)); + } + + struct object_fns + { + void (*destroy)(any_executor_base&); + void (*copy)(any_executor_base&, const any_executor_base&); + void (*move)(any_executor_base&, any_executor_base&); + const void* (*target)(const any_executor_base&); + }; + + static void destroy_void(any_executor_base&) + { + } + + static void copy_void(any_executor_base& ex1, const any_executor_base&) + { + ex1.target_ = 0; + } + + static void move_void(any_executor_base& ex1, any_executor_base&) + { + ex1.target_ = 0; + } + + static const void* target_void(const any_executor_base&) + { + return 0; + } + + template + static const object_fns* object_fns_table( + typename enable_if< + is_same::value + >::type* = 0) + { + static const object_fns fns = + { + &any_executor_base::destroy_void, + &any_executor_base::copy_void, + &any_executor_base::move_void, + &any_executor_base::target_void + }; + return &fns; + } + + static void destroy_shared(any_executor_base& ex) + { + typedef asio::detail::shared_ptr type; + ex.object().~type(); + } + + static void copy_shared(any_executor_base& ex1, const any_executor_base& ex2) + { + typedef asio::detail::shared_ptr type; + new (&ex1.object_) type(ex2.object()); + ex1.target_ = ex2.target_; + } + + static void move_shared(any_executor_base& ex1, any_executor_base& ex2) + { + typedef asio::detail::shared_ptr type; + new (&ex1.object_) type(ASIO_MOVE_CAST(type)(ex2.object())); + ex1.target_ = ex2.target_; + ex2.object().~type(); + } + + static const void* target_shared(const any_executor_base& ex) + { + typedef asio::detail::shared_ptr type; + return ex.object().get(); + } + + template + static const object_fns* object_fns_table( + typename enable_if< + is_same >::value + >::type* = 0) + { + static const object_fns fns = + { + &any_executor_base::destroy_shared, + &any_executor_base::copy_shared, + &any_executor_base::move_shared, + &any_executor_base::target_shared + }; + return &fns; + } + + template + static void destroy_object(any_executor_base& ex) + { + ex.object().~Obj(); + } + + template + static void copy_object(any_executor_base& ex1, const any_executor_base& ex2) + { + new (&ex1.object_) Obj(ex2.object()); + ex1.target_ = &ex1.object(); + } + + template + static void move_object(any_executor_base& ex1, any_executor_base& ex2) + { + new (&ex1.object_) Obj(ASIO_MOVE_CAST(Obj)(ex2.object())); + ex1.target_ = &ex1.object(); + ex2.object().~Obj(); + } + + template + static const void* target_object(const any_executor_base& ex) + { + return &ex.object(); + } + + template + static const object_fns* object_fns_table( + typename enable_if< + !is_same::value + && !is_same >::value + >::type* = 0) + { + static const object_fns fns = + { + &any_executor_base::destroy_object, + &any_executor_base::copy_object, + &any_executor_base::move_object, + &any_executor_base::target_object + }; + return &fns; + } + + typedef asio::detail::executor_function function; + typedef asio::detail::executor_function_view function_view; + + struct target_fns + { +#if !defined(ASIO_NO_TYPEID) + const std::type_info& (*target_type)(); +#else // !defined(ASIO_NO_TYPEID) + const void* (*target_type)(); +#endif // !defined(ASIO_NO_TYPEID) + bool (*equal)(const any_executor_base&, const any_executor_base&); + void (*execute)(const any_executor_base&, ASIO_MOVE_ARG(function)); + void (*blocking_execute)(const any_executor_base&, function_view); + }; + +#if !defined(ASIO_NO_TYPEID) + static const std::type_info& target_type_void() + { + return typeid(void); + } +#else // !defined(ASIO_NO_TYPEID) + static const void* target_type_void() + { + return 0; + } +#endif // !defined(ASIO_NO_TYPEID) + + static bool equal_void(const any_executor_base&, const any_executor_base&) + { + return true; + } + + static void execute_void(const any_executor_base&, + ASIO_MOVE_ARG(function)) + { + bad_executor ex; + asio::detail::throw_exception(ex); + } + + static void blocking_execute_void(const any_executor_base&, function_view) + { + bad_executor ex; + asio::detail::throw_exception(ex); + } + + template + static const target_fns* target_fns_table( + typename enable_if< + is_same::value + >::type* = 0) + { + static const target_fns fns = + { + &any_executor_base::target_type_void, + &any_executor_base::equal_void, + &any_executor_base::execute_void, + &any_executor_base::blocking_execute_void + }; + return &fns; + } + +#if !defined(ASIO_NO_TYPEID) + template + static const std::type_info& target_type_ex() + { + return typeid(Ex); + } +#else // !defined(ASIO_NO_TYPEID) + template + static const void* target_type_ex() + { + static int unique_id; + return &unique_id; + } +#endif // !defined(ASIO_NO_TYPEID) + + template + static bool equal_ex(const any_executor_base& ex1, + const any_executor_base& ex2) + { + return *ex1.target() == *ex2.target(); + } + + template + static void execute_ex(const any_executor_base& ex, + ASIO_MOVE_ARG(function) f) + { + execution::execute(*ex.target(), ASIO_MOVE_CAST(function)(f)); + } + + template + static void blocking_execute_ex(const any_executor_base& ex, function_view f) + { + execution::execute(*ex.target(), f); + } + + template + static const target_fns* target_fns_table(bool is_always_blocking, + typename enable_if< + !is_same::value + >::type* = 0) + { + static const target_fns fns_with_execute = + { + &any_executor_base::target_type_ex, + &any_executor_base::equal_ex, + &any_executor_base::execute_ex, + 0 + }; + + static const target_fns fns_with_blocking_execute = + { + &any_executor_base::target_type_ex, + &any_executor_base::equal_ex, + 0, + &any_executor_base::blocking_execute_ex + }; + + return is_always_blocking ? &fns_with_blocking_execute : &fns_with_execute; + } + +#if defined(ASIO_MSVC) +# pragma warning (push) +# pragma warning (disable:4702) +#endif // defined(ASIO_MSVC) + + static void query_fn_void(void*, const void*, const void*) + { + bad_executor ex; + asio::detail::throw_exception(ex); + } + + template + static void query_fn_non_void(void*, const void* ex, const void* prop, + typename enable_if< + asio::can_query::value + && is_same::value + >::type*) + { + asio::query(*static_cast(ex), + *static_cast(prop)); + } + + template + static void query_fn_non_void(void*, const void*, const void*, + typename enable_if< + !asio::can_query::value + && is_same::value + >::type*) + { + } + + template + static void query_fn_non_void(void* result, const void* ex, const void* prop, + typename enable_if< + asio::can_query::value + && !is_same::value + && is_reference::value + >::type*) + { + *static_cast::type**>(result) + = &static_cast( + asio::query(*static_cast(ex), + *static_cast(prop))); + } + + template + static void query_fn_non_void(void*, const void*, const void*, + typename enable_if< + !asio::can_query::value + && !is_same::value + && is_reference::value + >::type*) + { + std::terminate(); // Combination should not be possible. + } + + template + static void query_fn_non_void(void* result, const void* ex, const void* prop, + typename enable_if< + asio::can_query::value + && !is_same::value + && is_scalar::value + >::type*) + { + *static_cast(result) + = static_cast( + asio::query(*static_cast(ex), + *static_cast(prop))); + } + + template + static void query_fn_non_void(void* result, const void*, const void*, + typename enable_if< + !asio::can_query::value + && !is_same::value + && is_scalar::value + >::type*) + { + *static_cast(result) + = typename Prop::polymorphic_query_result_type(); + } + + template + static void query_fn_non_void(void* result, const void* ex, const void* prop, + typename enable_if< + asio::can_query::value + && !is_same::value + && !is_reference::value + && !is_scalar::value + >::type*) + { + *static_cast(result) + = new typename Prop::polymorphic_query_result_type( + asio::query(*static_cast(ex), + *static_cast(prop))); + } + + template + static void query_fn_non_void(void* result, const void*, const void*, ...) + { + *static_cast(result) + = new typename Prop::polymorphic_query_result_type(); + } + + template + static void query_fn_impl(void* result, const void* ex, const void* prop, + typename enable_if< + is_same::value + >::type*) + { + query_fn_void(result, ex, prop); + } + + template + static void query_fn_impl(void* result, const void* ex, const void* prop, + typename enable_if< + !is_same::value + >::type*) + { + query_fn_non_void(result, ex, prop, 0); + } + + template + static void query_fn(void* result, const void* ex, const void* prop) + { + query_fn_impl(result, ex, prop, 0); + } + + template + static Poly require_fn_impl(const void*, const void*, + typename enable_if< + is_same::value + >::type*) + { + bad_executor ex; + asio::detail::throw_exception(ex); + return Poly(); + } + + template + static Poly require_fn_impl(const void* ex, const void* prop, + typename enable_if< + !is_same::value && Prop::is_requirable + >::type*) + { + return asio::require(*static_cast(ex), + *static_cast(prop)); + } + + template + static Poly require_fn_impl(const void*, const void*, ...) + { + return Poly(); + } + + template + static Poly require_fn(const void* ex, const void* prop) + { + return require_fn_impl(ex, prop, 0); + } + + template + static Poly prefer_fn_impl(const void*, const void*, + typename enable_if< + is_same::value + >::type*) + { + bad_executor ex; + asio::detail::throw_exception(ex); + return Poly(); + } + + template + static Poly prefer_fn_impl(const void* ex, const void* prop, + typename enable_if< + !is_same::value && Prop::is_preferable + >::type*) + { + return asio::prefer(*static_cast(ex), + *static_cast(prop)); + } + + template + static Poly prefer_fn_impl(const void*, const void*, ...) + { + return Poly(); + } + + template + static Poly prefer_fn(const void* ex, const void* prop) + { + return prefer_fn_impl(ex, prop, 0); + } + + template + struct prop_fns + { + void (*query)(void*, const void*, const void*); + Poly (*require)(const void*, const void*); + Poly (*prefer)(const void*, const void*); + }; + +#if defined(ASIO_MSVC) +# pragma warning (pop) +#endif // defined(ASIO_MSVC) + +private: + template + static execution::blocking_t query_blocking(const Executor& ex, true_type) + { + return asio::query(ex, execution::blocking); + } + + template + static execution::blocking_t query_blocking(const Executor&, false_type) + { + return execution::blocking_t(); + } + + template + void construct_object(Executor& ex, true_type) + { + object_fns_ = object_fns_table(); + target_ = new (&object_) Executor(ASIO_MOVE_CAST(Executor)(ex)); + } + + template + void construct_object(Executor& ex, false_type) + { + object_fns_ = object_fns_table >(); + asio::detail::shared_ptr p = + asio::detail::make_shared( + ASIO_MOVE_CAST(Executor)(ex)); + target_ = p.get(); + new (&object_) asio::detail::shared_ptr( + ASIO_MOVE_CAST(asio::detail::shared_ptr)(p)); + } + +/*private:*/public: +// template friend class any_executor; + + typedef aligned_storage< + sizeof(asio::detail::shared_ptr), + alignment_of >::value + >::type object_type; + + object_type object_; + const object_fns* object_fns_; + void* target_; + const target_fns* target_fns_; +}; + +template +struct any_executor_context +{ +}; + +#if !defined(ASIO_NO_TS_EXECUTORS) + +template +struct any_executor_context::type> +{ + typename Property::query_result_type context() const + { + return static_cast(this)->query(typename Property::type()); + } +}; + +#endif // !defined(ASIO_NO_TS_EXECUTORS) + +} // namespace detail + +template <> +class any_executor<> : public detail::any_executor_base +{ +public: + any_executor() ASIO_NOEXCEPT + : detail::any_executor_base() + { + } + + any_executor(nullptr_t) ASIO_NOEXCEPT + : detail::any_executor_base() + { + } + + template + any_executor(Executor ex, + typename enable_if< + conditional< + !is_same::value + && !is_base_of::value, + is_executor, + false_type + >::type::value + >::type* = 0) + : detail::any_executor_base( + ASIO_MOVE_CAST(Executor)(ex), false_type()) + { + } + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + any_executor(any_executor other) + : detail::any_executor_base( + static_cast(other)) + { + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + any_executor(any_executor other) + : detail::any_executor_base( + static_cast(other)) + { + } + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + any_executor(const any_executor& other) ASIO_NOEXCEPT + : detail::any_executor_base( + static_cast(other)) + { + } + + any_executor& operator=(const any_executor& other) ASIO_NOEXCEPT + { + if (this != &other) + { + detail::any_executor_base::operator=( + static_cast(other)); + } + return *this; + } + + any_executor& operator=(nullptr_t p) ASIO_NOEXCEPT + { + detail::any_executor_base::operator=(p); + return *this; + } + +#if defined(ASIO_HAS_MOVE) + + any_executor(any_executor&& other) ASIO_NOEXCEPT + : detail::any_executor_base( + static_cast( + static_cast(other))) + { + } + + any_executor& operator=(any_executor&& other) ASIO_NOEXCEPT + { + if (this != &other) + { + detail::any_executor_base::operator=( + static_cast( + static_cast(other))); + } + return *this; + } + +#endif // defined(ASIO_HAS_MOVE) + + void swap(any_executor& other) ASIO_NOEXCEPT + { + detail::any_executor_base::swap( + static_cast(other)); + } + + using detail::any_executor_base::execute; + using detail::any_executor_base::target; + using detail::any_executor_base::target_type; + using detail::any_executor_base::operator unspecified_bool_type; + using detail::any_executor_base::operator!; + + bool equality_helper(const any_executor& other) const ASIO_NOEXCEPT + { + return any_executor_base::equality_helper(other); + } + + template + friend typename enable_if< + is_same::value + || is_same::value, + bool + >::type operator==(const AnyExecutor1& a, + const AnyExecutor2& b) ASIO_NOEXCEPT + { + return static_cast(a).equality_helper(b); + } + + template + friend typename enable_if< + is_same::value, + bool + >::type operator==(const AnyExecutor& a, nullptr_t) ASIO_NOEXCEPT + { + return !a; + } + + template + friend typename enable_if< + is_same::value, + bool + >::type operator==(nullptr_t, const AnyExecutor& b) ASIO_NOEXCEPT + { + return !b; + } + + template + friend typename enable_if< + is_same::value + || is_same::value, + bool + >::type operator!=(const AnyExecutor1& a, + const AnyExecutor2& b) ASIO_NOEXCEPT + { + return !static_cast(a).equality_helper(b); + } + + template + friend typename enable_if< + is_same::value, + bool + >::type operator!=(const AnyExecutor& a, nullptr_t) ASIO_NOEXCEPT + { + return !!a; + } + + template + friend typename enable_if< + is_same::value, + bool + >::type operator!=(nullptr_t, const AnyExecutor& b) ASIO_NOEXCEPT + { + return !!b; + } +}; + +inline void swap(any_executor<>& a, any_executor<>& b) ASIO_NOEXCEPT +{ + return a.swap(b); +} + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +class any_executor : + public detail::any_executor_base, + public detail::any_executor_context< + any_executor, + typename detail::supportable_properties< + 0, void(SupportableProperties...)>::find_context_as_property> +{ +public: + any_executor() ASIO_NOEXCEPT + : detail::any_executor_base(), + prop_fns_(prop_fns_table()) + { + } + + any_executor(nullptr_t) ASIO_NOEXCEPT + : detail::any_executor_base(), + prop_fns_(prop_fns_table()) + { + } + + template + any_executor(Executor ex, + typename enable_if< + conditional< + !is_same::value + && !is_base_of::value, + detail::is_valid_target_executor< + Executor, void(SupportableProperties...)>, + false_type + >::type::value + >::type* = 0) + : detail::any_executor_base( + ASIO_MOVE_CAST(Executor)(ex), false_type()), + prop_fns_(prop_fns_table()) + { + } + + template + any_executor(any_executor other, + typename enable_if< + conditional< + !is_same< + any_executor, + any_executor + >::value, + typename detail::supportable_properties< + 0, void(SupportableProperties...)>::template is_valid_target< + any_executor >, + false_type + >::type::value + >::type* = 0) + : detail::any_executor_base(ASIO_MOVE_CAST( + any_executor)(other), true_type()), + prop_fns_(prop_fns_table >()) + { + } + + any_executor(const any_executor& other) ASIO_NOEXCEPT + : detail::any_executor_base( + static_cast(other)), + prop_fns_(other.prop_fns_) + { + } + + any_executor& operator=(const any_executor& other) ASIO_NOEXCEPT + { + if (this != &other) + { + prop_fns_ = other.prop_fns_; + detail::any_executor_base::operator=( + static_cast(other)); + } + return *this; + } + + any_executor& operator=(nullptr_t p) ASIO_NOEXCEPT + { + prop_fns_ = prop_fns_table(); + detail::any_executor_base::operator=(p); + return *this; + } + +#if defined(ASIO_HAS_MOVE) + + any_executor(any_executor&& other) ASIO_NOEXCEPT + : detail::any_executor_base( + static_cast( + static_cast(other))), + prop_fns_(other.prop_fns_) + { + other.prop_fns_ = prop_fns_table(); + } + + any_executor& operator=(any_executor&& other) ASIO_NOEXCEPT + { + if (this != &other) + { + prop_fns_ = other.prop_fns_; + detail::any_executor_base::operator=( + static_cast( + static_cast(other))); + } + return *this; + } + +#endif // defined(ASIO_HAS_MOVE) + + void swap(any_executor& other) ASIO_NOEXCEPT + { + if (this != &other) + { + detail::any_executor_base::swap( + static_cast(other)); + const prop_fns* tmp_prop_fns = other.prop_fns_; + other.prop_fns_ = prop_fns_; + prop_fns_ = tmp_prop_fns; + } + } + + using detail::any_executor_base::execute; + using detail::any_executor_base::target; + using detail::any_executor_base::target_type; + using detail::any_executor_base::operator unspecified_bool_type; + using detail::any_executor_base::operator!; + + bool equality_helper(const any_executor& other) const ASIO_NOEXCEPT + { + return any_executor_base::equality_helper(other); + } + + template + friend typename enable_if< + is_same::value + || is_same::value, + bool + >::type operator==(const AnyExecutor1& a, + const AnyExecutor2& b) ASIO_NOEXCEPT + { + return static_cast(a).equality_helper(b); + } + + template + friend typename enable_if< + is_same::value, + bool + >::type operator==(const AnyExecutor& a, nullptr_t) ASIO_NOEXCEPT + { + return !a; + } + + template + friend typename enable_if< + is_same::value, + bool + >::type operator==(nullptr_t, const AnyExecutor& b) ASIO_NOEXCEPT + { + return !b; + } + + template + friend typename enable_if< + is_same::value + || is_same::value, + bool + >::type operator!=(const AnyExecutor1& a, + const AnyExecutor2& b) ASIO_NOEXCEPT + { + return !static_cast(a).equality_helper(b); + } + + template + friend typename enable_if< + is_same::value, + bool + >::type operator!=(const AnyExecutor& a, nullptr_t) ASIO_NOEXCEPT + { + return !!a; + } + + template + friend typename enable_if< + is_same::value, + bool + >::type operator!=(nullptr_t, const AnyExecutor& b) ASIO_NOEXCEPT + { + return !!b; + } + + template + struct find_convertible_property : + detail::supportable_properties< + 0, void(SupportableProperties...)>::template + find_convertible_property {}; + + template + void query(const Property& p, + typename enable_if< + is_same< + typename find_convertible_property::query_result_type, + void + >::value + >::type* = 0) const + { + typedef find_convertible_property found; + prop_fns_[found::index].query(0, object_fns_->target(*this), + &static_cast(p)); + } + + template + typename find_convertible_property::query_result_type + query(const Property& p, + typename enable_if< + !is_same< + typename find_convertible_property::query_result_type, + void + >::value + && + is_reference< + typename find_convertible_property::query_result_type + >::value + >::type* = 0) const + { + typedef find_convertible_property found; + typename remove_reference< + typename found::query_result_type>::type* result = 0; + prop_fns_[found::index].query(&result, object_fns_->target(*this), + &static_cast(p)); + return *result; + } + + template + typename find_convertible_property::query_result_type + query(const Property& p, + typename enable_if< + !is_same< + typename find_convertible_property::query_result_type, + void + >::value + && + is_scalar< + typename find_convertible_property::query_result_type + >::value + >::type* = 0) const + { + typedef find_convertible_property found; + typename found::query_result_type result; + prop_fns_[found::index].query(&result, object_fns_->target(*this), + &static_cast(p)); + return result; + } + + template + typename find_convertible_property::query_result_type + query(const Property& p, + typename enable_if< + !is_same< + typename find_convertible_property::query_result_type, + void + >::value + && + !is_reference< + typename find_convertible_property::query_result_type + >::value + && + !is_scalar< + typename find_convertible_property::query_result_type + >::value + >::type* = 0) const + { + typedef find_convertible_property found; + typename found::query_result_type* result; + prop_fns_[found::index].query(&result, object_fns_->target(*this), + &static_cast(p)); + return *asio::detail::scoped_ptr< + typename found::query_result_type>(result); + } + + template + struct find_convertible_requirable_property : + detail::supportable_properties< + 0, void(SupportableProperties...)>::template + find_convertible_requirable_property {}; + + template + any_executor require(const Property& p, + typename enable_if< + find_convertible_requirable_property::value + >::type* = 0) const + { + typedef find_convertible_requirable_property found; + return prop_fns_[found::index].require(object_fns_->target(*this), + &static_cast(p)); + } + + template + struct find_convertible_preferable_property : + detail::supportable_properties< + 0, void(SupportableProperties...)>::template + find_convertible_preferable_property {}; + + template + any_executor prefer(const Property& p, + typename enable_if< + find_convertible_preferable_property::value + >::type* = 0) const + { + typedef find_convertible_preferable_property found; + return prop_fns_[found::index].prefer(object_fns_->target(*this), + &static_cast(p)); + } + +//private: + template + static const prop_fns* prop_fns_table() + { + static const prop_fns fns[] = + { + { + &detail::any_executor_base::query_fn< + Ex, SupportableProperties>, + &detail::any_executor_base::require_fn< + any_executor, Ex, SupportableProperties>, + &detail::any_executor_base::prefer_fn< + any_executor, Ex, SupportableProperties> + }... + }; + return fns; + } + + const prop_fns* prop_fns_; +}; + +template +inline void swap(any_executor& a, + any_executor& b) ASIO_NOEXCEPT +{ + return a.swap(b); +} + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#define ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS(n) \ + ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_##n + +#define ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_1 \ + { \ + &detail::any_executor_base::query_fn, \ + &detail::any_executor_base::require_fn, \ + &detail::any_executor_base::prefer_fn \ + } +#define ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_2 \ + ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_1, \ + { \ + &detail::any_executor_base::query_fn, \ + &detail::any_executor_base::require_fn, \ + &detail::any_executor_base::prefer_fn \ + } +#define ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_3 \ + ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_2, \ + { \ + &detail::any_executor_base::query_fn, \ + &detail::any_executor_base::require_fn, \ + &detail::any_executor_base::prefer_fn \ + } +#define ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_4 \ + ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_3, \ + { \ + &detail::any_executor_base::query_fn, \ + &detail::any_executor_base::require_fn, \ + &detail::any_executor_base::prefer_fn \ + } +#define ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_5 \ + ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_4, \ + { \ + &detail::any_executor_base::query_fn, \ + &detail::any_executor_base::require_fn, \ + &detail::any_executor_base::prefer_fn \ + } +#define ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_6 \ + ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_5, \ + { \ + &detail::any_executor_base::query_fn, \ + &detail::any_executor_base::require_fn, \ + &detail::any_executor_base::prefer_fn \ + } +#define ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_7 \ + ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_6, \ + { \ + &detail::any_executor_base::query_fn, \ + &detail::any_executor_base::require_fn, \ + &detail::any_executor_base::prefer_fn \ + } +#define ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_8 \ + ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_7, \ + { \ + &detail::any_executor_base::query_fn, \ + &detail::any_executor_base::require_fn, \ + &detail::any_executor_base::prefer_fn \ + } + +#if defined(ASIO_HAS_MOVE) + +# define ASIO_PRIVATE_ANY_EXECUTOR_MOVE_OPS \ + any_executor(any_executor&& other) ASIO_NOEXCEPT \ + : detail::any_executor_base( \ + static_cast( \ + static_cast(other))), \ + prop_fns_(other.prop_fns_) \ + { \ + other.prop_fns_ = prop_fns_table(); \ + } \ + \ + any_executor& operator=(any_executor&& other) ASIO_NOEXCEPT \ + { \ + if (this != &other) \ + { \ + prop_fns_ = other.prop_fns_; \ + detail::any_executor_base::operator=( \ + static_cast( \ + static_cast(other))); \ + } \ + return *this; \ + } \ + /**/ +#else // defined(ASIO_HAS_MOVE) + +# define ASIO_PRIVATE_ANY_EXECUTOR_MOVE_OPS + +#endif // defined(ASIO_HAS_MOVE) + +#define ASIO_PRIVATE_ANY_EXECUTOR_DEF(n) \ + template \ + class any_executor : \ + public detail::any_executor_base, \ + public detail::any_executor_context< \ + any_executor, \ + typename detail::supportable_properties< \ + 0, void(ASIO_VARIADIC_TARGS(n))>::find_context_as_property> \ + { \ + public: \ + any_executor() ASIO_NOEXCEPT \ + : detail::any_executor_base(), \ + prop_fns_(prop_fns_table()) \ + { \ + } \ + \ + any_executor(nullptr_t) ASIO_NOEXCEPT \ + : detail::any_executor_base(), \ + prop_fns_(prop_fns_table()) \ + { \ + } \ + \ + template \ + any_executor(Executor ex, \ + typename enable_if< \ + conditional< \ + !is_same::value \ + && !is_base_of::value, \ + detail::is_valid_target_executor< \ + Executor, void(ASIO_VARIADIC_TARGS(n))>, \ + false_type \ + >::type::value \ + >::type* = 0) \ + : detail::any_executor_base(ASIO_MOVE_CAST( \ + Executor)(ex), false_type()), \ + prop_fns_(prop_fns_table()) \ + { \ + } \ + \ + any_executor(const any_executor& other) ASIO_NOEXCEPT \ + : detail::any_executor_base( \ + static_cast(other)), \ + prop_fns_(other.prop_fns_) \ + { \ + } \ + \ + any_executor(any_executor<> other) \ + : detail::any_executor_base(ASIO_MOVE_CAST( \ + any_executor<>)(other), true_type()), \ + prop_fns_(prop_fns_table >()) \ + { \ + } \ + \ + template \ + any_executor(OtherAnyExecutor other, \ + typename enable_if< \ + conditional< \ + !is_same::value \ + && is_base_of::value, \ + typename detail::supportable_properties< \ + 0, void(ASIO_VARIADIC_TARGS(n))>::template \ + is_valid_target, \ + false_type \ + >::type::value \ + >::type* = 0) \ + : detail::any_executor_base(ASIO_MOVE_CAST( \ + OtherAnyExecutor)(other), true_type()), \ + prop_fns_(prop_fns_table()) \ + { \ + } \ + \ + any_executor& operator=(const any_executor& other) ASIO_NOEXCEPT \ + { \ + if (this != &other) \ + { \ + prop_fns_ = other.prop_fns_; \ + detail::any_executor_base::operator=( \ + static_cast(other)); \ + } \ + return *this; \ + } \ + \ + any_executor& operator=(nullptr_t p) ASIO_NOEXCEPT \ + { \ + prop_fns_ = prop_fns_table(); \ + detail::any_executor_base::operator=(p); \ + return *this; \ + } \ + \ + ASIO_PRIVATE_ANY_EXECUTOR_MOVE_OPS \ + \ + void swap(any_executor& other) ASIO_NOEXCEPT \ + { \ + if (this != &other) \ + { \ + detail::any_executor_base::swap( \ + static_cast(other)); \ + const prop_fns* tmp_prop_fns = other.prop_fns_; \ + other.prop_fns_ = prop_fns_; \ + prop_fns_ = tmp_prop_fns; \ + } \ + } \ + \ + using detail::any_executor_base::execute; \ + using detail::any_executor_base::target; \ + using detail::any_executor_base::target_type; \ + using detail::any_executor_base::operator unspecified_bool_type; \ + using detail::any_executor_base::operator!; \ + \ + bool equality_helper(const any_executor& other) const ASIO_NOEXCEPT \ + { \ + return any_executor_base::equality_helper(other); \ + } \ + \ + template \ + friend typename enable_if< \ + is_same::value \ + || is_same::value, \ + bool \ + >::type operator==(const AnyExecutor1& a, \ + const AnyExecutor2& b) ASIO_NOEXCEPT \ + { \ + return static_cast(a).equality_helper(b); \ + } \ + \ + template \ + friend typename enable_if< \ + is_same::value, \ + bool \ + >::type operator==(const AnyExecutor& a, nullptr_t) ASIO_NOEXCEPT \ + { \ + return !a; \ + } \ + \ + template \ + friend typename enable_if< \ + is_same::value, \ + bool \ + >::type operator==(nullptr_t, const AnyExecutor& b) ASIO_NOEXCEPT \ + { \ + return !b; \ + } \ + \ + template \ + friend typename enable_if< \ + is_same::value \ + || is_same::value, \ + bool \ + >::type operator!=(const AnyExecutor1& a, \ + const AnyExecutor2& b) ASIO_NOEXCEPT \ + { \ + return !static_cast(a).equality_helper(b); \ + } \ + \ + template \ + friend typename enable_if< \ + is_same::value, \ + bool \ + >::type operator!=(const AnyExecutor& a, nullptr_t) ASIO_NOEXCEPT \ + { \ + return !!a; \ + } \ + \ + template \ + friend typename enable_if< \ + is_same::value, \ + bool \ + >::type operator!=(nullptr_t, const AnyExecutor& b) ASIO_NOEXCEPT \ + { \ + return !!b; \ + } \ + \ + template \ + struct find_convertible_property : \ + detail::supportable_properties< \ + 0, void(ASIO_VARIADIC_TARGS(n))>::template \ + find_convertible_property {}; \ + \ + template \ + void query(const Property& p, \ + typename enable_if< \ + is_same< \ + typename find_convertible_property::query_result_type, \ + void \ + >::value \ + >::type* = 0) const \ + { \ + typedef find_convertible_property found; \ + prop_fns_[found::index].query(0, object_fns_->target(*this), \ + &static_cast(p)); \ + } \ + \ + template \ + typename find_convertible_property::query_result_type \ + query(const Property& p, \ + typename enable_if< \ + !is_same< \ + typename find_convertible_property::query_result_type, \ + void \ + >::value \ + && \ + is_reference< \ + typename find_convertible_property::query_result_type \ + >::value \ + >::type* = 0) const \ + { \ + typedef find_convertible_property found; \ + typename remove_reference< \ + typename found::query_result_type>::type* result; \ + prop_fns_[found::index].query(&result, object_fns_->target(*this), \ + &static_cast(p)); \ + return *result; \ + } \ + \ + template \ + typename find_convertible_property::query_result_type \ + query(const Property& p, \ + typename enable_if< \ + !is_same< \ + typename find_convertible_property::query_result_type, \ + void \ + >::value \ + && \ + is_scalar< \ + typename find_convertible_property::query_result_type \ + >::value \ + >::type* = 0) const \ + { \ + typedef find_convertible_property found; \ + typename found::query_result_type result; \ + prop_fns_[found::index].query(&result, object_fns_->target(*this), \ + &static_cast(p)); \ + return result; \ + } \ + \ + template \ + typename find_convertible_property::query_result_type \ + query(const Property& p, \ + typename enable_if< \ + !is_same< \ + typename find_convertible_property::query_result_type, \ + void \ + >::value \ + && \ + !is_reference< \ + typename find_convertible_property::query_result_type \ + >::value \ + && \ + !is_scalar< \ + typename find_convertible_property::query_result_type \ + >::value \ + >::type* = 0) const \ + { \ + typedef find_convertible_property found; \ + typename found::query_result_type* result; \ + prop_fns_[found::index].query(&result, object_fns_->target(*this), \ + &static_cast(p)); \ + return *asio::detail::scoped_ptr< \ + typename found::query_result_type>(result); \ + } \ + \ + template \ + struct find_convertible_requirable_property : \ + detail::supportable_properties< \ + 0, void(ASIO_VARIADIC_TARGS(n))>::template \ + find_convertible_requirable_property {}; \ + \ + template \ + any_executor require(const Property& p, \ + typename enable_if< \ + find_convertible_requirable_property::value \ + >::type* = 0) const \ + { \ + typedef find_convertible_requirable_property found; \ + return prop_fns_[found::index].require(object_fns_->target(*this), \ + &static_cast(p)); \ + } \ + \ + template \ + struct find_convertible_preferable_property : \ + detail::supportable_properties< \ + 0, void(ASIO_VARIADIC_TARGS(n))>::template \ + find_convertible_preferable_property {}; \ + \ + template \ + any_executor prefer(const Property& p, \ + typename enable_if< \ + find_convertible_preferable_property::value \ + >::type* = 0) const \ + { \ + typedef find_convertible_preferable_property found; \ + return prop_fns_[found::index].prefer(object_fns_->target(*this), \ + &static_cast(p)); \ + } \ + \ + template \ + static const prop_fns* prop_fns_table() \ + { \ + static const prop_fns fns[] = \ + { \ + ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS(n) \ + }; \ + return fns; \ + } \ + \ + const prop_fns* prop_fns_; \ + typedef detail::supportable_properties<0, \ + void(ASIO_VARIADIC_TARGS(n))> supportable_properties_type; \ + }; \ + \ + template \ + inline void swap(any_executor& a, \ + any_executor& b) ASIO_NOEXCEPT \ + { \ + return a.swap(b); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_ANY_EXECUTOR_DEF) +#undef ASIO_PRIVATE_ANY_EXECUTOR_DEF +#undef ASIO_PRIVATE_ANY_EXECUTOR_MOVE_OPS +#undef ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS +#undef ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_1 +#undef ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_2 +#undef ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_3 +#undef ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_4 +#undef ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_5 +#undef ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_6 +#undef ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_7 +#undef ASIO_PRIVATE_ANY_EXECUTOR_PROP_FNS_8 + +#endif // if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +} // namespace execution +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct equality_comparable > +{ + static const bool is_valid = true; + static const bool is_noexcept = true; +}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template <> +struct equality_comparable > +{ + static const bool is_valid = true; + static const bool is_noexcept = true; +}; + +#define ASIO_PRIVATE_ANY_EXECUTOR_EQUALITY_COMPARABLE_DEF(n) \ + template \ + struct equality_comparable< \ + execution::any_executor > \ + { \ + static const bool is_valid = true; \ + static const bool is_noexcept = true; \ + }; \ + /**/ + ASIO_VARIADIC_GENERATE( + ASIO_PRIVATE_ANY_EXECUTOR_EQUALITY_COMPARABLE_DEF) +#undef ASIO_PRIVATE_ANY_EXECUTOR_EQUALITY_COMPARABLE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +#endif // !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct execute_member, F> +{ + static const bool is_valid = true; + static const bool is_noexcept = false; + typedef void result_type; +}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct execute_member, F> +{ + static const bool is_valid = true; + static const bool is_noexcept = false; + typedef void result_type; +}; + +#define ASIO_PRIVATE_ANY_EXECUTOR_EXECUTE_MEMBER_DEF(n) \ + template \ + struct execute_member< \ + execution::any_executor, F> \ + { \ + static const bool is_valid = true; \ + static const bool is_noexcept = false; \ + typedef void result_type; \ + }; \ + /**/ + ASIO_VARIADIC_GENERATE( + ASIO_PRIVATE_ANY_EXECUTOR_EXECUTE_MEMBER_DEF) +#undef ASIO_PRIVATE_ANY_EXECUTOR_EXECUTE_MEMBER_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +#endif // !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct query_member< + execution::any_executor, Prop, + typename enable_if< + execution::detail::supportable_properties< + 0, void(SupportableProperties...)>::template + find_convertible_property::value + >::type> +{ + static const bool is_valid = true; + static const bool is_noexcept = false; + typedef typename execution::detail::supportable_properties< + 0, void(SupportableProperties...)>::template + find_convertible_property::query_result_type result_type; +}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#define ASIO_PRIVATE_ANY_EXECUTOR_QUERY_MEMBER_DEF(n) \ + template \ + struct query_member< \ + execution::any_executor, Prop, \ + typename enable_if< \ + execution::detail::supportable_properties< \ + 0, void(ASIO_VARIADIC_TARGS(n))>::template \ + find_convertible_property::value \ + >::type> \ + { \ + static const bool is_valid = true; \ + static const bool is_noexcept = false; \ + typedef typename execution::detail::supportable_properties< \ + 0, void(ASIO_VARIADIC_TARGS(n))>::template \ + find_convertible_property::query_result_type result_type; \ + }; \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_ANY_EXECUTOR_QUERY_MEMBER_DEF) +#undef ASIO_PRIVATE_ANY_EXECUTOR_QUERY_MEMBER_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT) +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct require_member< + execution::any_executor, Prop, + typename enable_if< + execution::detail::supportable_properties< + 0, void(SupportableProperties...)>::template + find_convertible_requirable_property::value + >::type> +{ + static const bool is_valid = true; + static const bool is_noexcept = false; + typedef execution::any_executor result_type; +}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#define ASIO_PRIVATE_ANY_EXECUTOR_REQUIRE_MEMBER_DEF(n) \ + template \ + struct require_member< \ + execution::any_executor, Prop, \ + typename enable_if< \ + execution::detail::supportable_properties< \ + 0, void(ASIO_VARIADIC_TARGS(n))>::template \ + find_convertible_requirable_property::value \ + >::type> \ + { \ + static const bool is_valid = true; \ + static const bool is_noexcept = false; \ + typedef execution::any_executor result_type; \ + }; \ + /**/ + ASIO_VARIADIC_GENERATE( + ASIO_PRIVATE_ANY_EXECUTOR_REQUIRE_MEMBER_DEF) +#undef ASIO_PRIVATE_ANY_EXECUTOR_REQUIRE_MEMBER_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +#endif // !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_PREFER_FREE_TRAIT) +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct prefer_member< + execution::any_executor, Prop, + typename enable_if< + execution::detail::supportable_properties< + 0, void(SupportableProperties...)>::template + find_convertible_preferable_property::value + >::type> +{ + static const bool is_valid = true; + static const bool is_noexcept = false; + typedef execution::any_executor result_type; +}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#define ASIO_PRIVATE_ANY_EXECUTOR_PREFER_FREE_DEF(n) \ + template \ + struct prefer_member< \ + execution::any_executor, Prop, \ + typename enable_if< \ + execution::detail::supportable_properties< \ + 0, void(ASIO_VARIADIC_TARGS(n))>::template \ + find_convertible_preferable_property::value \ + >::type> \ + { \ + static const bool is_valid = true; \ + static const bool is_noexcept = false; \ + typedef execution::any_executor result_type; \ + }; \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_ANY_EXECUTOR_PREFER_FREE_DEF) +#undef ASIO_PRIVATE_ANY_EXECUTOR_PREFER_FREE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +#endif // !defined(ASIO_HAS_DEDUCED_PREFER_FREE_TRAIT) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_ANY_EXECUTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/bad_executor.hpp b/third_party/asio/1.18.2/include/asio/execution/bad_executor.hpp new file mode 100644 index 000000000..0532ceff1 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/bad_executor.hpp @@ -0,0 +1,47 @@ +// +// execution/bad_executor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_BAD_EXECUTOR_HPP +#define ASIO_EXECUTION_BAD_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { + +/// Exception thrown when trying to access an empty polymorphic executor. +class bad_executor + : public std::exception +{ +public: + /// Constructor. + ASIO_DECL bad_executor() ASIO_NOEXCEPT; + + /// Obtain message associated with exception. + ASIO_DECL virtual const char* what() const + ASIO_NOEXCEPT_OR_NOTHROW; +}; + +} // namespace execution +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/execution/impl/bad_executor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_EXECUTION_BAD_EXECUTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/blocking.hpp b/third_party/asio/1.18.2/include/asio/execution/blocking.hpp new file mode 100644 index 000000000..76a2eb523 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/blocking.hpp @@ -0,0 +1,1551 @@ +// +// execution/blocking.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_BLOCKING_HPP +#define ASIO_EXECUTION_BLOCKING_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/execute.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/prefer.hpp" +#include "asio/query.hpp" +#include "asio/require.hpp" +#include "asio/traits/query_free.hpp" +#include "asio/traits/query_member.hpp" +#include "asio/traits/query_static_constexpr_member.hpp" +#include "asio/traits/static_query.hpp" +#include "asio/traits/static_require.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property to describe what guarantees an executor makes about the blocking +/// behaviour of their execution functions. +struct blocking_t +{ + /// The blocking_t property applies to executors, senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The top-level blocking_t property cannot be required. + static constexpr bool is_requirable = false; + + /// The top-level blocking_t property cannot be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef blocking_t polymorphic_query_result_type; + + /// A sub-property that indicates that invocation of an executor's execution + /// function may block pending completion of one or more invocations of the + /// submitted function object. + struct possibly_t + { + /// The blocking_t::possibly_t property applies to executors, senders, and + /// schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The blocking_t::possibly_t property can be required. + static constexpr bool is_requirable = true; + + /// The blocking_t::possibly_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef blocking_t polymorphic_query_result_type; + + /// Default constructor. + constexpr possibly_t(); + + /// Get the value associated with a property object. + /** + * @returns possibly_t(); + */ + static constexpr blocking_t value(); + }; + + /// A sub-property that indicates that invocation of an executor's execution + /// function shall block until completion of all invocations of the submitted + /// function object. + struct always_t + { + /// The blocking_t::always_t property applies to executors, senders, and + /// schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The blocking_t::always_t property can be required. + static constexpr bool is_requirable = true; + + /// The blocking_t::always_t property can be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef blocking_t polymorphic_query_result_type; + + /// Default constructor. + constexpr always_t(); + + /// Get the value associated with a property object. + /** + * @returns always_t(); + */ + static constexpr blocking_t value(); + }; + + /// A sub-property that indicates that invocation of an executor's execution + /// function shall not block pending completion of the invocations of the + /// submitted function object. + struct never_t + { + /// The blocking_t::never_t property applies to executors, senders, and + /// schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The blocking_t::never_t property can be required. + static constexpr bool is_requirable = true; + + /// The blocking_t::never_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef blocking_t polymorphic_query_result_type; + + /// Default constructor. + constexpr never_t(); + + /// Get the value associated with a property object. + /** + * @returns never_t(); + */ + static constexpr blocking_t value(); + }; + + /// A special value used for accessing the blocking_t::possibly_t property. + static constexpr possibly_t possibly; + + /// A special value used for accessing the blocking_t::always_t property. + static constexpr always_t always; + + /// A special value used for accessing the blocking_t::never_t property. + static constexpr never_t never; + + /// Default constructor. + constexpr blocking_t(); + + /// Construct from a sub-property value. + constexpr blocking_t(possibly_t); + + /// Construct from a sub-property value. + constexpr blocking_t(always_t); + + /// Construct from a sub-property value. + constexpr blocking_t(never_t); + + /// Compare property values for equality. + friend constexpr bool operator==( + const blocking_t& a, const blocking_t& b) noexcept; + + /// Compare property values for inequality. + friend constexpr bool operator!=( + const blocking_t& a, const blocking_t& b) noexcept; +}; + +/// A special value used for accessing the blocking_t property. +constexpr blocking_t blocking; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { +namespace detail { +namespace blocking { + +template struct possibly_t; +template struct always_t; +template struct never_t; + +} // namespace blocking +namespace blocking_adaptation { + +template struct allowed_t; + +template +void blocking_execute( + ASIO_MOVE_ARG(Executor) ex, + ASIO_MOVE_ARG(Function) func); + +} // namespace blocking_adaptation + +template +struct blocking_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = false); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + typedef blocking_t polymorphic_query_result_type; + + typedef detail::blocking::possibly_t possibly_t; + typedef detail::blocking::always_t always_t; + typedef detail::blocking::never_t never_t; + + ASIO_CONSTEXPR blocking_t() + : value_(-1) + { + } + + ASIO_CONSTEXPR blocking_t(possibly_t) + : value_(0) + { + } + + ASIO_CONSTEXPR blocking_t(always_t) + : value_(1) + { + } + + ASIO_CONSTEXPR blocking_t(never_t) + : value_(2) + { + } + + template + struct proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + struct type + { + template + auto query(ASIO_MOVE_ARG(P) p) const + noexcept( + noexcept( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ); + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + }; + + template + struct static_proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + struct type + { + template + static constexpr auto query(ASIO_MOVE_ARG(P) p) + noexcept( + noexcept( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + { + return T::query(ASIO_MOVE_CAST(P)(p)); + } + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + }; + + template + struct query_member : + traits::query_member::type, blocking_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename static_proxy::type, blocking_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = blocking_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + friend ASIO_CONSTEXPR bool operator==( + const blocking_t& a, const blocking_t& b) + { + return a.value_ == b.value_; + } + + friend ASIO_CONSTEXPR bool operator!=( + const blocking_t& a, const blocking_t& b) + { + return a.value_ != b.value_; + } + + struct convertible_from_blocking_t + { + ASIO_CONSTEXPR convertible_from_blocking_t(blocking_t) {} + }; + + template + friend ASIO_CONSTEXPR blocking_t query( + const Executor& ex, convertible_from_blocking_t, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::possibly_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, possibly_t()); + } + + template + friend ASIO_CONSTEXPR blocking_t query( + const Executor& ex, convertible_from_blocking_t, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::always_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, always_t()); + } + + template + friend ASIO_CONSTEXPR blocking_t query( + const Executor& ex, convertible_from_blocking_t, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::never_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, never_t()); + } + + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(possibly_t, possibly); + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(always_t, always); + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(never_t, never); + +#if !defined(ASIO_HAS_CONSTEXPR) + static const blocking_t instance; +#endif // !defined(ASIO_HAS_CONSTEXPR) + +private: + int value_; +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T blocking_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_CONSTEXPR) +template +const blocking_t blocking_t::instance; +#endif + +template +const typename blocking_t::possibly_t blocking_t::possibly; + +template +const typename blocking_t::always_t blocking_t::always; + +template +const typename blocking_t::never_t blocking_t::never; + +namespace blocking { + +template +struct possibly_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef blocking_t polymorphic_query_result_type; + + ASIO_CONSTEXPR possibly_t() + { + } + + template + struct query_member : + traits::query_member< + typename blocking_t::template proxy::type, possibly_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename blocking_t::template static_proxy::type, possibly_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR possibly_t static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::query_free::is_valid + >::type* = 0, + typename enable_if< + !can_query >::value + >::type* = 0, + typename enable_if< + !can_query >::value + >::type* = 0) ASIO_NOEXCEPT + { + return possibly_t(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = possibly_t::static_query(); +#endif // defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR blocking_t value() + { + return possibly_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const possibly_t&, const possibly_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const possibly_t&, const possibly_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator==( + const possibly_t&, const always_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const possibly_t&, const always_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator==( + const possibly_t&, const never_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const possibly_t&, const never_t&) + { + return true; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T possibly_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +class adapter +{ +public: + adapter(int, const Executor& e) ASIO_NOEXCEPT + : executor_(e) + { + } + + adapter(const adapter& other) ASIO_NOEXCEPT + : executor_(other.executor_) + { + } + +#if defined(ASIO_HAS_MOVE) + adapter(adapter&& other) ASIO_NOEXCEPT + : executor_(ASIO_MOVE_CAST(Executor)(other.executor_)) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + template + static ASIO_CONSTEXPR always_t query( + blocking_t) ASIO_NOEXCEPT + { + return always_t(); + } + + template + static ASIO_CONSTEXPR always_t query( + possibly_t) ASIO_NOEXCEPT + { + return always_t(); + } + + template + static ASIO_CONSTEXPR always_t query( + always_t) ASIO_NOEXCEPT + { + return always_t(); + } + + template + static ASIO_CONSTEXPR always_t query( + never_t) ASIO_NOEXCEPT + { + return always_t(); + } + + template + typename enable_if< + can_query::value, + typename query_result::type + >::type query(const Property& p) const + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) + { + return asio::query(executor_, p); + } + + template + typename enable_if< + can_require >::value, + typename require_result >::type + >::type require(possibly_t) const ASIO_NOEXCEPT + { + return asio::require(executor_, possibly_t()); + } + + template + typename enable_if< + can_require >::value, + typename require_result >::type + >::type require(never_t) const ASIO_NOEXCEPT + { + return asio::require(executor_, never_t()); + } + + template + typename enable_if< + can_require::value, + adapter::type + >::type> + >::type require(const Property& p) const + ASIO_NOEXCEPT_IF(( + is_nothrow_require::value)) + { + return adapter::type + >::type>(0, asio::require(executor_, p)); + } + + template + typename enable_if< + can_prefer::value, + adapter::type + >::type> + >::type prefer(const Property& p) const + ASIO_NOEXCEPT_IF(( + is_nothrow_prefer::value)) + { + return adapter::type + >::type>(0, asio::prefer(executor_, p)); + } + + template + typename enable_if< + execution::can_execute::value + >::type execute(ASIO_MOVE_ARG(Function) f) const + { + blocking_adaptation::blocking_execute( + executor_, ASIO_MOVE_CAST(Function)(f)); + } + + friend bool operator==(const adapter& a, const adapter& b) ASIO_NOEXCEPT + { + return a.executor_ == b.executor_; + } + + friend bool operator!=(const adapter& a, const adapter& b) ASIO_NOEXCEPT + { + return a.executor_ != b.executor_; + } + +private: + Executor executor_; +}; + +template +struct always_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + typedef blocking_t polymorphic_query_result_type; + + ASIO_CONSTEXPR always_t() + { + } + + template + struct query_member : + traits::query_member< + typename blocking_t::template proxy::type, always_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename blocking_t::template static_proxy::type, always_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = always_t::static_query(); +#endif // defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR blocking_t value() + { + return always_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const always_t&, const always_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const always_t&, const always_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator==( + const always_t&, const possibly_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const always_t&, const possibly_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator==( + const always_t&, const never_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const always_t&, const never_t&) + { + return true; + } + + template + friend adapter require( + const Executor& e, const always_t&, + typename enable_if< + is_executor::value + >::type* = 0, + typename enable_if< + traits::static_require< + const Executor&, + blocking_adaptation::allowed_t<0> + >::is_valid + >::type* = 0) + { + return adapter(0, e); + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T always_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct never_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef blocking_t polymorphic_query_result_type; + + ASIO_CONSTEXPR never_t() + { + } + + template + struct query_member : + traits::query_member< + typename blocking_t::template proxy::type, never_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename blocking_t::template static_proxy::type, never_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = never_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR blocking_t value() + { + return never_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const never_t&, const never_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const never_t&, const never_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator==( + const never_t&, const possibly_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const never_t&, const possibly_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator==( + const never_t&, const always_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const never_t&, const always_t&) + { + return true; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T never_t::static_query_v; +#endif // defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +} // namespace blocking +} // namespace detail + +typedef detail::blocking_t<> blocking_t; + +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr blocking_t blocking; +#else // defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +namespace { static const blocking_t& blocking = blocking_t::instance; } +#endif + +} // namespace execution + +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +template +struct query_free_default::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::blocking_t result_type; +}; + +template +struct query_free_default::value + && can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::blocking_t result_type; +}; + +template +struct query_free_default::value + && !can_query::value + && can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::blocking_t result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::blocking_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::blocking_t::query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::blocking_t<0>:: + query_member::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::blocking_t<0>:: + query_member::is_valid + && !traits::static_query::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::blocking_t<0>:: + query_member::is_valid + && !traits::static_query::is_valid + && !traits::static_query::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::blocking::possibly_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::blocking::possibly_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::blocking::possibly_t<0>:: + query_member::is_valid + && !traits::query_free::is_valid + && !can_query::value + && !can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef execution::blocking_t::possibly_t result_type; + + static ASIO_CONSTEXPR result_type value() + { + return result_type(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::blocking::always_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::blocking::always_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::blocking::never_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::blocking::never_t<0>:: + query_static_constexpr_member::value(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::blocking_t::possibly_t>::value)); +}; + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::blocking_t::always_t>::value)); +}; + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::blocking_t::never_t>::value)); +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_REQUIRE_FREE_TRAIT) + +template +struct require_free_default::type>::value + && execution::is_executor::value + && traits::static_require< + const T&, + execution::detail::blocking_adaptation::allowed_t<0> + >::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef execution::detail::blocking::adapter result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_REQUIRE_FREE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) + +template +struct equality_comparable< + execution::detail::blocking::adapter > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); +}; + +#endif // !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + +template +struct execute_member< + execution::detail::blocking::adapter, Function> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + +template +struct query_static_constexpr_member< + execution::detail::blocking::adapter, + execution::detail::blocking_t > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef execution::blocking_t::always_t result_type; + + static ASIO_CONSTEXPR result_type value() ASIO_NOEXCEPT + { + return result_type(); + } +}; + +template +struct query_static_constexpr_member< + execution::detail::blocking::adapter, + execution::detail::blocking::always_t > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef execution::blocking_t::always_t result_type; + + static ASIO_CONSTEXPR result_type value() ASIO_NOEXCEPT + { + return result_type(); + } +}; + +template +struct query_static_constexpr_member< + execution::detail::blocking::adapter, + execution::detail::blocking::possibly_t > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef execution::blocking_t::always_t result_type; + + static ASIO_CONSTEXPR result_type value() ASIO_NOEXCEPT + { + return result_type(); + } +}; + +template +struct query_static_constexpr_member< + execution::detail::blocking::adapter, + execution::detail::blocking::never_t > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef execution::blocking_t::always_t result_type; + + static ASIO_CONSTEXPR result_type value() ASIO_NOEXCEPT + { + return result_type(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + +template +struct query_member< + execution::detail::blocking::adapter, Property, + typename enable_if< + can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + typedef typename query_result::type result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT) + +template +struct require_member< + execution::detail::blocking::adapter, + execution::detail::blocking::possibly_t, + typename enable_if< + can_require< + const Executor&, + execution::detail::blocking::possibly_t + >::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_require >::value)); + typedef typename require_result >::type result_type; +}; + +template +struct require_member< + execution::detail::blocking::adapter, + execution::detail::blocking::never_t, + typename enable_if< + can_require< + const Executor&, + execution::detail::blocking::never_t + >::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_require >::value)); + typedef typename require_result >::type result_type; +}; + +template +struct require_member< + execution::detail::blocking::adapter, Property, + typename enable_if< + can_require::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_require::value)); + typedef execution::detail::blocking::adapter::type + >::type> result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT) + +template +struct prefer_member< + execution::detail::blocking::adapter, Property, + typename enable_if< + can_prefer::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_prefer::value)); + typedef execution::detail::blocking::adapter::type + >::type> result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_BLOCKING_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/blocking_adaptation.hpp b/third_party/asio/1.18.2/include/asio/execution/blocking_adaptation.hpp new file mode 100644 index 000000000..2a3fe908d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/blocking_adaptation.hpp @@ -0,0 +1,1212 @@ +// +// execution/blocking_adaptation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_BLOCKING_ADAPTATION_HPP +#define ASIO_EXECUTION_BLOCKING_ADAPTATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/event.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/execute.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/prefer.hpp" +#include "asio/query.hpp" +#include "asio/require.hpp" +#include "asio/traits/prefer_member.hpp" +#include "asio/traits/query_free.hpp" +#include "asio/traits/query_member.hpp" +#include "asio/traits/query_static_constexpr_member.hpp" +#include "asio/traits/require_member.hpp" +#include "asio/traits/static_query.hpp" +#include "asio/traits/static_require.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property to describe whether automatic adaptation of an executor is +/// allowed in order to apply the blocking_adaptation_t::allowed_t property. +struct blocking_adaptation_t +{ + /// The blocking_adaptation_t property applies to executors, senders, and + /// schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The top-level blocking_adaptation_t property cannot be required. + static constexpr bool is_requirable = false; + + /// The top-level blocking_adaptation_t property cannot be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef blocking_adaptation_t polymorphic_query_result_type; + + /// A sub-property that indicates that automatic adaptation is not allowed. + struct disallowed_t + { + /// The blocking_adaptation_t::disallowed_t property applies to executors, + /// senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The blocking_adaptation_t::disallowed_t property can be required. + static constexpr bool is_requirable = true; + + /// The blocking_adaptation_t::disallowed_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef blocking_adaptation_t polymorphic_query_result_type; + + /// Default constructor. + constexpr disallowed_t(); + + /// Get the value associated with a property object. + /** + * @returns disallowed_t(); + */ + static constexpr blocking_adaptation_t value(); + }; + + /// A sub-property that indicates that automatic adaptation is allowed. + struct allowed_t + { + /// The blocking_adaptation_t::allowed_t property applies to executors, + /// senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The blocking_adaptation_t::allowed_t property can be required. + static constexpr bool is_requirable = true; + + /// The blocking_adaptation_t::allowed_t property can be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef blocking_adaptation_t polymorphic_query_result_type; + + /// Default constructor. + constexpr allowed_t(); + + /// Get the value associated with a property object. + /** + * @returns allowed_t(); + */ + static constexpr blocking_adaptation_t value(); + }; + + /// A special value used for accessing the blocking_adaptation_t::disallowed_t + /// property. + static constexpr disallowed_t disallowed; + + /// A special value used for accessing the blocking_adaptation_t::allowed_t + /// property. + static constexpr allowed_t allowed; + + /// Default constructor. + constexpr blocking_adaptation_t(); + + /// Construct from a sub-property value. + constexpr blocking_adaptation_t(disallowed_t); + + /// Construct from a sub-property value. + constexpr blocking_adaptation_t(allowed_t); + + /// Compare property values for equality. + friend constexpr bool operator==( + const blocking_adaptation_t& a, const blocking_adaptation_t& b) noexcept; + + /// Compare property values for inequality. + friend constexpr bool operator!=( + const blocking_adaptation_t& a, const blocking_adaptation_t& b) noexcept; +}; + +/// A special value used for accessing the blocking_adaptation_t property. +constexpr blocking_adaptation_t blocking_adaptation; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { +namespace detail { +namespace blocking_adaptation { + +template struct disallowed_t; +template struct allowed_t; + +} // namespace blocking_adaptation + +template +struct blocking_adaptation_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = false); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + typedef blocking_adaptation_t polymorphic_query_result_type; + + typedef detail::blocking_adaptation::disallowed_t disallowed_t; + typedef detail::blocking_adaptation::allowed_t allowed_t; + + ASIO_CONSTEXPR blocking_adaptation_t() + : value_(-1) + { + } + + ASIO_CONSTEXPR blocking_adaptation_t(disallowed_t) + : value_(0) + { + } + + ASIO_CONSTEXPR blocking_adaptation_t(allowed_t) + : value_(1) + { + } + + template + struct proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + struct type + { + template + auto query(ASIO_MOVE_ARG(P) p) const + noexcept( + noexcept( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ); + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + }; + + template + struct static_proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + struct type + { + template + static constexpr auto query(ASIO_MOVE_ARG(P) p) + noexcept( + noexcept( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + { + return T::query(ASIO_MOVE_CAST(P)(p)); + } + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + }; + + template + struct query_member : + traits::query_member::type, blocking_adaptation_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename static_proxy::type, blocking_adaptation_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = blocking_adaptation_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + friend ASIO_CONSTEXPR bool operator==( + const blocking_adaptation_t& a, const blocking_adaptation_t& b) + { + return a.value_ == b.value_; + } + + friend ASIO_CONSTEXPR bool operator!=( + const blocking_adaptation_t& a, const blocking_adaptation_t& b) + { + return a.value_ != b.value_; + } + + struct convertible_from_blocking_adaptation_t + { + ASIO_CONSTEXPR convertible_from_blocking_adaptation_t( + blocking_adaptation_t) + { + } + }; + + template + friend ASIO_CONSTEXPR blocking_adaptation_t query( + const Executor& ex, convertible_from_blocking_adaptation_t, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::disallowed_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, disallowed_t()); + } + + template + friend ASIO_CONSTEXPR blocking_adaptation_t query( + const Executor& ex, convertible_from_blocking_adaptation_t, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::allowed_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, allowed_t()); + } + + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(disallowed_t, disallowed); + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(allowed_t, allowed); + +#if !defined(ASIO_HAS_CONSTEXPR) + static const blocking_adaptation_t instance; +#endif // !defined(ASIO_HAS_CONSTEXPR) + +private: + int value_; +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T blocking_adaptation_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_CONSTEXPR) +template +const blocking_adaptation_t blocking_adaptation_t::instance; +#endif + +template +const typename blocking_adaptation_t::disallowed_t +blocking_adaptation_t::disallowed; + +template +const typename blocking_adaptation_t::allowed_t +blocking_adaptation_t::allowed; + +namespace blocking_adaptation { + +template +struct disallowed_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef blocking_adaptation_t polymorphic_query_result_type; + + ASIO_CONSTEXPR disallowed_t() + { + } + + template + struct query_member : + traits::query_member< + typename blocking_adaptation_t::template proxy::type, + disallowed_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename blocking_adaptation_t::template static_proxy::type, + disallowed_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR disallowed_t static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::query_free::is_valid + >::type* = 0, + typename enable_if< + !can_query >::value + >::type* = 0) ASIO_NOEXCEPT + { + return disallowed_t(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = disallowed_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR blocking_adaptation_t value() + { + return disallowed_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const disallowed_t&, const disallowed_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const disallowed_t&, const disallowed_t&) + { + return false; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T disallowed_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +class adapter +{ +public: + adapter(int, const Executor& e) ASIO_NOEXCEPT + : executor_(e) + { + } + + adapter(const adapter& other) ASIO_NOEXCEPT + : executor_(other.executor_) + { + } + +#if defined(ASIO_HAS_MOVE) + adapter(adapter&& other) ASIO_NOEXCEPT + : executor_(ASIO_MOVE_CAST(Executor)(other.executor_)) + { + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + template + static ASIO_CONSTEXPR allowed_t query( + blocking_adaptation_t) ASIO_NOEXCEPT + { + return allowed_t(); + } + + template + static ASIO_CONSTEXPR allowed_t query( + allowed_t) ASIO_NOEXCEPT + { + return allowed_t(); + } + + template + static ASIO_CONSTEXPR allowed_t query( + disallowed_t) ASIO_NOEXCEPT + { + return allowed_t(); + } + + template + typename enable_if< + can_query::value, + typename query_result::type + >::type query(const Property& p) const + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) + { + return asio::query(executor_, p); + } + + template + Executor require(disallowed_t) const ASIO_NOEXCEPT + { + return executor_; + } + + template + typename enable_if< + can_require::value, + adapter::type + >::type> + >::type require(const Property& p) const + ASIO_NOEXCEPT_IF(( + is_nothrow_require::value)) + { + return adapter::type + >::type>(0, asio::require(executor_, p)); + } + + template + typename enable_if< + can_prefer::value, + adapter::type + >::type> + >::type prefer(const Property& p) const + ASIO_NOEXCEPT_IF(( + is_nothrow_prefer::value)) + { + return adapter::type + >::type>(0, asio::prefer(executor_, p)); + } + + template + typename enable_if< + execution::can_execute::value + >::type execute(ASIO_MOVE_ARG(Function) f) const + { + execution::execute(executor_, ASIO_MOVE_CAST(Function)(f)); + } + + friend bool operator==(const adapter& a, const adapter& b) ASIO_NOEXCEPT + { + return a.executor_ == b.executor_; + } + + friend bool operator!=(const adapter& a, const adapter& b) ASIO_NOEXCEPT + { + return a.executor_ != b.executor_; + } + +private: + Executor executor_; +}; + +template +struct allowed_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + typedef blocking_adaptation_t polymorphic_query_result_type; + + ASIO_CONSTEXPR allowed_t() + { + } + + template + struct query_member : + traits::query_member< + typename blocking_adaptation_t::template proxy::type, + allowed_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename blocking_adaptation_t::template static_proxy::type, + allowed_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = allowed_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR blocking_adaptation_t value() + { + return allowed_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const allowed_t&, const allowed_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const allowed_t&, const allowed_t&) + { + return false; + } + + template + friend adapter require( + const Executor& e, const allowed_t&, + typename enable_if< + is_executor::value + >::type* = 0) + { + return adapter(0, e); + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T allowed_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +class blocking_execute_state +{ +public: + template + blocking_execute_state(ASIO_MOVE_ARG(F) f) + : func_(ASIO_MOVE_CAST(F)(f)), + is_complete_(false) + { + } + + template + void execute_and_wait(ASIO_MOVE_ARG(Executor) ex) + { + handler h = { this }; + execution::execute(ASIO_MOVE_CAST(Executor)(ex), h); + asio::detail::mutex::scoped_lock lock(mutex_); + while (!is_complete_) + event_.wait(lock); + } + + struct cleanup + { + ~cleanup() + { + asio::detail::mutex::scoped_lock lock(state_->mutex_); + state_->is_complete_ = true; + state_->event_.unlock_and_signal_one_for_destruction(lock); + } + + blocking_execute_state* state_; + }; + + struct handler + { + void operator()() + { + cleanup c = { state_ }; + state_->func_(); + } + + blocking_execute_state* state_; + }; + + Function func_; + asio::detail::mutex mutex_; + asio::detail::event event_; + bool is_complete_; +}; + +template +void blocking_execute( + ASIO_MOVE_ARG(Executor) ex, + ASIO_MOVE_ARG(Function) func) +{ + typedef typename decay::type func_t; + blocking_execute_state state(ASIO_MOVE_CAST(Function)(func)); + state.execute_and_wait(ex); +} + +} // namespace blocking_adaptation +} // namespace detail + +typedef detail::blocking_adaptation_t<> blocking_adaptation_t; + +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr blocking_adaptation_t blocking_adaptation; +#else // defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +namespace { static const blocking_adaptation_t& + blocking_adaptation = blocking_adaptation_t::instance; } +#endif + +} // namespace execution + +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +template +struct query_free_default::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = (is_nothrow_query::value)); + + typedef execution::blocking_adaptation_t result_type; +}; + +template +struct query_free_default::value + && can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::blocking_adaptation_t result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::blocking_adaptation_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::blocking_adaptation_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::blocking_adaptation_t<0>:: + query_member::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::blocking_adaptation_t<0>:: + query_member::is_valid + && !traits::static_query::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::blocking_adaptation::disallowed_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::blocking_adaptation::disallowed_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::blocking_adaptation::disallowed_t<0>:: + query_member::is_valid + && !traits::query_free::is_valid + && !can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef execution::blocking_adaptation_t::disallowed_t result_type; + + static ASIO_CONSTEXPR result_type value() + { + return result_type(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::blocking_adaptation::allowed_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::blocking_adaptation::allowed_t<0>:: + query_static_constexpr_member::value(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::blocking_adaptation_t::disallowed_t>::value)); +}; + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::blocking_adaptation_t::allowed_t>::value)); +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_REQUIRE_FREE_TRAIT) + +template +struct require_free_default::type>::value + && execution::is_executor::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef execution::detail::blocking_adaptation::adapter result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_REQUIRE_FREE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) + +template +struct equality_comparable< + execution::detail::blocking_adaptation::adapter > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); +}; + +#endif // !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + +template +struct execute_member< + execution::detail::blocking_adaptation::adapter, Function> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + +template +struct query_static_constexpr_member< + execution::detail::blocking_adaptation::adapter, + execution::detail::blocking_adaptation_t > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef execution::blocking_adaptation_t::allowed_t result_type; + + static ASIO_CONSTEXPR result_type value() ASIO_NOEXCEPT + { + return result_type(); + } +}; + +template +struct query_static_constexpr_member< + execution::detail::blocking_adaptation::adapter, + execution::detail::blocking_adaptation::allowed_t > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef execution::blocking_adaptation_t::allowed_t result_type; + + static ASIO_CONSTEXPR result_type value() ASIO_NOEXCEPT + { + return result_type(); + } +}; + +template +struct query_static_constexpr_member< + execution::detail::blocking_adaptation::adapter, + execution::detail::blocking_adaptation::disallowed_t > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef execution::blocking_adaptation_t::allowed_t result_type; + + static ASIO_CONSTEXPR result_type value() ASIO_NOEXCEPT + { + return result_type(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + +template +struct query_member< + execution::detail::blocking_adaptation::adapter, Property, + typename enable_if< + can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + typedef typename query_result::type result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT) + +template +struct require_member< + execution::detail::blocking_adaptation::adapter, + execution::detail::blocking_adaptation::disallowed_t > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef Executor result_type; +}; + +template +struct require_member< + execution::detail::blocking_adaptation::adapter, Property, + typename enable_if< + can_require::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_require::value)); + typedef execution::detail::blocking_adaptation::adapter::type + >::type> result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT) + +template +struct prefer_member< + execution::detail::blocking_adaptation::adapter, Property, + typename enable_if< + can_prefer::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_prefer::value)); + typedef execution::detail::blocking_adaptation::adapter::type + >::type> result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_BLOCKING_ADAPTATION_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/bulk_execute.hpp b/third_party/asio/1.18.2/include/asio/execution/bulk_execute.hpp new file mode 100644 index 000000000..ef76e06f3 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/bulk_execute.hpp @@ -0,0 +1,395 @@ +// +// execution/bulk_execute.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_BULK_EXECUTE_HPP +#define ASIO_EXECUTION_BULK_EXECUTE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/bulk_guarantee.hpp" +#include "asio/execution/detail/bulk_sender.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/sender.hpp" +#include "asio/traits/bulk_execute_member.hpp" +#include "asio/traits/bulk_execute_free.hpp" + +#include "asio/detail/push_options.hpp" + +#if defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace execution { + +/// A customisation point that creates a bulk sender. +/** + * The name execution::bulk_execute denotes a customisation point + * object. If is_convertible_v is true, then the expression + * execution::bulk_execute(S, F, N) for some subexpressions + * S, F, and N is expression-equivalent to: + * + * @li S.bulk_execute(F, N), if that expression is valid. If the + * function selected does not execute N invocations of the function + * object F on the executor S in bulk with forward progress + * guarantee asio::query(S, execution::bulk_guarantee), and + * the result of that function does not model sender, the + * program is ill-formed with no diagnostic required. + * + * @li Otherwise, bulk_execute(S, F, N), if that expression is valid, + * with overload resolution performed in a context that includes the + * declaration void bulk_execute(); and that does not include a + * declaration of execution::bulk_execute. If the function selected + * by overload resolution does not execute N invocations of the + * function object F on the executor S in bulk with forward + * progress guarantee asio::query(E, + * execution::bulk_guarantee), and the result of that function does not + * model sender, the program is ill-formed with no diagnostic + * required. + * + * @li Otherwise, if the types F and + * executor_index_t> model invocable and + * if asio::query(S, execution::bulk_guarantee) equals + * execution::bulk_guarantee.unsequenced, then + * + * - Evaluates DECAY_COPY(std::forward(F)) on the + * calling thread to create a function object cf. [Note: + * Additional copies of cf may subsequently be created. --end + * note.] + * + * - For each value of i in N, cf(i) (or copy of + * cf)) will be invoked at most once by an execution agent that is + * unique for each value of i. + * + * - May block pending completion of one or more invocations of cf. + * + * - Synchronizes with (C++Std [intro.multithread]) the invocations of + * cf. + * + * @li Otherwise, execution::bulk_execute(S, F, N) is ill-formed. + */ +inline constexpr unspecified bulk_execute = unspecified; + +/// A type trait that determines whether a @c bulk_execute expression is +/// well-formed. +/** + * Class template @c can_bulk_execute is a trait that is derived from @c + * true_type if the expression execution::bulk_execute(std::declval(), + * std::declval(), std::declval) is well formed; otherwise @c + * false_type. + */ +template +struct can_bulk_execute : + integral_constant +{ +}; + +} // namespace execution +} // namespace asio + +#else // defined(GENERATING_DOCUMENTATION) + +namespace asio_execution_bulk_execute_fn { + +using asio::declval; +using asio::enable_if; +using asio::execution::bulk_guarantee_t; +using asio::execution::detail::bulk_sender; +using asio::execution::executor_index; +using asio::execution::is_sender; +using asio::is_convertible; +using asio::is_same; +using asio::remove_cvref; +using asio::result_of; +using asio::traits::bulk_execute_free; +using asio::traits::bulk_execute_member; +using asio::traits::static_require; + +void bulk_execute(); + +enum overload_type +{ + call_member, + call_free, + adapter, + ill_formed +}; + +template +struct call_traits +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = ill_formed); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef void result_type; +}; + +template +struct call_traits::value + >::type, + typename enable_if< + bulk_execute_member::is_valid + >::type, + typename enable_if< + is_sender< + typename bulk_execute_member::result_type + >::value + >::type> : + bulk_execute_member +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = call_member); +}; + +template +struct call_traits::value + >::type, + typename enable_if< + !bulk_execute_member::is_valid + >::type, + typename enable_if< + bulk_execute_free::is_valid + >::type, + typename enable_if< + is_sender< + typename bulk_execute_free::result_type + >::value + >::type> : + bulk_execute_free +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = call_free); +}; + +template +struct call_traits::value + >::type, + typename enable_if< + !bulk_execute_member::is_valid + >::type, + typename enable_if< + !bulk_execute_free::is_valid + >::type, + typename enable_if< + is_sender::value + >::type, + typename enable_if< + is_same< + typename result_of< + F(typename executor_index::type>::type) + >::type, + typename result_of< + F(typename executor_index::type>::type) + >::type + >::value + >::type, + typename enable_if< + static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = adapter); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef bulk_sender result_type; +}; + +struct impl +{ +#if defined(ASIO_HAS_MOVE) + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(S&& s, F&& f, N&& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return ASIO_MOVE_CAST(S)(s).bulk_execute( + ASIO_MOVE_CAST(F)(f), ASIO_MOVE_CAST(N)(n)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(S&& s, F&& f, N&& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return bulk_execute(ASIO_MOVE_CAST(S)(s), + ASIO_MOVE_CAST(F)(f), ASIO_MOVE_CAST(N)(n)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(S&& s, F&& f, N&& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type( + ASIO_MOVE_CAST(S)(s), ASIO_MOVE_CAST(F)(f), + ASIO_MOVE_CAST(N)(n)); + } +#else // defined(ASIO_HAS_MOVE) + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(S& s, const F& f, const N& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return s.bulk_execute(ASIO_MOVE_CAST(F)(f), + ASIO_MOVE_CAST(N)(n)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(const S& s, const F& f, const N& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return s.bulk_execute(ASIO_MOVE_CAST(F)(f), + ASIO_MOVE_CAST(N)(n)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(S& s, const F& f, const N& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return bulk_execute(s, ASIO_MOVE_CAST(F)(f), + ASIO_MOVE_CAST(N)(n)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(const S& s, const F& f, const N& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return bulk_execute(s, ASIO_MOVE_CAST(F)(f), + ASIO_MOVE_CAST(N)(n)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(S& s, const F& f, const N& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type( + s, ASIO_MOVE_CAST(F)(f), ASIO_MOVE_CAST(N)(n)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(const S& s, const F& f, const N& n) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type( + s, ASIO_MOVE_CAST(F)(f), ASIO_MOVE_CAST(N)(n)); + } +#endif // defined(ASIO_HAS_MOVE) +}; + +template +struct static_instance +{ + static const T instance; +}; + +template +const T static_instance::instance = {}; + +} // namespace asio_execution_bulk_execute_fn +namespace asio { +namespace execution { +namespace { + +static ASIO_CONSTEXPR + const asio_execution_bulk_execute_fn::impl& bulk_execute = + asio_execution_bulk_execute_fn::static_instance<>::instance; + +} // namespace + +template +struct can_bulk_execute : + integral_constant::overload != + asio_execution_bulk_execute_fn::ill_formed> +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +constexpr bool can_bulk_execute_v = can_bulk_execute::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_nothrow_bulk_execute : + integral_constant::is_noexcept> +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +constexpr bool is_nothrow_bulk_execute_v + = is_nothrow_bulk_execute::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct bulk_execute_result +{ + typedef typename asio_execution_bulk_execute_fn::call_traits< + S, void(F, N)>::result_type type; +}; + +} // namespace execution +} // namespace asio + +#endif // defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_BULK_EXECUTE_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/bulk_guarantee.hpp b/third_party/asio/1.18.2/include/asio/execution/bulk_guarantee.hpp new file mode 100644 index 000000000..648221835 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/bulk_guarantee.hpp @@ -0,0 +1,1215 @@ +// +// execution/bulk_guarantee.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_BULK_GUARANTEE_HPP +#define ASIO_EXECUTION_BULK_GUARANTEE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/query.hpp" +#include "asio/traits/query_free.hpp" +#include "asio/traits/query_member.hpp" +#include "asio/traits/query_static_constexpr_member.hpp" +#include "asio/traits/static_query.hpp" +#include "asio/traits/static_require.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property to communicate the forward progress and ordering guarantees of +/// execution agents associated with the bulk execution. +struct bulk_guarantee_t +{ + /// The bulk_guarantee_t property applies to executors, senders, and + /// schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The top-level bulk_guarantee_t property cannot be required. + static constexpr bool is_requirable = false; + + /// The top-level bulk_guarantee_t property cannot be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef bulk_guarantee_t polymorphic_query_result_type; + + /// A sub-property that indicates that execution agents within the same bulk + /// execution may be parallelised and vectorised. + struct unsequenced_t + { + /// The bulk_guarantee_t::unsequenced_t property applies to executors, + /// senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The bulk_guarantee_t::unsequenced_t property can be required. + static constexpr bool is_requirable = true; + + /// The bulk_guarantee_t::unsequenced_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef bulk_guarantee_t polymorphic_query_result_type; + + /// Default constructor. + constexpr unsequenced_t(); + + /// Get the value associated with a property object. + /** + * @returns unsequenced_t(); + */ + static constexpr bulk_guarantee_t value(); + }; + + /// A sub-property that indicates that execution agents within the same bulk + /// execution may not be parallelised and vectorised. + struct sequenced_t + { + /// The bulk_guarantee_t::sequenced_t property applies to executors, + /// senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The bulk_guarantee_t::sequenced_t property can be required. + static constexpr bool is_requirable = true; + + /// The bulk_guarantee_t::sequenced_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef bulk_guarantee_t polymorphic_query_result_type; + + /// Default constructor. + constexpr sequenced_t(); + + /// Get the value associated with a property object. + /** + * @returns sequenced_t(); + */ + static constexpr bulk_guarantee_t value(); + }; + + /// A sub-property that indicates that execution agents within the same bulk + /// execution may be parallelised. + struct parallel_t + { + /// The bulk_guarantee_t::parallel_t property applies to executors, + /// senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The bulk_guarantee_t::parallel_t property can be required. + static constexpr bool is_requirable = true; + + /// The bulk_guarantee_t::parallel_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef bulk_guarantee_t polymorphic_query_result_type; + + /// Default constructor. + constexpr parallel_t(); + + /// Get the value associated with a property object. + /** + * @returns parallel_t(); + */ + static constexpr bulk_guarantee_t value(); + }; + + /// A special value used for accessing the bulk_guarantee_t::unsequenced_t + /// property. + static constexpr unsequenced_t unsequenced; + + /// A special value used for accessing the bulk_guarantee_t::sequenced_t + /// property. + static constexpr sequenced_t sequenced; + + /// A special value used for accessing the bulk_guarantee_t::parallel_t + /// property. + static constexpr parallel_t parallel; + + /// Default constructor. + constexpr bulk_guarantee_t(); + + /// Construct from a sub-property value. + constexpr bulk_guarantee_t(unsequenced_t); + + /// Construct from a sub-property value. + constexpr bulk_guarantee_t(sequenced_t); + + /// Construct from a sub-property value. + constexpr bulk_guarantee_t(parallel_t); + + /// Compare property values for equality. + friend constexpr bool operator==( + const bulk_guarantee_t& a, const bulk_guarantee_t& b) noexcept; + + /// Compare property values for inequality. + friend constexpr bool operator!=( + const bulk_guarantee_t& a, const bulk_guarantee_t& b) noexcept; +}; + +/// A special value used for accessing the bulk_guarantee_t property. +constexpr bulk_guarantee_t bulk_guarantee; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { +namespace detail { +namespace bulk_guarantee { + +template struct unsequenced_t; +template struct sequenced_t; +template struct parallel_t; + +} // namespace bulk_guarantee + +template +struct bulk_guarantee_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = false); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + typedef bulk_guarantee_t polymorphic_query_result_type; + + typedef detail::bulk_guarantee::unsequenced_t unsequenced_t; + typedef detail::bulk_guarantee::sequenced_t sequenced_t; + typedef detail::bulk_guarantee::parallel_t parallel_t; + + ASIO_CONSTEXPR bulk_guarantee_t() + : value_(-1) + { + } + + ASIO_CONSTEXPR bulk_guarantee_t(unsequenced_t) + : value_(0) + { + } + + ASIO_CONSTEXPR bulk_guarantee_t(sequenced_t) + : value_(1) + { + } + + ASIO_CONSTEXPR bulk_guarantee_t(parallel_t) + : value_(2) + { + } + + template + struct proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + struct type + { + template + auto query(ASIO_MOVE_ARG(P) p) const + noexcept( + noexcept( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ); + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + }; + + template + struct static_proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + struct type + { + template + static constexpr auto query(ASIO_MOVE_ARG(P) p) + noexcept( + noexcept( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + { + return T::query(ASIO_MOVE_CAST(P)(p)); + } + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + }; + + template + struct query_member : + traits::query_member::type, bulk_guarantee_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename static_proxy::type, bulk_guarantee_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = bulk_guarantee_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + friend ASIO_CONSTEXPR bool operator==( + const bulk_guarantee_t& a, const bulk_guarantee_t& b) + { + return a.value_ == b.value_; + } + + friend ASIO_CONSTEXPR bool operator!=( + const bulk_guarantee_t& a, const bulk_guarantee_t& b) + { + return a.value_ != b.value_; + } + + struct convertible_from_bulk_guarantee_t + { + ASIO_CONSTEXPR convertible_from_bulk_guarantee_t(bulk_guarantee_t) {} + }; + + template + friend ASIO_CONSTEXPR bulk_guarantee_t query( + const Executor& ex, convertible_from_bulk_guarantee_t, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::unsequenced_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, unsequenced_t()); + } + + template + friend ASIO_CONSTEXPR bulk_guarantee_t query( + const Executor& ex, convertible_from_bulk_guarantee_t, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::sequenced_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, sequenced_t()); + } + + template + friend ASIO_CONSTEXPR bulk_guarantee_t query( + const Executor& ex, convertible_from_bulk_guarantee_t, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::parallel_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, parallel_t()); + } + + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(unsequenced_t, unsequenced); + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(sequenced_t, sequenced); + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(parallel_t, parallel); + +#if !defined(ASIO_HAS_CONSTEXPR) + static const bulk_guarantee_t instance; +#endif // !defined(ASIO_HAS_CONSTEXPR) + +private: + int value_; +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T bulk_guarantee_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_CONSTEXPR) +template +const bulk_guarantee_t bulk_guarantee_t::instance; +#endif + +template +const typename bulk_guarantee_t::unsequenced_t +bulk_guarantee_t::unsequenced; + +template +const typename bulk_guarantee_t::sequenced_t +bulk_guarantee_t::sequenced; + +template +const typename bulk_guarantee_t::parallel_t +bulk_guarantee_t::parallel; + +namespace bulk_guarantee { + +template +struct unsequenced_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef bulk_guarantee_t polymorphic_query_result_type; + + ASIO_CONSTEXPR unsequenced_t() + { + } + + template + struct query_member : + traits::query_member< + typename bulk_guarantee_t::template proxy::type, + unsequenced_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename bulk_guarantee_t::template static_proxy::type, + unsequenced_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR unsequenced_t static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::query_free::is_valid + >::type* = 0, + typename enable_if< + !can_query >::value + >::type* = 0, + typename enable_if< + !can_query >::value + >::type* = 0) ASIO_NOEXCEPT + { + return unsequenced_t(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = unsequenced_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR bulk_guarantee_t value() + { + return unsequenced_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const unsequenced_t&, const unsequenced_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const unsequenced_t&, const unsequenced_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator==( + const unsequenced_t&, const sequenced_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const unsequenced_t&, const sequenced_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator==( + const unsequenced_t&, const parallel_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const unsequenced_t&, const parallel_t&) + { + return true; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T unsequenced_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct sequenced_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef bulk_guarantee_t polymorphic_query_result_type; + + ASIO_CONSTEXPR sequenced_t() + { + } + + template + struct query_member : + traits::query_member< + typename bulk_guarantee_t::template proxy::type, + sequenced_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename bulk_guarantee_t::template static_proxy::type, + sequenced_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = sequenced_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR bulk_guarantee_t value() + { + return sequenced_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const sequenced_t&, const sequenced_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const sequenced_t&, const sequenced_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator==( + const sequenced_t&, const unsequenced_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const sequenced_t&, const unsequenced_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator==( + const sequenced_t&, const parallel_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const sequenced_t&, const parallel_t&) + { + return true; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T sequenced_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct parallel_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef bulk_guarantee_t polymorphic_query_result_type; + + ASIO_CONSTEXPR parallel_t() + { + } + + template + struct query_member : + traits::query_member< + typename bulk_guarantee_t::template proxy::type, + parallel_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename bulk_guarantee_t::template static_proxy::type, + parallel_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = parallel_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR bulk_guarantee_t value() + { + return parallel_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const parallel_t&, const parallel_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const parallel_t&, const parallel_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator==( + const parallel_t&, const unsequenced_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const parallel_t&, const unsequenced_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator==( + const parallel_t&, const sequenced_t&) + { + return false; + } + + friend ASIO_CONSTEXPR bool operator!=( + const parallel_t&, const sequenced_t&) + { + return true; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T parallel_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +} // namespace bulk_guarantee +} // namespace detail + +typedef detail::bulk_guarantee_t<> bulk_guarantee_t; + +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr bulk_guarantee_t bulk_guarantee; +#else // defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +namespace { static const bulk_guarantee_t& + bulk_guarantee = bulk_guarantee_t::instance; } +#endif + +} // namespace execution + +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +template +struct query_free_default::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::bulk_guarantee_t result_type; +}; + +template +struct query_free_default::value + && can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::bulk_guarantee_t result_type; +}; + +template +struct query_free_default::value + && !can_query::value + && can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::bulk_guarantee_t result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::bulk_guarantee_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::bulk_guarantee_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::bulk_guarantee_t<0>:: + query_member::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::bulk_guarantee_t<0>:: + query_member::is_valid + && !traits::static_query::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::bulk_guarantee_t<0>:: + query_member::is_valid + && !traits::static_query::is_valid + && !traits::static_query::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::bulk_guarantee::unsequenced_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::bulk_guarantee::unsequenced_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::bulk_guarantee::unsequenced_t<0>:: + query_member::is_valid + && !traits::query_free::is_valid + && !can_query::value + && !can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef execution::bulk_guarantee_t::unsequenced_t result_type; + + static ASIO_CONSTEXPR result_type value() + { + return result_type(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::bulk_guarantee::sequenced_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::bulk_guarantee::sequenced_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::bulk_guarantee::parallel_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::bulk_guarantee::parallel_t<0>:: + query_static_constexpr_member::value(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::bulk_guarantee_t::unsequenced_t>::value)); +}; + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::bulk_guarantee_t::sequenced_t>::value)); +}; + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::bulk_guarantee_t::parallel_t>::value)); +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_BULK_GUARANTEE_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/connect.hpp b/third_party/asio/1.18.2/include/asio/execution/connect.hpp new file mode 100644 index 000000000..527f12329 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/connect.hpp @@ -0,0 +1,489 @@ +// +// execution/connect.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_CONNECT_HPP +#define ASIO_EXECUTION_CONNECT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/detail/as_invocable.hpp" +#include "asio/execution/detail/as_operation.hpp" +#include "asio/execution/detail/as_receiver.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/operation_state.hpp" +#include "asio/execution/receiver.hpp" +#include "asio/execution/sender.hpp" +#include "asio/traits/connect_member.hpp" +#include "asio/traits/connect_free.hpp" + +#include "asio/detail/push_options.hpp" + +#if defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace execution { + +/// A customisation point that connects a sender to a receiver. +/** + * The name execution::connect denotes a customisation point object. + * For some subexpressions s and r, let S be a type + * such that decltype((s)) is S and let R be a type + * such that decltype((r)) is R. The expression + * execution::connect(s, r) is expression-equivalent to: + * + * @li s.connect(r), if that expression is valid, if its type + * satisfies operation_state, and if S satisfies + * sender. + * + * @li Otherwise, connect(s, r), if that expression is valid, if its + * type satisfies operation_state, and if S satisfies + * sender, with overload resolution performed in a context that + * includes the declaration void connect(); and that does not include + * a declaration of execution::connect. + * + * @li Otherwise, as_operation{s, r}, if r is not an instance + * of as_receiver for some type F, and if + * receiver_of && executor_of, + * as_invocable, S>> is true, where + * as_operation is an implementation-defined class equivalent to + * @code template + * struct as_operation + * { + * remove_cvref_t e_; + * remove_cvref_t r_; + * void start() noexcept try { + * execution::execute(std::move(e_), + * as_invocable, S>{r_}); + * } catch(...) { + * execution::set_error(std::move(r_), current_exception()); + * } + * }; @endcode + * and as_invocable is a class template equivalent to the following: + * @code template + * struct as_invocable + * { + * R* r_; + * explicit as_invocable(R& r) noexcept + * : r_(std::addressof(r)) {} + * as_invocable(as_invocable && other) noexcept + * : r_(std::exchange(other.r_, nullptr)) {} + * ~as_invocable() { + * if(r_) + * execution::set_done(std::move(*r_)); + * } + * void operator()() & noexcept try { + * execution::set_value(std::move(*r_)); + * r_ = nullptr; + * } catch(...) { + * execution::set_error(std::move(*r_), current_exception()); + * r_ = nullptr; + * } + * }; + * @endcode + * + * @li Otherwise, execution::connect(s, r) is ill-formed. + */ +inline constexpr unspecified connect = unspecified; + +/// A type trait that determines whether a @c connect expression is +/// well-formed. +/** + * Class template @c can_connect is a trait that is derived from + * @c true_type if the expression execution::connect(std::declval(), + * std::declval()) is well formed; otherwise @c false_type. + */ +template +struct can_connect : + integral_constant +{ +}; + +/// A type trait to determine the result of a @c connect expression. +template +struct connect_result +{ + /// The type of the connect expression. + /** + * The type of the expression execution::connect(std::declval(), + * std::declval()). + */ + typedef automatically_determined type; +}; + +/// A type alis to determine the result of a @c connect expression. +template +using connect_result_t = typename connect_result::type; + +} // namespace execution +} // namespace asio + +#else // defined(GENERATING_DOCUMENTATION) + +namespace asio_execution_connect_fn { + +using asio::conditional; +using asio::declval; +using asio::enable_if; +using asio::execution::detail::as_invocable; +using asio::execution::detail::as_operation; +using asio::execution::detail::is_as_receiver; +using asio::execution::is_executor_of; +using asio::execution::is_operation_state; +using asio::execution::is_receiver; +using asio::execution::is_sender; +using asio::false_type; +using asio::remove_cvref; +using asio::traits::connect_free; +using asio::traits::connect_member; + +void connect(); + +enum overload_type +{ + call_member, + call_free, + adapter, + ill_formed +}; + +template +struct call_traits +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = ill_formed); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef void result_type; +}; + +template +struct call_traits::is_valid + >::type, + typename enable_if< + is_operation_state::result_type>::value + >::type, + typename enable_if< + is_sender::type>::value + >::type> : + connect_member +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = call_member); +}; + +template +struct call_traits::is_valid + >::type, + typename enable_if< + connect_free::is_valid + >::type, + typename enable_if< + is_operation_state::result_type>::value + >::type, + typename enable_if< + is_sender::type>::value + >::type> : + connect_free +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = call_free); +}; + +template +struct call_traits::is_valid + >::type, + typename enable_if< + !connect_free::is_valid + >::type, + typename enable_if< + is_receiver::value + >::type, + typename enable_if< + conditional< + !is_as_receiver< + typename remove_cvref::type + >::value, + is_executor_of< + typename remove_cvref::type, + as_invocable::type, S> + >, + false_type + >::type::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = adapter); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef as_operation result_type; +}; + +struct impl +{ +#if defined(ASIO_HAS_MOVE) + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(S&& s, R&& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return ASIO_MOVE_CAST(S)(s).connect(ASIO_MOVE_CAST(R)(r)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(S&& s, R&& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return connect(ASIO_MOVE_CAST(S)(s), ASIO_MOVE_CAST(R)(r)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(S&& s, R&& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type( + ASIO_MOVE_CAST(S)(s), ASIO_MOVE_CAST(R)(r)); + } +#else // defined(ASIO_HAS_MOVE) + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(S& s, R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return s.connect(r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(const S& s, R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return s.connect(r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(S& s, R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return connect(s, r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(const S& s, R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return connect(s, r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(S& s, R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type(s, r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(const S& s, R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type(s, r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(S& s, const R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return s.connect(r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(const S& s, const R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return s.connect(r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(S& s, const R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return connect(s, r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(const S& s, const R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return connect(s, r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(S& s, const R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type(s, r); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()(const S& s, const R& r) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return typename call_traits::result_type(s, r); + } +#endif // defined(ASIO_HAS_MOVE) +}; + +template +struct static_instance +{ + static const T instance; +}; + +template +const T static_instance::instance = {}; + +} // namespace asio_execution_connect_fn +namespace asio { +namespace execution { +namespace { + +static ASIO_CONSTEXPR const asio_execution_connect_fn::impl& + connect = asio_execution_connect_fn::static_instance<>::instance; + +} // namespace + +template +struct can_connect : + integral_constant::overload != + asio_execution_connect_fn::ill_formed> +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +constexpr bool can_connect_v = can_connect::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_nothrow_connect : + integral_constant::is_noexcept> +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +constexpr bool is_nothrow_connect_v + = is_nothrow_connect::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct connect_result +{ + typedef typename asio_execution_connect_fn::call_traits< + S, void(R)>::result_type type; +}; + +#if defined(ASIO_HAS_ALIAS_TEMPLATES) + +template +using connect_result_t = typename connect_result::type; + +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + +} // namespace execution +} // namespace asio + +#endif // defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_CONNECT_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/context.hpp b/third_party/asio/1.18.2/include/asio/execution/context.hpp new file mode 100644 index 000000000..f751c7a6e --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/context.hpp @@ -0,0 +1,233 @@ +// +// execution/context.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_CONTEXT2_HPP +#define ASIO_EXECUTION_CONTEXT2_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/traits/query_static_constexpr_member.hpp" +#include "asio/traits/static_query.hpp" + +#if defined(ASIO_HAS_STD_ANY) +# include +#endif // defined(ASIO_HAS_STD_ANY) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property that is used to obtain the execution context that is associated +/// with an executor. +struct context_t +{ + /// The context_t property applies to executors, senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The context_t property cannot be required. + static constexpr bool is_requirable = false; + + /// The context_t property cannot be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef std::any polymorphic_query_result_type; +}; + +/// A special value used for accessing the context_t property. +constexpr context_t context; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { +namespace detail { + +template +struct context_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = false); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + +#if defined(ASIO_HAS_STD_ANY) + typedef std::any polymorphic_query_result_type; +#endif // defined(ASIO_HAS_STD_ANY) + + ASIO_CONSTEXPR context_t() + { + } + + template + struct static_proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + struct type + { + template + static constexpr auto query(ASIO_MOVE_ARG(P) p) + noexcept( + noexcept( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + { + return T::query(ASIO_MOVE_CAST(P)(p)); + } + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + }; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename static_proxy::type, context_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = context_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_CONSTEXPR) + static const context_t instance; +#endif // !defined(ASIO_HAS_CONSTEXPR) +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T context_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_CONSTEXPR) +template +const context_t context_t::instance; +#endif + +} // namespace detail + +typedef detail::context_t<> context_t; + +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr context_t context; +#else // defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +namespace { static const context_t& context = context_t::instance; } +#endif + +} // namespace execution + +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::context_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::context_t<0>:: + query_static_constexpr_member::value(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_CONTEXT2_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/context_as.hpp b/third_party/asio/1.18.2/include/asio/execution/context_as.hpp new file mode 100644 index 000000000..740918c79 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/context_as.hpp @@ -0,0 +1,221 @@ +// +// execution/context_as.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_CONTEXT_AS_HPP +#define ASIO_EXECUTION_CONTEXT_AS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/context.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/query.hpp" +#include "asio/traits/query_static_constexpr_member.hpp" +#include "asio/traits/static_query.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property that is used to obtain the execution context that is associated +/// with an executor. +template +struct context_as_t +{ + /// The context_as_t property applies to executors, senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The context_t property cannot be required. + static constexpr bool is_requirable = false; + + /// The context_t property cannot be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef T polymorphic_query_result_type; +}; + +/// A special value used for accessing the context_as_t property. +template +constexpr context_as_t context_as; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { + +template +struct context_as_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = false); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + + typedef T polymorphic_query_result_type; + + ASIO_CONSTEXPR context_as_t() + { + } + + ASIO_CONSTEXPR context_as_t(context_t) + { + } + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename context_t::query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + context_t::query_static_constexpr_member::is_noexcept)) + { + return context_t::query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const U static_query_v + = context_as_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + template + friend ASIO_CONSTEXPR U query( + const Executor& ex, const context_as_t&, + typename enable_if< + is_same::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, context); + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const U context_as_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if (defined(ASIO_HAS_VARIABLE_TEMPLATES) \ + && defined(ASIO_HAS_CONSTEXPR)) \ + || defined(GENERATING_DOCUMENTATION) +template +constexpr context_as_t context_as{}; +#endif // (defined(ASIO_HAS_VARIABLE_TEMPLATES) + // && defined(ASIO_HAS_CONSTEXPR)) + // || defined(GENERATING_DOCUMENTATION) + +} // namespace execution + +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_applicable_property > + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query, + typename enable_if< + static_query::is_valid + >::type> : static_query +{ +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +template +struct query_free, + typename enable_if< + can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef U result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_CONTEXT_AS_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/detail/as_invocable.hpp b/third_party/asio/1.18.2/include/asio/execution/detail/as_invocable.hpp new file mode 100644 index 000000000..13b6e44dd --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/detail/as_invocable.hpp @@ -0,0 +1,152 @@ +// +// execution/detail/as_invocable.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_DETAIL_AS_INVOCABLE_HPP +#define ASIO_EXECUTION_DETAIL_AS_INVOCABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/atomic_count.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/receiver_invocation_error.hpp" +#include "asio/execution/set_done.hpp" +#include "asio/execution/set_error.hpp" +#include "asio/execution/set_value.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +#if defined(ASIO_HAS_MOVE) + +template +struct as_invocable +{ + Receiver* receiver_; + + explicit as_invocable(Receiver& r) ASIO_NOEXCEPT + : receiver_(asio::detail::addressof(r)) + { + } + + as_invocable(as_invocable&& other) ASIO_NOEXCEPT + : receiver_(other.receiver_) + { + other.receiver_ = 0; + } + + ~as_invocable() + { + if (receiver_) + execution::set_done(ASIO_MOVE_OR_LVALUE(Receiver)(*receiver_)); + } + + void operator()() ASIO_LVALUE_REF_QUAL ASIO_NOEXCEPT + { +#if !defined(ASIO_NO_EXCEPTIONS) + try + { +#endif // !defined(ASIO_NO_EXCEPTIONS) + execution::set_value(ASIO_MOVE_CAST(Receiver)(*receiver_)); + receiver_ = 0; +#if !defined(ASIO_NO_EXCEPTIONS) + } + catch (...) + { +#if defined(ASIO_HAS_STD_EXCEPTION_PTR) + execution::set_error(ASIO_MOVE_CAST(Receiver)(*receiver_), + std::make_exception_ptr(receiver_invocation_error())); + receiver_ = 0; +#else // defined(ASIO_HAS_STD_EXCEPTION_PTR) + std::terminate(); +#endif // defined(ASIO_HAS_STD_EXCEPTION_PTR) + } +#endif // !defined(ASIO_NO_EXCEPTIONS) + } +}; + +#else // defined(ASIO_HAS_MOVE) + +template +struct as_invocable +{ + Receiver* receiver_; + asio::detail::shared_ptr ref_count_; + + explicit as_invocable(Receiver& r, + const asio::detail::shared_ptr< + asio::detail::atomic_count>& c) ASIO_NOEXCEPT + : receiver_(asio::detail::addressof(r)), + ref_count_(c) + { + } + + as_invocable(const as_invocable& other) ASIO_NOEXCEPT + : receiver_(other.receiver_), + ref_count_(other.ref_count_) + { + ++(*ref_count_); + } + + ~as_invocable() + { + if (--(*ref_count_) == 0) + execution::set_done(*receiver_); + } + + void operator()() ASIO_LVALUE_REF_QUAL ASIO_NOEXCEPT + { +#if !defined(ASIO_NO_EXCEPTIONS) + try + { +#endif // !defined(ASIO_NO_EXCEPTIONS) + execution::set_value(*receiver_); + ++(*ref_count_); + } +#if !defined(ASIO_NO_EXCEPTIONS) + catch (...) + { +#if defined(ASIO_HAS_STD_EXCEPTION_PTR) + execution::set_error(*receiver_, + std::make_exception_ptr(receiver_invocation_error())); + ++(*ref_count_); +#else // defined(ASIO_HAS_STD_EXCEPTION_PTR) + std::terminate(); +#endif // defined(ASIO_HAS_STD_EXCEPTION_PTR) + } +#endif // !defined(ASIO_NO_EXCEPTIONS) + } +}; + +#endif // defined(ASIO_HAS_MOVE) + +template +struct is_as_invocable : false_type +{ +}; + +template +struct is_as_invocable > : true_type +{ +}; + +} // namespace detail +} // namespace execution +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_DETAIL_AS_INVOCABLE_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/detail/as_operation.hpp b/third_party/asio/1.18.2/include/asio/execution/detail/as_operation.hpp new file mode 100644 index 000000000..565bf3634 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/detail/as_operation.hpp @@ -0,0 +1,105 @@ +// +// execution/detail/as_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_DETAIL_AS_OPERATION_HPP +#define ASIO_EXECUTION_DETAIL_AS_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/memory.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/detail/as_invocable.hpp" +#include "asio/execution/execute.hpp" +#include "asio/execution/set_error.hpp" +#include "asio/traits/start_member.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +template +struct as_operation +{ + typename remove_cvref::type ex_; + typename remove_cvref::type receiver_; +#if !defined(ASIO_HAS_MOVE) + asio::detail::shared_ptr ref_count_; +#endif // !defined(ASIO_HAS_MOVE) + + template + explicit as_operation(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(R) r) + : ex_(ASIO_MOVE_CAST(E)(e)), + receiver_(ASIO_MOVE_CAST(R)(r)) +#if !defined(ASIO_HAS_MOVE) + , ref_count_(new asio::detail::atomic_count(1)) +#endif // !defined(ASIO_HAS_MOVE) + { + } + + void start() ASIO_NOEXCEPT + { +#if !defined(ASIO_NO_EXCEPTIONS) + try + { +#endif // !defined(ASIO_NO_EXCEPTIONS) + execution::execute( + ASIO_MOVE_CAST(typename remove_cvref::type)(ex_), + as_invocable::type, + Executor>(receiver_ +#if !defined(ASIO_HAS_MOVE) + , ref_count_ +#endif // !defined(ASIO_HAS_MOVE) + )); +#if !defined(ASIO_NO_EXCEPTIONS) + } + catch (...) + { +#if defined(ASIO_HAS_STD_EXCEPTION_PTR) + execution::set_error( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)( + receiver_), + std::current_exception()); +#else // defined(ASIO_HAS_STD_EXCEPTION_PTR) + std::terminate(); +#endif // defined(ASIO_HAS_STD_EXCEPTION_PTR) + } +#endif // !defined(ASIO_NO_EXCEPTIONS) + } +}; + +} // namespace detail +} // namespace execution +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_START_MEMBER_TRAIT) + +template +struct start_member< + asio::execution::detail::as_operation > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_START_MEMBER_TRAIT) + +} // namespace traits +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_DETAIL_AS_OPERATION_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/detail/as_receiver.hpp b/third_party/asio/1.18.2/include/asio/execution/detail/as_receiver.hpp new file mode 100644 index 000000000..a10bdc7f9 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/detail/as_receiver.hpp @@ -0,0 +1,128 @@ +// +// execution/detail/as_receiver.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_DETAIL_AS_RECEIVER_HPP +#define ASIO_EXECUTION_DETAIL_AS_RECEIVER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/traits/set_done_member.hpp" +#include "asio/traits/set_error_member.hpp" +#include "asio/traits/set_value_member.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +template +struct as_receiver +{ + Function f_; + + template + explicit as_receiver(ASIO_MOVE_ARG(F) f, int) + : f_(ASIO_MOVE_CAST(F)(f)) + { + } + +#if defined(ASIO_MSVC) && defined(ASIO_HAS_MOVE) + as_receiver(as_receiver&& other) + : f_(ASIO_MOVE_CAST(Function)(other.f_)) + { + } +#endif // defined(ASIO_MSVC) && defined(ASIO_HAS_MOVE) + + void set_value() + ASIO_NOEXCEPT_IF(noexcept(declval()())) + { + f_(); + } + + template + void set_error(E) ASIO_NOEXCEPT + { + std::terminate(); + } + + void set_done() ASIO_NOEXCEPT + { + } +}; + +template +struct is_as_receiver : false_type +{ +}; + +template +struct is_as_receiver > : true_type +{ +}; + +} // namespace detail +} // namespace execution +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT) + +template +struct set_value_member< + asio::execution::detail::as_receiver, void()> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); +#if defined(ASIO_HAS_NOEXCEPT) + ASIO_STATIC_CONSTEXPR(bool, + is_noexcept = noexcept(declval()())); +#else // defined(ASIO_HAS_NOEXCEPT) + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); +#endif // defined(ASIO_HAS_NOEXCEPT) + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT) + +template +struct set_error_member< + asio::execution::detail::as_receiver, E> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT) + +template +struct set_done_member< + asio::execution::detail::as_receiver > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT) + +} // namespace traits +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_DETAIL_AS_RECEIVER_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/detail/bulk_sender.hpp b/third_party/asio/1.18.2/include/asio/execution/detail/bulk_sender.hpp new file mode 100644 index 000000000..0e4e5c12d --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/detail/bulk_sender.hpp @@ -0,0 +1,261 @@ +// +// execution/detail/bulk_sender.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_DETAIL_BULK_SENDER_HPP +#define ASIO_EXECUTION_DETAIL_BULK_SENDER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/connect.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/set_done.hpp" +#include "asio/execution/set_error.hpp" +#include "asio/traits/connect_member.hpp" +#include "asio/traits/set_done_member.hpp" +#include "asio/traits/set_error_member.hpp" +#include "asio/traits/set_value_member.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +template +struct bulk_receiver +{ + typename remove_cvref::type receiver_; + typename decay::type f_; + typename decay::type n_; + + template + explicit bulk_receiver(ASIO_MOVE_ARG(R) r, + ASIO_MOVE_ARG(F) f, ASIO_MOVE_ARG(N) n) + : receiver_(ASIO_MOVE_CAST(R)(r)), + f_(ASIO_MOVE_CAST(F)(f)), + n_(ASIO_MOVE_CAST(N)(n)) + { + } + + void set_value() + { + for (Index i = 0; i < n_; ++i) + f_(i); + + execution::set_value( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)(receiver_)); + } + + template + void set_error(ASIO_MOVE_ARG(Error) e) ASIO_NOEXCEPT + { + execution::set_error( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)(receiver_), + ASIO_MOVE_CAST(Error)(e)); + } + + void set_done() ASIO_NOEXCEPT + { + execution::set_done( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)(receiver_)); + } +}; + +template +struct bulk_receiver_traits +{ + typedef bulk_receiver< + Receiver, Function, Number, + typename execution::executor_index< + typename remove_cvref::type + >::type + > type; + +#if defined(ASIO_HAS_MOVE) + typedef type arg_type; +#else // defined(ASIO_HAS_MOVE) + typedef const type& arg_type; +#endif // defined(ASIO_HAS_MOVE) +}; + +template +struct bulk_sender : sender_base +{ + typename remove_cvref::type sender_; + typename decay::type f_; + typename decay::type n_; + + template + explicit bulk_sender(ASIO_MOVE_ARG(S) s, + ASIO_MOVE_ARG(F) f, ASIO_MOVE_ARG(N) n) + : sender_(ASIO_MOVE_CAST(S)(s)), + f_(ASIO_MOVE_CAST(F)(f)), + n_(ASIO_MOVE_CAST(N)(n)) + { + } + + template + typename connect_result< + ASIO_MOVE_OR_LVALUE_TYPE(typename remove_cvref::type), + typename bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::type connect(ASIO_MOVE_ARG(Receiver) r, + typename enable_if< + can_connect< + typename remove_cvref::type, + typename bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::value + >::type* = 0) ASIO_RVALUE_REF_QUAL ASIO_NOEXCEPT + { + return execution::connect( + ASIO_MOVE_OR_LVALUE(typename remove_cvref::type)(sender_), + typename bulk_receiver_traits::type( + ASIO_MOVE_CAST(Receiver)(r), + ASIO_MOVE_CAST(typename decay::type)(f_), + ASIO_MOVE_CAST(typename decay::type)(n_))); + } + + template + typename connect_result< + const typename remove_cvref::type&, + typename bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::type connect(ASIO_MOVE_ARG(Receiver) r, + typename enable_if< + can_connect< + const typename remove_cvref::type&, + typename bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::value + >::type* = 0) const ASIO_LVALUE_REF_QUAL ASIO_NOEXCEPT + { + return execution::connect(sender_, + typename bulk_receiver_traits::type( + ASIO_MOVE_CAST(Receiver)(r), f_, n_)); + } +}; + +} // namespace detail +} // namespace execution +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT) + +template +struct set_value_member< + execution::detail::bulk_receiver, + void()> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT) + +template +struct set_error_member< + execution::detail::bulk_receiver, + Error> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT) + +template +struct set_done_member< + execution::detail::bulk_receiver > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_CONNECT_MEMBER_TRAIT) + +template +struct connect_member< + execution::detail::bulk_sender, + Receiver, + typename enable_if< + execution::can_connect< + ASIO_MOVE_OR_LVALUE_TYPE(typename remove_cvref::type), + typename execution::detail::bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef typename execution::connect_result< + ASIO_MOVE_OR_LVALUE_TYPE(typename remove_cvref::type), + typename execution::detail::bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::type result_type; +}; + +template +struct connect_member< + const execution::detail::bulk_sender, + Receiver, + typename enable_if< + execution::can_connect< + const typename remove_cvref::type&, + typename execution::detail::bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef typename execution::connect_result< + const typename remove_cvref::type&, + typename execution::detail::bulk_receiver_traits< + Sender, Receiver, Function, Number + >::arg_type + >::type result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_CONNECT_MEMBER_TRAIT) + +} // namespace traits +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_DETAIL_BULK_SENDER_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/detail/submit_receiver.hpp b/third_party/asio/1.18.2/include/asio/execution/detail/submit_receiver.hpp new file mode 100644 index 000000000..6faa119d2 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/detail/submit_receiver.hpp @@ -0,0 +1,233 @@ +// +// execution/detail/submit_receiver.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_DETAIL_SUBMIT_RECEIVER_HPP +#define ASIO_EXECUTION_DETAIL_SUBMIT_RECEIVER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/detail/variadic_templates.hpp" +#include "asio/execution/connect.hpp" +#include "asio/execution/receiver.hpp" +#include "asio/execution/set_done.hpp" +#include "asio/execution/set_error.hpp" +#include "asio/execution/set_value.hpp" +#include "asio/traits/set_done_member.hpp" +#include "asio/traits/set_error_member.hpp" +#include "asio/traits/set_value_member.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +template +struct submit_receiver; + +template +struct submit_receiver_wrapper +{ + submit_receiver* p_; + + explicit submit_receiver_wrapper(submit_receiver* p) + : p_(p) + { + } + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + typename enable_if::value>::type + set_value(ASIO_MOVE_ARG(Args)... args) ASIO_RVALUE_REF_QUAL + ASIO_NOEXCEPT_IF((is_nothrow_receiver_of::value)) + { + execution::set_value( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)(p_->r_), + ASIO_MOVE_CAST(Args)(args)...); + delete p_; + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + void set_value() ASIO_RVALUE_REF_QUAL + ASIO_NOEXCEPT_IF((is_nothrow_receiver_of::value)) + { + execution::set_value( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)(p_->r_)); + delete p_; + } + +#define ASIO_PRIVATE_SUBMIT_RECEIVER_SET_VALUE_DEF(n) \ + template \ + typename enable_if::value>::type \ + set_value(ASIO_VARIADIC_MOVE_PARAMS(n)) ASIO_RVALUE_REF_QUAL \ + ASIO_NOEXCEPT_IF((is_nothrow_receiver_of< \ + Receiver, ASIO_VARIADIC_TARGS(n)>::value)) \ + { \ + execution::set_value( \ + ASIO_MOVE_OR_LVALUE( \ + typename remove_cvref::type)(p_->r_), \ + ASIO_VARIADIC_MOVE_ARGS(n)); \ + delete p_; \ + } \ + /**/ +ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_SUBMIT_RECEIVER_SET_VALUE_DEF) +#undef ASIO_PRIVATE_SUBMIT_RECEIVER_SET_VALUE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + void set_error(ASIO_MOVE_ARG(E) e) + ASIO_RVALUE_REF_QUAL ASIO_NOEXCEPT + { + execution::set_error( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)(p_->r_), + ASIO_MOVE_CAST(E)(e)); + delete p_; + } + + void set_done() ASIO_RVALUE_REF_QUAL ASIO_NOEXCEPT + { + execution::set_done( + ASIO_MOVE_OR_LVALUE( + typename remove_cvref::type)(p_->r_)); + delete p_; + } +}; + +template +struct submit_receiver +{ + typename remove_cvref::type r_; +#if defined(ASIO_HAS_MOVE) + typename connect_result >::type state_; +#else // defined(ASIO_HAS_MOVE) + typename connect_result& >::type state_; +#endif // defined(ASIO_HAS_MOVE) + +#if defined(ASIO_HAS_MOVE) + template + explicit submit_receiver(ASIO_MOVE_ARG(S) s, ASIO_MOVE_ARG(R) r) + : r_(ASIO_MOVE_CAST(R)(r)), + state_(execution::connect(ASIO_MOVE_CAST(S)(s), + submit_receiver_wrapper(this))) + { + } +#else // defined(ASIO_HAS_MOVE) + explicit submit_receiver(Sender s, Receiver r) + : r_(r), + state_(execution::connect(s, + submit_receiver_wrapper(this))) + { + } +#endif // defined(ASIO_HAS_MOVE) +}; + +} // namespace detail +} // namespace execution +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT) + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct set_value_member< + asio::execution::detail::submit_receiver_wrapper< + Sender, Receiver>, + void(Args...)> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (asio::execution::is_nothrow_receiver_of::value)); + typedef void result_type; +}; + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +struct set_value_member< + asio::execution::detail::submit_receiver_wrapper< + Sender, Receiver>, + void()> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + asio::execution::is_nothrow_receiver_of::value); + typedef void result_type; +}; + +#define ASIO_PRIVATE_SUBMIT_RECEIVER_TRAIT_DEF(n) \ + template \ + struct set_value_member< \ + asio::execution::detail::submit_receiver_wrapper< \ + Sender, Receiver>, \ + void(ASIO_VARIADIC_TARGS(n))> \ + { \ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); \ + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = \ + (asio::execution::is_nothrow_receiver_of::value)); \ + typedef void result_type; \ + }; \ + /**/ +ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_SUBMIT_RECEIVER_TRAIT_DEF) +#undef ASIO_PRIVATE_SUBMIT_RECEIVER_TRAIT_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +#endif // !defined(ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT) + +template +struct set_error_member< + asio::execution::detail::submit_receiver_wrapper< + Sender, Receiver>, E> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT) + +template +struct set_done_member< + asio::execution::detail::submit_receiver_wrapper< + Sender, Receiver> > +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT) + +} // namespace traits +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_DETAIL_SUBMIT_RECEIVER_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/detail/void_receiver.hpp b/third_party/asio/1.18.2/include/asio/execution/detail/void_receiver.hpp new file mode 100644 index 000000000..fb2d53f58 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/detail/void_receiver.hpp @@ -0,0 +1,90 @@ +// +// execution/detail/void_receiver.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_DETAIL_VOID_RECEIVER_HPP +#define ASIO_EXECUTION_DETAIL_VOID_RECEIVER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/traits/set_done_member.hpp" +#include "asio/traits/set_error_member.hpp" +#include "asio/traits/set_value_member.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +struct void_receiver +{ + void set_value() ASIO_NOEXCEPT + { + } + + template + void set_error(ASIO_MOVE_ARG(E)) ASIO_NOEXCEPT + { + } + + void set_done() ASIO_NOEXCEPT + { + } +}; + +} // namespace detail +} // namespace execution +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT) + +template <> +struct set_value_member +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT) + +template +struct set_error_member +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT) + +template <> +struct set_done_member +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + typedef void result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT) + +} // namespace traits +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_DETAIL_VOID_RECEIVER_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/execute.hpp b/third_party/asio/1.18.2/include/asio/execution/execute.hpp new file mode 100644 index 000000000..54eb31beb --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/execute.hpp @@ -0,0 +1,283 @@ +// +// execution/execute.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_EXECUTE_HPP +#define ASIO_EXECUTION_EXECUTE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/detail/as_invocable.hpp" +#include "asio/execution/detail/as_receiver.hpp" +#include "asio/traits/execute_member.hpp" +#include "asio/traits/execute_free.hpp" + +#include "asio/detail/push_options.hpp" + +#if defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace execution { + +/// A customisation point that executes a function on an executor. +/** + * The name execution::execute denotes a customisation point object. + * + * For some subexpressions e and f, let E be a type + * such that decltype((e)) is E and let F be a type + * such that decltype((f)) is F. The expression + * execution::execute(e, f) is ill-formed if F does not model + * invocable, or if E does not model either executor + * or sender. Otherwise, it is expression-equivalent to: + * + * @li e.execute(f), if that expression is valid. If the function + * selected does not execute the function object f on the executor + * e, the program is ill-formed with no diagnostic required. + * + * @li Otherwise, execute(e, f), if that expression is valid, with + * overload resolution performed in a context that includes the declaration + * void execute(); and that does not include a declaration of + * execution::execute. If the function selected by overload + * resolution does not execute the function object f on the executor + * e, the program is ill-formed with no diagnostic required. + */ +inline constexpr unspecified execute = unspecified; + +/// A type trait that determines whether a @c execute expression is well-formed. +/** + * Class template @c can_execute is a trait that is derived from + * @c true_type if the expression execution::execute(std::declval(), + * std::declval()) is well formed; otherwise @c false_type. + */ +template +struct can_execute : + integral_constant +{ +}; + +} // namespace execution +} // namespace asio + +#else // defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace execution { + +template +struct is_sender_to; + +namespace detail { + +template +void submit_helper(ASIO_MOVE_ARG(S) s, ASIO_MOVE_ARG(R) r); + +} // namespace detail +} // namespace execution +} // namespace asio +namespace asio_execution_execute_fn { + +using asio::conditional; +using asio::decay; +using asio::declval; +using asio::enable_if; +using asio::execution::detail::as_receiver; +using asio::execution::detail::is_as_invocable; +using asio::execution::is_sender_to; +using asio::false_type; +using asio::result_of; +using asio::traits::execute_free; +using asio::traits::execute_member; +using asio::true_type; +using asio::void_type; + +void execute(); + +enum overload_type +{ + call_member, + call_free, + adapter, + ill_formed +}; + +template +struct call_traits +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = ill_formed); +}; + +template +struct call_traits::type, F>::is_valid + >::type> : + execute_member::type, F> +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = call_member); +}; + +template +struct call_traits, F>::is_valid + >::type, + typename enable_if< + execute_free::is_valid + >::type> : + execute_free +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = call_free); +}; + +template +struct call_traits::type, F>::is_valid + >::type, + typename enable_if< + !execute_free::is_valid + >::type, + typename void_type< + typename result_of::type&()>::type + >::type, + typename enable_if< + !is_as_invocable::type>::value + >::type, + typename enable_if< + is_sender_to::type, T> >::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = adapter); + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef void result_type; +}; + +struct impl +{ + template + struct proxy + { +#if defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + struct type + { + template + auto execute(ASIO_MOVE_ARG(F) f) + noexcept( + noexcept( + declval::type>().execute( + ASIO_MOVE_CAST(F)(f)) + ) + ) + -> decltype( + declval::type>().execute( + ASIO_MOVE_CAST(F)(f)) + ); + }; +#else // defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + }; + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()( + ASIO_MOVE_ARG(T) t, + ASIO_MOVE_ARG(F) f) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return ASIO_MOVE_CAST(T)(t).execute(ASIO_MOVE_CAST(F)(f)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()( + ASIO_MOVE_ARG(T) t, + ASIO_MOVE_ARG(F) f) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return execute(ASIO_MOVE_CAST(T)(t), ASIO_MOVE_CAST(F)(f)); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == adapter, + typename call_traits::result_type + >::type + operator()( + ASIO_MOVE_ARG(T) t, + ASIO_MOVE_ARG(F) f) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return asio::execution::detail::submit_helper( + ASIO_MOVE_CAST(T)(t), + as_receiver::type, T>( + ASIO_MOVE_CAST(F)(f), 0)); + } +}; + +template +struct static_instance +{ + static const T instance; +}; + +template +const T static_instance::instance = {}; + +} // namespace asio_execution_execute_fn +namespace asio { +namespace execution { +namespace { + +static ASIO_CONSTEXPR const asio_execution_execute_fn::impl& + execute = asio_execution_execute_fn::static_instance<>::instance; + +} // namespace + +typedef asio_execution_execute_fn::impl execute_t; + +template +struct can_execute : + integral_constant::overload != + asio_execution_execute_fn::ill_formed> +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +constexpr bool can_execute_v = can_execute::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +} // namespace execution +} // namespace asio + +#endif // defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_EXECUTE_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/executor.hpp b/third_party/asio/1.18.2/include/asio/execution/executor.hpp new file mode 100644 index 000000000..7e05d5a0f --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/executor.hpp @@ -0,0 +1,252 @@ +// +// execution/executor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_EXECUTOR_HPP +#define ASIO_EXECUTION_EXECUTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/execute.hpp" +#include "asio/execution/invocable_archetype.hpp" +#include "asio/traits/equality_comparable.hpp" + +#if defined(ASIO_HAS_DEDUCED_EXECUTE_FREE_TRAIT) \ + && defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) \ + && defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) +# define ASIO_HAS_DEDUCED_EXECUTION_IS_EXECUTOR_TRAIT 1 +#endif // defined(ASIO_HAS_DEDUCED_EXECUTE_FREE_TRAIT) + // && defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) + // && defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +template +struct is_executor_of_impl : false_type +{ +}; + +template +struct is_executor_of_impl::type, F>::value + >::type, + typename void_type< + typename result_of::type&()>::type + >::type, + typename enable_if< + is_constructible::type, F>::value + >::type, + typename enable_if< + is_move_constructible::type>::value + >::type, +#if defined(ASIO_HAS_NOEXCEPT) + typename enable_if< + is_nothrow_copy_constructible::value + >::type, + typename enable_if< + is_nothrow_destructible::value + >::type, +#else // defined(ASIO_HAS_NOEXCEPT) + typename enable_if< + is_copy_constructible::value + >::type, + typename enable_if< + is_destructible::value + >::type, +#endif // defined(ASIO_HAS_NOEXCEPT) + typename enable_if< + traits::equality_comparable::is_valid + >::type, + typename enable_if< + traits::equality_comparable::is_noexcept + >::type> : true_type +{ +}; + +template +struct executor_shape +{ + typedef std::size_t type; +}; + +template +struct executor_shape::type> +{ + typedef typename T::shape_type type; +}; + +template +struct executor_index +{ + typedef Default type; +}; + +template +struct executor_index::type> +{ + typedef typename T::index_type type; +}; + +} // namespace detail + +/// The is_executor trait detects whether a type T satisfies the +/// execution::executor concept. +/** + * Class template @c is_executor is a UnaryTypeTrait that is derived from @c + * true_type if the type @c T meets the concept definition for an executor, + * otherwise @c false_type. + */ +template +struct is_executor : +#if defined(GENERATING_DOCUMENTATION) + integral_constant +#else // defined(GENERATING_DOCUMENTATION) + detail::is_executor_of_impl +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +ASIO_CONSTEXPR const bool is_executor_v = is_executor::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +#if defined(ASIO_HAS_CONCEPTS) + +template +ASIO_CONCEPT executor = is_executor::value; + +#define ASIO_EXECUTION_EXECUTOR ::asio::execution::executor + +#else // defined(ASIO_HAS_CONCEPTS) + +#define ASIO_EXECUTION_EXECUTOR typename + +#endif // defined(ASIO_HAS_CONCEPTS) + +/// The is_executor_of trait detects whether a type T satisfies the +/// execution::executor_of concept for some set of value arguments. +/** + * Class template @c is_executor_of is a type trait that is derived from @c + * true_type if the type @c T meets the concept definition for an executor + * that is invocable with a function object of type @c F, otherwise @c + * false_type. + */ +template +struct is_executor_of : +#if defined(GENERATING_DOCUMENTATION) + integral_constant +#else // defined(GENERATING_DOCUMENTATION) + integral_constant::value && detail::is_executor_of_impl::value + > +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +ASIO_CONSTEXPR const bool is_executor_of_v = + is_executor_of::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +#if defined(ASIO_HAS_CONCEPTS) + +template +ASIO_CONCEPT executor_of = is_executor_of::value; + +#define ASIO_EXECUTION_EXECUTOR_OF(f) \ + ::asio::execution::executor_of + +#else // defined(ASIO_HAS_CONCEPTS) + +#define ASIO_EXECUTION_EXECUTOR_OF typename + +#endif // defined(ASIO_HAS_CONCEPTS) + +/// The executor_shape trait detects the type used by an executor to represent +/// the shape of a bulk operation. +/** + * Class template @c executor_shape is a type trait with a nested type alias + * @c type whose type is @c T::shape_type if @c T::shape_type is valid, + * otherwise @c std::size_t. + */ +template +struct executor_shape +#if !defined(GENERATING_DOCUMENTATION) + : detail::executor_shape +#endif // !defined(GENERATING_DOCUMENTATION) +{ +#if defined(GENERATING_DOCUMENTATION) + /// @c T::shape_type if @c T::shape_type is valid, otherwise @c std::size_t. + typedef automatically_determined type; +#endif // defined(GENERATING_DOCUMENTATION) +}; + +#if defined(ASIO_HAS_ALIAS_TEMPLATES) + +template +using executor_shape_t = typename executor_shape::type; + +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + +/// The executor_index trait detects the type used by an executor to represent +/// an index within a bulk operation. +/** + * Class template @c executor_index is a type trait with a nested type alias + * @c type whose type is @c T::index_type if @c T::index_type is valid, + * otherwise @c executor_shape_t. + */ +template +struct executor_index +#if !defined(GENERATING_DOCUMENTATION) + : detail::executor_index::type> +#endif // !defined(GENERATING_DOCUMENTATION) +{ +#if defined(GENERATING_DOCUMENTATION) + /// @c T::index_type if @c T::index_type is valid, otherwise + /// @c executor_shape_t. + typedef automatically_determined type; +#endif // defined(GENERATING_DOCUMENTATION) +}; + +#if defined(ASIO_HAS_ALIAS_TEMPLATES) + +template +using executor_index_t = typename executor_index::type; + +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + +} // namespace execution +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_EXECUTOR_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/impl/bad_executor.ipp b/third_party/asio/1.18.2/include/asio/execution/impl/bad_executor.ipp new file mode 100644 index 000000000..7505e5c85 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/impl/bad_executor.ipp @@ -0,0 +1,40 @@ +// +// exection/impl/bad_executor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_IMPL_BAD_EXECUTOR_IPP +#define ASIO_EXECUTION_IMPL_BAD_EXECUTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/execution/bad_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { + +bad_executor::bad_executor() ASIO_NOEXCEPT +{ +} + +const char* bad_executor::what() const ASIO_NOEXCEPT_OR_NOTHROW +{ + return "bad executor"; +} + +} // namespace execution +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_IMPL_BAD_EXECUTOR_IPP diff --git a/third_party/asio/1.18.2/include/asio/execution/impl/receiver_invocation_error.ipp b/third_party/asio/1.18.2/include/asio/execution/impl/receiver_invocation_error.ipp new file mode 100644 index 000000000..5fd4e9119 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/impl/receiver_invocation_error.ipp @@ -0,0 +1,36 @@ +// +// exection/impl/receiver_invocation_error.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_IMPL_RECEIVER_INVOCATION_ERROR_IPP +#define ASIO_EXECUTION_IMPL_RECEIVER_INVOCATION_ERROR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/execution/receiver_invocation_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { + +receiver_invocation_error::receiver_invocation_error() + : std::runtime_error("receiver invocation error") +{ +} + +} // namespace execution +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_IMPL_RECEIVER_INVOCATION_ERROR_IPP diff --git a/third_party/asio/1.18.2/include/asio/execution/invocable_archetype.hpp b/third_party/asio/1.18.2/include/asio/execution/invocable_archetype.hpp new file mode 100644 index 000000000..b141979ca --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/invocable_archetype.hpp @@ -0,0 +1,71 @@ +// +// execution/invocable_archetype.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_INVOCABLE_ARCHETYPE_HPP +#define ASIO_EXECUTION_INVOCABLE_ARCHETYPE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/detail/variadic_templates.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { + +/// An archetypal function object used for determining adherence to the +/// execution::executor concept. +struct invocable_archetype +{ +#if !defined(GENERATING_DOCUMENTATION) + // Necessary for compatibility with a C++03 implementation of result_of. + typedef void result_type; +#endif // !defined(GENERATING_DOCUMENTATION) + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) \ + || defined(GENERATING_DOCUMENTATION) + + /// Function call operator. + template + void operator()(ASIO_MOVE_ARG(Args)...) + { + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + + void operator()() + { + } + +#define ASIO_PRIVATE_INVOCABLE_ARCHETYPE_CALL_DEF(n) \ + template \ + void operator()(ASIO_VARIADIC_UNNAMED_MOVE_PARAMS(n)) \ + { \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_INVOCABLE_ARCHETYPE_CALL_DEF) +#undef ASIO_PRIVATE_INVOCABLE_ARCHETYPE_CALL_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) +}; + +} // namespace execution +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_INVOCABLE_ARCHETYPE_HPP + diff --git a/third_party/asio/1.18.2/include/asio/execution/mapping.hpp b/third_party/asio/1.18.2/include/asio/execution/mapping.hpp new file mode 100644 index 000000000..33924c814 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/mapping.hpp @@ -0,0 +1,1116 @@ +// +// execution/mapping.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_MAPPING_HPP +#define ASIO_EXECUTION_MAPPING_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/query.hpp" +#include "asio/traits/query_free.hpp" +#include "asio/traits/query_member.hpp" +#include "asio/traits/query_static_constexpr_member.hpp" +#include "asio/traits/static_query.hpp" +#include "asio/traits/static_require.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property to describe what guarantees an executor makes about the mapping +/// of execution agents on to threads of execution. +struct mapping_t +{ + /// The mapping_t property applies to executors, senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The top-level mapping_t property cannot be required. + static constexpr bool is_requirable = false; + + /// The top-level mapping_t property cannot be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef mapping_t polymorphic_query_result_type; + + /// A sub-property that indicates that execution agents are mapped on to + /// threads of execution. + struct thread_t + { + /// The mapping_t::thread_t property applies to executors, senders, and + /// schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The mapping_t::thread_t property can be required. + static constexpr bool is_requirable = true; + + /// The mapping_t::thread_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef mapping_t polymorphic_query_result_type; + + /// Default constructor. + constexpr thread_t(); + + /// Get the value associated with a property object. + /** + * @returns thread_t(); + */ + static constexpr mapping_t value(); + }; + + /// A sub-property that indicates that execution agents are mapped on to + /// new threads of execution. + struct new_thread_t + { + /// The mapping_t::new_thread_t property applies to executors, senders, and + /// schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The mapping_t::new_thread_t property can be required. + static constexpr bool is_requirable = true; + + /// The mapping_t::new_thread_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef mapping_t polymorphic_query_result_type; + + /// Default constructor. + constexpr new_thread_t(); + + /// Get the value associated with a property object. + /** + * @returns new_thread_t(); + */ + static constexpr mapping_t value(); + }; + + /// A sub-property that indicates that the mapping of execution agents is + /// implementation-defined. + struct other_t + { + /// The mapping_t::other_t property applies to executors, senders, and + /// schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The mapping_t::other_t property can be required. + static constexpr bool is_requirable = true; + + /// The mapping_t::other_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef mapping_t polymorphic_query_result_type; + + /// Default constructor. + constexpr other_t(); + + /// Get the value associated with a property object. + /** + * @returns other_t(); + */ + static constexpr mapping_t value(); + }; + + /// A special value used for accessing the mapping_t::thread_t property. + static constexpr thread_t thread; + + /// A special value used for accessing the mapping_t::new_thread_t property. + static constexpr new_thread_t new_thread; + + /// A special value used for accessing the mapping_t::other_t property. + static constexpr other_t other; + + /// Default constructor. + constexpr mapping_t(); + + /// Construct from a sub-property value. + constexpr mapping_t(thread_t); + + /// Construct from a sub-property value. + constexpr mapping_t(new_thread_t); + + /// Construct from a sub-property value. + constexpr mapping_t(other_t); + + /// Compare property values for equality. + friend constexpr bool operator==( + const mapping_t& a, const mapping_t& b) noexcept; + + /// Compare property values for inequality. + friend constexpr bool operator!=( + const mapping_t& a, const mapping_t& b) noexcept; +}; + +/// A special value used for accessing the mapping_t property. +constexpr mapping_t mapping; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { +namespace detail { +namespace mapping { + +template struct thread_t; +template struct new_thread_t; +template struct other_t; + +} // namespace mapping + +template +struct mapping_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = false); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + typedef mapping_t polymorphic_query_result_type; + + typedef detail::mapping::thread_t thread_t; + typedef detail::mapping::new_thread_t new_thread_t; + typedef detail::mapping::other_t other_t; + + ASIO_CONSTEXPR mapping_t() + : value_(-1) + { + } + + ASIO_CONSTEXPR mapping_t(thread_t) + : value_(0) + { + } + + ASIO_CONSTEXPR mapping_t(new_thread_t) + : value_(1) + { + } + + ASIO_CONSTEXPR mapping_t(other_t) + : value_(2) + { + } + + template + struct proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + struct type + { + template + auto query(ASIO_MOVE_ARG(P) p) const + noexcept( + noexcept( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ); + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + }; + + template + struct static_proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + struct type + { + template + static constexpr auto query(ASIO_MOVE_ARG(P) p) + noexcept( + noexcept( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + { + return T::query(ASIO_MOVE_CAST(P)(p)); + } + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + }; + + template + struct query_member : + traits::query_member::type, mapping_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename static_proxy::type, mapping_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = mapping_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + friend ASIO_CONSTEXPR bool operator==( + const mapping_t& a, const mapping_t& b) + { + return a.value_ == b.value_; + } + + friend ASIO_CONSTEXPR bool operator!=( + const mapping_t& a, const mapping_t& b) + { + return a.value_ != b.value_; + } + + struct convertible_from_mapping_t + { + ASIO_CONSTEXPR convertible_from_mapping_t(mapping_t) {} + }; + + template + friend ASIO_CONSTEXPR mapping_t query( + const Executor& ex, convertible_from_mapping_t, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::thread_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, thread_t()); + } + + template + friend ASIO_CONSTEXPR mapping_t query( + const Executor& ex, convertible_from_mapping_t, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::new_thread_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, new_thread_t()); + } + + template + friend ASIO_CONSTEXPR mapping_t query( + const Executor& ex, convertible_from_mapping_t, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::other_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, other_t()); + } + + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(thread_t, thread); + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(new_thread_t, new_thread); + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(other_t, other); + +#if !defined(ASIO_HAS_CONSTEXPR) + static const mapping_t instance; +#endif // !defined(ASIO_HAS_CONSTEXPR) + +private: + int value_; +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T mapping_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_CONSTEXPR) +template +const mapping_t mapping_t::instance; +#endif + +template +const typename mapping_t::thread_t mapping_t::thread; + +template +const typename mapping_t::new_thread_t mapping_t::new_thread; + +template +const typename mapping_t::other_t mapping_t::other; + +namespace mapping { + +template +struct thread_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef mapping_t polymorphic_query_result_type; + + ASIO_CONSTEXPR thread_t() + { + } + + template + struct query_member : + traits::query_member< + typename mapping_t::template proxy::type, thread_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename mapping_t::template static_proxy::type, thread_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR thread_t static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::query_free::is_valid + >::type* = 0, + typename enable_if< + !can_query >::value + >::type* = 0, + typename enable_if< + !can_query >::value + >::type* = 0) ASIO_NOEXCEPT + { + return thread_t(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = thread_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR mapping_t value() + { + return thread_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const thread_t&, const thread_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const thread_t&, const thread_t&) + { + return false; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T thread_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct new_thread_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef mapping_t polymorphic_query_result_type; + + ASIO_CONSTEXPR new_thread_t() + { + } + + template + struct query_member : + traits::query_member< + typename mapping_t::template proxy::type, new_thread_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename mapping_t::template static_proxy::type, new_thread_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = new_thread_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR mapping_t value() + { + return new_thread_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const new_thread_t&, const new_thread_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const new_thread_t&, const new_thread_t&) + { + return false; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T new_thread_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct other_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef mapping_t polymorphic_query_result_type; + + ASIO_CONSTEXPR other_t() + { + } + + template + struct query_member : + traits::query_member< + typename mapping_t::template proxy::type, other_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename mapping_t::template static_proxy::type, other_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = other_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR mapping_t value() + { + return other_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const other_t&, const other_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const other_t&, const other_t&) + { + return false; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T other_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +} // namespace mapping +} // namespace detail + +typedef detail::mapping_t<> mapping_t; + +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr mapping_t mapping; +#else // defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +namespace { static const mapping_t& mapping = mapping_t::instance; } +#endif + +} // namespace execution + +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +template +struct query_free_default::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::mapping_t result_type; +}; + +template +struct query_free_default::value + && can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::mapping_t result_type; +}; + +template +struct query_free_default::value + && !can_query::value + && can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::mapping_t result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::mapping_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::mapping_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::mapping_t<0>:: + query_member::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::mapping_t<0>:: + query_member::is_valid + && !traits::static_query::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::mapping_t<0>:: + query_member::is_valid + && !traits::static_query::is_valid + && !traits::static_query::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::mapping::thread_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::mapping::thread_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::mapping::thread_t<0>:: + query_member::is_valid + && !traits::query_free::is_valid + && !can_query::value + && !can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef execution::mapping_t::thread_t result_type; + + static ASIO_CONSTEXPR result_type value() + { + return result_type(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::mapping::new_thread_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::mapping::new_thread_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::mapping::other_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::mapping::other_t<0>:: + query_static_constexpr_member::value(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::mapping_t::thread_t>::value)); +}; + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::mapping_t::new_thread_t>::value)); +}; + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::mapping_t::other_t>::value)); +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_MAPPING_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/occupancy.hpp b/third_party/asio/1.18.2/include/asio/execution/occupancy.hpp new file mode 100644 index 000000000..e1842a46a --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/occupancy.hpp @@ -0,0 +1,226 @@ +// +// execution/occupancy.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_OCCUPANCY_HPP +#define ASIO_EXECUTION_OCCUPANCY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/traits/query_static_constexpr_member.hpp" +#include "asio/traits/static_query.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property that gives an estimate of the number of execution agents that +/// should occupy the associated execution context. +struct occupancy_t +{ + /// The occupancy_t property applies to executors, senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The occupancy_t property cannot be required. + static constexpr bool is_requirable = false; + + /// The occupancy_t property cannot be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef std::size_t polymorphic_query_result_type; +}; + +/// A special value used for accessing the occupancy_t property. +constexpr occupancy_t occupancy; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { +namespace detail { + +template +struct occupancy_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = false); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + typedef std::size_t polymorphic_query_result_type; + + ASIO_CONSTEXPR occupancy_t() + { + } + + template + struct static_proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + struct type + { + template + static constexpr auto query(ASIO_MOVE_ARG(P) p) + noexcept( + noexcept( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + { + return T::query(ASIO_MOVE_CAST(P)(p)); + } + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + }; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename static_proxy::type, occupancy_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = occupancy_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_CONSTEXPR) + static const occupancy_t instance; +#endif // !defined(ASIO_HAS_CONSTEXPR) +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T occupancy_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_CONSTEXPR) +template +const occupancy_t occupancy_t::instance; +#endif + +} // namespace detail + +typedef detail::occupancy_t<> occupancy_t; + +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr occupancy_t occupancy; +#else // defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +namespace { static const occupancy_t& occupancy = occupancy_t::instance; } +#endif + +} // namespace execution + +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::occupancy_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::occupancy_t<0>:: + query_static_constexpr_member::value(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_OCCUPANCY_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/operation_state.hpp b/third_party/asio/1.18.2/include/asio/execution/operation_state.hpp new file mode 100644 index 000000000..8fb7a94b8 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/operation_state.hpp @@ -0,0 +1,94 @@ +// +// execution/operation_state.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_OPERATION_STATE_HPP +#define ASIO_EXECUTION_OPERATION_STATE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/start.hpp" + +#if defined(ASIO_HAS_DEDUCED_START_FREE_TRAIT) \ + && defined(ASIO_HAS_DEDUCED_START_MEMBER_TRAIT) +# define ASIO_HAS_DEDUCED_EXECUTION_IS_OPERATION_STATE_TRAIT 1 +#endif // defined(ASIO_HAS_DEDUCED_START_FREE_TRAIT) + // && defined(ASIO_HAS_DEDUCED_START_MEMBER_TRAIT) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +template +struct is_operation_state_base : + integral_constant::value + && is_object::value + > +{ +}; + +} // namespace detail + +/// The is_operation_state trait detects whether a type T satisfies the +/// execution::operation_state concept. +/** + * Class template @c is_operation_state is a type trait that is derived from + * @c true_type if the type @c T meets the concept definition for an + * @c operation_state, otherwise @c false_type. + */ +template +struct is_operation_state : +#if defined(GENERATING_DOCUMENTATION) + integral_constant +#else // defined(GENERATING_DOCUMENTATION) + conditional< + can_start::type>::value + && is_nothrow_start::type>::value, + detail::is_operation_state_base, + false_type + >::type +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +ASIO_CONSTEXPR const bool is_operation_state_v = + is_operation_state::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +#if defined(ASIO_HAS_CONCEPTS) + +template +ASIO_CONCEPT operation_state = is_operation_state::value; + +#define ASIO_EXECUTION_OPERATION_STATE \ + ::asio::execution::operation_state + +#else // defined(ASIO_HAS_CONCEPTS) + +#define ASIO_EXECUTION_OPERATION_STATE typename + +#endif // defined(ASIO_HAS_CONCEPTS) + +} // namespace execution +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_OPERATION_STATE_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/outstanding_work.hpp b/third_party/asio/1.18.2/include/asio/execution/outstanding_work.hpp new file mode 100644 index 000000000..48ed71d15 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/outstanding_work.hpp @@ -0,0 +1,867 @@ +// +// execution/outstanding_work.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_OUTSTANDING_WORK_HPP +#define ASIO_EXECUTION_OUTSTANDING_WORK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/query.hpp" +#include "asio/traits/query_free.hpp" +#include "asio/traits/query_member.hpp" +#include "asio/traits/query_static_constexpr_member.hpp" +#include "asio/traits/static_query.hpp" +#include "asio/traits/static_require.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property to describe whether task submission is likely in the future. +struct outstanding_work_t +{ + /// The outstanding_work_t property applies to executors, senders, and + /// schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The top-level outstanding_work_t property cannot be required. + static constexpr bool is_requirable = false; + + /// The top-level outstanding_work_t property cannot be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef outstanding_work_t polymorphic_query_result_type; + + /// A sub-property that indicates that the executor does not represent likely + /// future submission of a function object. + struct untracked_t + { + /// The outstanding_work_t::untracked_t property applies to executors, + /// senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The outstanding_work_t::untracked_t property can be required. + static constexpr bool is_requirable = true; + + /// The outstanding_work_t::untracked_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef outstanding_work_t polymorphic_query_result_type; + + /// Default constructor. + constexpr untracked_t(); + + /// Get the value associated with a property object. + /** + * @returns untracked_t(); + */ + static constexpr outstanding_work_t value(); + }; + + /// A sub-property that indicates that the executor represents likely + /// future submission of a function object. + struct tracked_t + { + /// The outstanding_work_t::untracked_t property applies to executors, + /// senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The outstanding_work_t::tracked_t property can be required. + static constexpr bool is_requirable = true; + + /// The outstanding_work_t::tracked_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef outstanding_work_t polymorphic_query_result_type; + + /// Default constructor. + constexpr tracked_t(); + + /// Get the value associated with a property object. + /** + * @returns tracked_t(); + */ + static constexpr outstanding_work_t value(); + }; + + /// A special value used for accessing the outstanding_work_t::untracked_t + /// property. + static constexpr untracked_t untracked; + + /// A special value used for accessing the outstanding_work_t::tracked_t + /// property. + static constexpr tracked_t tracked; + + /// Default constructor. + constexpr outstanding_work_t(); + + /// Construct from a sub-property value. + constexpr outstanding_work_t(untracked_t); + + /// Construct from a sub-property value. + constexpr outstanding_work_t(tracked_t); + + /// Compare property values for equality. + friend constexpr bool operator==( + const outstanding_work_t& a, const outstanding_work_t& b) noexcept; + + /// Compare property values for inequality. + friend constexpr bool operator!=( + const outstanding_work_t& a, const outstanding_work_t& b) noexcept; +}; + +/// A special value used for accessing the outstanding_work_t property. +constexpr outstanding_work_t outstanding_work; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { +namespace detail { +namespace outstanding_work { + +template struct untracked_t; +template struct tracked_t; + +} // namespace outstanding_work + +template +struct outstanding_work_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = false); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + typedef outstanding_work_t polymorphic_query_result_type; + + typedef detail::outstanding_work::untracked_t untracked_t; + typedef detail::outstanding_work::tracked_t tracked_t; + + ASIO_CONSTEXPR outstanding_work_t() + : value_(-1) + { + } + + ASIO_CONSTEXPR outstanding_work_t(untracked_t) + : value_(0) + { + } + + ASIO_CONSTEXPR outstanding_work_t(tracked_t) + : value_(1) + { + } + + template + struct proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + struct type + { + template + auto query(ASIO_MOVE_ARG(P) p) const + noexcept( + noexcept( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ); + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + }; + + template + struct static_proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + struct type + { + template + static constexpr auto query(ASIO_MOVE_ARG(P) p) + noexcept( + noexcept( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + { + return T::query(ASIO_MOVE_CAST(P)(p)); + } + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + }; + + template + struct query_member : + traits::query_member::type, outstanding_work_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename static_proxy::type, outstanding_work_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = outstanding_work_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + friend ASIO_CONSTEXPR bool operator==( + const outstanding_work_t& a, const outstanding_work_t& b) + { + return a.value_ == b.value_; + } + + friend ASIO_CONSTEXPR bool operator!=( + const outstanding_work_t& a, const outstanding_work_t& b) + { + return a.value_ != b.value_; + } + + struct convertible_from_outstanding_work_t + { + ASIO_CONSTEXPR convertible_from_outstanding_work_t(outstanding_work_t) + { + } + }; + + template + friend ASIO_CONSTEXPR outstanding_work_t query( + const Executor& ex, convertible_from_outstanding_work_t, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::untracked_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, untracked_t()); + } + + template + friend ASIO_CONSTEXPR outstanding_work_t query( + const Executor& ex, convertible_from_outstanding_work_t, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::tracked_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, tracked_t()); + } + + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(untracked_t, untracked); + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(tracked_t, tracked); + +#if !defined(ASIO_HAS_CONSTEXPR) + static const outstanding_work_t instance; +#endif // !defined(ASIO_HAS_CONSTEXPR) + +private: + int value_; +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T outstanding_work_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_CONSTEXPR) +template +const outstanding_work_t outstanding_work_t::instance; +#endif + +template +const typename outstanding_work_t::untracked_t +outstanding_work_t::untracked; + +template +const typename outstanding_work_t::tracked_t +outstanding_work_t::tracked; + +namespace outstanding_work { + +template +struct untracked_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef outstanding_work_t polymorphic_query_result_type; + + ASIO_CONSTEXPR untracked_t() + { + } + + template + struct query_member : + traits::query_member< + typename outstanding_work_t::template proxy::type, untracked_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename outstanding_work_t::template static_proxy::type, + untracked_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR untracked_t static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::query_free::is_valid + >::type* = 0, + typename enable_if< + !can_query >::value + >::type* = 0) ASIO_NOEXCEPT + { + return untracked_t(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = untracked_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR outstanding_work_t value() + { + return untracked_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const untracked_t&, const untracked_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const untracked_t&, const untracked_t&) + { + return false; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T untracked_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct tracked_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef outstanding_work_t polymorphic_query_result_type; + + ASIO_CONSTEXPR tracked_t() + { + } + + template + struct query_member : + traits::query_member< + typename outstanding_work_t::template proxy::type, tracked_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename outstanding_work_t::template static_proxy::type, + tracked_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = tracked_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR outstanding_work_t value() + { + return tracked_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const tracked_t&, const tracked_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const tracked_t&, const tracked_t&) + { + return false; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T tracked_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +} // namespace outstanding_work +} // namespace detail + +typedef detail::outstanding_work_t<> outstanding_work_t; + +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr outstanding_work_t outstanding_work; +#else // defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +namespace { static const outstanding_work_t& + outstanding_work = outstanding_work_t::instance; } +#endif + +} // namespace execution + +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +template +struct query_free_default::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::outstanding_work_t result_type; +}; + +template +struct query_free_default::value + && can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::outstanding_work_t result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::outstanding_work_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::outstanding_work_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::outstanding_work_t<0>:: + query_member::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::outstanding_work_t<0>:: + query_member::is_valid + && !traits::static_query::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::outstanding_work::untracked_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::outstanding_work::untracked_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::outstanding_work::untracked_t<0>:: + query_member::is_valid + && !traits::query_free::is_valid + && !can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef execution::outstanding_work_t::untracked_t result_type; + + static ASIO_CONSTEXPR result_type value() + { + return result_type(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::outstanding_work::tracked_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::outstanding_work::tracked_t<0>:: + query_static_constexpr_member::value(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::outstanding_work_t::untracked_t>::value)); +}; + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::outstanding_work_t::tracked_t>::value)); +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_OUTSTANDING_WORK_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/prefer_only.hpp b/third_party/asio/1.18.2/include/asio/execution/prefer_only.hpp new file mode 100644 index 000000000..e608a7707 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/prefer_only.hpp @@ -0,0 +1,331 @@ +// +// execution/prefer_only.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_PREFER_ONLY_HPP +#define ASIO_EXECUTION_PREFER_ONLY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/prefer.hpp" +#include "asio/query.hpp" +#include "asio/traits/static_query.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property adapter that is used with the polymorphic executor wrapper +/// to mark properties as preferable, but not requirable. +template +struct prefer_only +{ + /// The prefer_only adapter applies to the same types as the nested property. + template + static constexpr bool is_applicable_property_v = + is_applicable_property::value; + + /// The context_t property cannot be required. + static constexpr bool is_requirable = false; + + /// The context_t property can be preferred, it the underlying property can + /// be preferred. + /** + * @c true if @c Property::is_preferable is @c true, otherwise @c false. + */ + static constexpr bool is_preferable = automatically_determined; + + /// The type returned by queries against an @c any_executor. + typedef typename Property::polymorphic_query_result_type + polymorphic_query_result_type; +}; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { +namespace detail { + +template +struct prefer_only_is_preferable +{ + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); +}; + +template +struct prefer_only_is_preferable::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); +}; + +template +struct prefer_only_polymorphic_query_result_type +{ +}; + +template +struct prefer_only_polymorphic_query_result_type::type> +{ + typedef typename InnerProperty::polymorphic_query_result_type + polymorphic_query_result_type; +}; + +template +struct prefer_only_property +{ + InnerProperty property; + + prefer_only_property(const InnerProperty& p) + : property(p) + { + } +}; + +#if defined(ASIO_HAS_DECLTYPE) \ + && defined(ASIO_HAS_WORKING_EXPRESSION_SFINAE) + +template +struct prefer_only_property().value()) + >::type> +{ + InnerProperty property; + + prefer_only_property(const InnerProperty& p) + : property(p) + { + } + + ASIO_CONSTEXPR auto value() const + ASIO_NOEXCEPT_IF(( + noexcept(asio::declval().value()))) + -> decltype(asio::declval().value()) + { + return property.value(); + } +}; + +#else // defined(ASIO_HAS_DECLTYPE) + // && defined(ASIO_HAS_WORKING_EXPRESSION_SFINAE) + +struct prefer_only_memfns_base +{ + void value(); +}; + +template +struct prefer_only_memfns_derived + : T, prefer_only_memfns_base +{ +}; + +template +struct prefer_only_memfns_check +{ +}; + +template +char (&prefer_only_value_memfn_helper(...))[2]; + +template +char prefer_only_value_memfn_helper( + prefer_only_memfns_check< + void (prefer_only_memfns_base::*)(), + &prefer_only_memfns_derived::value>*); + +template +struct prefer_only_property(0)) != 1 + && !is_same::value + >::type> +{ + InnerProperty property; + + prefer_only_property(const InnerProperty& p) + : property(p) + { + } + + ASIO_CONSTEXPR typename InnerProperty::polymorphic_query_result_type + value() const + { + return property.value(); + } +}; + +#endif // defined(ASIO_HAS_DECLTYPE) + // && defined(ASIO_HAS_WORKING_EXPRESSION_SFINAE) + +} // namespace detail + +template +struct prefer_only : + detail::prefer_only_is_preferable, + detail::prefer_only_polymorphic_query_result_type, + detail::prefer_only_property +{ + ASIO_STATIC_CONSTEXPR(bool, is_requirable = false); + + ASIO_CONSTEXPR prefer_only(const InnerProperty& p) + : detail::prefer_only_property(p) + { + } + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query() + ASIO_NOEXCEPT_IF(( + traits::static_query::is_noexcept)) + { + return traits::static_query::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = prefer_only::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + template + friend ASIO_CONSTEXPR + typename prefer_result::type + prefer(const Executor& ex, const prefer_only& p, + typename enable_if< + is_same::value + >::type* = 0, + typename enable_if< + can_prefer::value + >::type* = 0) +#if !defined(ASIO_MSVC) \ + && !defined(__clang__) // Clang crashes if noexcept is used here. + ASIO_NOEXCEPT_IF(( + is_nothrow_prefer::value)) +#endif // !defined(ASIO_MSVC) + // && !defined(__clang__) + { + return asio::prefer(ex, p.property); + } + + template + friend ASIO_CONSTEXPR + typename query_result::type + query(const Executor& ex, const prefer_only& p, + typename enable_if< + is_same::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(ASIO_MSVC) \ + && !defined(__clang__) // Clang crashes if noexcept is used here. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // !defined(ASIO_MSVC) + // && !defined(__clang__) + { + return asio::query(ex, p.property); + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T prefer_only::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +} // namespace execution + +template +struct is_applicable_property > + : is_applicable_property +{ +}; + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query > : + static_query +{ +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_DEDUCED_PREFER_FREE_TRAIT) + +template +struct prefer_free_default, + typename enable_if< + can_prefer::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_prefer::value)); + + typedef typename prefer_result::type result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_PREFER_FREE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +template +struct query_free, + typename enable_if< + can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef typename query_result::type result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_PREFER_ONLY_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/receiver.hpp b/third_party/asio/1.18.2/include/asio/execution/receiver.hpp new file mode 100644 index 000000000..4f205aa52 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/receiver.hpp @@ -0,0 +1,280 @@ +// +// execution/receiver.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_RECEIVER_HPP +#define ASIO_EXECUTION_RECEIVER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/detail/variadic_templates.hpp" +#include "asio/execution/set_done.hpp" +#include "asio/execution/set_error.hpp" +#include "asio/execution/set_value.hpp" + +#if defined(ASIO_HAS_STD_EXCEPTION_PTR) +# include +#else // defined(ASIO_HAS_STD_EXCEPTION_PTR) +# include "asio/error_code.hpp" +#endif // defined(ASIO_HAS_STD_EXCEPTION_PTR) + +#if defined(ASIO_HAS_DEDUCED_SET_DONE_FREE_TRAIT) \ + && defined(ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT) \ + && defined(ASIO_HAS_DEDUCED_SET_ERROR_FREE_TRAIT) \ + && defined(ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT) \ + && defined(ASIO_HAS_DEDUCED_SET_VALUE_FREE_TRAIT) \ + && defined(ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT) \ + && defined(ASIO_HAS_DEDUCED_RECEIVER_OF_FREE_TRAIT) \ + && defined(ASIO_HAS_DEDUCED_RECEIVER_OF_MEMBER_TRAIT) +# define ASIO_HAS_DEDUCED_EXECUTION_IS_RECEIVER_TRAIT 1 +#endif // defined(ASIO_HAS_DEDUCED_SET_DONE_FREE_TRAIT) + // && defined(ASIO_HAS_DEDUCED_SET_DONE_MEMBER_TRAIT) + // && defined(ASIO_HAS_DEDUCED_SET_ERROR_FREE_TRAIT) + // && defined(ASIO_HAS_DEDUCED_SET_ERROR_MEMBER_TRAIT) + // && defined(ASIO_HAS_DEDUCED_SET_VALUE_FREE_TRAIT) + // && defined(ASIO_HAS_DEDUCED_SET_VALUE_MEMBER_TRAIT) + // && defined(ASIO_HAS_DEDUCED_RECEIVER_OF_FREE_TRAIT) + // && defined(ASIO_HAS_DEDUCED_RECEIVER_OF_MEMBER_TRAIT) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +template +struct is_receiver_base : + integral_constant::type>::value + && is_constructible::type, T>::value + > +{ +}; + +} // namespace detail + +#if defined(ASIO_HAS_STD_EXCEPTION_PTR) +# define ASIO_EXECUTION_RECEIVER_ERROR_DEFAULT = std::exception_ptr +#else // defined(ASIO_HAS_STD_EXCEPTION_PTR) +# define ASIO_EXECUTION_RECEIVER_ERROR_DEFAULT \ + = ::asio::error_code +#endif // defined(ASIO_HAS_STD_EXCEPTION_PTR) + +/// The is_receiver trait detects whether a type T satisfies the +/// execution::receiver concept. +/** + * Class template @c is_receiver is a type trait that is derived from @c + * true_type if the type @c T meets the concept definition for a receiver for + * error type @c E, otherwise @c false_type. + */ +template +struct is_receiver : +#if defined(GENERATING_DOCUMENTATION) + integral_constant +#else // defined(GENERATING_DOCUMENTATION) + conditional< + can_set_done::type>::value + && is_nothrow_set_done::type>::value + && can_set_error::type, E>::value + && is_nothrow_set_error::type, E>::value, + detail::is_receiver_base, + false_type + >::type +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +ASIO_CONSTEXPR const bool is_receiver_v = is_receiver::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +#if defined(ASIO_HAS_CONCEPTS) + +template +ASIO_CONCEPT receiver = is_receiver::value; + +#define ASIO_EXECUTION_RECEIVER ::asio::execution::receiver + +#else // defined(ASIO_HAS_CONCEPTS) + +#define ASIO_EXECUTION_RECEIVER typename + +#endif // defined(ASIO_HAS_CONCEPTS) + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) \ + || defined(GENERATING_DOCUMENTATION) + +/// The is_receiver_of trait detects whether a type T satisfies the +/// execution::receiver_of concept for some set of value arguments. +/** + * Class template @c is_receiver_of is a type trait that is derived from @c + * true_type if the type @c T meets the concept definition for a receiver for + * value arguments @c Vs, otherwise @c false_type. + */ +template +struct is_receiver_of : +#if defined(GENERATING_DOCUMENTATION) + integral_constant +#else // defined(GENERATING_DOCUMENTATION) + conditional< + is_receiver::value, + can_set_value::type, Vs...>, + false_type + >::type +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +ASIO_CONSTEXPR const bool is_receiver_of_v = + is_receiver_of::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +#if defined(ASIO_HAS_CONCEPTS) + +template +ASIO_CONCEPT receiver_of = is_receiver_of::value; + +#define ASIO_EXECUTION_RECEIVER_OF_0 \ + ::asio::execution::receiver_of + +#define ASIO_EXECUTION_RECEIVER_OF_1(v) \ + ::asio::execution::receiver_of + +#else // defined(ASIO_HAS_CONCEPTS) + +#define ASIO_EXECUTION_RECEIVER_OF_0 typename +#define ASIO_EXECUTION_RECEIVER_OF_1(v) typename + +#endif // defined(ASIO_HAS_CONCEPTS) + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + +template +struct is_receiver_of; + +template +struct is_receiver_of : + conditional< + is_receiver::value, + can_set_value::type>, + false_type + >::type +{ +}; + +#define ASIO_PRIVATE_RECEIVER_OF_TRAITS_DEF(n) \ + template \ + struct is_receiver_of : \ + conditional< \ + conditional, void>::type::value, \ + can_set_value< \ + typename remove_cvref::type, \ + ASIO_VARIADIC_TARGS(n)>, \ + false_type \ + >::type \ + { \ + }; \ + /**/ +ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_RECEIVER_OF_TRAITS_DEF) +#undef ASIO_PRIVATE_RECEIVER_OF_TRAITS_DEF + +#define ASIO_EXECUTION_RECEIVER_OF_0 typename +#define ASIO_EXECUTION_RECEIVER_OF_1(v) typename + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) \ + || defined(GENERATING_DOCUMENTATION) + +/// The is_nothrow_receiver_of trait detects whether a type T satisfies the +/// execution::receiver_of concept for some set of value arguments, with a +/// noexcept @c set_value operation. +/** + * Class template @c is_nothrow_receiver_of is a type trait that is derived + * from @c true_type if the type @c T meets the concept definition for a + * receiver for value arguments @c Vs, and the expression + * execution::set_value(declval(), declval()...) is noexcept, + * otherwise @c false_type. + */ +template +struct is_nothrow_receiver_of : +#if defined(GENERATING_DOCUMENTATION) + integral_constant +#else // defined(GENERATING_DOCUMENTATION) + integral_constant::value + && is_nothrow_set_value::type, Vs...>::value + > +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +ASIO_CONSTEXPR const bool is_nothrow_receiver_of_v = + is_nothrow_receiver_of::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + +template +struct is_nothrow_receiver_of; + +template +struct is_nothrow_receiver_of : + integral_constant::value + && is_nothrow_set_value::type>::value + > +{ +}; + +#define ASIO_PRIVATE_NOTHROW_RECEIVER_OF_TRAITS_DEF(n) \ + template \ + struct is_nothrow_receiver_of : \ + integral_constant::value \ + && is_nothrow_set_value::type, \ + ASIO_VARIADIC_TARGS(n)>::value \ + > \ + { \ + }; \ + /**/ +ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_NOTHROW_RECEIVER_OF_TRAITS_DEF) +#undef ASIO_PRIVATE_NOTHROW_RECEIVER_OF_TRAITS_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + +} // namespace execution +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_RECEIVER_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/receiver_invocation_error.hpp b/third_party/asio/1.18.2/include/asio/execution/receiver_invocation_error.hpp new file mode 100644 index 000000000..00557da85 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/receiver_invocation_error.hpp @@ -0,0 +1,48 @@ +// +// execution/receiver_invocation_error.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_RECEIVER_INVOCATION_ERROR_HPP +#define ASIO_EXECUTION_RECEIVER_INVOCATION_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { + +/// Exception reported via @c set_error when an exception escapes from +/// @c set_value. +class receiver_invocation_error + : public std::runtime_error +#if defined(ASIO_HAS_STD_NESTED_EXCEPTION) + , public std::nested_exception +#endif // defined(ASIO_HAS_STD_NESTED_EXCEPTION) +{ +public: + /// Constructor. + ASIO_DECL receiver_invocation_error(); +}; + +} // namespace execution +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/execution/impl/receiver_invocation_error.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_EXECUTION_RECEIVER_INVOCATION_ERROR_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/relationship.hpp b/third_party/asio/1.18.2/include/asio/execution/relationship.hpp new file mode 100644 index 000000000..95160e07f --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/relationship.hpp @@ -0,0 +1,865 @@ +// +// execution/relationship.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_RELATIONSHIP_HPP +#define ASIO_EXECUTION_RELATIONSHIP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/scheduler.hpp" +#include "asio/execution/sender.hpp" +#include "asio/is_applicable_property.hpp" +#include "asio/query.hpp" +#include "asio/traits/query_free.hpp" +#include "asio/traits/query_member.hpp" +#include "asio/traits/query_static_constexpr_member.hpp" +#include "asio/traits/static_query.hpp" +#include "asio/traits/static_require.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) + +namespace execution { + +/// A property to describe whether submitted tasks represent continuations of +/// the calling context. +struct relationship_t +{ + /// The relationship_t property applies to executors, senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The top-level relationship_t property cannot be required. + static constexpr bool is_requirable = false; + + /// The top-level relationship_t property cannot be preferred. + static constexpr bool is_preferable = false; + + /// The type returned by queries against an @c any_executor. + typedef relationship_t polymorphic_query_result_type; + + /// A sub-property that indicates that the executor does not represent a + /// continuation of the calling context. + struct fork_t + { + /// The relationship_t::fork_t property applies to executors, senders, and + /// schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The relationship_t::fork_t property can be required. + static constexpr bool is_requirable = true; + + /// The relationship_t::fork_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef relationship_t polymorphic_query_result_type; + + /// Default constructor. + constexpr fork_t(); + + /// Get the value associated with a property object. + /** + * @returns fork_t(); + */ + static constexpr relationship_t value(); + }; + + /// A sub-property that indicates that the executor represents a continuation + /// of the calling context. + struct continuation_t + { + /// The relationship_t::continuation_t property applies to executors, + /// senders, and schedulers. + template + static constexpr bool is_applicable_property_v = + is_executor_v || is_sender_v || is_scheduler_v; + + /// The relationship_t::continuation_t property can be required. + static constexpr bool is_requirable = true; + + /// The relationship_t::continuation_t property can be preferred. + static constexpr bool is_preferable = true; + + /// The type returned by queries against an @c any_executor. + typedef relationship_t polymorphic_query_result_type; + + /// Default constructor. + constexpr continuation_t(); + + /// Get the value associated with a property object. + /** + * @returns continuation_t(); + */ + static constexpr relationship_t value(); + }; + + /// A special value used for accessing the relationship_t::fork_t property. + static constexpr fork_t fork; + + /// A special value used for accessing the relationship_t::continuation_t + /// property. + static constexpr continuation_t continuation; + + /// Default constructor. + constexpr relationship_t(); + + /// Construct from a sub-property value. + constexpr relationship_t(fork_t); + + /// Construct from a sub-property value. + constexpr relationship_t(continuation_t); + + /// Compare property values for equality. + friend constexpr bool operator==( + const relationship_t& a, const relationship_t& b) noexcept; + + /// Compare property values for inequality. + friend constexpr bool operator!=( + const relationship_t& a, const relationship_t& b) noexcept; +}; + +/// A special value used for accessing the relationship_t property. +constexpr relationship_t relationship; + +} // namespace execution + +#else // defined(GENERATING_DOCUMENTATION) + +namespace execution { +namespace detail { +namespace relationship { + +template struct fork_t; +template struct continuation_t; + +} // namespace relationship + +template +struct relationship_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = false); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = false); + typedef relationship_t polymorphic_query_result_type; + + typedef detail::relationship::fork_t fork_t; + typedef detail::relationship::continuation_t continuation_t; + + ASIO_CONSTEXPR relationship_t() + : value_(-1) + { + } + + ASIO_CONSTEXPR relationship_t(fork_t) + : value_(0) + { + } + + ASIO_CONSTEXPR relationship_t(continuation_t) + : value_(1) + { + } + + template + struct proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + struct type + { + template + auto query(ASIO_MOVE_ARG(P) p) const + noexcept( + noexcept( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + declval::type>().query( + ASIO_MOVE_CAST(P)(p)) + ); + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) + }; + + template + struct static_proxy + { +#if defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + struct type + { + template + static constexpr auto query(ASIO_MOVE_ARG(P) p) + noexcept( + noexcept( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + ) + -> decltype( + conditional::type::query(ASIO_MOVE_CAST(P)(p)) + ) + { + return T::query(ASIO_MOVE_CAST(P)(p)); + } + }; +#else // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + typedef T type; +#endif // defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) + }; + + template + struct query_member : + traits::query_member::type, relationship_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename static_proxy::type, relationship_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template + static ASIO_CONSTEXPR + typename traits::static_query::result_type + static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::static_query::is_valid + >::type* = 0, + typename enable_if< + traits::static_query::is_valid + >::type* = 0) ASIO_NOEXCEPT + { + return traits::static_query::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = relationship_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + friend ASIO_CONSTEXPR bool operator==( + const relationship_t& a, const relationship_t& b) + { + return a.value_ == b.value_; + } + + friend ASIO_CONSTEXPR bool operator!=( + const relationship_t& a, const relationship_t& b) + { + return a.value_ != b.value_; + } + + struct convertible_from_relationship_t + { + ASIO_CONSTEXPR convertible_from_relationship_t(relationship_t) + { + } + }; + + template + friend ASIO_CONSTEXPR relationship_t query( + const Executor& ex, convertible_from_relationship_t, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::fork_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, fork_t()); + } + + template + friend ASIO_CONSTEXPR relationship_t query( + const Executor& ex, convertible_from_relationship_t, + typename enable_if< + !can_query::value + >::type* = 0, + typename enable_if< + can_query::value + >::type* = 0) +#if !defined(__clang__) // Clang crashes if noexcept is used here. +#if defined(ASIO_MSVC) // Visual C++ wants the type to be qualified. + ASIO_NOEXCEPT_IF(( + is_nothrow_query::continuation_t>::value)) +#else // defined(ASIO_MSVC) + ASIO_NOEXCEPT_IF(( + is_nothrow_query::value)) +#endif // defined(ASIO_MSVC) +#endif // !defined(__clang__) + { + return asio::query(ex, continuation_t()); + } + + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(fork_t, fork); + ASIO_STATIC_CONSTEXPR_DEFAULT_INIT(continuation_t, continuation); + +#if !defined(ASIO_HAS_CONSTEXPR) + static const relationship_t instance; +#endif // !defined(ASIO_HAS_CONSTEXPR) + +private: + int value_; +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T relationship_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_CONSTEXPR) +template +const relationship_t relationship_t::instance; +#endif + +template +const typename relationship_t::fork_t +relationship_t::fork; + +template +const typename relationship_t::continuation_t +relationship_t::continuation; + +namespace relationship { + +template +struct fork_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef relationship_t polymorphic_query_result_type; + + ASIO_CONSTEXPR fork_t() + { + } + + template + struct query_member : + traits::query_member< + typename relationship_t::template proxy::type, fork_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename relationship_t::template static_proxy::type, fork_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template + static ASIO_CONSTEXPR fork_t static_query( + typename enable_if< + !query_static_constexpr_member::is_valid + >::type* = 0, + typename enable_if< + !query_member::is_valid + >::type* = 0, + typename enable_if< + !traits::query_free::is_valid + >::type* = 0, + typename enable_if< + !can_query >::value + >::type* = 0) ASIO_NOEXCEPT + { + return fork_t(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = fork_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR relationship_t value() + { + return fork_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const fork_t&, const fork_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const fork_t&, const fork_t&) + { + return false; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T fork_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct continuation_t +{ +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + template + ASIO_STATIC_CONSTEXPR(bool, + is_applicable_property_v = ( + is_executor::value + || conditional< + is_executor::value, + false_type, + is_sender + >::type::value + || conditional< + is_executor::value, + false_type, + is_scheduler + >::type::value)); +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + + ASIO_STATIC_CONSTEXPR(bool, is_requirable = true); + ASIO_STATIC_CONSTEXPR(bool, is_preferable = true); + typedef relationship_t polymorphic_query_result_type; + + ASIO_CONSTEXPR continuation_t() + { + } + + template + struct query_member : + traits::query_member< + typename relationship_t::template proxy::type, continuation_t> {}; + + template + struct query_static_constexpr_member : + traits::query_static_constexpr_member< + typename relationship_t::template static_proxy::type, + continuation_t> {}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + template + static ASIO_CONSTEXPR + typename query_static_constexpr_member::result_type + static_query() + ASIO_NOEXCEPT_IF(( + query_static_constexpr_member::is_noexcept)) + { + return query_static_constexpr_member::value(); + } + + template ())> + static ASIO_CONSTEXPR const T static_query_v + = continuation_t::static_query(); +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + + static ASIO_CONSTEXPR relationship_t value() + { + return continuation_t(); + } + + friend ASIO_CONSTEXPR bool operator==( + const continuation_t&, const continuation_t&) + { + return true; + } + + friend ASIO_CONSTEXPR bool operator!=( + const continuation_t&, const continuation_t&) + { + return false; + } +}; + +#if defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) +template template +const T continuation_t::static_query_v; +#endif // defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // && defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +} // namespace relationship +} // namespace detail + +typedef detail::relationship_t<> relationship_t; + +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr relationship_t relationship; +#else // defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +namespace { static const relationship_t& + relationship = relationship_t::instance; } +#endif + +} // namespace execution + +#if !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +template +struct is_applicable_property + : integral_constant::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_sender + >::type::value + || conditional< + execution::is_executor::value, + false_type, + execution::is_scheduler + >::type::value> +{ +}; + +#endif // !defined(ASIO_HAS_VARIABLE_TEMPLATES) + +namespace traits { + +#if !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +template +struct query_free_default::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::relationship_t result_type; +}; + +template +struct query_free_default::value + && can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = + (is_nothrow_query::value)); + + typedef execution::relationship_t result_type; +}; + +#endif // !defined(ASIO_HAS_DEDUCED_QUERY_FREE_TRAIT) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) \ + || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::relationship_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::relationship_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::relationship_t<0>:: + query_member::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::relationship_t<0>:: + query_member::is_valid + && !traits::static_query::is_valid + && traits::static_query::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename traits::static_query::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return traits::static_query::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::relationship::fork_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::relationship::fork_t<0>:: + query_static_constexpr_member::value(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + && !execution::detail::relationship::fork_t<0>:: + query_member::is_valid + && !traits::query_free::is_valid + && !can_query::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef execution::relationship_t::fork_t result_type; + + static ASIO_CONSTEXPR result_type value() + { + return result_type(); + } +}; + +template +struct static_query:: + query_static_constexpr_member::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = true); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + + typedef typename execution::detail::relationship::continuation_t<0>:: + query_static_constexpr_member::result_type result_type; + + static ASIO_CONSTEXPR result_type value() + { + return execution::detail::relationship::continuation_t<0>:: + query_static_constexpr_member::value(); + } +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_QUERY_TRAIT) + // || !defined(ASIO_HAS_SFINAE_VARIABLE_TEMPLATES) + +#if !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::relationship_t::fork_t>::value)); +}; + +template +struct static_require::is_valid + >::type> +{ + ASIO_STATIC_CONSTEXPR(bool, is_valid = + (is_same::result_type, + execution::relationship_t::continuation_t>::value)); +}; + +#endif // !defined(ASIO_HAS_DEDUCED_STATIC_REQUIRE_TRAIT) + +} // namespace traits + +#endif // defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_RELATIONSHIP_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/schedule.hpp b/third_party/asio/1.18.2/include/asio/execution/schedule.hpp new file mode 100644 index 000000000..fde0cdda5 --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/schedule.hpp @@ -0,0 +1,287 @@ +// +// execution/schedule.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_SCHEDULE_HPP +#define ASIO_EXECUTION_SCHEDULE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/executor.hpp" +#include "asio/traits/schedule_member.hpp" +#include "asio/traits/schedule_free.hpp" + +#include "asio/detail/push_options.hpp" + +#if defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace execution { + +/// A customisation point that is used to obtain a sender from a scheduler. +/** + * The name execution::schedule denotes a customisation point object. + * For some subexpression s, let S be a type such that + * decltype((s)) is S. The expression + * execution::schedule(s) is expression-equivalent to: + * + * @li s.schedule(), if that expression is valid and its type models + * sender. + * + * @li Otherwise, schedule(s), if that expression is valid and its + * type models sender with overload resolution performed in a context + * that includes the declaration void schedule(); and that does not + * include a declaration of execution::schedule. + * + * @li Otherwise, S if S satisfies executor. + * + * @li Otherwise, execution::schedule(s) is ill-formed. + */ +inline constexpr unspecified schedule = unspecified; + +/// A type trait that determines whether a @c schedule expression is +/// well-formed. +/** + * Class template @c can_schedule is a trait that is derived from @c true_type + * if the expression execution::schedule(std::declval()) is well + * formed; otherwise @c false_type. + */ +template +struct can_schedule : + integral_constant +{ +}; + +} // namespace execution +} // namespace asio + +#else // defined(GENERATING_DOCUMENTATION) + +namespace asio_execution_schedule_fn { + +using asio::decay; +using asio::declval; +using asio::enable_if; +using asio::execution::is_executor; +using asio::traits::schedule_free; +using asio::traits::schedule_member; + +void schedule(); + +enum overload_type +{ + identity, + call_member, + call_free, + ill_formed +}; + +template +struct call_traits +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = ill_formed); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); + typedef void result_type; +}; + +template +struct call_traits::is_valid + >::type> : + schedule_member +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = call_member); +}; + +template +struct call_traits::is_valid + >::type, + typename enable_if< + schedule_free::is_valid + >::type> : + schedule_free +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = call_free); +}; + +template +struct call_traits::is_valid + >::type, + typename enable_if< + !schedule_free::is_valid + >::type, + typename enable_if< + is_executor::type>::value + >::type> +{ + ASIO_STATIC_CONSTEXPR(overload_type, overload = identity); + ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); + +#if defined(ASIO_HAS_MOVE) + typedef ASIO_MOVE_ARG(S) result_type; +#else // defined(ASIO_HAS_MOVE) + typedef ASIO_MOVE_ARG(typename decay::type) result_type; +#endif // defined(ASIO_HAS_MOVE) +}; + +struct impl +{ + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == identity, + typename call_traits::result_type + >::type + operator()(ASIO_MOVE_ARG(S) s) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return ASIO_MOVE_CAST(S)(s); + } + +#if defined(ASIO_HAS_MOVE) + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(S&& s) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return ASIO_MOVE_CAST(S)(s).schedule(); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(S&& s) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return schedule(ASIO_MOVE_CAST(S)(s)); + } +#else // defined(ASIO_HAS_MOVE) + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(S& s) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return s.schedule(); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_member, + typename call_traits::result_type + >::type + operator()(const S& s) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return s.schedule(); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(S& s) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return schedule(s); + } + + template + ASIO_CONSTEXPR typename enable_if< + call_traits::overload == call_free, + typename call_traits::result_type + >::type + operator()(const S& s) const + ASIO_NOEXCEPT_IF(( + call_traits::is_noexcept)) + { + return schedule(s); + } +#endif // defined(ASIO_HAS_MOVE) +}; + +template +struct static_instance +{ + static const T instance; +}; + +template +const T static_instance::instance = {}; + +} // namespace asio_execution_schedule_fn +namespace asio { +namespace execution { +namespace { + +static ASIO_CONSTEXPR const asio_execution_schedule_fn::impl& + schedule = asio_execution_schedule_fn::static_instance<>::instance; + +} // namespace + +template +struct can_schedule : + integral_constant::overload != + asio_execution_schedule_fn::ill_formed> +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +constexpr bool can_schedule_v = can_schedule::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +struct is_nothrow_schedule : + integral_constant::is_noexcept> +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +constexpr bool is_nothrow_schedule_v + = is_nothrow_schedule::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +} // namespace execution +} // namespace asio + +#endif // defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_SCHEDULE_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/scheduler.hpp b/third_party/asio/1.18.2/include/asio/execution/scheduler.hpp new file mode 100644 index 000000000..b99ab3c7e --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/scheduler.hpp @@ -0,0 +1,86 @@ +// +// execution/scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_SCHEDULER_HPP +#define ASIO_EXECUTION_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/schedule.hpp" +#include "asio/traits/equality_comparable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace execution { +namespace detail { + +template +struct is_scheduler_base : + integral_constant::type>::value + && traits::equality_comparable::type>::is_valid + > +{ +}; + +} // namespace detail + +/// The is_scheduler trait detects whether a type T satisfies the +/// execution::scheduler concept. +/** + * Class template @c is_scheduler is a type trait that is derived from @c + * true_type if the type @c T meets the concept definition for a scheduler for + * error type @c E, otherwise @c false_type. + */ +template +struct is_scheduler : +#if defined(GENERATING_DOCUMENTATION) + integral_constant +#else // defined(GENERATING_DOCUMENTATION) + conditional< + can_schedule::value, + detail::is_scheduler_base, + false_type + >::type +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +ASIO_CONSTEXPR const bool is_scheduler_v = is_scheduler::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +#if defined(ASIO_HAS_CONCEPTS) + +template +ASIO_CONCEPT scheduler = is_scheduler::value; + +#define ASIO_EXECUTION_SCHEDULER ::asio::execution::scheduler + +#else // defined(ASIO_HAS_CONCEPTS) + +#define ASIO_EXECUTION_SCHEDULER typename + +#endif // defined(ASIO_HAS_CONCEPTS) + +} // namespace execution +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXECUTION_SCHEDULER_HPP diff --git a/third_party/asio/1.18.2/include/asio/execution/sender.hpp b/third_party/asio/1.18.2/include/asio/execution/sender.hpp new file mode 100644 index 000000000..4e4ba476b --- /dev/null +++ b/third_party/asio/1.18.2/include/asio/execution/sender.hpp @@ -0,0 +1,311 @@ +// +// execution/sender.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXECUTION_SENDER_HPP +#define ASIO_EXECUTION_SENDER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/execution/detail/as_invocable.hpp" +#include "asio/execution/detail/void_receiver.hpp" +#include "asio/execution/executor.hpp" +#include "asio/execution/receiver.hpp" + +#include "asio/detail/push_options.hpp" + +#if defined(ASIO_HAS_ALIAS_TEMPLATES) \ + && defined(ASIO_HAS_VARIADIC_TEMPLATES) \ + && defined(ASIO_HAS_DECLTYPE) \ + && !defined(ASIO_MSVC) || (_MSC_VER >= 1910) +# define ASIO_HAS_DEDUCED_EXECUTION_IS_TYPED_SENDER_TRAIT 1 +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + // && defined(ASIO_HAS_VARIADIC_TEMPLATES) + // && defined(ASIO_HAS_DECLTYPE) + // && !defined(ASIO_MSVC) || (_MSC_VER >= 1910) + +namespace asio { +namespace execution { +namespace detail { + +namespace sender_base_ns { struct sender_base {}; } + +template +struct sender_traits_base +{ + typedef void asio_execution_sender_traits_base_is_unspecialised; +}; + +template +struct sender_traits_base::value + >::type> +{ +}; + +template +struct has_sender_types : false_type +{ +}; + +#if defined(ASIO_HAS_DEDUCED_EXECUTION_IS_TYPED_SENDER_TRAIT) + +template < + template < + template class Tuple, + template class Variant + > class> +struct has_value_types +{ + typedef void type; +}; + +template < + template < + template class Variant + > class> +struct has_error_types +{ + typedef void type; +}; + +template +struct has_sender_types::type, + typename has_error_types::type, + typename conditional::type> : true_type +{ +}; + +template +struct sender_traits_base::value + >::type> +{ + template < + template class Tuple, + template class Variant> + using value_types = typename S::template value_types; + + template