Add lite branch
This commit is contained in:
parent
391215262b
commit
d6cd7b3d18
|
@ -1,81 +0,0 @@
|
|||
---
|
||||
BasedOnStyle: LLVM
|
||||
---
|
||||
Language: Cpp
|
||||
Standard: Cpp11
|
||||
ColumnLimit: 100
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
UseTab: ForContinuationAndIndentation
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: false
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping: {
|
||||
AfterClass: true,
|
||||
AfterControlStatement: true,
|
||||
AfterEnum: true,
|
||||
AfterFunction: true,
|
||||
AfterNamespace: true,
|
||||
AfterStruct: true,
|
||||
AfterUnion: true,
|
||||
BeforeCatch: true,
|
||||
BeforeElse: true,
|
||||
IndentBraces: false,
|
||||
#SplitEmptyFunctionBody: false
|
||||
}
|
||||
BreakBeforeInheritanceComma: true
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: false
|
||||
IncludeCategories:
|
||||
- Regex: '^<cppcoro/config.hpp>$'
|
||||
Priority: 1
|
||||
- Regex: '^<cppcoro/detail/'
|
||||
Priority: 3
|
||||
- Regex: '^<cppcoro/'
|
||||
Priority: 2
|
||||
- Regex: '^"doctest/'
|
||||
Priority: 7
|
||||
- Regex: '^"'
|
||||
Priority: 4
|
||||
- Regex: '^<experimental/'
|
||||
Priority: 6
|
||||
- Regex: '^<'
|
||||
Priority: 5
|
||||
IndentCaseLabels: true
|
||||
#IndentPPDirectives: AfterHash
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpacesInSquareBrackets: false
|
||||
SpacesInParentheses: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpacesInAngles: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceAfterTemplateKeyword: false
|
|
@ -1,16 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.hpp,*.cpp]
|
||||
indent_style = tab
|
||||
indent_size = tab
|
||||
tab_width = 4
|
||||
|
||||
[*.cake,*.py]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -1,3 +0,0 @@
|
|||
build
|
||||
*.cakec
|
||||
*.pyc
|
|
@ -1,4 +0,0 @@
|
|||
[submodule "cake"]
|
||||
path = tools/cake
|
||||
url = https://github.com/lewissbaker/cake.git
|
||||
branch = clang
|
69
.travis.yml
69
.travis.yml
|
@ -1,69 +0,0 @@
|
|||
# Copyright Lewis Baker 2017
|
||||
#
|
||||
# Distributed under MIT license.
|
||||
# See LICENSE.txt file for details.
|
||||
|
||||
language: cpp
|
||||
|
||||
os: linux
|
||||
|
||||
dist: bionic
|
||||
|
||||
git:
|
||||
submodules: true
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
packages:
|
||||
- python2.7
|
||||
- clang-11
|
||||
- lld-11
|
||||
- ninja-build
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- ${TOOLS_DIR}/cmake-${CMAKE_VERSION}
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
|
||||
env:
|
||||
global:
|
||||
- TOOLS_DIR=${TRAVIS_BUILD_DIR}/tools
|
||||
- LLVM_INSTALL_PREFIX=${TOOLS_DIR}/llvm
|
||||
- CMAKE_VERSION="3.9.1"
|
||||
matrix:
|
||||
include:
|
||||
- env: RELEASE=debug ARCH=x64 CLANG_VERSION=11
|
||||
- env: RELEASE=optimised ARCH=x64 CLANG_VERSION=11
|
||||
|
||||
before_install:
|
||||
- export CC="$CC-$CLANG_VERSION"
|
||||
- export CXX="$CXX-$CLANG_VERSION"
|
||||
- |
|
||||
if [ ! -f "${TOOLS_DIR}/cmake-${CMAKE_VERSION}/cached" ]; then
|
||||
CMAKE_URL="https://cmake.org/files/v3.9/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz"
|
||||
mkdir -p "${TOOLS_DIR}/cmake-${CMAKE_VERSION}"
|
||||
travis_retry wget --no-check-certificate --quiet -O - "${CMAKE_URL}" | tar --strip-components=1 -xz -C "${TOOLS_DIR}/cmake-${CMAKE_VERSION}"
|
||||
touch "${TOOLS_DIR}/cmake-${CMAKE_VERSION}/cached"
|
||||
else
|
||||
echo "Using cached CMake version ${CMAKE_VERSION}"
|
||||
fi
|
||||
export PATH="${TOOLS_DIR}/cmake-${CMAKE_VERSION}/bin:$PATH"
|
||||
|
||||
install:
|
||||
- ./build-libcxx.sh
|
||||
- export LD_LIBRARY_PATH="${LLVM_INSTALL_PREFIX}/lib"
|
||||
|
||||
before_script:
|
||||
- source ./init.sh
|
||||
|
||||
script:
|
||||
- cake architecture=$ARCH release=$RELEASE --clang-executable="$CC" --libcxx-install-prefix="${LLVM_INSTALL_PREFIX}"
|
||||
|
||||
notifications:
|
||||
email: false
|
|
@ -0,0 +1,18 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(cppcoro LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
file(GLOB_RECURSE HEADERS "include/*.h")
|
||||
file(GLOB_RECURSE SOURCES "lib/*.cpp")
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED ${HEADERS} ${SOURCES})
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC "include")
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
28
README.md
28
README.md
|
@ -714,7 +714,7 @@ namespace cppcoro
|
|||
{
|
||||
public:
|
||||
bool await_ready() const noexcept;
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
|
||||
bool await_suspend(std::coroutine_handle<> awaiter) noexcept;
|
||||
void await_resume() const noexcept;
|
||||
};
|
||||
|
||||
|
@ -722,7 +722,7 @@ namespace cppcoro
|
|||
{
|
||||
public:
|
||||
bool await_ready() const noexcept;
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
|
||||
bool await_suspend(std::coroutine_handle<> awaiter) noexcept;
|
||||
[[nodiscard]] async_mutex_lock await_resume() const noexcept;
|
||||
};
|
||||
|
||||
|
@ -836,7 +836,7 @@ namespace cppcoro
|
|||
async_manual_reset_event_operation(async_manual_reset_event& event) noexcept;
|
||||
|
||||
bool await_ready() const noexcept;
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
|
||||
bool await_suspend(std::coroutine_handle<> awaiter) noexcept;
|
||||
void await_resume() const noexcept;
|
||||
};
|
||||
}
|
||||
|
@ -904,7 +904,7 @@ namespace cppcoro
|
|||
async_auto_reset_event_operation(const async_auto_reset_event_operation& other) noexcept;
|
||||
|
||||
bool await_ready() const noexcept;
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
|
||||
bool await_suspend(std::coroutine_handle<> awaiter) noexcept;
|
||||
void await_resume() const noexcept;
|
||||
|
||||
};
|
||||
|
@ -1389,7 +1389,7 @@ namespace cppcoro
|
|||
schedule_operation(static_thread_pool* tp) noexcept;
|
||||
|
||||
bool await_ready() noexcept;
|
||||
bool await_suspend(std::experimental::coroutine_handle<> h) noexcept;
|
||||
bool await_suspend(std::coroutine_handle<> h) noexcept;
|
||||
bool await_resume() noexcept;
|
||||
|
||||
private:
|
||||
|
@ -1546,7 +1546,7 @@ namespace cppcoro
|
|||
schedule_operation& operator=(const schedule_operation&) noexcept;
|
||||
|
||||
bool await_ready() const noexcept;
|
||||
void await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
|
||||
void await_suspend(std::coroutine_handle<> awaiter) noexcept;
|
||||
void await_resume() noexcept;
|
||||
};
|
||||
|
||||
|
@ -1560,7 +1560,7 @@ namespace cppcoro
|
|||
timed_schedule_operation& operator=(timed_schedule_operation&&) = delete;
|
||||
|
||||
bool await_ready() const noexcept;
|
||||
void await_suspend(std::experimental::coroutine_handle<> awaiter);
|
||||
void await_suspend(std::coroutine_handle<> awaiter);
|
||||
void await_resume();
|
||||
};
|
||||
|
||||
|
@ -1596,7 +1596,7 @@ Example:
|
|||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
cppcoro::task<std::uint64_t> count_lines(cppcoro::io_service& ioService, fs::path path)
|
||||
{
|
||||
|
@ -1740,7 +1740,7 @@ namespace cppcoro
|
|||
file_read_operation(file_read_operation&& other) noexcept;
|
||||
|
||||
bool await_ready() const noexcept;
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter);
|
||||
bool await_suspend(std::coroutine_handle<> awaiter);
|
||||
std::size_t await_resume();
|
||||
|
||||
};
|
||||
|
@ -1752,7 +1752,7 @@ namespace cppcoro
|
|||
file_write_operation(file_write_operation&& other) noexcept;
|
||||
|
||||
bool await_ready() const noexcept;
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter);
|
||||
bool await_suspend(std::coroutine_handle<> awaiter);
|
||||
std::size_t await_resume();
|
||||
|
||||
};
|
||||
|
@ -1774,7 +1774,7 @@ namespace cppcoro
|
|||
[[nodiscard]]
|
||||
static read_only_file open(
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
const std::filesystem::path& path,
|
||||
file_share_mode shareMode = file_share_mode::read,
|
||||
file_buffering_mode bufferingMode = file_buffering_mode::default_);
|
||||
|
||||
|
@ -1787,7 +1787,7 @@ namespace cppcoro
|
|||
[[nodiscard]]
|
||||
static write_only_file open(
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
const std::filesystem::path& path,
|
||||
file_open_mode openMode = file_open_mode::create_or_open,
|
||||
file_share_mode shareMode = file_share_mode::none,
|
||||
file_buffering_mode bufferingMode = file_buffering_mode::default_);
|
||||
|
@ -1801,7 +1801,7 @@ namespace cppcoro
|
|||
[[nodiscard]]
|
||||
static read_write_file open(
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
const std::filesystem::path& path,
|
||||
file_open_mode openMode = file_open_mode::create_or_open,
|
||||
file_share_mode shareMode = file_share_mode::none,
|
||||
file_buffering_mode bufferingMode = file_buffering_mode::default_);
|
||||
|
@ -2794,7 +2794,7 @@ coroutine.
|
|||
|
||||
A type that satisfies `Awaiter<T>` must have, for an instance of the type, `awaiter`:
|
||||
- `awaiter.await_ready()` -> `bool`
|
||||
- `awaiter.await_suspend(std::experimental::coroutine_handle<void>{})` -> `void` or `bool` or `std::experimental::coroutine_handle<P>` for some `P`.
|
||||
- `awaiter.await_suspend(std::coroutine_handle<void>{})` -> `void` or `bool` or `std::coroutine_handle<P>` for some `P`.
|
||||
- `awaiter.await_resume()` -> `T`
|
||||
|
||||
Any type that implements the `Awaiter<T>` concept also implements the `Awaitable<T>` concept.
|
||||
|
|
32
appveyor.yml
32
appveyor.yml
|
@ -1,32 +0,0 @@
|
|||
version: 1.0.{build}
|
||||
|
||||
image:
|
||||
- Visual Studio 2017
|
||||
- Visual Studio 2017 Preview
|
||||
|
||||
platform:
|
||||
- x64
|
||||
- x86
|
||||
|
||||
configuration:
|
||||
- debug
|
||||
- optimised
|
||||
|
||||
matrix:
|
||||
# Allow failures under MSVC x86 optimised since there are some known compiler
|
||||
# bugs causing failures here.
|
||||
allow_failures:
|
||||
- platform: x86
|
||||
configuration: optimised
|
||||
|
||||
clone_script:
|
||||
- ps: git clone -q $("--branch=" + $env:APPVEYOR_REPO_BRANCH) $("https://github.com/" + $env:APPVEYOR_REPO_NAME + ".git") $env:APPVEYOR_BUILD_FOLDER
|
||||
- ps: if (!$env:APPVEYOR_PULL_REQUEST_NUMBER) {$("git checkout -qf " + $env:APPVEYOR_REPO_COMMIT)}
|
||||
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER) {git fetch -q origin +refs/pull/$($env:APPVEYOR_PULL_REQUEST_NUMBER)/merge; git checkout -qf FETCH_HEAD}
|
||||
- ps: git submodule update -q --init --recursive
|
||||
|
||||
build_script:
|
||||
- cmd: "C:\\Python27-x64\\python.exe tools\\cake\\src\\run.py --debug=stack architecture=%PLATFORM% release=%CONFIGURATION% lib/build.cake test/build.cake@programs"
|
||||
|
||||
test_script:
|
||||
- cmd: "C:\\Python27-x64\\python.exe tools\\cake\\src\\run.py --debug=stack architecture=%PLATFORM% release=%CONFIGURATION% test/build.cake"
|
46
args.cake
46
args.cake
|
@ -1,46 +0,0 @@
|
|||
##############################################################################
|
||||
# cppcoro library
|
||||
#
|
||||
# This file defines extra command-line args specific to the cppcoro project.
|
||||
##############################################################################
|
||||
from cake.script import Script
|
||||
import cake.system
|
||||
|
||||
parser = Script.getCurrent().engine.parser
|
||||
|
||||
# Add a project generation option. It will be stored in 'engine.options' which
|
||||
# can later be accessed in our config.cake.
|
||||
parser.add_option(
|
||||
"-p", "--projects",
|
||||
action="store_true",
|
||||
dest="createProjects",
|
||||
help="Create projects instead of building a variant.",
|
||||
default=False,
|
||||
)
|
||||
|
||||
if cake.system.isLinux() or cake.system.isDarwin():
|
||||
parser.add_option(
|
||||
"--clang-install-prefix",
|
||||
dest="clangInstallPrefix",
|
||||
type="string",
|
||||
metavar="PATH",
|
||||
default=None,
|
||||
help="Path where clang has been installed."
|
||||
)
|
||||
|
||||
parser.add_option(
|
||||
"--clang-executable",
|
||||
dest="clangExecutable",
|
||||
type="string",
|
||||
metavar="FILE",
|
||||
default="clang",
|
||||
help="Name or full-path of clang executable to compile with")
|
||||
|
||||
parser.add_option(
|
||||
"--libcxx-install-prefix",
|
||||
dest="libcxxInstallPrefix",
|
||||
type="string",
|
||||
metavar="PATH",
|
||||
default=None,
|
||||
help="Path where libc++ has been installed.\n"
|
||||
"Defaults to value of --clang-install-prefix")
|
104
build-clang.sh
104
build-clang.sh
|
@ -1,104 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Builds Clang and LLD from source.
|
||||
|
||||
export WORKSPACE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
export LLVM_PROJECT="${WORKSPACE_ROOT}/tools/llvm-project"
|
||||
|
||||
: "${LLVM_REPO:=https://github.com/llvm-mirror/llvm.git}"
|
||||
: "${LLVM_REF:=master}"
|
||||
|
||||
: "${CLANG_REPO:=https://github.com/llvm-mirror/clang.git}"
|
||||
: "${CLANG_REF:=master}"
|
||||
|
||||
: "${LLD_REPO:=https://github.com/llvm-mirror/lld.git}"
|
||||
: "${LLD_REF:=master}"
|
||||
|
||||
: "${LIBCXX_REPO:=https://github.com/llvm-mirror/libcxx.git}"
|
||||
: "${LIBCXX_REF:=master}"
|
||||
|
||||
: "${LLVM_INSTALL_PREFIX:=$WORKSPACE_ROOT/build/llvm-install}"
|
||||
|
||||
: "${CXX:=clang++}"
|
||||
: "${CC:=clang}"
|
||||
|
||||
if [ ! -d "${LLVM_PROJECT}" ]; then
|
||||
mkdir -p "${LLVM_PROJECT}" || exit
|
||||
fi
|
||||
|
||||
cd "${LLVM_PROJECT}" || exit
|
||||
|
||||
if [ ! -d llvm/.git ]; then
|
||||
git clone --depth=1 -b "$LLVM_REF" -- "$LLVM_REPO" llvm || exit
|
||||
else
|
||||
(cd llvm &&
|
||||
git remote set-url origin "$LLVM_REPO" &&
|
||||
git fetch origin "$LLVM_REF" &&
|
||||
git checkout FETCH_HEAD) || exit
|
||||
fi
|
||||
|
||||
if [ ! -d llvm/tools/clang/.git ]; then
|
||||
git clone --depth=1 -b "$CLANG_REF" -- "$CLANG_REPO" llvm/tools/clang || exit
|
||||
ln -s llvm/tools/clang clang || exit
|
||||
else
|
||||
(cd llvm/tools/clang &&
|
||||
git remote set-url origin "$CLANG_REPO" &&
|
||||
git fetch --depth=1 origin "$CLANG_REF" &&
|
||||
git checkout FETCH_HEAD) || exit
|
||||
fi
|
||||
|
||||
if [ ! -d llvm/tools/lld/.git ]; then
|
||||
git clone --depth=1 -b "$LLD_REF" -- "$LLD_REPO" llvm/tools/lld || exit
|
||||
ln -s llvm/tools/lld lld || exit
|
||||
else
|
||||
(cd llvm/tools/lld &&
|
||||
git remote set-url origin "$LLD_REPO" &&
|
||||
git fetch --depth=1 origin "$LLD_REF" &&
|
||||
git checkout FETCH_HEAD) || exit
|
||||
fi
|
||||
|
||||
if [ ! -d llvm/projects/libcxx/.git ]; then
|
||||
git clone --depth=1 -b "$LIBCXX_REF" -- "$LIBCXX_REPO" llvm/projects/libcxx || exit
|
||||
ln -s llvm/projects/libcxx libcxx || exit
|
||||
else
|
||||
(cd llvm/projects/libcxx &&
|
||||
git remote set-url origin "$LIBCXX_REPO" &&
|
||||
git fetch --depth=1 origin "$LIBCXX_REF" &&
|
||||
git checkout FETCH_HEAD) || exit
|
||||
fi
|
||||
|
||||
cd "$WORKSPACE_ROOT" || exit
|
||||
|
||||
if [ ! -d build/clang ]; then
|
||||
mkdir -p build/clang || exit
|
||||
fi
|
||||
|
||||
# Build clang toolchain
|
||||
(mkdir -p build/clang && \
|
||||
cd build/clang && \
|
||||
cmake -GNinja \
|
||||
-DCMAKE_CXX_COMPILER="$CXX" \
|
||||
-DCMAKE_C_COMPILER="$CC" \
|
||||
-DCMAKE_BUILD_TYPE=MinSizeRel \
|
||||
-DCMAKE_INSTALL_PREFIX="${LLVM_INSTALL_PREFIX}" \
|
||||
-DCMAKE_BUILD_WITH_INSTALL_RPATH="yes" \
|
||||
-DLLVM_TARGETS_TO_BUILD=X86 \
|
||||
-DLLVM_ENABLE_PROJECTS="lld;clang" \
|
||||
"${LLVM_PROJECT}/llvm" && \
|
||||
ninja install-clang \
|
||||
install-clang-headers \
|
||||
install-llvm-ar \
|
||||
install-lld) || exit
|
||||
|
||||
# Build libcxx using clang we just built
|
||||
(mkdir -p build/libcxx && \
|
||||
cd build/libcxx && \
|
||||
cmake -GNinja \
|
||||
-DCMAKE_CXX_COMPILER="${LLVM_INSTALL_PREFIX}/bin/clang++" \
|
||||
-DCMAKE_C_COMPILER="${LLVM_INSTALL_PREFIX}/bin/clang" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${LLVM_INSTALL_PREFIX}" \
|
||||
-DLLVM_PATH="${LLVM_PROJECT}/llvm" \
|
||||
-DLIBCXX_CXX_ABI=libstdc++ \
|
||||
"${LLVM_PROJECT}/libcxx" && \
|
||||
ninja install) || exit
|
|
@ -1,56 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Builds libc++ from source
|
||||
|
||||
export WORKSPACE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
export LLVM_PROJECT="${WORKSPACE_ROOT}/tools/llvm-project"
|
||||
|
||||
: "${LLVM_REPO:=https://github.com/llvm-mirror/llvm.git}"
|
||||
: "${LLVM_REF:=master}"
|
||||
|
||||
: "${LIBCXX_REPO:=https://github.com/llvm-mirror/libcxx.git}"
|
||||
: "${LIBCXX_REF:=master}"
|
||||
|
||||
: "${LLVM_INSTALL_PREFIX:=$WORKSPACE_ROOT/build/llvm-install}"
|
||||
|
||||
: "${CXX:=clang++}"
|
||||
: "${CC:=clang}"
|
||||
|
||||
if [ ! -d "${LLVM_PROJECT}" ]; then
|
||||
mkdir -p "${LLVM_PROJECT}" || exit
|
||||
fi
|
||||
|
||||
cd "${LLVM_PROJECT}" || exit
|
||||
|
||||
if [ ! -d llvm/.git ]; then
|
||||
git clone --depth=1 -b "$LLVM_REF" -- "$LLVM_REPO" llvm || exit
|
||||
else
|
||||
(cd llvm &&
|
||||
git remote set-url origin "$LLVM_REPO" &&
|
||||
git fetch origin "$LLVM_REF" &&
|
||||
git checkout FETCH_HEAD) || exit
|
||||
fi
|
||||
|
||||
if [ ! -d llvm/projects/libcxx.git ]; then
|
||||
git clone --depth=1 -b "$LIBCXX_REF" -- "$LIBCXX_REPO" llvm/projects/libcxx || exit
|
||||
ln -s llvm/projects/libcxx libcxx || exit
|
||||
else
|
||||
(cd llvm/projects/libcxx &&
|
||||
git remote set-url origin "$LIBCXX_REPO" &&
|
||||
git fetch --depth=1 origin "$LIBCXX_REF" &&
|
||||
git checkout FETCH_HEAD) || exit
|
||||
fi
|
||||
|
||||
cd "$WORKSPACE_ROOT" || exit
|
||||
|
||||
(mkdir -p build/libcxx && \
|
||||
cd build/libcxx && \
|
||||
cmake -GNinja \
|
||||
-DCMAKE_CXX_COMPILER="${CXX}" \
|
||||
-DCMAKE_C_COMPILER="${CC}" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${LLVM_INSTALL_PREFIX}" \
|
||||
-DLLVM_PATH="${LLVM_PROJECT}/llvm" \
|
||||
-DLIBCXX_CXX_ABI=libstdc++ \
|
||||
"${LLVM_PROJECT}/libcxx" && \
|
||||
ninja install) || exit
|
30
build.cake
30
build.cake
|
@ -1,30 +0,0 @@
|
|||
from cake.tools import script, project, env
|
||||
|
||||
libScript = script.get(script.cwd('lib/build.cake'))
|
||||
script.addTarget('objects', libScript.getTarget('objects'))
|
||||
script.addTarget('libs', libScript.getTarget('libs'))
|
||||
script.addDefaultTarget(libScript.getDefaultTarget())
|
||||
libScript.execute()
|
||||
|
||||
testScript = script.get(script.cwd('test/build.cake'))
|
||||
script.addTarget('objects', testScript.getTarget('objects'))
|
||||
script.addDefaultTarget(testScript.getDefaultTarget())
|
||||
testScript.execute()
|
||||
|
||||
projects = [
|
||||
libScript.getResult('project'),
|
||||
testScript.getResult('project'),
|
||||
]
|
||||
|
||||
|
||||
sln = project.solution(
|
||||
target=env.expand('${CPPCORO_PROJECT}/cppcoro'),
|
||||
projects=projects,
|
||||
)
|
||||
|
||||
script.addTargets('projects', projects)
|
||||
script.addTarget('projects', sln)
|
||||
|
||||
script.setResult(
|
||||
projects=projects,
|
||||
)
|
334
config.cake
334
config.cake
|
@ -1,334 +0,0 @@
|
|||
##############################################################################
|
||||
# Copyright (c) Lewis Baker
|
||||
# Licenced under MIT license. See LICENSE.txt for details.
|
||||
#
|
||||
# cppcoro library
|
||||
#
|
||||
# This file defines the set of build variants available to be built and
|
||||
# their corresponding tools and settings.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import cake.path
|
||||
import cake.system
|
||||
|
||||
from cake.engine import Variant
|
||||
from cake.script import Script
|
||||
|
||||
configuration = Script.getCurrent().configuration
|
||||
engine = configuration.engine
|
||||
|
||||
# Add path containing custom cake extensions to Python's import path.
|
||||
toolsDir = configuration.abspath(cake.path.join('tools', 'cake_extensions'))
|
||||
sys.path.insert(0, toolsDir)
|
||||
|
||||
hostPlatform = cake.system.platform().lower()
|
||||
hostArchitecture = cake.system.architecture().lower()
|
||||
|
||||
baseVariant = Variant()
|
||||
|
||||
from cake.library.script import ScriptTool
|
||||
from cake.library.variant import VariantTool
|
||||
from cake.library.env import EnvironmentTool
|
||||
from cake.library.project import ProjectTool
|
||||
from cake.library.compilers import CompilerNotFoundError
|
||||
from testtool import UnitTestTool
|
||||
|
||||
baseVariant.tools["script"] = script = ScriptTool(configuration=configuration)
|
||||
baseVariant.tools["variant"] = variant = VariantTool(configuration=configuration)
|
||||
baseVariant.tools["test"] = test = UnitTestTool(configuration=configuration)
|
||||
|
||||
baseVariant.tools["env"] = env = EnvironmentTool(configuration=configuration)
|
||||
|
||||
env["VARIANT"] = "${PLATFORM}_${ARCHITECTURE}_${COMPILER}${COMPILER_VERSION}_${RELEASE}"
|
||||
env["BUILD"] = "build/${VARIANT}"
|
||||
|
||||
env["CPPCORO"] = "."
|
||||
env["CPPCORO_BUILD"] = "${BUILD}"
|
||||
env["CPPCORO_PROJECT"] = "build/project"
|
||||
env["CPPCORO_BIN"] = "${CPPCORO_BUILD}/bin"
|
||||
env["CPPCORO_LIB"] = "${CPPCORO_BUILD}/lib"
|
||||
|
||||
baseVariant.tools["project"] = project = ProjectTool(configuration=configuration)
|
||||
project.product = project.VS2015
|
||||
project.enabled = engine.options.createProjects
|
||||
if project.enabled:
|
||||
test.enabled = False
|
||||
engine.addBuildSuccessCallback(project.build)
|
||||
|
||||
if cake.system.isWindows() or cake.system.isCygwin():
|
||||
# Uncomment the next line to use an experimental MSVC compiler version
|
||||
# downloaded from the https://vcppdogfooding.azurewebsites.net/ NuGet repository.
|
||||
# Unzip the .nuget file to a folder and specify the path here.
|
||||
nugetPath = None #r'C:\Path\To\VisualCppTools.Community.VS2017Layout.14.11.25415-Pre'
|
||||
|
||||
for arch in ("x64", "x86"):
|
||||
try:
|
||||
from cake.library.compilers.msvc import getVisualStudio2017Compiler, findMsvc2017InstallDir
|
||||
if nugetPath:
|
||||
vcInstallDir = cake.path.join(nugetPath, 'lib', 'native')
|
||||
else:
|
||||
vcInstallDir = str(findMsvc2017InstallDir(targetArchitecture=arch, allowPreRelease=True))
|
||||
compiler = getVisualStudio2017Compiler(
|
||||
configuration,
|
||||
targetArchitecture=arch,
|
||||
vcInstallDir=vcInstallDir,
|
||||
)
|
||||
|
||||
engine.logger.outputInfo("MSVC (" + arch + "): " + vcInstallDir + "\n")
|
||||
|
||||
msvcVariant = baseVariant.clone(
|
||||
platform="windows",
|
||||
compiler="msvc",
|
||||
architecture=arch,
|
||||
)
|
||||
|
||||
msvcVariant.tools["compiler"] = compiler
|
||||
|
||||
project = msvcVariant.tools["project"]
|
||||
project.solutionPlatformName = "Windows " + arch
|
||||
if arch == "x86":
|
||||
project.projectPlatformName = "Win32"
|
||||
elif arch == "x64":
|
||||
project.projectPlatformName = "x64"
|
||||
|
||||
if engine.options.createProjects:
|
||||
compiler.enabled = False
|
||||
|
||||
compiler.enableRtti = True
|
||||
compiler.enableExceptions = True
|
||||
compiler.outputMapFile = True
|
||||
compiler.outputFullPath = True
|
||||
compiler.messageStyle = compiler.MSVS_CLICKABLE
|
||||
compiler.warningLevel = '4'
|
||||
compiler.warningsAsErrors = True
|
||||
|
||||
# Enable experimental C++ coroutines via command-line flag.
|
||||
compiler.addCppFlag('/await')
|
||||
|
||||
# Enable static analysis warnings (not as errors)
|
||||
compiler.addCppFlag('/analyze:WX-')
|
||||
compiler.addCppFlag('/analyze:max_paths')
|
||||
compiler.addCppFlag('512')
|
||||
|
||||
# Enable C++17 features like std::optional<>
|
||||
compiler.addCppFlag('/std:c++latest')
|
||||
|
||||
compiler.addDefine('_SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING')
|
||||
|
||||
compiler.addProgramFlag('/nodefaultlib')
|
||||
compiler.addModuleFlag('/nodefaultlib')
|
||||
|
||||
env = msvcVariant.tools["env"]
|
||||
env["COMPILER"] = "msvc"
|
||||
env["COMPILER_VERSION"] = "15"
|
||||
env["PLATFORM"] = "windows"
|
||||
env["ARCHITECTURE"] = arch
|
||||
|
||||
# Visual Studio - Debug
|
||||
msvcDebugVariant = msvcVariant.clone(release="debug")
|
||||
msvcDebugVariant.tools["env"]["RELEASE"] = "debug"
|
||||
compiler = msvcDebugVariant.tools["compiler"]
|
||||
compiler.debugSymbols = True
|
||||
compiler.useIncrementalLinking = True
|
||||
compiler.optimisation = compiler.NO_OPTIMISATION
|
||||
|
||||
compiler.runtimeLibraries = 'debug-dll'
|
||||
compiler.addLibrary('msvcrtd')
|
||||
compiler.addLibrary('msvcprtd')
|
||||
compiler.addLibrary('vcruntimed')
|
||||
compiler.addLibrary('ucrtd')
|
||||
compiler.addLibrary('oldnames')
|
||||
|
||||
project = msvcDebugVariant.tools["project"]
|
||||
project.projectConfigName = "Windows (" + arch + ") Msvc (Debug)"
|
||||
project.solutionConfigName = "Msvc Debug"
|
||||
|
||||
configuration.addVariant(msvcDebugVariant)
|
||||
|
||||
# Visual Studio - Optimised
|
||||
msvcOptVariant = msvcVariant.clone(release="optimised")
|
||||
msvcOptVariant.tools["env"]["RELEASE"] = "optimised"
|
||||
compiler = msvcOptVariant.tools["compiler"]
|
||||
compiler.debugSymbols = True
|
||||
compiler.useIncrementalLinking = False
|
||||
compiler.useFunctionLevelLinking = True
|
||||
compiler.optimisation = compiler.FULL_OPTIMISATION
|
||||
|
||||
compiler.runtimeLibraries = 'release-dll'
|
||||
compiler.addLibrary('msvcrt')
|
||||
compiler.addLibrary('msvcprt')
|
||||
compiler.addLibrary('vcruntime')
|
||||
compiler.addLibrary('ucrt')
|
||||
compiler.addLibrary('oldnames')
|
||||
|
||||
# Enable compiler to optimise-out heap allocations for coroutine frames
|
||||
# Only supported on X64
|
||||
if arch == 'x64':
|
||||
compiler.addCppFlag('/await:heapelide')
|
||||
|
||||
compiler.addDefine('NDEBUG')
|
||||
|
||||
project = msvcOptVariant.tools["project"]
|
||||
project.projectConfigName = "Windows (" + arch + ") Msvc (Optimised)"
|
||||
project.solutionConfigName = "Msvc Optimised"
|
||||
configuration.addVariant(msvcOptVariant)
|
||||
|
||||
except CompilerNotFoundError, e:
|
||||
print str(e)
|
||||
|
||||
elif cake.system.isLinux() or cake.system.isDarwin():
|
||||
|
||||
def firstExisting(paths, default=None):
|
||||
for p in paths:
|
||||
if os.path.exists(p):
|
||||
return p
|
||||
else:
|
||||
return default
|
||||
|
||||
|
||||
|
||||
if cake.system.isLinux():
|
||||
defaultInstallPaths = ["/usr"]
|
||||
elif cake.system.isDarwin():
|
||||
defaultInstallPaths = ["/usr/local/opt/llvm", "/usr"]
|
||||
|
||||
clangInstallPrefix = engine.options.clangInstallPrefix
|
||||
clangExe = engine.options.clangExecutable
|
||||
|
||||
if clangInstallPrefix is None:
|
||||
# If no clang install prefix was specified then try to deduce it
|
||||
# from the --clang-executable path.
|
||||
if os.path.isabs(clangExe):
|
||||
clangBinPath = cake.path.dirName(clangExe)
|
||||
if cake.path.baseName(clangBinPath) == 'bin':
|
||||
clangInstallPrefix = cake.path.dirName(clangBinPath)
|
||||
|
||||
# Use sensible system defaults if neither --clang-executable or
|
||||
# --clang-install-prefix command-line arguments are specified.
|
||||
if clangInstallPrefix is None:
|
||||
clangInstallPrefix = firstExisting(defaultInstallPaths, default="/usr")
|
||||
|
||||
libcxxInstallPrefix = engine.options.libcxxInstallPrefix
|
||||
if libcxxInstallPrefix is None:
|
||||
libcxxInstallPrefix = clangInstallPrefix
|
||||
|
||||
# If the user didn't specify an absolute path to clang then look for it in
|
||||
# the installed clang bin directory.
|
||||
if not os.path.isabs(clangExe):
|
||||
clangExe = cake.path.join(clangInstallPrefix, 'bin', clangExe)
|
||||
|
||||
# Extract the clang version suffix from the file-name
|
||||
# We'll try and use other LLVM tools with the same suffix if they exist.
|
||||
clangName = cake.path.baseName(clangExe)
|
||||
versionSuffix = clangName[5:] if clangName.startswith('clang-') else ""
|
||||
|
||||
clangExeDir = cake.path.dirName(clangExe)
|
||||
clangBinPath = cake.path.join(clangInstallPrefix, 'bin')
|
||||
|
||||
binPaths = [clangExeDir]
|
||||
if clangBinPath not in binPaths:
|
||||
binPaths.append(clangBinPath)
|
||||
|
||||
llvmArExe = firstExisting(
|
||||
cake.path.join(binPaths, ['llvm-ar' + versionSuffix, 'llvm-ar', 'ar']),
|
||||
default="/usr/bin/ar")
|
||||
|
||||
# Prefer lld if available, otherwise fall back to system ld
|
||||
lldExe = firstExisting(
|
||||
cake.path.join(binPaths, ['ld.lld' + versionSuffix, 'ld.lld']),
|
||||
)
|
||||
|
||||
engine.logger.outputInfo(
|
||||
"clang: " + clangExe + "\n" +
|
||||
"llvm-ar: " + llvmArExe + "\n")
|
||||
|
||||
from cake.library.compilers.clang import ClangCompiler
|
||||
|
||||
if cake.system.isLinux():
|
||||
platform = 'linux'
|
||||
elif cake.system.isDarwin():
|
||||
platform = 'darwin'
|
||||
|
||||
clangVariant = baseVariant.clone(compiler='clang',
|
||||
platform=platform,
|
||||
architecture='x64')
|
||||
|
||||
# If libc++ is installed in a non-standard location, add the path to libc++.so
|
||||
# to the library search path by adding libc++'s /lib directory to LD_LIBRARY_PATH
|
||||
if libcxxInstallPrefix not in defaultInstallPaths:
|
||||
libcxxLibPath = os.path.abspath(cake.path.join(libcxxInstallPrefix, 'lib'))
|
||||
ldPaths = [libcxxLibPath]
|
||||
test = clangVariant.tools["test"]
|
||||
oldLdPath = test.env.get('LD_LIBRARY_PATH', None)
|
||||
if oldLdPath:
|
||||
ldPaths.append(oldLdPath)
|
||||
test.env['LD_LIBRARY_PATH'] = os.path.pathsep.join(ldPaths)
|
||||
|
||||
compiler = ClangCompiler(
|
||||
configuration=configuration,
|
||||
clangExe=clangExe,
|
||||
llvmArExe=llvmArExe,
|
||||
binPaths=[clangBinPath])
|
||||
|
||||
compiler.addCppFlag('-std=c++1z')
|
||||
compiler.addCppFlag('-fcoroutines-ts')
|
||||
compiler.addCppFlag('-m64')
|
||||
|
||||
if lldExe:
|
||||
lldExeAbspath = configuration.abspath(lldExe)
|
||||
compiler.addModuleFlag('-fuse-ld=' + lldExeAbspath)
|
||||
compiler.addProgramFlag('-fuse-ld=' + lldExeAbspath)
|
||||
|
||||
if libcxxInstallPrefix != clangInstallPrefix:
|
||||
compiler.addCppFlag('-nostdinc++')
|
||||
compiler.addIncludePath(cake.path.join(
|
||||
libcxxInstallPrefix, 'include', 'c++', 'v1'))
|
||||
compiler.addLibraryPath(cake.path.join(
|
||||
libcxxInstallPrefix, 'lib'))
|
||||
else:
|
||||
compiler.addCppFlag('-stdlib=libc++')
|
||||
|
||||
compiler.addLibrary('c++')
|
||||
compiler.addLibrary('c')
|
||||
compiler.addLibrary('pthread')
|
||||
|
||||
#compiler.addProgramFlag('-Wl,--trace')
|
||||
#compiler.addProgramFlag('-Wl,-v')
|
||||
|
||||
clangVariant.tools['compiler'] = compiler
|
||||
|
||||
env = clangVariant.tools["env"]
|
||||
env["COMPILER"] = "clang"
|
||||
env["COMPILER_VERSION"] = compiler.version
|
||||
env["PLATFORM"] = platform
|
||||
env["ARCHITECTURE"] = "x64"
|
||||
|
||||
clangDebugVariant = clangVariant.clone(release='debug')
|
||||
clangDebugVariant.tools["env"]["RELEASE"] = 'debug'
|
||||
|
||||
# Configure debug-specific settings here
|
||||
compiler = clangDebugVariant.tools["compiler"]
|
||||
compiler.addCppFlag('-O0')
|
||||
compiler.addCppFlag('-g')
|
||||
|
||||
configuration.addVariant(clangDebugVariant)
|
||||
|
||||
clangOptimisedVariant = clangVariant.clone(release='optimised')
|
||||
clangOptimisedVariant.tools["env"]["RELEASE"] = 'optimised'
|
||||
|
||||
# Configure optimised-specific settings here
|
||||
compiler = clangOptimisedVariant.tools["compiler"]
|
||||
compiler.addCppFlag('-O3')
|
||||
compiler.addCppFlag('-g')
|
||||
|
||||
# Only use link-time optimisation if we're using LLD
|
||||
if lldExe:
|
||||
compiler.addCppFlag('-flto')
|
||||
compiler.addProgramFlag('-flto')
|
||||
compiler.addModuleFlag('-flto')
|
||||
|
||||
configuration.addVariant(clangOptimisedVariant)
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef CPPCORO_ASYNC_AUTO_RESET_EVENT_HPP_INCLUDED
|
||||
#define CPPCORO_ASYNC_AUTO_RESET_EVENT_HPP_INCLUDED
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
|
@ -80,7 +80,7 @@ namespace cppcoro
|
|||
async_auto_reset_event_operation(const async_auto_reset_event_operation& other) noexcept;
|
||||
|
||||
bool await_ready() const noexcept { return m_event == nullptr; }
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
|
||||
bool await_suspend(std::coroutine_handle<> awaiter) noexcept;
|
||||
void await_resume() const noexcept {}
|
||||
|
||||
private:
|
||||
|
@ -89,7 +89,7 @@ namespace cppcoro
|
|||
|
||||
const async_auto_reset_event* m_event;
|
||||
async_auto_reset_event_operation* m_next;
|
||||
std::experimental::coroutine_handle<> m_awaiter;
|
||||
std::coroutine_handle<> m_awaiter;
|
||||
std::atomic<std::uint32_t> m_refCount;
|
||||
|
||||
};
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <atomic>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <functional>
|
||||
#include <cassert>
|
||||
|
||||
|
@ -45,7 +45,7 @@ namespace cppcoro
|
|||
async_generator_promise_base(const async_generator_promise_base& other) = delete;
|
||||
async_generator_promise_base& operator=(const async_generator_promise_base& other) = delete;
|
||||
|
||||
std::experimental::suspend_always initial_suspend() const noexcept
|
||||
std::suspend_always initial_suspend() const noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ namespace cppcoro
|
|||
|
||||
std::exception_ptr m_exception;
|
||||
|
||||
std::experimental::coroutine_handle<> m_consumerCoroutine;
|
||||
std::coroutine_handle<> m_consumerCoroutine;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -100,7 +100,7 @@ namespace cppcoro
|
|||
{
|
||||
public:
|
||||
|
||||
async_generator_yield_operation(std::experimental::coroutine_handle<> consumer) noexcept
|
||||
async_generator_yield_operation(std::coroutine_handle<> consumer) noexcept
|
||||
: m_consumer(consumer)
|
||||
{}
|
||||
|
||||
|
@ -109,8 +109,8 @@ namespace cppcoro
|
|||
return false;
|
||||
}
|
||||
|
||||
std::experimental::coroutine_handle<>
|
||||
await_suspend([[maybe_unused]] std::experimental::coroutine_handle<> producer) noexcept
|
||||
std::coroutine_handle<>
|
||||
await_suspend([[maybe_unused]] std::coroutine_handle<> producer) noexcept
|
||||
{
|
||||
return m_consumer;
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ namespace cppcoro
|
|||
|
||||
private:
|
||||
|
||||
std::experimental::coroutine_handle<> m_consumer;
|
||||
std::coroutine_handle<> m_consumer;
|
||||
|
||||
};
|
||||
|
||||
|
@ -145,7 +145,7 @@ namespace cppcoro
|
|||
|
||||
async_generator_advance_operation(
|
||||
async_generator_promise_base& promise,
|
||||
std::experimental::coroutine_handle<> producerCoroutine) noexcept
|
||||
std::coroutine_handle<> producerCoroutine) noexcept
|
||||
: m_promise(std::addressof(promise))
|
||||
, m_producerCoroutine(producerCoroutine)
|
||||
{
|
||||
|
@ -155,8 +155,8 @@ namespace cppcoro
|
|||
|
||||
bool await_ready() const noexcept { return false; }
|
||||
|
||||
std::experimental::coroutine_handle<>
|
||||
await_suspend(std::experimental::coroutine_handle<> consumerCoroutine) noexcept
|
||||
std::coroutine_handle<>
|
||||
await_suspend(std::coroutine_handle<> consumerCoroutine) noexcept
|
||||
{
|
||||
m_promise->m_consumerCoroutine = consumerCoroutine;
|
||||
return m_producerCoroutine;
|
||||
|
@ -165,7 +165,7 @@ namespace cppcoro
|
|||
protected:
|
||||
|
||||
async_generator_promise_base* m_promise;
|
||||
std::experimental::coroutine_handle<> m_producerCoroutine;
|
||||
std::coroutine_handle<> m_producerCoroutine;
|
||||
|
||||
};
|
||||
|
||||
|
@ -242,7 +242,7 @@ namespace cppcoro
|
|||
class async_generator_iterator final
|
||||
{
|
||||
using promise_type = async_generator_promise<T>;
|
||||
using handle_type = std::experimental::coroutine_handle<promise_type>;
|
||||
using handle_type = std::coroutine_handle<promise_type>;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -307,7 +307,7 @@ namespace cppcoro
|
|||
class async_generator_begin_operation final : public async_generator_advance_operation
|
||||
{
|
||||
using promise_type = async_generator_promise<T>;
|
||||
using handle_type = std::experimental::coroutine_handle<promise_type>;
|
||||
using handle_type = std::coroutine_handle<promise_type>;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -358,7 +358,7 @@ namespace cppcoro
|
|||
{}
|
||||
|
||||
explicit async_generator(promise_type& promise) noexcept
|
||||
: m_coroutine(std::experimental::coroutine_handle<promise_type>::from_promise(promise))
|
||||
: m_coroutine(std::coroutine_handle<promise_type>::from_promise(promise))
|
||||
{}
|
||||
|
||||
async_generator(async_generator&& other) noexcept
|
||||
|
@ -408,7 +408,7 @@ namespace cppcoro
|
|||
|
||||
private:
|
||||
|
||||
std::experimental::coroutine_handle<promise_type> m_coroutine;
|
||||
std::coroutine_handle<promise_type> m_coroutine;
|
||||
|
||||
};
|
||||
|
||||
|
@ -451,7 +451,7 @@ namespace cppcoro
|
|||
async_generator_promise_base(const async_generator_promise_base& other) = delete;
|
||||
async_generator_promise_base& operator=(const async_generator_promise_base& other) = delete;
|
||||
|
||||
std::experimental::suspend_always initial_suspend() const noexcept
|
||||
std::suspend_always initial_suspend() const noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
@ -556,7 +556,7 @@ namespace cppcoro
|
|||
|
||||
std::exception_ptr m_exception;
|
||||
|
||||
std::experimental::coroutine_handle<> m_consumerCoroutine;
|
||||
std::coroutine_handle<> m_consumerCoroutine;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -579,7 +579,7 @@ namespace cppcoro
|
|||
return m_initialState == state::value_not_ready_consumer_suspended;
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> producer) noexcept;
|
||||
bool await_suspend(std::coroutine_handle<> producer) noexcept;
|
||||
|
||||
void await_resume() noexcept {}
|
||||
|
||||
|
@ -625,7 +625,7 @@ namespace cppcoro
|
|||
}
|
||||
|
||||
inline bool async_generator_yield_operation::await_suspend(
|
||||
std::experimental::coroutine_handle<> producer) noexcept
|
||||
std::coroutine_handle<> producer) noexcept
|
||||
{
|
||||
state currentState = m_initialState;
|
||||
if (currentState == state::value_not_ready_consumer_active)
|
||||
|
@ -711,7 +711,7 @@ namespace cppcoro
|
|||
|
||||
async_generator_advance_operation(
|
||||
async_generator_promise_base& promise,
|
||||
std::experimental::coroutine_handle<> producerCoroutine) noexcept
|
||||
std::coroutine_handle<> producerCoroutine) noexcept
|
||||
: m_promise(std::addressof(promise))
|
||||
, m_producerCoroutine(producerCoroutine)
|
||||
{
|
||||
|
@ -740,7 +740,7 @@ namespace cppcoro
|
|||
return m_initialState == state::value_ready_producer_suspended;
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> consumerCoroutine) noexcept
|
||||
bool await_suspend(std::coroutine_handle<> consumerCoroutine) noexcept
|
||||
{
|
||||
m_promise->m_consumerCoroutine = consumerCoroutine;
|
||||
|
||||
|
@ -791,7 +791,7 @@ namespace cppcoro
|
|||
protected:
|
||||
|
||||
async_generator_promise_base* m_promise;
|
||||
std::experimental::coroutine_handle<> m_producerCoroutine;
|
||||
std::coroutine_handle<> m_producerCoroutine;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -872,7 +872,7 @@ namespace cppcoro
|
|||
class async_generator_iterator final
|
||||
{
|
||||
using promise_type = async_generator_promise<T>;
|
||||
using handle_type = std::experimental::coroutine_handle<promise_type>;
|
||||
using handle_type = std::coroutine_handle<promise_type>;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -937,7 +937,7 @@ namespace cppcoro
|
|||
class async_generator_begin_operation final : public async_generator_advance_operation
|
||||
{
|
||||
using promise_type = async_generator_promise<T>;
|
||||
using handle_type = std::experimental::coroutine_handle<promise_type>;
|
||||
using handle_type = std::coroutine_handle<promise_type>;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -988,7 +988,7 @@ namespace cppcoro
|
|||
{}
|
||||
|
||||
explicit async_generator(promise_type& promise) noexcept
|
||||
: m_coroutine(std::experimental::coroutine_handle<promise_type>::from_promise(promise))
|
||||
: m_coroutine(std::coroutine_handle<promise_type>::from_promise(promise))
|
||||
{}
|
||||
|
||||
async_generator(async_generator&& other) noexcept
|
||||
|
@ -1041,7 +1041,7 @@ namespace cppcoro
|
|||
|
||||
private:
|
||||
|
||||
std::experimental::coroutine_handle<promise_type> m_coroutine;
|
||||
std::coroutine_handle<promise_type> m_coroutine;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef CPPCORO_ASYNC_MANUAL_RESET_EVENT_HPP_INCLUDED
|
||||
#define CPPCORO_ASYNC_MANUAL_RESET_EVENT_HPP_INCLUDED
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
|
@ -87,7 +87,7 @@ namespace cppcoro
|
|||
explicit async_manual_reset_event_operation(const async_manual_reset_event& event) noexcept;
|
||||
|
||||
bool await_ready() const noexcept;
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
|
||||
bool await_suspend(std::coroutine_handle<> awaiter) noexcept;
|
||||
void await_resume() const noexcept {}
|
||||
|
||||
private:
|
||||
|
@ -96,7 +96,7 @@ namespace cppcoro
|
|||
|
||||
const async_manual_reset_event& m_event;
|
||||
async_manual_reset_event_operation* m_next;
|
||||
std::experimental::coroutine_handle<> m_awaiter;
|
||||
std::coroutine_handle<> m_awaiter;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef CPPCORO_ASYNC_MUTEX_HPP_INCLUDED
|
||||
#define CPPCORO_ASYNC_MUTEX_HPP_INCLUDED
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <mutex> // for std::adopt_lock_t
|
||||
|
@ -166,7 +166,7 @@ namespace cppcoro
|
|||
{}
|
||||
|
||||
bool await_ready() const noexcept { return false; }
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
|
||||
bool await_suspend(std::coroutine_handle<> awaiter) noexcept;
|
||||
void await_resume() const noexcept {}
|
||||
|
||||
protected:
|
||||
|
@ -178,7 +178,7 @@ namespace cppcoro
|
|||
private:
|
||||
|
||||
async_mutex_lock_operation* m_next;
|
||||
std::experimental::coroutine_handle<> m_awaiter;
|
||||
std::coroutine_handle<> m_awaiter;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_ASYNC_SCOPE_HPP_INCLUDED
|
||||
#define CPPCORO_ASYNC_SCOPE_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/on_scope_exit.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <experimental/coroutine>
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class async_scope
|
||||
{
|
||||
public:
|
||||
|
||||
async_scope() noexcept
|
||||
: m_count(1u)
|
||||
{}
|
||||
|
||||
~async_scope()
|
||||
{
|
||||
// scope must be co_awaited before it destructs.
|
||||
assert(m_continuation);
|
||||
}
|
||||
|
||||
template<typename AWAITABLE>
|
||||
void spawn(AWAITABLE&& awaitable)
|
||||
{
|
||||
[](async_scope* scope, std::decay_t<AWAITABLE> awaitable) -> oneway_task
|
||||
{
|
||||
scope->on_work_started();
|
||||
auto decrementOnCompletion = on_scope_exit([scope] { scope->on_work_finished(); });
|
||||
co_await std::move(awaitable);
|
||||
}(this, std::forward<AWAITABLE>(awaitable));
|
||||
}
|
||||
|
||||
[[nodiscard]] auto join() noexcept
|
||||
{
|
||||
class awaiter
|
||||
{
|
||||
async_scope* m_scope;
|
||||
public:
|
||||
awaiter(async_scope* scope) noexcept : m_scope(scope) {}
|
||||
|
||||
bool await_ready() noexcept
|
||||
{
|
||||
return m_scope->m_count.load(std::memory_order_acquire) == 0;
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> continuation) noexcept
|
||||
{
|
||||
m_scope->m_continuation = continuation;
|
||||
return m_scope->m_count.fetch_sub(1u, std::memory_order_acq_rel) > 1u;
|
||||
}
|
||||
|
||||
void await_resume() noexcept
|
||||
{}
|
||||
};
|
||||
|
||||
return awaiter{ this };
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void on_work_finished() noexcept
|
||||
{
|
||||
if (m_count.fetch_sub(1u, std::memory_order_acq_rel) == 1)
|
||||
{
|
||||
m_continuation.resume();
|
||||
}
|
||||
}
|
||||
|
||||
void on_work_started() noexcept
|
||||
{
|
||||
assert(m_count.load(std::memory_order_relaxed) != 0);
|
||||
m_count.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
struct oneway_task
|
||||
{
|
||||
struct promise_type
|
||||
{
|
||||
std::experimental::suspend_never initial_suspend() { return {}; }
|
||||
std::experimental::suspend_never final_suspend() { return {}; }
|
||||
void unhandled_exception() { std::terminate(); }
|
||||
oneway_task get_return_object() { return {}; }
|
||||
void return_void() {}
|
||||
};
|
||||
};
|
||||
|
||||
std::atomic<size_t> m_count;
|
||||
std::experimental::coroutine_handle<> m_continuation;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -6,7 +6,7 @@
|
|||
#define CPPCORO_DETAIL_IS_AWAITER_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
|
@ -18,7 +18,7 @@ namespace cppcoro
|
|||
{};
|
||||
|
||||
template<typename PROMISE>
|
||||
struct is_coroutine_handle<std::experimental::coroutine_handle<PROMISE>>
|
||||
struct is_coroutine_handle<std::coroutine_handle<PROMISE>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
|
@ -42,12 +42,12 @@ namespace cppcoro
|
|||
template<typename T>
|
||||
struct is_awaiter<T, std::void_t<
|
||||
decltype(std::declval<T>().await_ready()),
|
||||
decltype(std::declval<T>().await_suspend(std::declval<std::experimental::coroutine_handle<>>())),
|
||||
decltype(std::declval<T>().await_suspend(std::declval<std::coroutine_handle<>>())),
|
||||
decltype(std::declval<T>().await_resume())>> :
|
||||
std::conjunction<
|
||||
std::is_constructible<bool, decltype(std::declval<T>().await_ready())>,
|
||||
detail::is_valid_await_suspend_return_value<
|
||||
decltype(std::declval<T>().await_suspend(std::declval<std::experimental::coroutine_handle<>>()))>>
|
||||
decltype(std::declval<T>().await_suspend(std::declval<std::coroutine_handle<>>()))>>
|
||||
{};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <cppcoro/awaitable_traits.hpp>
|
||||
#include <cppcoro/detail/lightweight_manual_reset_event.hpp>
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
|
@ -24,7 +24,7 @@ namespace cppcoro
|
|||
template<typename RESULT>
|
||||
class sync_wait_task_promise final
|
||||
{
|
||||
using coroutine_handle_t = std::experimental::coroutine_handle<sync_wait_task_promise<RESULT>>;
|
||||
using coroutine_handle_t = std::coroutine_handle<sync_wait_task_promise<RESULT>>;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace cppcoro
|
|||
return coroutine_handle_t::from_promise(*this);
|
||||
}
|
||||
|
||||
std::experimental::suspend_always initial_suspend() noexcept
|
||||
std::suspend_always initial_suspend() noexcept
|
||||
{
|
||||
return{};
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ namespace cppcoro
|
|||
bool await_ready() noexcept {
|
||||
return true;
|
||||
}
|
||||
void await_suspend(std::experimental::coroutine_handle<>) noexcept {}
|
||||
void await_suspend(std::coroutine_handle<>) noexcept {}
|
||||
sync_wait_task_promise& await_resume() noexcept
|
||||
{
|
||||
return *m_promise;
|
||||
|
@ -140,7 +140,7 @@ namespace cppcoro
|
|||
template<>
|
||||
class sync_wait_task_promise<void>
|
||||
{
|
||||
using coroutine_handle_t = std::experimental::coroutine_handle<sync_wait_task_promise<void>>;
|
||||
using coroutine_handle_t = std::coroutine_handle<sync_wait_task_promise<void>>;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -158,7 +158,7 @@ namespace cppcoro
|
|||
return coroutine_handle_t::from_promise(*this);
|
||||
}
|
||||
|
||||
std::experimental::suspend_always initial_suspend() noexcept
|
||||
std::suspend_always initial_suspend() noexcept
|
||||
{
|
||||
return{};
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ namespace cppcoro
|
|||
|
||||
using promise_type = sync_wait_task_promise<RESULT>;
|
||||
|
||||
using coroutine_handle_t = std::experimental::coroutine_handle<promise_type>;
|
||||
using coroutine_handle_t = std::coroutine_handle<promise_type>;
|
||||
|
||||
sync_wait_task(coroutine_handle_t coroutine) noexcept
|
||||
: m_coroutine(coroutine)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef CPPCORO_DETAIL_WHEN_ALL_COUNTER_HPP_INCLUDED
|
||||
#define CPPCORO_DETAIL_WHEN_ALL_COUNTER_HPP_INCLUDED
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
|
@ -29,7 +29,7 @@ namespace cppcoro
|
|||
return static_cast<bool>(m_awaitingCoroutine);
|
||||
}
|
||||
|
||||
bool try_await(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool try_await(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
m_awaitingCoroutine = awaitingCoroutine;
|
||||
return m_count.fetch_sub(1, std::memory_order_acq_rel) > 1;
|
||||
|
@ -46,7 +46,7 @@ namespace cppcoro
|
|||
protected:
|
||||
|
||||
std::atomic<std::size_t> m_count;
|
||||
std::experimental::coroutine_handle<> m_awaitingCoroutine;
|
||||
std::coroutine_handle<> m_awaitingCoroutine;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include <cppcoro/detail/when_all_counter.hpp>
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <tuple>
|
||||
|
||||
namespace cppcoro
|
||||
|
@ -26,7 +26,7 @@ namespace cppcoro
|
|||
explicit constexpr when_all_ready_awaitable(std::tuple<>) noexcept {}
|
||||
|
||||
constexpr bool await_ready() const noexcept { return true; }
|
||||
void await_suspend(std::experimental::coroutine_handle<>) noexcept {}
|
||||
void await_suspend(std::coroutine_handle<>) noexcept {}
|
||||
std::tuple<> await_resume() const noexcept { return {}; }
|
||||
|
||||
};
|
||||
|
@ -66,7 +66,7 @@ namespace cppcoro
|
|||
return m_awaitable.is_ready();
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
return m_awaitable.try_await(awaitingCoroutine);
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ namespace cppcoro
|
|||
return m_awaitable.is_ready();
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
return m_awaitable.try_await(awaitingCoroutine);
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ namespace cppcoro
|
|||
return m_counter.is_ready();
|
||||
}
|
||||
|
||||
bool try_await(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool try_await(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
start_tasks(std::make_integer_sequence<std::size_t, sizeof...(TASKS)>{});
|
||||
return m_counter.try_await(awaitingCoroutine);
|
||||
|
@ -177,7 +177,7 @@ namespace cppcoro
|
|||
return m_awaitable.is_ready();
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
return m_awaitable.try_await(awaitingCoroutine);
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ namespace cppcoro
|
|||
return m_awaitable.is_ready();
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
return m_awaitable.try_await(awaitingCoroutine);
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ namespace cppcoro
|
|||
return m_counter.is_ready();
|
||||
}
|
||||
|
||||
bool try_await(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool try_await(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
for (auto&& task : m_tasks)
|
||||
{
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <cppcoro/detail/when_all_counter.hpp>
|
||||
#include <cppcoro/detail/void_value.hpp>
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <cassert>
|
||||
|
||||
namespace cppcoro
|
||||
|
@ -28,7 +28,7 @@ namespace cppcoro
|
|||
{
|
||||
public:
|
||||
|
||||
using coroutine_handle_t = std::experimental::coroutine_handle<when_all_task_promise<RESULT>>;
|
||||
using coroutine_handle_t = std::coroutine_handle<when_all_task_promise<RESULT>>;
|
||||
|
||||
when_all_task_promise() noexcept
|
||||
{}
|
||||
|
@ -38,7 +38,7 @@ namespace cppcoro
|
|||
return coroutine_handle_t::from_promise(*this);
|
||||
}
|
||||
|
||||
std::experimental::suspend_always initial_suspend() noexcept
|
||||
std::suspend_always initial_suspend() noexcept
|
||||
{
|
||||
return{};
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ namespace cppcoro
|
|||
bool await_ready() noexcept {
|
||||
return true;
|
||||
}
|
||||
void await_suspend(std::experimental::coroutine_handle<>) noexcept {}
|
||||
void await_suspend(std::coroutine_handle<>) noexcept {}
|
||||
when_all_task_promise& await_resume() noexcept
|
||||
{
|
||||
return *m_promise;
|
||||
|
@ -155,7 +155,7 @@ namespace cppcoro
|
|||
{
|
||||
public:
|
||||
|
||||
using coroutine_handle_t = std::experimental::coroutine_handle<when_all_task_promise<void>>;
|
||||
using coroutine_handle_t = std::coroutine_handle<when_all_task_promise<void>>;
|
||||
|
||||
when_all_task_promise() noexcept
|
||||
{}
|
||||
|
@ -165,7 +165,7 @@ namespace cppcoro
|
|||
return coroutine_handle_t::from_promise(*this);
|
||||
}
|
||||
|
||||
std::experimental::suspend_always initial_suspend() noexcept
|
||||
std::suspend_always initial_suspend() noexcept
|
||||
{
|
||||
return{};
|
||||
}
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
#include <cppcoro/config.hpp>
|
||||
|
||||
#if !CPPCORO_OS_WINNT
|
||||
# error <cppcoro/detail/win32.hpp> is only supported on the Windows platform.
|
||||
#endif
|
||||
#if CPPCORO_OS_WINNT
|
||||
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
|
@ -177,3 +175,4 @@ namespace cppcoro
|
|||
}
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -1,376 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_DETAIL_WIN32_OVERLAPPED_OPERATION_HPP_INCLUDED
|
||||
#define CPPCORO_DETAIL_WIN32_OVERLAPPED_OPERATION_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/cancellation_registration.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
#include <cppcoro/operation_cancelled.hpp>
|
||||
|
||||
#include <cppcoro/detail/win32.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <system_error>
|
||||
#include <experimental/coroutine>
|
||||
#include <cassert>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
class win32_overlapped_operation_base
|
||||
: protected detail::win32::io_state
|
||||
{
|
||||
public:
|
||||
|
||||
win32_overlapped_operation_base(
|
||||
detail::win32::io_state::callback_type* callback) noexcept
|
||||
: detail::win32::io_state(callback)
|
||||
, m_errorCode(0)
|
||||
, m_numberOfBytesTransferred(0)
|
||||
{}
|
||||
|
||||
win32_overlapped_operation_base(
|
||||
void* pointer,
|
||||
detail::win32::io_state::callback_type* callback) noexcept
|
||||
: detail::win32::io_state(pointer, callback)
|
||||
, m_errorCode(0)
|
||||
, m_numberOfBytesTransferred(0)
|
||||
{}
|
||||
|
||||
win32_overlapped_operation_base(
|
||||
std::uint64_t offset,
|
||||
detail::win32::io_state::callback_type* callback) noexcept
|
||||
: detail::win32::io_state(offset, callback)
|
||||
, m_errorCode(0)
|
||||
, m_numberOfBytesTransferred(0)
|
||||
{}
|
||||
|
||||
_OVERLAPPED* get_overlapped() noexcept
|
||||
{
|
||||
return reinterpret_cast<_OVERLAPPED*>(
|
||||
static_cast<detail::win32::overlapped*>(this));
|
||||
}
|
||||
|
||||
std::size_t get_result()
|
||||
{
|
||||
if (m_errorCode != 0)
|
||||
{
|
||||
throw std::system_error{
|
||||
static_cast<int>(m_errorCode),
|
||||
std::system_category()
|
||||
};
|
||||
}
|
||||
|
||||
return m_numberOfBytesTransferred;
|
||||
}
|
||||
|
||||
detail::win32::dword_t m_errorCode;
|
||||
detail::win32::dword_t m_numberOfBytesTransferred;
|
||||
|
||||
};
|
||||
|
||||
template<typename OPERATION>
|
||||
class win32_overlapped_operation
|
||||
: protected win32_overlapped_operation_base
|
||||
{
|
||||
protected:
|
||||
|
||||
win32_overlapped_operation() noexcept
|
||||
: win32_overlapped_operation_base(
|
||||
&win32_overlapped_operation::on_operation_completed)
|
||||
{}
|
||||
|
||||
win32_overlapped_operation(void* pointer) noexcept
|
||||
: win32_overlapped_operation_base(
|
||||
pointer,
|
||||
&win32_overlapped_operation::on_operation_completed)
|
||||
{}
|
||||
|
||||
win32_overlapped_operation(std::uint64_t offset) noexcept
|
||||
: win32_overlapped_operation_base(
|
||||
offset,
|
||||
&win32_overlapped_operation::on_operation_completed)
|
||||
{}
|
||||
|
||||
public:
|
||||
|
||||
bool await_ready() const noexcept { return false; }
|
||||
|
||||
CPPCORO_NOINLINE
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine)
|
||||
{
|
||||
static_assert(std::is_base_of_v<win32_overlapped_operation, OPERATION>);
|
||||
|
||||
m_awaitingCoroutine = awaitingCoroutine;
|
||||
return static_cast<OPERATION*>(this)->try_start();
|
||||
}
|
||||
|
||||
decltype(auto) await_resume()
|
||||
{
|
||||
return static_cast<OPERATION*>(this)->get_result();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static void on_operation_completed(
|
||||
detail::win32::io_state* ioState,
|
||||
detail::win32::dword_t errorCode,
|
||||
detail::win32::dword_t numberOfBytesTransferred,
|
||||
[[maybe_unused]] detail::win32::ulongptr_t completionKey) noexcept
|
||||
{
|
||||
auto* operation = static_cast<win32_overlapped_operation*>(ioState);
|
||||
operation->m_errorCode = errorCode;
|
||||
operation->m_numberOfBytesTransferred = numberOfBytesTransferred;
|
||||
operation->m_awaitingCoroutine.resume();
|
||||
}
|
||||
|
||||
std::experimental::coroutine_handle<> m_awaitingCoroutine;
|
||||
|
||||
};
|
||||
|
||||
template<typename OPERATION>
|
||||
class win32_overlapped_operation_cancellable
|
||||
: protected win32_overlapped_operation_base
|
||||
{
|
||||
// ERROR_OPERATION_ABORTED value from <Windows.h>
|
||||
static constexpr detail::win32::dword_t error_operation_aborted = 995L;
|
||||
|
||||
protected:
|
||||
|
||||
win32_overlapped_operation_cancellable(cancellation_token&& ct) noexcept
|
||||
: win32_overlapped_operation_base(&win32_overlapped_operation_cancellable::on_operation_completed)
|
||||
, m_state(ct.is_cancellation_requested() ? state::completed : state::not_started)
|
||||
, m_cancellationToken(std::move(ct))
|
||||
{
|
||||
m_errorCode = error_operation_aborted;
|
||||
}
|
||||
|
||||
win32_overlapped_operation_cancellable(
|
||||
void* pointer,
|
||||
cancellation_token&& ct) noexcept
|
||||
: win32_overlapped_operation_base(pointer, &win32_overlapped_operation_cancellable::on_operation_completed)
|
||||
, m_state(ct.is_cancellation_requested() ? state::completed : state::not_started)
|
||||
, m_cancellationToken(std::move(ct))
|
||||
{
|
||||
m_errorCode = error_operation_aborted;
|
||||
}
|
||||
|
||||
win32_overlapped_operation_cancellable(
|
||||
std::uint64_t offset,
|
||||
cancellation_token&& ct) noexcept
|
||||
: win32_overlapped_operation_base(offset, &win32_overlapped_operation_cancellable::on_operation_completed)
|
||||
, m_state(ct.is_cancellation_requested() ? state::completed : state::not_started)
|
||||
, m_cancellationToken(std::move(ct))
|
||||
{
|
||||
m_errorCode = error_operation_aborted;
|
||||
}
|
||||
|
||||
win32_overlapped_operation_cancellable(
|
||||
win32_overlapped_operation_cancellable&& other) noexcept
|
||||
: win32_overlapped_operation_base(std::move(other))
|
||||
, m_state(other.m_state.load(std::memory_order_relaxed))
|
||||
, m_cancellationToken(std::move(other.m_cancellationToken))
|
||||
{
|
||||
assert(m_errorCode == other.m_errorCode);
|
||||
assert(m_numberOfBytesTransferred == other.m_numberOfBytesTransferred);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool await_ready() const noexcept
|
||||
{
|
||||
return m_state.load(std::memory_order_relaxed) == state::completed;
|
||||
}
|
||||
|
||||
CPPCORO_NOINLINE
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine)
|
||||
{
|
||||
static_assert(std::is_base_of_v<win32_overlapped_operation_cancellable, OPERATION>);
|
||||
|
||||
m_awaitingCoroutine = awaitingCoroutine;
|
||||
|
||||
// TRICKY: Register cancellation callback before starting the operation
|
||||
// in case the callback registration throws due to insufficient
|
||||
// memory. We need to make sure that the logic that occurs after
|
||||
// starting the operation is noexcept, otherwise we run into the
|
||||
// problem of not being able to cancel the started operation and
|
||||
// the dilemma of what to do with the exception.
|
||||
//
|
||||
// However, doing this means that the cancellation callback may run
|
||||
// prior to returning below so in the case that cancellation may
|
||||
// occur we defer setting the state to 'started' until after
|
||||
// the operation has finished starting. The cancellation callback
|
||||
// will only attempt to request cancellation of the operation with
|
||||
// CancelIoEx() once the state has been set to 'started'.
|
||||
const bool canBeCancelled = m_cancellationToken.can_be_cancelled();
|
||||
if (canBeCancelled)
|
||||
{
|
||||
m_cancellationCallback.emplace(
|
||||
std::move(m_cancellationToken),
|
||||
[this] { this->on_cancellation_requested(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state.store(state::started, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Now start the operation.
|
||||
const bool willCompleteAsynchronously = static_cast<OPERATION*>(this)->try_start();
|
||||
if (!willCompleteAsynchronously)
|
||||
{
|
||||
// Operation completed synchronously, resume awaiting coroutine immediately.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (canBeCancelled)
|
||||
{
|
||||
// Need to flag that the operation has finished starting now.
|
||||
|
||||
// However, the operation may have completed concurrently on
|
||||
// another thread, transitioning directly from not_started -> complete.
|
||||
// Or it may have had the cancellation callback execute and transition
|
||||
// from not_started -> cancellation_requested. We use a compare-exchange
|
||||
// to determine a winner between these potential racing cases.
|
||||
state oldState = state::not_started;
|
||||
if (!m_state.compare_exchange_strong(
|
||||
oldState,
|
||||
state::started,
|
||||
std::memory_order_release,
|
||||
std::memory_order_acquire))
|
||||
{
|
||||
if (oldState == state::cancellation_requested)
|
||||
{
|
||||
// Request the operation be cancelled.
|
||||
// Note that it may have already completed on a background
|
||||
// thread by now so this request for cancellation may end up
|
||||
// being ignored.
|
||||
static_cast<OPERATION*>(this)->cancel();
|
||||
|
||||
if (!m_state.compare_exchange_strong(
|
||||
oldState,
|
||||
state::started,
|
||||
std::memory_order_release,
|
||||
std::memory_order_acquire))
|
||||
{
|
||||
assert(oldState == state::completed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(oldState == state::completed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
decltype(auto) await_resume()
|
||||
{
|
||||
// Free memory used by the cancellation callback now that the operation
|
||||
// has completed rather than waiting until the operation object destructs.
|
||||
// eg. If the operation is passed to when_all() then the operation object
|
||||
// may not be destructed until all of the operations complete.
|
||||
m_cancellationCallback.reset();
|
||||
|
||||
if (m_errorCode == error_operation_aborted)
|
||||
{
|
||||
throw operation_cancelled{};
|
||||
}
|
||||
|
||||
return static_cast<OPERATION*>(this)->get_result();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum class state
|
||||
{
|
||||
not_started,
|
||||
started,
|
||||
cancellation_requested,
|
||||
completed
|
||||
};
|
||||
|
||||
void on_cancellation_requested() noexcept
|
||||
{
|
||||
auto oldState = m_state.load(std::memory_order_acquire);
|
||||
if (oldState == state::not_started)
|
||||
{
|
||||
// This callback is running concurrently with await_suspend().
|
||||
// The call to start the operation may not have returned yet so
|
||||
// we can't safely request cancellation of it. Instead we try to
|
||||
// notify the await_suspend() thread by transitioning the state
|
||||
// to state::cancellation_requested so that the await_suspend()
|
||||
// thread can request cancellation after it has finished starting
|
||||
// the operation.
|
||||
const bool transferredCancelResponsibility =
|
||||
m_state.compare_exchange_strong(
|
||||
oldState,
|
||||
state::cancellation_requested,
|
||||
std::memory_order_release,
|
||||
std::memory_order_acquire);
|
||||
if (transferredCancelResponsibility)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No point requesting cancellation if the operation has already completed.
|
||||
if (oldState != state::completed)
|
||||
{
|
||||
static_cast<OPERATION*>(this)->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
static void on_operation_completed(
|
||||
detail::win32::io_state* ioState,
|
||||
detail::win32::dword_t errorCode,
|
||||
detail::win32::dword_t numberOfBytesTransferred,
|
||||
[[maybe_unused]] detail::win32::ulongptr_t completionKey) noexcept
|
||||
{
|
||||
auto* operation = static_cast<win32_overlapped_operation_cancellable*>(ioState);
|
||||
|
||||
operation->m_errorCode = errorCode;
|
||||
operation->m_numberOfBytesTransferred = numberOfBytesTransferred;
|
||||
|
||||
auto state = operation->m_state.load(std::memory_order_acquire);
|
||||
if (state == state::started)
|
||||
{
|
||||
operation->m_state.store(state::completed, std::memory_order_relaxed);
|
||||
operation->m_awaitingCoroutine.resume();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are racing with await_suspend() call suspending.
|
||||
// Try to mark it as completed using an atomic exchange and look
|
||||
// at the previous value to determine whether the coroutine suspended
|
||||
// first (in which case we resume it now) or we marked it as completed
|
||||
// first (in which case await_suspend() will return false and immediately
|
||||
// resume the coroutine).
|
||||
state = operation->m_state.exchange(
|
||||
state::completed,
|
||||
std::memory_order_acq_rel);
|
||||
if (state == state::started)
|
||||
{
|
||||
// The await_suspend() method returned (or will return) 'true' and so
|
||||
// we need to resume the coroutine.
|
||||
operation->m_awaitingCoroutine.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::atomic<state> m_state;
|
||||
cppcoro::cancellation_token m_cancellationToken;
|
||||
std::optional<cppcoro::cancellation_registration> m_cancellationCallback;
|
||||
std::experimental::coroutine_handle<> m_awaitingCoroutine;
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,54 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_FILE_HPP_INCLUDED
|
||||
#define CPPCORO_FILE_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
|
||||
#include <cppcoro/file_open_mode.hpp>
|
||||
#include <cppcoro/file_share_mode.hpp>
|
||||
#include <cppcoro/file_buffering_mode.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
#endif
|
||||
|
||||
#include <experimental/filesystem>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class io_service;
|
||||
|
||||
class file
|
||||
{
|
||||
public:
|
||||
|
||||
file(file&& other) noexcept = default;
|
||||
|
||||
virtual ~file();
|
||||
|
||||
/// Get the size of the file in bytes.
|
||||
std::uint64_t size() const;
|
||||
|
||||
protected:
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
file(detail::win32::safe_handle&& fileHandle) noexcept;
|
||||
|
||||
static detail::win32::safe_handle open(
|
||||
detail::win32::dword_t fileAccess,
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
file_open_mode openMode,
|
||||
file_share_mode shareMode,
|
||||
file_buffering_mode bufferingMode);
|
||||
|
||||
detail::win32::safe_handle m_fileHandle;
|
||||
#endif
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,32 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_FILE_BUFFERING_MODE_HPP_INCLUDED
|
||||
#define CPPCORO_FILE_BUFFERING_MODE_HPP_INCLUDED
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
enum class file_buffering_mode
|
||||
{
|
||||
default_ = 0,
|
||||
sequential = 1,
|
||||
random_access = 2,
|
||||
unbuffered = 4,
|
||||
write_through = 8,
|
||||
temporary = 16
|
||||
};
|
||||
|
||||
constexpr file_buffering_mode operator&(file_buffering_mode a, file_buffering_mode b)
|
||||
{
|
||||
return static_cast<file_buffering_mode>(
|
||||
static_cast<int>(a) & static_cast<int>(b));
|
||||
}
|
||||
|
||||
constexpr file_buffering_mode operator|(file_buffering_mode a, file_buffering_mode b)
|
||||
{
|
||||
return static_cast<file_buffering_mode>(static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,40 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_FILE_OPEN_MODE_HPP_INCLUDED
|
||||
#define CPPCORO_FILE_OPEN_MODE_HPP_INCLUDED
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
enum class file_open_mode
|
||||
{
|
||||
/// Open an existing file.
|
||||
///
|
||||
/// If file does not already exist when opening the file then raises
|
||||
/// an exception.
|
||||
open_existing,
|
||||
|
||||
/// Create a new file, overwriting an existing file if one exists.
|
||||
///
|
||||
/// If a file exists at the path then it is overwitten with a new file.
|
||||
/// If no file exists at the path then a new one is created.
|
||||
create_always,
|
||||
|
||||
/// Create a new file.
|
||||
///
|
||||
/// If the file already exists then raises an exception.
|
||||
create_new,
|
||||
|
||||
/// Open the existing file if one exists, otherwise create a new empty
|
||||
/// file.
|
||||
create_or_open,
|
||||
|
||||
/// Open the existing file, truncating the file size to zero.
|
||||
///
|
||||
/// If the file does not exist then raises an exception.
|
||||
truncate_existing
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,100 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_FILE_READ_OPERATION_HPP_INCLUDED
|
||||
#define CPPCORO_FILE_READ_OPERATION_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/cancellation_registration.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include <experimental/coroutine>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
# include <cppcoro/detail/win32_overlapped_operation.hpp>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class file_read_operation_impl
|
||||
{
|
||||
public:
|
||||
|
||||
file_read_operation_impl(
|
||||
detail::win32::handle_t fileHandle,
|
||||
void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: m_fileHandle(fileHandle)
|
||||
, m_buffer(buffer)
|
||||
, m_byteCount(byteCount)
|
||||
{}
|
||||
|
||||
bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
detail::win32::handle_t m_fileHandle;
|
||||
void* m_buffer;
|
||||
std::size_t m_byteCount;
|
||||
|
||||
};
|
||||
|
||||
class file_read_operation
|
||||
: public cppcoro::detail::win32_overlapped_operation<file_read_operation>
|
||||
{
|
||||
public:
|
||||
|
||||
file_read_operation(
|
||||
detail::win32::handle_t fileHandle,
|
||||
std::uint64_t fileOffset,
|
||||
void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation<file_read_operation>(fileOffset)
|
||||
, m_impl(fileHandle, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation<file_read_operation>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
|
||||
file_read_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
class file_read_operation_cancellable
|
||||
: public cppcoro::detail::win32_overlapped_operation_cancellable<file_read_operation_cancellable>
|
||||
{
|
||||
public:
|
||||
|
||||
file_read_operation_cancellable(
|
||||
detail::win32::handle_t fileHandle,
|
||||
std::uint64_t fileOffset,
|
||||
void* buffer,
|
||||
std::size_t byteCount,
|
||||
cancellation_token&& cancellationToken) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation_cancellable<file_read_operation_cancellable>(
|
||||
fileOffset, std::move(cancellationToken))
|
||||
, m_impl(fileHandle, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation_cancellable<file_read_operation_cancellable>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void cancel() noexcept { m_impl.cancel(*this); }
|
||||
|
||||
file_read_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,45 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_FILE_SHARE_MODE_HPP_INCLUDED
|
||||
#define CPPCORO_FILE_SHARE_MODE_HPP_INCLUDED
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
enum class file_share_mode
|
||||
{
|
||||
/// Don't allow any other processes to open the file concurrently.
|
||||
none = 0,
|
||||
|
||||
/// Allow other processes to open the file in read-only mode
|
||||
/// concurrently with this process opening the file.
|
||||
read = 1,
|
||||
|
||||
/// Allow other processes to open the file in write-only mode
|
||||
/// concurrently with this process opening the file.
|
||||
write = 2,
|
||||
|
||||
/// Allow other processes to open the file in read and/or write mode
|
||||
/// concurrently with this process opening the file.
|
||||
read_write = read | write,
|
||||
|
||||
/// Allow other processes to delete the file while this process
|
||||
/// has the file open.
|
||||
delete_ = 4
|
||||
};
|
||||
|
||||
constexpr file_share_mode operator|(file_share_mode a, file_share_mode b)
|
||||
{
|
||||
return static_cast<file_share_mode>(
|
||||
static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
|
||||
constexpr file_share_mode operator&(file_share_mode a, file_share_mode b)
|
||||
{
|
||||
return static_cast<file_share_mode>(
|
||||
static_cast<int>(a) & static_cast<int>(b));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,99 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_FILE_WRITE_OPERATION_HPP_INCLUDED
|
||||
#define CPPCORO_FILE_WRITE_OPERATION_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/cancellation_registration.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include <experimental/coroutine>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
# include <cppcoro/detail/win32_overlapped_operation.hpp>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class file_write_operation_impl
|
||||
{
|
||||
public:
|
||||
|
||||
file_write_operation_impl(
|
||||
detail::win32::handle_t fileHandle,
|
||||
const void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: m_fileHandle(fileHandle)
|
||||
, m_buffer(buffer)
|
||||
, m_byteCount(byteCount)
|
||||
{}
|
||||
|
||||
bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
detail::win32::handle_t m_fileHandle;
|
||||
const void* m_buffer;
|
||||
std::size_t m_byteCount;
|
||||
|
||||
};
|
||||
|
||||
class file_write_operation
|
||||
: public cppcoro::detail::win32_overlapped_operation<file_write_operation>
|
||||
{
|
||||
public:
|
||||
|
||||
file_write_operation(
|
||||
detail::win32::handle_t fileHandle,
|
||||
std::uint64_t fileOffset,
|
||||
const void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation<file_write_operation>(fileOffset)
|
||||
, m_impl(fileHandle, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation<file_write_operation>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
|
||||
file_write_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
class file_write_operation_cancellable
|
||||
: public cppcoro::detail::win32_overlapped_operation_cancellable<file_write_operation_cancellable>
|
||||
{
|
||||
public:
|
||||
|
||||
file_write_operation_cancellable(
|
||||
detail::win32::handle_t fileHandle,
|
||||
std::uint64_t fileOffset,
|
||||
const void* buffer,
|
||||
std::size_t byteCount,
|
||||
cancellation_token&& ct) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation_cancellable<file_write_operation_cancellable>(fileOffset, std::move(ct))
|
||||
, m_impl(fileHandle, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation_cancellable<file_write_operation_cancellable>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void cancel() noexcept { m_impl.cancel(*this); }
|
||||
|
||||
file_write_operation_impl m_impl;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
||||
|
||||
#endif
|
|
@ -40,7 +40,7 @@ namespace cppcoro
|
|||
}
|
||||
|
||||
template<typename PROMISE>
|
||||
decltype(auto) await_suspend(std::experimental::coroutine_handle<PROMISE> coro)
|
||||
decltype(auto) await_suspend(std::coroutine_handle<PROMISE> coro)
|
||||
noexcept(noexcept(static_cast<awaiter_t&&>(m_awaiter).await_suspend(std::move(coro))))
|
||||
{
|
||||
return static_cast<awaiter_t&&>(m_awaiter).await_suspend(std::move(coro));
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef CPPCORO_GENERATOR_HPP_INCLUDED
|
||||
#define CPPCORO_GENERATOR_HPP_INCLUDED
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <exception>
|
||||
|
@ -32,19 +32,19 @@ namespace cppcoro
|
|||
|
||||
generator<T> get_return_object() noexcept;
|
||||
|
||||
constexpr std::experimental::suspend_always initial_suspend() const noexcept { return {}; }
|
||||
constexpr std::experimental::suspend_always final_suspend() const noexcept { return {}; }
|
||||
constexpr std::suspend_always initial_suspend() const noexcept { return {}; }
|
||||
constexpr std::suspend_always final_suspend() const noexcept { return {}; }
|
||||
|
||||
template<
|
||||
typename U = T,
|
||||
std::enable_if_t<!std::is_rvalue_reference<U>::value, int> = 0>
|
||||
std::experimental::suspend_always yield_value(std::remove_reference_t<T>& value) noexcept
|
||||
std::suspend_always yield_value(std::remove_reference_t<T>& value) noexcept
|
||||
{
|
||||
m_value = std::addressof(value);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::experimental::suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept
|
||||
std::suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept
|
||||
{
|
||||
m_value = std::addressof(value);
|
||||
return {};
|
||||
|
@ -66,7 +66,7 @@ namespace cppcoro
|
|||
|
||||
// Don't allow any use of 'co_await' inside the generator coroutine.
|
||||
template<typename U>
|
||||
std::experimental::suspend_never await_transform(U&& value) = delete;
|
||||
std::suspend_never await_transform(U&& value) = delete;
|
||||
|
||||
void rethrow_if_exception()
|
||||
{
|
||||
|
@ -88,7 +88,7 @@ namespace cppcoro
|
|||
template<typename T>
|
||||
class generator_iterator
|
||||
{
|
||||
using coroutine_handle = std::experimental::coroutine_handle<generator_promise<T>>;
|
||||
using coroutine_handle = std::coroutine_handle<generator_promise<T>>;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -223,11 +223,11 @@ namespace cppcoro
|
|||
|
||||
friend class detail::generator_promise<T>;
|
||||
|
||||
explicit generator(std::experimental::coroutine_handle<promise_type> coroutine) noexcept
|
||||
explicit generator(std::coroutine_handle<promise_type> coroutine) noexcept
|
||||
: m_coroutine(coroutine)
|
||||
{}
|
||||
|
||||
std::experimental::coroutine_handle<promise_type> m_coroutine;
|
||||
std::coroutine_handle<promise_type> m_coroutine;
|
||||
|
||||
};
|
||||
|
||||
|
@ -242,7 +242,7 @@ namespace cppcoro
|
|||
template<typename T>
|
||||
generator<T> generator_promise<T>::get_return_object() noexcept
|
||||
{
|
||||
using coroutine_handle = std::experimental::coroutine_handle<generator_promise<T>>;
|
||||
using coroutine_handle = std::coroutine_handle<generator_promise<T>>;
|
||||
return generator<T>{ coroutine_handle::from_promise(*this) };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_INLINE_SCHEDULER_HPP_INCLUDED
|
||||
#define CPPCORO_INLINE_SCHEDULER_HPP_INCLUDED
|
||||
|
||||
#include <experimental/coroutine>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class inline_scheduler
|
||||
{
|
||||
public:
|
||||
|
||||
inline_scheduler() noexcept = default;
|
||||
|
||||
std::experimental::suspend_never schedule() const noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,321 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_IO_SERVICE_HPP_INCLUDED
|
||||
#define CPPCORO_IO_SERVICE_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
#include <cppcoro/cancellation_registration.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
#endif
|
||||
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
#include <mutex>
|
||||
#include <experimental/coroutine>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class io_service
|
||||
{
|
||||
public:
|
||||
|
||||
class schedule_operation;
|
||||
class timed_schedule_operation;
|
||||
|
||||
/// Initialises the io_service.
|
||||
///
|
||||
/// Does not set a concurrency hint. All threads that enter the
|
||||
/// event loop will actively process events.
|
||||
io_service();
|
||||
|
||||
/// Initialise the io_service with a concurrency hint.
|
||||
///
|
||||
/// \param concurrencyHint
|
||||
/// Specifies the target maximum number of I/O threads to be
|
||||
/// actively processing events.
|
||||
/// Note that the number of active threads may temporarily go
|
||||
/// above this number.
|
||||
io_service(std::uint32_t concurrencyHint);
|
||||
|
||||
~io_service();
|
||||
|
||||
io_service(io_service&& other) = delete;
|
||||
io_service(const io_service& other) = delete;
|
||||
io_service& operator=(io_service&& other) = delete;
|
||||
io_service& operator=(const io_service& other) = delete;
|
||||
|
||||
/// Returns an operation that when awaited suspends the awaiting
|
||||
/// coroutine and reschedules it for resumption on an I/O thread
|
||||
/// associated with this io_service.
|
||||
[[nodiscard]]
|
||||
schedule_operation schedule() noexcept;
|
||||
|
||||
/// Returns an operation that when awaited will suspend the
|
||||
/// awaiting coroutine for the specified delay. Once the delay
|
||||
/// has elapsed, the coroutine will resume execution on an
|
||||
/// I/O thread associated with this io_service.
|
||||
///
|
||||
/// \param delay
|
||||
/// The amount of time to delay scheduling resumption of the coroutine
|
||||
/// on an I/O thread. There is no guarantee that the coroutine will
|
||||
/// be resumed exactly after this delay.
|
||||
///
|
||||
/// \param cancellationToken [optional]
|
||||
/// A cancellation token that can be used to communicate a request to
|
||||
/// cancel the delayed schedule operation and schedule it for resumption
|
||||
/// immediately.
|
||||
/// The co_await operation will throw cppcoro::operation_cancelled if
|
||||
/// cancellation was requested before the coroutine could be resumed.
|
||||
template<typename REP, typename PERIOD>
|
||||
[[nodiscard]]
|
||||
timed_schedule_operation schedule_after(
|
||||
const std::chrono::duration<REP, PERIOD>& delay,
|
||||
cancellation_token cancellationToken = {}) noexcept;
|
||||
|
||||
/// Process events until the io_service is stopped.
|
||||
///
|
||||
/// \return
|
||||
/// The number of events processed during this call.
|
||||
std::uint64_t process_events();
|
||||
|
||||
/// Process events until either the io_service is stopped or
|
||||
/// there are no more pending events in the queue.
|
||||
///
|
||||
/// \return
|
||||
/// The number of events processed during this call.
|
||||
std::uint64_t process_pending_events();
|
||||
|
||||
/// Block until either one event is processed or the io_service is stopped.
|
||||
///
|
||||
/// \return
|
||||
/// The number of events processed during this call.
|
||||
/// This will either be 0 or 1.
|
||||
std::uint64_t process_one_event();
|
||||
|
||||
/// Process one event if there are any events pending, otherwise if there
|
||||
/// are no events pending or the io_service is stopped then return immediately.
|
||||
///
|
||||
/// \return
|
||||
/// The number of events processed during this call.
|
||||
/// This will either be 0 or 1.
|
||||
std::uint64_t process_one_pending_event();
|
||||
|
||||
/// Shut down the io_service.
|
||||
///
|
||||
/// This will cause any threads currently in a call to one of the process_xxx() methods
|
||||
/// to return from that call once they finish processing the current event.
|
||||
///
|
||||
/// This call does not wait until all threads have exited the event loop so you
|
||||
/// must use other synchronisation mechanisms to wait for those threads.
|
||||
void stop() noexcept;
|
||||
|
||||
/// Reset an io_service to prepare it for resuming processing of events.
|
||||
///
|
||||
/// Call this after a call to stop() to allow calls to process_xxx() methods
|
||||
/// to process events.
|
||||
///
|
||||
/// After calling stop() you should ensure that all threads have returned from
|
||||
/// calls to process_xxx() methods before calling reset().
|
||||
void reset();
|
||||
|
||||
bool is_stop_requested() const noexcept;
|
||||
|
||||
void notify_work_started() noexcept;
|
||||
|
||||
void notify_work_finished() noexcept;
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
detail::win32::handle_t native_iocp_handle() noexcept;
|
||||
void ensure_winsock_initialised();
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
class timer_thread_state;
|
||||
class timer_queue;
|
||||
|
||||
friend class schedule_operation;
|
||||
friend class timed_schedule_operation;
|
||||
|
||||
void schedule_impl(schedule_operation* operation) noexcept;
|
||||
|
||||
void try_reschedule_overflow_operations() noexcept;
|
||||
|
||||
bool try_enter_event_loop() noexcept;
|
||||
void exit_event_loop() noexcept;
|
||||
|
||||
bool try_process_one_event(bool waitForEvent);
|
||||
|
||||
void post_wake_up_event() noexcept;
|
||||
|
||||
timer_thread_state* ensure_timer_thread_started();
|
||||
|
||||
static constexpr std::uint32_t stop_requested_flag = 1;
|
||||
static constexpr std::uint32_t active_thread_count_increment = 2;
|
||||
|
||||
// Bit 0: stop_requested_flag
|
||||
// Bit 1-31: count of active threads currently running the event loop
|
||||
std::atomic<std::uint32_t> m_threadState;
|
||||
|
||||
std::atomic<std::uint32_t> m_workCount;
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
detail::win32::safe_handle m_iocpHandle;
|
||||
|
||||
std::atomic<bool> m_winsockInitialised;
|
||||
std::mutex m_winsockInitialisationMutex;
|
||||
#endif
|
||||
|
||||
// Head of a linked-list of schedule operations that are
|
||||
// ready to run but that failed to be queued to the I/O
|
||||
// completion port (eg. due to low memory).
|
||||
std::atomic<schedule_operation*> m_scheduleOperations;
|
||||
|
||||
std::atomic<timer_thread_state*> m_timerState;
|
||||
|
||||
};
|
||||
|
||||
class io_service::schedule_operation
|
||||
{
|
||||
public:
|
||||
|
||||
schedule_operation(io_service& service) noexcept
|
||||
: m_service(service)
|
||||
{}
|
||||
|
||||
bool await_ready() const noexcept { return false; }
|
||||
void await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
|
||||
void await_resume() const noexcept {}
|
||||
|
||||
private:
|
||||
|
||||
friend class io_service;
|
||||
friend class io_service::timed_schedule_operation;
|
||||
|
||||
io_service& m_service;
|
||||
std::experimental::coroutine_handle<> m_awaiter;
|
||||
schedule_operation* m_next;
|
||||
|
||||
};
|
||||
|
||||
class io_service::timed_schedule_operation
|
||||
{
|
||||
public:
|
||||
|
||||
timed_schedule_operation(
|
||||
io_service& service,
|
||||
std::chrono::high_resolution_clock::time_point resumeTime,
|
||||
cppcoro::cancellation_token cancellationToken) noexcept;
|
||||
|
||||
timed_schedule_operation(timed_schedule_operation&& other) noexcept;
|
||||
|
||||
~timed_schedule_operation();
|
||||
|
||||
timed_schedule_operation& operator=(timed_schedule_operation&& other) = delete;
|
||||
timed_schedule_operation(const timed_schedule_operation& other) = delete;
|
||||
timed_schedule_operation& operator=(const timed_schedule_operation& other) = delete;
|
||||
|
||||
bool await_ready() const noexcept;
|
||||
void await_suspend(std::experimental::coroutine_handle<> awaiter);
|
||||
void await_resume();
|
||||
|
||||
private:
|
||||
|
||||
friend class io_service::timer_queue;
|
||||
friend class io_service::timer_thread_state;
|
||||
|
||||
io_service::schedule_operation m_scheduleOperation;
|
||||
std::chrono::high_resolution_clock::time_point m_resumeTime;
|
||||
|
||||
cppcoro::cancellation_token m_cancellationToken;
|
||||
std::optional<cppcoro::cancellation_registration> m_cancellationRegistration;
|
||||
|
||||
timed_schedule_operation* m_next;
|
||||
|
||||
std::atomic<std::uint32_t> m_refCount;
|
||||
|
||||
};
|
||||
|
||||
class io_work_scope
|
||||
{
|
||||
public:
|
||||
|
||||
explicit io_work_scope(io_service& service) noexcept
|
||||
: m_service(&service)
|
||||
{
|
||||
service.notify_work_started();
|
||||
}
|
||||
|
||||
io_work_scope(const io_work_scope& other) noexcept
|
||||
: m_service(other.m_service)
|
||||
{
|
||||
if (m_service != nullptr)
|
||||
{
|
||||
m_service->notify_work_started();
|
||||
}
|
||||
}
|
||||
|
||||
io_work_scope(io_work_scope&& other) noexcept
|
||||
: m_service(other.m_service)
|
||||
{
|
||||
other.m_service = nullptr;
|
||||
}
|
||||
|
||||
~io_work_scope()
|
||||
{
|
||||
if (m_service != nullptr)
|
||||
{
|
||||
m_service->notify_work_finished();
|
||||
}
|
||||
}
|
||||
|
||||
void swap(io_work_scope& other) noexcept
|
||||
{
|
||||
std::swap(m_service, other.m_service);
|
||||
}
|
||||
|
||||
io_work_scope& operator=(io_work_scope other) noexcept
|
||||
{
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
io_service& service() noexcept
|
||||
{
|
||||
return *m_service;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
io_service* m_service;
|
||||
|
||||
};
|
||||
|
||||
inline void swap(io_work_scope& a, io_work_scope& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename REP, typename RATIO>
|
||||
cppcoro::io_service::timed_schedule_operation
|
||||
cppcoro::io_service::schedule_after(
|
||||
const std::chrono::duration<REP, RATIO>& duration,
|
||||
cppcoro::cancellation_token cancellationToken) noexcept
|
||||
{
|
||||
return timed_schedule_operation{
|
||||
*this,
|
||||
std::chrono::high_resolution_clock::now() + duration,
|
||||
std::move(cancellationToken)
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -194,7 +194,7 @@ namespace cppcoro
|
|||
return m_barrierWait.await_ready();
|
||||
}
|
||||
|
||||
auto await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
auto await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
return m_barrierWait.await_suspend(awaitingCoroutine);
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ namespace cppcoro
|
|||
return m_waitOp.await_ready();
|
||||
}
|
||||
|
||||
auto await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
auto await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
return m_waitOp.await_suspend(awaitingCoroutine);
|
||||
}
|
||||
|
@ -347,7 +347,7 @@ namespace cppcoro
|
|||
return !TRAITS::precedes(m_lastKnownPublished, m_targetSequence);
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
m_awaitingCoroutine = awaitingCoroutine;
|
||||
|
||||
|
@ -384,7 +384,7 @@ namespace cppcoro
|
|||
SEQUENCE m_targetSequence;
|
||||
SEQUENCE m_lastKnownPublished;
|
||||
multi_producer_sequencer_wait_operation_base* m_next;
|
||||
std::experimental::coroutine_handle<> m_awaitingCoroutine;
|
||||
std::coroutine_handle<> m_awaitingCoroutine;
|
||||
std::atomic<bool> m_readyToResume;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,147 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_IP_ADDRESS_HPP_INCLUDED
|
||||
#define CPPCORO_NET_IP_ADDRESS_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/net/ipv4_address.hpp>
|
||||
#include <cppcoro/net/ipv6_address.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
class ip_address
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructs to IPv4 address 0.0.0.0
|
||||
ip_address() noexcept;
|
||||
|
||||
ip_address(ipv4_address address) noexcept;
|
||||
ip_address(ipv6_address address) noexcept;
|
||||
|
||||
bool is_ipv4() const noexcept { return m_family == family::ipv4; }
|
||||
bool is_ipv6() const noexcept { return m_family == family::ipv6; }
|
||||
|
||||
const ipv4_address& to_ipv4() const;
|
||||
const ipv6_address& to_ipv6() const;
|
||||
|
||||
const std::uint8_t* bytes() const noexcept;
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
static std::optional<ip_address> from_string(std::string_view string) noexcept;
|
||||
|
||||
bool operator==(const ip_address& rhs) const noexcept;
|
||||
bool operator!=(const ip_address& rhs) const noexcept;
|
||||
|
||||
// ipv4_address sorts less than ipv6_address
|
||||
bool operator<(const ip_address& rhs) const noexcept;
|
||||
bool operator>(const ip_address& rhs) const noexcept;
|
||||
bool operator<=(const ip_address& rhs) const noexcept;
|
||||
bool operator>=(const ip_address& rhs) const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
enum class family
|
||||
{
|
||||
ipv4,
|
||||
ipv6
|
||||
};
|
||||
|
||||
family m_family;
|
||||
|
||||
union
|
||||
{
|
||||
ipv4_address m_ipv4;
|
||||
ipv6_address m_ipv6;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
inline ip_address::ip_address() noexcept
|
||||
: m_family(family::ipv4)
|
||||
, m_ipv4()
|
||||
{}
|
||||
|
||||
inline ip_address::ip_address(ipv4_address address) noexcept
|
||||
: m_family(family::ipv4)
|
||||
, m_ipv4(address)
|
||||
{}
|
||||
|
||||
inline ip_address::ip_address(ipv6_address address) noexcept
|
||||
: m_family(family::ipv6)
|
||||
, m_ipv6(address)
|
||||
{
|
||||
}
|
||||
|
||||
inline const ipv4_address& ip_address::to_ipv4() const
|
||||
{
|
||||
assert(is_ipv4());
|
||||
return m_ipv4;
|
||||
}
|
||||
|
||||
inline const ipv6_address& ip_address::to_ipv6() const
|
||||
{
|
||||
assert(is_ipv6());
|
||||
return m_ipv6;
|
||||
}
|
||||
|
||||
inline const std::uint8_t* ip_address::bytes() const noexcept
|
||||
{
|
||||
return is_ipv4() ? m_ipv4.bytes() : m_ipv6.bytes();
|
||||
}
|
||||
|
||||
inline bool ip_address::operator==(const ip_address& rhs) const noexcept
|
||||
{
|
||||
if (is_ipv4())
|
||||
{
|
||||
return rhs.is_ipv4() && m_ipv4 == rhs.m_ipv4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return rhs.is_ipv6() && m_ipv6 == rhs.m_ipv6;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ip_address::operator!=(const ip_address& rhs) const noexcept
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
inline bool ip_address::operator<(const ip_address& rhs) const noexcept
|
||||
{
|
||||
if (is_ipv4())
|
||||
{
|
||||
return !rhs.is_ipv4() || m_ipv4 < rhs.m_ipv4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return rhs.is_ipv6() && m_ipv6 < rhs.m_ipv6;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ip_address::operator>(const ip_address& rhs) const noexcept
|
||||
{
|
||||
return rhs < *this;
|
||||
}
|
||||
|
||||
inline bool ip_address::operator<=(const ip_address& rhs) const noexcept
|
||||
{
|
||||
return !(rhs < *this);
|
||||
}
|
||||
|
||||
inline bool ip_address::operator>=(const ip_address& rhs) const noexcept
|
||||
{
|
||||
return !(*this < rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,161 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_IP_ENDPOINT_HPP_INCLUDED
|
||||
#define CPPCORO_NET_IP_ENDPOINT_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/net/ip_address.hpp>
|
||||
#include <cppcoro/net/ipv4_endpoint.hpp>
|
||||
#include <cppcoro/net/ipv6_endpoint.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
class ip_endpoint
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructs to IPv4 end-point 0.0.0.0:0
|
||||
ip_endpoint() noexcept;
|
||||
|
||||
ip_endpoint(ipv4_endpoint endpoint) noexcept;
|
||||
ip_endpoint(ipv6_endpoint endpoint) noexcept;
|
||||
|
||||
bool is_ipv4() const noexcept { return m_family == family::ipv4; }
|
||||
bool is_ipv6() const noexcept { return m_family == family::ipv6; }
|
||||
|
||||
const ipv4_endpoint& to_ipv4() const;
|
||||
const ipv6_endpoint& to_ipv6() const;
|
||||
|
||||
ip_address address() const noexcept;
|
||||
std::uint16_t port() const noexcept;
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
static std::optional<ip_endpoint> from_string(std::string_view string) noexcept;
|
||||
|
||||
bool operator==(const ip_endpoint& rhs) const noexcept;
|
||||
bool operator!=(const ip_endpoint& rhs) const noexcept;
|
||||
|
||||
// ipv4_endpoint sorts less than ipv6_endpoint
|
||||
bool operator<(const ip_endpoint& rhs) const noexcept;
|
||||
bool operator>(const ip_endpoint& rhs) const noexcept;
|
||||
bool operator<=(const ip_endpoint& rhs) const noexcept;
|
||||
bool operator>=(const ip_endpoint& rhs) const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
enum class family
|
||||
{
|
||||
ipv4,
|
||||
ipv6
|
||||
};
|
||||
|
||||
family m_family;
|
||||
|
||||
union
|
||||
{
|
||||
ipv4_endpoint m_ipv4;
|
||||
ipv6_endpoint m_ipv6;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
inline ip_endpoint::ip_endpoint() noexcept
|
||||
: m_family(family::ipv4)
|
||||
, m_ipv4()
|
||||
{}
|
||||
|
||||
inline ip_endpoint::ip_endpoint(ipv4_endpoint endpoint) noexcept
|
||||
: m_family(family::ipv4)
|
||||
, m_ipv4(endpoint)
|
||||
{}
|
||||
|
||||
inline ip_endpoint::ip_endpoint(ipv6_endpoint endpoint) noexcept
|
||||
: m_family(family::ipv6)
|
||||
, m_ipv6(endpoint)
|
||||
{
|
||||
}
|
||||
|
||||
inline const ipv4_endpoint& ip_endpoint::to_ipv4() const
|
||||
{
|
||||
assert(is_ipv4());
|
||||
return m_ipv4;
|
||||
}
|
||||
|
||||
inline const ipv6_endpoint& ip_endpoint::to_ipv6() const
|
||||
{
|
||||
assert(is_ipv6());
|
||||
return m_ipv6;
|
||||
}
|
||||
|
||||
inline ip_address ip_endpoint::address() const noexcept
|
||||
{
|
||||
if (is_ipv4())
|
||||
{
|
||||
return m_ipv4.address();
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_ipv6.address();
|
||||
}
|
||||
}
|
||||
|
||||
inline std::uint16_t ip_endpoint::port() const noexcept
|
||||
{
|
||||
return is_ipv4() ? m_ipv4.port() : m_ipv6.port();
|
||||
}
|
||||
|
||||
inline bool ip_endpoint::operator==(const ip_endpoint& rhs) const noexcept
|
||||
{
|
||||
if (is_ipv4())
|
||||
{
|
||||
return rhs.is_ipv4() && m_ipv4 == rhs.m_ipv4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return rhs.is_ipv6() && m_ipv6 == rhs.m_ipv6;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ip_endpoint::operator!=(const ip_endpoint& rhs) const noexcept
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
inline bool ip_endpoint::operator<(const ip_endpoint& rhs) const noexcept
|
||||
{
|
||||
if (is_ipv4())
|
||||
{
|
||||
return !rhs.is_ipv4() || m_ipv4 < rhs.m_ipv4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return rhs.is_ipv6() && m_ipv6 < rhs.m_ipv6;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ip_endpoint::operator>(const ip_endpoint& rhs) const noexcept
|
||||
{
|
||||
return rhs < *this;
|
||||
}
|
||||
|
||||
inline bool ip_endpoint::operator<=(const ip_endpoint& rhs) const noexcept
|
||||
{
|
||||
return !(rhs < *this);
|
||||
}
|
||||
|
||||
inline bool ip_endpoint::operator>=(const ip_endpoint& rhs) const noexcept
|
||||
{
|
||||
return !(*this < rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,134 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_IPV4_ADDRESS_HPP_INCLUDED
|
||||
#define CPPCORO_NET_IPV4_ADDRESS_HPP_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace cppcoro::net
|
||||
{
|
||||
class ipv4_address
|
||||
{
|
||||
using bytes_t = std::uint8_t[4];
|
||||
|
||||
public:
|
||||
|
||||
constexpr ipv4_address()
|
||||
: m_bytes{ 0, 0, 0, 0 }
|
||||
{}
|
||||
|
||||
explicit constexpr ipv4_address(std::uint32_t integer)
|
||||
: m_bytes{
|
||||
static_cast<std::uint8_t>(integer >> 24),
|
||||
static_cast<std::uint8_t>(integer >> 16),
|
||||
static_cast<std::uint8_t>(integer >> 8),
|
||||
static_cast<std::uint8_t>(integer) }
|
||||
{}
|
||||
|
||||
explicit constexpr ipv4_address(const std::uint8_t(&bytes)[4])
|
||||
: m_bytes{ bytes[0], bytes[1], bytes[2], bytes[3] }
|
||||
{}
|
||||
|
||||
explicit constexpr ipv4_address(
|
||||
std::uint8_t b0,
|
||||
std::uint8_t b1,
|
||||
std::uint8_t b2,
|
||||
std::uint8_t b3)
|
||||
: m_bytes{ b0, b1, b2, b3 }
|
||||
{}
|
||||
|
||||
constexpr const bytes_t& bytes() const { return m_bytes; }
|
||||
|
||||
constexpr std::uint32_t to_integer() const
|
||||
{
|
||||
return
|
||||
std::uint32_t(m_bytes[0]) << 24 |
|
||||
std::uint32_t(m_bytes[1]) << 16 |
|
||||
std::uint32_t(m_bytes[2]) << 8 |
|
||||
std::uint32_t(m_bytes[3]);
|
||||
}
|
||||
|
||||
static constexpr ipv4_address loopback()
|
||||
{
|
||||
return ipv4_address(127, 0, 0, 1);
|
||||
}
|
||||
|
||||
constexpr bool is_loopback() const
|
||||
{
|
||||
return m_bytes[0] == 127;
|
||||
}
|
||||
|
||||
constexpr bool is_private_network() const
|
||||
{
|
||||
return m_bytes[0] == 10 ||
|
||||
(m_bytes[0] == 172 && (m_bytes[1] & 0xF0) == 0x10) ||
|
||||
(m_bytes[0] == 192 && m_bytes[2] == 168);
|
||||
}
|
||||
|
||||
constexpr bool operator==(ipv4_address other) const
|
||||
{
|
||||
return
|
||||
m_bytes[0] == other.m_bytes[0] &&
|
||||
m_bytes[1] == other.m_bytes[1] &&
|
||||
m_bytes[2] == other.m_bytes[2] &&
|
||||
m_bytes[3] == other.m_bytes[3];
|
||||
}
|
||||
|
||||
constexpr bool operator!=(ipv4_address other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr bool operator<(ipv4_address other) const
|
||||
{
|
||||
return to_integer() < other.to_integer();
|
||||
}
|
||||
|
||||
constexpr bool operator>(ipv4_address other) const
|
||||
{
|
||||
return other < *this;
|
||||
}
|
||||
|
||||
constexpr bool operator<=(ipv4_address other) const
|
||||
{
|
||||
return !(other < *this);
|
||||
}
|
||||
|
||||
constexpr bool operator>=(ipv4_address other) const
|
||||
{
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
/// Parse a string representation of an IP address.
|
||||
///
|
||||
/// Parses strings of the form:
|
||||
/// - "num.num.num.num" where num is an integer in range [0, 255].
|
||||
/// - A single integer value in range [0, 2^32).
|
||||
///
|
||||
/// \param string
|
||||
/// The string to parse.
|
||||
/// Must be in ASCII, UTF-8 or Latin-1 encoding.
|
||||
///
|
||||
/// \return
|
||||
/// The IP address if successful, otherwise std::nullopt if the string
|
||||
/// could not be parsed as an IPv4 address.
|
||||
static std::optional<ipv4_address> from_string(std::string_view string) noexcept;
|
||||
|
||||
/// Convert the IP address to dotted decimal notation.
|
||||
///
|
||||
/// eg. "12.67.190.23"
|
||||
std::string to_string() const;
|
||||
|
||||
private:
|
||||
|
||||
alignas(std::uint32_t) std::uint8_t m_bytes[4];
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,82 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_IPV4_ENDPOINT_HPP_INCLUDED
|
||||
#define CPPCORO_NET_IPV4_ENDPOINT_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/net/ipv4_address.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
class ipv4_endpoint
|
||||
{
|
||||
public:
|
||||
|
||||
// Construct to 0.0.0.0:0
|
||||
ipv4_endpoint() noexcept
|
||||
: m_address()
|
||||
, m_port(0)
|
||||
{}
|
||||
|
||||
explicit ipv4_endpoint(ipv4_address address, std::uint16_t port = 0) noexcept
|
||||
: m_address(address)
|
||||
, m_port(port)
|
||||
{}
|
||||
|
||||
const ipv4_address& address() const noexcept { return m_address; }
|
||||
|
||||
std::uint16_t port() const noexcept { return m_port; }
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
static std::optional<ipv4_endpoint> from_string(std::string_view string) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
ipv4_address m_address;
|
||||
std::uint16_t m_port;
|
||||
|
||||
};
|
||||
|
||||
inline bool operator==(const ipv4_endpoint& a, const ipv4_endpoint& b)
|
||||
{
|
||||
return a.address() == b.address() &&
|
||||
a.port() == b.port();
|
||||
}
|
||||
|
||||
inline bool operator!=(const ipv4_endpoint& a, const ipv4_endpoint& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
inline bool operator<(const ipv4_endpoint& a, const ipv4_endpoint& b)
|
||||
{
|
||||
return a.address() < b.address() ||
|
||||
(a.address() == b.address() && a.port() < b.port());
|
||||
}
|
||||
|
||||
inline bool operator>(const ipv4_endpoint& a, const ipv4_endpoint& b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
|
||||
inline bool operator<=(const ipv4_endpoint& a, const ipv4_endpoint& b)
|
||||
{
|
||||
return !(b < a);
|
||||
}
|
||||
|
||||
inline bool operator>=(const ipv4_endpoint& a, const ipv4_endpoint& b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,245 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_IPV6_ADDRESS_HPP_INCLUDED
|
||||
#define CPPCORO_NET_IPV6_ADDRESS_HPP_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace cppcoro::net
|
||||
{
|
||||
class ipv4_address;
|
||||
|
||||
class ipv6_address
|
||||
{
|
||||
using bytes_t = std::uint8_t[16];
|
||||
|
||||
public:
|
||||
|
||||
constexpr ipv6_address();
|
||||
|
||||
explicit constexpr ipv6_address(
|
||||
std::uint64_t subnetPrefix,
|
||||
std::uint64_t interfaceIdentifier);
|
||||
|
||||
constexpr ipv6_address(
|
||||
std::uint16_t part0,
|
||||
std::uint16_t part1,
|
||||
std::uint16_t part2,
|
||||
std::uint16_t part3,
|
||||
std::uint16_t part4,
|
||||
std::uint16_t part5,
|
||||
std::uint16_t part6,
|
||||
std::uint16_t part7);
|
||||
|
||||
explicit constexpr ipv6_address(
|
||||
const std::uint16_t(&parts)[8]);
|
||||
|
||||
explicit constexpr ipv6_address(
|
||||
const std::uint8_t(&bytes)[16]);
|
||||
|
||||
constexpr const bytes_t& bytes() const { return m_bytes; }
|
||||
|
||||
constexpr std::uint64_t subnet_prefix() const;
|
||||
|
||||
constexpr std::uint64_t interface_identifier() const;
|
||||
|
||||
/// Get the IPv6 unspedified address :: (all zeroes).
|
||||
static constexpr ipv6_address unspecified();
|
||||
|
||||
/// Get the IPv6 loopback address ::1.
|
||||
static constexpr ipv6_address loopback();
|
||||
|
||||
/// Parse a string representation of an IPv6 address.
|
||||
///
|
||||
/// \param string
|
||||
/// The string to parse.
|
||||
/// Must be in ASCII, UTF-8 or Latin-1 encoding.
|
||||
///
|
||||
/// \return
|
||||
/// The IP address if successful, otherwise std::nullopt if the string
|
||||
/// could not be parsed as an IPv4 address.
|
||||
static std::optional<ipv6_address> from_string(std::string_view string) noexcept;
|
||||
|
||||
/// Convert the IP address to contracted string form.
|
||||
///
|
||||
/// Address is broken up into 16-bit parts, with each part represended in 1-4
|
||||
/// lower-case hexadecimal with leading zeroes omitted. Parts are separated
|
||||
/// by separated by a ':'. The longest contiguous run of zero parts is contracted
|
||||
/// to "::".
|
||||
///
|
||||
/// For example:
|
||||
/// ipv6_address::unspecified() -> "::"
|
||||
/// ipv6_address::loopback() -> "::1"
|
||||
/// ipv6_address(0x0011223344556677, 0x8899aabbccddeeff) ->
|
||||
/// "11:2233:4455:6677:8899:aabb:ccdd:eeff"
|
||||
/// ipv6_address(0x0102030400000000, 0x003fc447ab991011) ->
|
||||
/// "102:304::3f:c447:ab99:1011"
|
||||
std::string to_string() const;
|
||||
|
||||
constexpr bool operator==(const ipv6_address& other) const;
|
||||
constexpr bool operator!=(const ipv6_address& other) const;
|
||||
constexpr bool operator<(const ipv6_address& other) const;
|
||||
constexpr bool operator>(const ipv6_address& other) const;
|
||||
constexpr bool operator<=(const ipv6_address& other) const;
|
||||
constexpr bool operator>=(const ipv6_address& other) const;
|
||||
|
||||
private:
|
||||
|
||||
alignas(std::uint64_t) std::uint8_t m_bytes[16];
|
||||
|
||||
};
|
||||
|
||||
constexpr ipv6_address::ipv6_address()
|
||||
: m_bytes{
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
{}
|
||||
|
||||
constexpr ipv6_address::ipv6_address(
|
||||
std::uint64_t subnetPrefix,
|
||||
std::uint64_t interfaceIdentifier)
|
||||
: m_bytes{
|
||||
static_cast<std::uint8_t>(subnetPrefix >> 56),
|
||||
static_cast<std::uint8_t>(subnetPrefix >> 48),
|
||||
static_cast<std::uint8_t>(subnetPrefix >> 40),
|
||||
static_cast<std::uint8_t>(subnetPrefix >> 32),
|
||||
static_cast<std::uint8_t>(subnetPrefix >> 24),
|
||||
static_cast<std::uint8_t>(subnetPrefix >> 16),
|
||||
static_cast<std::uint8_t>(subnetPrefix >> 8),
|
||||
static_cast<std::uint8_t>(subnetPrefix),
|
||||
static_cast<std::uint8_t>(interfaceIdentifier >> 56),
|
||||
static_cast<std::uint8_t>(interfaceIdentifier >> 48),
|
||||
static_cast<std::uint8_t>(interfaceIdentifier >> 40),
|
||||
static_cast<std::uint8_t>(interfaceIdentifier >> 32),
|
||||
static_cast<std::uint8_t>(interfaceIdentifier >> 24),
|
||||
static_cast<std::uint8_t>(interfaceIdentifier >> 16),
|
||||
static_cast<std::uint8_t>(interfaceIdentifier >> 8),
|
||||
static_cast<std::uint8_t>(interfaceIdentifier) }
|
||||
{}
|
||||
|
||||
constexpr ipv6_address::ipv6_address(
|
||||
std::uint16_t part0,
|
||||
std::uint16_t part1,
|
||||
std::uint16_t part2,
|
||||
std::uint16_t part3,
|
||||
std::uint16_t part4,
|
||||
std::uint16_t part5,
|
||||
std::uint16_t part6,
|
||||
std::uint16_t part7)
|
||||
: m_bytes{
|
||||
static_cast<std::uint8_t>(part0 >> 8),
|
||||
static_cast<std::uint8_t>(part0),
|
||||
static_cast<std::uint8_t>(part1 >> 8),
|
||||
static_cast<std::uint8_t>(part1),
|
||||
static_cast<std::uint8_t>(part2 >> 8),
|
||||
static_cast<std::uint8_t>(part2),
|
||||
static_cast<std::uint8_t>(part3 >> 8),
|
||||
static_cast<std::uint8_t>(part3),
|
||||
static_cast<std::uint8_t>(part4 >> 8),
|
||||
static_cast<std::uint8_t>(part4),
|
||||
static_cast<std::uint8_t>(part5 >> 8),
|
||||
static_cast<std::uint8_t>(part5),
|
||||
static_cast<std::uint8_t>(part6 >> 8),
|
||||
static_cast<std::uint8_t>(part6),
|
||||
static_cast<std::uint8_t>(part7 >> 8),
|
||||
static_cast<std::uint8_t>(part7) }
|
||||
{}
|
||||
|
||||
constexpr ipv6_address::ipv6_address(
|
||||
const std::uint16_t(&parts)[8])
|
||||
: ipv6_address(
|
||||
parts[0], parts[1], parts[2], parts[3],
|
||||
parts[4], parts[5], parts[6], parts[7])
|
||||
{}
|
||||
|
||||
constexpr ipv6_address::ipv6_address(const std::uint8_t(&bytes)[16])
|
||||
: m_bytes{
|
||||
bytes[0], bytes[1], bytes[2], bytes[3],
|
||||
bytes[4], bytes[5], bytes[6], bytes[7],
|
||||
bytes[8], bytes[9], bytes[10], bytes[11],
|
||||
bytes[12], bytes[13], bytes[14], bytes[15] }
|
||||
{}
|
||||
|
||||
constexpr std::uint64_t ipv6_address::subnet_prefix() const
|
||||
{
|
||||
return
|
||||
static_cast<std::uint64_t>(m_bytes[0]) << 56 |
|
||||
static_cast<std::uint64_t>(m_bytes[1]) << 48 |
|
||||
static_cast<std::uint64_t>(m_bytes[2]) << 40 |
|
||||
static_cast<std::uint64_t>(m_bytes[3]) << 32 |
|
||||
static_cast<std::uint64_t>(m_bytes[4]) << 24 |
|
||||
static_cast<std::uint64_t>(m_bytes[5]) << 16 |
|
||||
static_cast<std::uint64_t>(m_bytes[6]) << 8 |
|
||||
static_cast<std::uint64_t>(m_bytes[7]);
|
||||
}
|
||||
|
||||
constexpr std::uint64_t ipv6_address::interface_identifier() const
|
||||
{
|
||||
return
|
||||
static_cast<std::uint64_t>(m_bytes[8]) << 56 |
|
||||
static_cast<std::uint64_t>(m_bytes[9]) << 48 |
|
||||
static_cast<std::uint64_t>(m_bytes[10]) << 40 |
|
||||
static_cast<std::uint64_t>(m_bytes[11]) << 32 |
|
||||
static_cast<std::uint64_t>(m_bytes[12]) << 24 |
|
||||
static_cast<std::uint64_t>(m_bytes[13]) << 16 |
|
||||
static_cast<std::uint64_t>(m_bytes[14]) << 8 |
|
||||
static_cast<std::uint64_t>(m_bytes[15]);
|
||||
}
|
||||
|
||||
constexpr ipv6_address ipv6_address::unspecified()
|
||||
{
|
||||
return ipv6_address{};
|
||||
}
|
||||
|
||||
constexpr ipv6_address ipv6_address::loopback()
|
||||
{
|
||||
return ipv6_address{ 0, 0, 0, 0, 0, 0, 0, 1 };
|
||||
}
|
||||
|
||||
constexpr bool ipv6_address::operator==(const ipv6_address& other) const
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (m_bytes[i] != other.m_bytes[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool ipv6_address::operator!=(const ipv6_address& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr bool ipv6_address::operator<(const ipv6_address& other) const
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (m_bytes[i] != other.m_bytes[i])
|
||||
return m_bytes[i] < other.m_bytes[i];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool ipv6_address::operator>(const ipv6_address& other) const
|
||||
{
|
||||
return (other < *this);
|
||||
}
|
||||
|
||||
constexpr bool ipv6_address::operator<=(const ipv6_address& other) const
|
||||
{
|
||||
return !(other < *this);
|
||||
}
|
||||
|
||||
constexpr bool ipv6_address::operator>=(const ipv6_address& other) const
|
||||
{
|
||||
return !(*this < other);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,82 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_IPV6_ENDPOINT_HPP_INCLUDED
|
||||
#define CPPCORO_NET_IPV6_ENDPOINT_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/net/ipv6_address.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
class ipv6_endpoint
|
||||
{
|
||||
public:
|
||||
|
||||
// Construct to [::]:0
|
||||
ipv6_endpoint() noexcept
|
||||
: m_address()
|
||||
, m_port(0)
|
||||
{}
|
||||
|
||||
explicit ipv6_endpoint(ipv6_address address, std::uint16_t port = 0) noexcept
|
||||
: m_address(address)
|
||||
, m_port(port)
|
||||
{}
|
||||
|
||||
const ipv6_address& address() const noexcept { return m_address; }
|
||||
|
||||
std::uint16_t port() const noexcept { return m_port; }
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
static std::optional<ipv6_endpoint> from_string(std::string_view string) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
ipv6_address m_address;
|
||||
std::uint16_t m_port;
|
||||
|
||||
};
|
||||
|
||||
inline bool operator==(const ipv6_endpoint& a, const ipv6_endpoint& b)
|
||||
{
|
||||
return a.address() == b.address() &&
|
||||
a.port() == b.port();
|
||||
}
|
||||
|
||||
inline bool operator!=(const ipv6_endpoint& a, const ipv6_endpoint& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
inline bool operator<(const ipv6_endpoint& a, const ipv6_endpoint& b)
|
||||
{
|
||||
return a.address() < b.address() ||
|
||||
(a.address() == b.address() && a.port() < b.port());
|
||||
}
|
||||
|
||||
inline bool operator>(const ipv6_endpoint& a, const ipv6_endpoint& b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
|
||||
inline bool operator<=(const ipv6_endpoint& a, const ipv6_endpoint& b)
|
||||
{
|
||||
return !(b < a);
|
||||
}
|
||||
|
||||
inline bool operator>=(const ipv6_endpoint& a, const ipv6_endpoint& b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,268 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_SOCKET_HPP_INCLUDED
|
||||
#define CPPCORO_NET_SOCKET_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
|
||||
#include <cppcoro/net/ip_endpoint.hpp>
|
||||
#include <cppcoro/net/socket_accept_operation.hpp>
|
||||
#include <cppcoro/net/socket_connect_operation.hpp>
|
||||
#include <cppcoro/net/socket_disconnect_operation.hpp>
|
||||
#include <cppcoro/net/socket_recv_operation.hpp>
|
||||
#include <cppcoro/net/socket_recv_from_operation.hpp>
|
||||
#include <cppcoro/net/socket_send_operation.hpp>
|
||||
#include <cppcoro/net/socket_send_to_operation.hpp>
|
||||
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
#endif
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class io_service;
|
||||
|
||||
namespace net
|
||||
{
|
||||
class socket
|
||||
{
|
||||
public:
|
||||
|
||||
/// Create a socket that can be used to communicate using TCP/IPv4 protocol.
|
||||
///
|
||||
/// \param ioSvc
|
||||
/// The I/O service the socket will use for dispatching I/O completion events.
|
||||
///
|
||||
/// \return
|
||||
/// The newly created socket.
|
||||
///
|
||||
/// \throws std::system_error
|
||||
/// If the socket could not be created for some reason.
|
||||
static socket create_tcpv4(io_service& ioSvc);
|
||||
|
||||
/// Create a socket that can be used to communicate using TCP/IPv6 protocol.
|
||||
///
|
||||
/// \param ioSvc
|
||||
/// The I/O service the socket will use for dispatching I/O completion events.
|
||||
///
|
||||
/// \return
|
||||
/// The newly created socket.
|
||||
///
|
||||
/// \throws std::system_error
|
||||
/// If the socket could not be created for some reason.
|
||||
static socket create_tcpv6(io_service& ioSvc);
|
||||
|
||||
/// Create a socket that can be used to communicate using UDP/IPv4 protocol.
|
||||
///
|
||||
/// \param ioSvc
|
||||
/// The I/O service the socket will use for dispatching I/O completion events.
|
||||
///
|
||||
/// \return
|
||||
/// The newly created socket.
|
||||
///
|
||||
/// \throws std::system_error
|
||||
/// If the socket could not be created for some reason.
|
||||
static socket create_udpv4(io_service& ioSvc);
|
||||
|
||||
/// Create a socket that can be used to communicate using UDP/IPv6 protocol.
|
||||
///
|
||||
/// \param ioSvc
|
||||
/// The I/O service the socket will use for dispatching I/O completion events.
|
||||
///
|
||||
/// \return
|
||||
/// The newly created socket.
|
||||
///
|
||||
/// \throws std::system_error
|
||||
/// If the socket could not be created for some reason.
|
||||
static socket create_udpv6(io_service& ioSvc);
|
||||
|
||||
socket(socket&& other) noexcept;
|
||||
|
||||
/// Closes the socket, releasing any associated resources.
|
||||
///
|
||||
/// If the socket still has an open connection then the connection will be
|
||||
/// reset. The destructor will not block waiting for queueud data to be sent.
|
||||
/// If you need to ensure that queued data is delivered then you must call
|
||||
/// disconnect() and wait until the disconnect operation completes.
|
||||
~socket();
|
||||
|
||||
socket& operator=(socket&& other) noexcept;
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
/// Get the Win32 socket handle assocaited with this socket.
|
||||
cppcoro::detail::win32::socket_t native_handle() noexcept { return m_handle; }
|
||||
|
||||
/// Query whether I/O operations that complete synchronously will skip posting
|
||||
/// an I/O completion event to the I/O completion port.
|
||||
///
|
||||
/// The operation class implementations can use this to determine whether or not
|
||||
/// it should immediately resume the coroutine on the current thread upon an
|
||||
/// operation completing synchronously or whether it should suspend the coroutine
|
||||
/// and wait until the I/O completion event is dispatched to an I/O thread.
|
||||
bool skip_completion_on_success() noexcept { return m_skipCompletionOnSuccess; }
|
||||
#endif
|
||||
|
||||
/// Get the address and port of the local end-point.
|
||||
///
|
||||
/// If the socket is not bound then this will be the unspecified end-point
|
||||
/// of the socket's associated address-family.
|
||||
const ip_endpoint& local_endpoint() const noexcept { return m_localEndPoint; }
|
||||
|
||||
/// Get the address and port of the remote end-point.
|
||||
///
|
||||
/// If the socket is not in the connected state then this will be the unspecified
|
||||
/// end-point of the socket's associated address-family.
|
||||
const ip_endpoint& remote_endpoint() const noexcept { return m_remoteEndPoint; }
|
||||
|
||||
/// Bind the local end of this socket to the specified local end-point.
|
||||
///
|
||||
/// \param localEndPoint
|
||||
/// The end-point to bind to.
|
||||
/// This can be either an unspecified address (in which case it binds to all available
|
||||
/// interfaces) and/or an unspecified port (in which case a random port is allocated).
|
||||
///
|
||||
/// \throws std::system_error
|
||||
/// If the socket could not be bound for some reason.
|
||||
void bind(const ip_endpoint& localEndPoint);
|
||||
|
||||
/// Put the socket into a passive listening state that will start acknowledging
|
||||
/// and queueing up new connections ready to be accepted by a call to 'accept()'.
|
||||
///
|
||||
/// The backlog of connections ready to be accepted will be set to some default
|
||||
/// suitable large value, depending on the network provider. If you need more
|
||||
/// control over the size of the queue then use the overload of listen()
|
||||
/// that accepts a 'backlog' parameter.
|
||||
///
|
||||
/// \throws std::system_error
|
||||
/// If the socket could not be placed into a listening mode.
|
||||
void listen();
|
||||
|
||||
/// Put the socket into a passive listening state that will start acknowledging
|
||||
/// and queueing up new connections ready to be accepted by a call to 'accept()'.
|
||||
///
|
||||
/// \param backlog
|
||||
/// The maximum number of pending connections to allow in the queue of ready-to-accept
|
||||
/// connections.
|
||||
///
|
||||
/// \throws std::system_error
|
||||
/// If the socket could not be placed into a listening mode.
|
||||
void listen(std::uint32_t backlog);
|
||||
|
||||
/// Connect the socket to the specified remote end-point.
|
||||
///
|
||||
/// The socket must be in a bound but unconnected state prior to this call.
|
||||
///
|
||||
/// \param remoteEndPoint
|
||||
/// The IP address and port-number to connect to.
|
||||
///
|
||||
/// \return
|
||||
/// An awaitable object that must be co_await'ed to perform the async connect
|
||||
/// operation. The result of the co_await expression is type void.
|
||||
[[nodiscard]]
|
||||
socket_connect_operation connect(const ip_endpoint& remoteEndPoint) noexcept;
|
||||
|
||||
/// Connect to the specified remote end-point.
|
||||
///
|
||||
/// \param remoteEndPoint
|
||||
/// The IP address and port of the remote end-point to connect to.
|
||||
///
|
||||
/// \param ct
|
||||
/// A cancellation token that can be used to communicate a request to
|
||||
/// later cancel the operation. If the operation is successfully
|
||||
/// cancelled then it will complete by throwing a cppcoro::operation_cancelled
|
||||
/// exception.
|
||||
///
|
||||
/// \return
|
||||
/// An awaitable object that will start the connect operation when co_await'ed
|
||||
/// and will suspend the coroutine, resuming it when the operation completes.
|
||||
/// The result of the co_await expression has type 'void'.
|
||||
[[nodiscard]]
|
||||
socket_connect_operation_cancellable connect(
|
||||
const ip_endpoint& remoteEndPoint,
|
||||
cancellation_token ct) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
socket_accept_operation accept(socket& acceptingSocket) noexcept;
|
||||
[[nodiscard]]
|
||||
socket_accept_operation_cancellable accept(
|
||||
socket& acceptingSocket,
|
||||
cancellation_token ct) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
socket_disconnect_operation disconnect() noexcept;
|
||||
[[nodiscard]]
|
||||
socket_disconnect_operation_cancellable disconnect(cancellation_token ct) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
socket_send_operation send(
|
||||
const void* buffer,
|
||||
std::size_t size) noexcept;
|
||||
[[nodiscard]]
|
||||
socket_send_operation_cancellable send(
|
||||
const void* buffer,
|
||||
std::size_t size,
|
||||
cancellation_token ct) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
socket_recv_operation recv(
|
||||
void* buffer,
|
||||
std::size_t size) noexcept;
|
||||
[[nodiscard]]
|
||||
socket_recv_operation_cancellable recv(
|
||||
void* buffer,
|
||||
std::size_t size,
|
||||
cancellation_token ct) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
socket_recv_from_operation recv_from(
|
||||
void* buffer,
|
||||
std::size_t size) noexcept;
|
||||
[[nodiscard]]
|
||||
socket_recv_from_operation_cancellable recv_from(
|
||||
void* buffer,
|
||||
std::size_t size,
|
||||
cancellation_token ct) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
socket_send_to_operation send_to(
|
||||
const ip_endpoint& destination,
|
||||
const void* buffer,
|
||||
std::size_t size) noexcept;
|
||||
[[nodiscard]]
|
||||
socket_send_to_operation_cancellable send_to(
|
||||
const ip_endpoint& destination,
|
||||
const void* buffer,
|
||||
std::size_t size,
|
||||
cancellation_token ct) noexcept;
|
||||
|
||||
void close_send();
|
||||
void close_recv();
|
||||
|
||||
private:
|
||||
|
||||
friend class socket_accept_operation_impl;
|
||||
friend class socket_connect_operation_impl;
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
explicit socket(
|
||||
cppcoro::detail::win32::socket_t handle,
|
||||
bool skipCompletionOnSuccess) noexcept;
|
||||
#endif
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
cppcoro::detail::win32::socket_t m_handle;
|
||||
bool m_skipCompletionOnSuccess;
|
||||
#endif
|
||||
|
||||
ip_endpoint m_localEndPoint;
|
||||
ip_endpoint m_remoteEndPoint;
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,109 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_SOCKET_ACCEPT_OPERATION_HPP_INCLUDED
|
||||
#define CPPCORO_NET_SOCKET_ACCEPT_OPERATION_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
#include <cppcoro/cancellation_registration.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
# include <cppcoro/detail/win32_overlapped_operation.hpp>
|
||||
|
||||
# include <atomic>
|
||||
# include <optional>
|
||||
# include <experimental/coroutine>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
class socket;
|
||||
|
||||
class socket_accept_operation_impl
|
||||
{
|
||||
public:
|
||||
|
||||
socket_accept_operation_impl(
|
||||
socket& listeningSocket,
|
||||
socket& acceptingSocket) noexcept
|
||||
: m_listeningSocket(listeningSocket)
|
||||
, m_acceptingSocket(acceptingSocket)
|
||||
{}
|
||||
|
||||
bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void get_result(cppcoro::detail::win32_overlapped_operation_base& operation);
|
||||
|
||||
private:
|
||||
|
||||
#if CPPCORO_COMPILER_MSVC
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4324) // Structure padded due to alignment
|
||||
#endif
|
||||
|
||||
socket& m_listeningSocket;
|
||||
socket& m_acceptingSocket;
|
||||
alignas(8) std::uint8_t m_addressBuffer[88];
|
||||
|
||||
#if CPPCORO_COMPILER_MSVC
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
class socket_accept_operation
|
||||
: public cppcoro::detail::win32_overlapped_operation<socket_accept_operation>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_accept_operation(
|
||||
socket& listeningSocket,
|
||||
socket& acceptingSocket) noexcept
|
||||
: m_impl(listeningSocket, acceptingSocket)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation<socket_accept_operation>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void get_result() { m_impl.get_result(*this); }
|
||||
|
||||
socket_accept_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
class socket_accept_operation_cancellable
|
||||
: public cppcoro::detail::win32_overlapped_operation_cancellable<socket_accept_operation_cancellable>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_accept_operation_cancellable(
|
||||
socket& listeningSocket,
|
||||
socket& acceptingSocket,
|
||||
cancellation_token&& ct) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation_cancellable<socket_accept_operation_cancellable>(std::move(ct))
|
||||
, m_impl(listeningSocket, acceptingSocket)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation_cancellable<socket_accept_operation_cancellable>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void cancel() noexcept { m_impl.cancel(*this); }
|
||||
void get_result() { m_impl.get_result(*this); }
|
||||
|
||||
socket_accept_operation_impl m_impl;
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
||||
|
||||
#endif
|
|
@ -1,95 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_SOCKET_CONNECT_OPERATION_HPP_INCLUDED
|
||||
#define CPPCORO_NET_SOCKET_CONNECT_OPERATION_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
#include <cppcoro/net/ip_endpoint.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
# include <cppcoro/detail/win32_overlapped_operation.hpp>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
class socket;
|
||||
|
||||
class socket_connect_operation_impl
|
||||
{
|
||||
public:
|
||||
|
||||
socket_connect_operation_impl(
|
||||
socket& socket,
|
||||
const ip_endpoint& remoteEndPoint) noexcept
|
||||
: m_socket(socket)
|
||||
, m_remoteEndPoint(remoteEndPoint)
|
||||
{}
|
||||
|
||||
bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void get_result(cppcoro::detail::win32_overlapped_operation_base& operation);
|
||||
|
||||
private:
|
||||
|
||||
socket& m_socket;
|
||||
ip_endpoint m_remoteEndPoint;
|
||||
|
||||
};
|
||||
|
||||
class socket_connect_operation
|
||||
: public cppcoro::detail::win32_overlapped_operation<socket_connect_operation>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_connect_operation(
|
||||
socket& socket,
|
||||
const ip_endpoint& remoteEndPoint) noexcept
|
||||
: m_impl(socket, remoteEndPoint)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation<socket_connect_operation>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
decltype(auto) get_result() { return m_impl.get_result(*this); }
|
||||
|
||||
socket_connect_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
class socket_connect_operation_cancellable
|
||||
: public cppcoro::detail::win32_overlapped_operation_cancellable<socket_connect_operation_cancellable>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_connect_operation_cancellable(
|
||||
socket& socket,
|
||||
const ip_endpoint& remoteEndPoint,
|
||||
cancellation_token&& ct) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation_cancellable<socket_connect_operation_cancellable>(std::move(ct))
|
||||
, m_impl(socket, remoteEndPoint)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation_cancellable<socket_connect_operation_cancellable>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void cancel() noexcept { m_impl.cancel(*this); }
|
||||
void get_result() { m_impl.get_result(*this); }
|
||||
|
||||
socket_connect_operation_impl m_impl;
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
||||
|
||||
#endif
|
|
@ -1,85 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_SOCKET_DISCONNECT_OPERATION_HPP_INCLUDED
|
||||
#define CPPCORO_NET_SOCKET_DISCONNECT_OPERATION_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
# include <cppcoro/detail/win32_overlapped_operation.hpp>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
class socket;
|
||||
|
||||
class socket_disconnect_operation_impl
|
||||
{
|
||||
public:
|
||||
|
||||
socket_disconnect_operation_impl(socket& socket) noexcept
|
||||
: m_socket(socket)
|
||||
{}
|
||||
|
||||
bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void get_result(cppcoro::detail::win32_overlapped_operation_base& operation);
|
||||
|
||||
private:
|
||||
|
||||
socket& m_socket;
|
||||
|
||||
};
|
||||
|
||||
class socket_disconnect_operation
|
||||
: public cppcoro::detail::win32_overlapped_operation<socket_disconnect_operation>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_disconnect_operation(socket& socket) noexcept
|
||||
: m_impl(socket)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation<socket_disconnect_operation>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void get_result() { m_impl.get_result(*this); }
|
||||
|
||||
socket_disconnect_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
class socket_disconnect_operation_cancellable
|
||||
: public cppcoro::detail::win32_overlapped_operation_cancellable<socket_disconnect_operation_cancellable>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_disconnect_operation_cancellable(socket& socket, cancellation_token&& ct) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation_cancellable<socket_disconnect_operation_cancellable>(std::move(ct))
|
||||
, m_impl(socket)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation_cancellable<socket_disconnect_operation_cancellable>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void cancel() noexcept { m_impl.cancel(*this); }
|
||||
void get_result() { m_impl.get_result(*this); }
|
||||
|
||||
socket_disconnect_operation_impl m_impl;
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
||||
|
||||
#endif
|
|
@ -1,106 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_SOCKET_RECV_FROM_OPERATION_HPP_INCLUDED
|
||||
#define CPPCORO_NET_SOCKET_RECV_FROM_OPERATION_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
#include <cppcoro/net/ip_endpoint.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
# include <cppcoro/detail/win32_overlapped_operation.hpp>
|
||||
|
||||
namespace cppcoro::net
|
||||
{
|
||||
class socket;
|
||||
|
||||
class socket_recv_from_operation_impl
|
||||
{
|
||||
public:
|
||||
|
||||
socket_recv_from_operation_impl(
|
||||
socket& socket,
|
||||
void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: m_socket(socket)
|
||||
, m_buffer(buffer, byteCount)
|
||||
{}
|
||||
|
||||
bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
std::tuple<std::size_t, ip_endpoint> get_result(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation);
|
||||
|
||||
private:
|
||||
|
||||
socket& m_socket;
|
||||
cppcoro::detail::win32::wsabuf m_buffer;
|
||||
|
||||
static constexpr std::size_t sockaddrStorageAlignment = 4;
|
||||
|
||||
// Storage suitable for either SOCKADDR_IN or SOCKADDR_IN6
|
||||
alignas(sockaddrStorageAlignment) std::uint8_t m_sourceSockaddrStorage[28];
|
||||
int m_sourceSockaddrLength;
|
||||
|
||||
};
|
||||
|
||||
class socket_recv_from_operation
|
||||
: public cppcoro::detail::win32_overlapped_operation<socket_recv_from_operation>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_recv_from_operation(
|
||||
socket& socket,
|
||||
void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: m_impl(socket, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation<socket_recv_from_operation>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
decltype(auto) get_result() { return m_impl.get_result(*this); }
|
||||
|
||||
socket_recv_from_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
class socket_recv_from_operation_cancellable
|
||||
: public cppcoro::detail::win32_overlapped_operation_cancellable<socket_recv_from_operation_cancellable>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_recv_from_operation_cancellable(
|
||||
socket& socket,
|
||||
void* buffer,
|
||||
std::size_t byteCount,
|
||||
cancellation_token&& ct) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation_cancellable<socket_recv_from_operation_cancellable>(std::move(ct))
|
||||
, m_impl(socket, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation_cancellable<socket_recv_from_operation_cancellable>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void cancel() noexcept { m_impl.cancel(*this); }
|
||||
decltype(auto) get_result() { return m_impl.get_result(*this); }
|
||||
|
||||
socket_recv_from_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
||||
|
||||
#endif
|
|
@ -1,94 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_SOCKET_RECV_OPERATION_HPP_INCLUDED
|
||||
#define CPPCORO_NET_SOCKET_RECV_OPERATION_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
# include <cppcoro/detail/win32_overlapped_operation.hpp>
|
||||
|
||||
namespace cppcoro::net
|
||||
{
|
||||
class socket;
|
||||
|
||||
class socket_recv_operation_impl
|
||||
{
|
||||
public:
|
||||
|
||||
socket_recv_operation_impl(
|
||||
socket& s,
|
||||
void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: m_socket(s)
|
||||
, m_buffer(buffer, byteCount)
|
||||
{}
|
||||
|
||||
bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
socket& m_socket;
|
||||
cppcoro::detail::win32::wsabuf m_buffer;
|
||||
|
||||
};
|
||||
|
||||
class socket_recv_operation
|
||||
: public cppcoro::detail::win32_overlapped_operation<socket_recv_operation>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_recv_operation(
|
||||
socket& s,
|
||||
void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: m_impl(s, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation<socket_recv_operation>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
|
||||
socket_recv_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
class socket_recv_operation_cancellable
|
||||
: public cppcoro::detail::win32_overlapped_operation_cancellable<socket_recv_operation_cancellable>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_recv_operation_cancellable(
|
||||
socket& s,
|
||||
void* buffer,
|
||||
std::size_t byteCount,
|
||||
cancellation_token&& ct) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation_cancellable<socket_recv_operation_cancellable>(std::move(ct))
|
||||
, m_impl(s, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation_cancellable<socket_recv_operation_cancellable>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void cancel() noexcept { m_impl.cancel(*this); }
|
||||
|
||||
socket_recv_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
||||
|
||||
#endif
|
|
@ -1,94 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_SOCKET_SEND_OPERATION_HPP_INCLUDED
|
||||
#define CPPCORO_NET_SOCKET_SEND_OPERATION_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
# include <cppcoro/detail/win32_overlapped_operation.hpp>
|
||||
|
||||
namespace cppcoro::net
|
||||
{
|
||||
class socket;
|
||||
|
||||
class socket_send_operation_impl
|
||||
{
|
||||
public:
|
||||
|
||||
socket_send_operation_impl(
|
||||
socket& s,
|
||||
const void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: m_socket(s)
|
||||
, m_buffer(const_cast<void*>(buffer), byteCount)
|
||||
{}
|
||||
|
||||
bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
socket& m_socket;
|
||||
cppcoro::detail::win32::wsabuf m_buffer;
|
||||
|
||||
};
|
||||
|
||||
class socket_send_operation
|
||||
: public cppcoro::detail::win32_overlapped_operation<socket_send_operation>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_send_operation(
|
||||
socket& s,
|
||||
const void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: m_impl(s, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation<socket_send_operation>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
|
||||
socket_send_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
class socket_send_operation_cancellable
|
||||
: public cppcoro::detail::win32_overlapped_operation_cancellable<socket_send_operation_cancellable>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_send_operation_cancellable(
|
||||
socket& s,
|
||||
const void* buffer,
|
||||
std::size_t byteCount,
|
||||
cancellation_token&& ct) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation_cancellable<socket_send_operation_cancellable>(std::move(ct))
|
||||
, m_impl(s, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation_cancellable<socket_send_operation_cancellable>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void cancel() noexcept { return m_impl.cancel(*this); }
|
||||
|
||||
socket_send_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
||||
|
||||
#endif
|
|
@ -1,100 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_NET_SOCKET_SEND_TO_OPERATION_HPP_INCLUDED
|
||||
#define CPPCORO_NET_SOCKET_SEND_TO_OPERATION_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
#include <cppcoro/net/ip_endpoint.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
# include <cppcoro/detail/win32_overlapped_operation.hpp>
|
||||
|
||||
namespace cppcoro::net
|
||||
{
|
||||
class socket;
|
||||
|
||||
class socket_send_to_operation_impl
|
||||
{
|
||||
public:
|
||||
|
||||
socket_send_to_operation_impl(
|
||||
socket& s,
|
||||
const ip_endpoint& destination,
|
||||
const void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: m_socket(s)
|
||||
, m_destination(destination)
|
||||
, m_buffer(const_cast<void*>(buffer), byteCount)
|
||||
{}
|
||||
|
||||
bool try_start(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
void cancel(cppcoro::detail::win32_overlapped_operation_base& operation) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
socket& m_socket;
|
||||
ip_endpoint m_destination;
|
||||
cppcoro::detail::win32::wsabuf m_buffer;
|
||||
|
||||
};
|
||||
|
||||
class socket_send_to_operation
|
||||
: public cppcoro::detail::win32_overlapped_operation<socket_send_to_operation>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_send_to_operation(
|
||||
socket& s,
|
||||
const ip_endpoint& destination,
|
||||
const void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
: m_impl(s, destination, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation<socket_send_to_operation>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
|
||||
socket_send_to_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
class socket_send_to_operation_cancellable
|
||||
: public cppcoro::detail::win32_overlapped_operation_cancellable<socket_send_to_operation_cancellable>
|
||||
{
|
||||
public:
|
||||
|
||||
socket_send_to_operation_cancellable(
|
||||
socket& s,
|
||||
const ip_endpoint& destination,
|
||||
const void* buffer,
|
||||
std::size_t byteCount,
|
||||
cancellation_token&& ct) noexcept
|
||||
: cppcoro::detail::win32_overlapped_operation_cancellable<socket_send_to_operation_cancellable>(std::move(ct))
|
||||
, m_impl(s, destination, buffer, byteCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
friend class cppcoro::detail::win32_overlapped_operation_cancellable<socket_send_to_operation_cancellable>;
|
||||
|
||||
bool try_start() noexcept { return m_impl.try_start(*this); }
|
||||
void cancel() noexcept { return m_impl.cancel(*this); }
|
||||
|
||||
socket_send_to_operation_impl m_impl;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
||||
|
||||
#endif
|
|
@ -1,147 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_ON_SCOPE_EXIT_HPP_INCLUDED
|
||||
#define CPPCORO_ON_SCOPE_EXIT_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <exception>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
template<typename FUNC>
|
||||
class scoped_lambda
|
||||
{
|
||||
public:
|
||||
|
||||
scoped_lambda(FUNC&& func)
|
||||
: m_func(std::forward<FUNC>(func))
|
||||
, m_cancelled(false)
|
||||
{}
|
||||
|
||||
scoped_lambda(const scoped_lambda& other) = delete;
|
||||
|
||||
scoped_lambda(scoped_lambda&& other)
|
||||
: m_func(std::forward<FUNC>(other.m_func))
|
||||
, m_cancelled(other.m_cancelled)
|
||||
{
|
||||
other.cancel();
|
||||
}
|
||||
|
||||
~scoped_lambda()
|
||||
{
|
||||
if (!m_cancelled)
|
||||
{
|
||||
m_func();
|
||||
}
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
m_cancelled = true;
|
||||
}
|
||||
|
||||
void call_now()
|
||||
{
|
||||
m_cancelled = true;
|
||||
m_func();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
FUNC m_func;
|
||||
bool m_cancelled;
|
||||
|
||||
};
|
||||
|
||||
/// A scoped lambda that executes the lambda when the object destructs
|
||||
/// but only if exiting due to an exception (CALL_ON_FAILURE = true) or
|
||||
/// only if not exiting due to an exception (CALL_ON_FAILURE = false).
|
||||
template<typename FUNC, bool CALL_ON_FAILURE>
|
||||
class conditional_scoped_lambda
|
||||
{
|
||||
public:
|
||||
|
||||
conditional_scoped_lambda(FUNC&& func)
|
||||
: m_func(std::forward<FUNC>(func))
|
||||
, m_uncaughtExceptionCount(std::uncaught_exceptions())
|
||||
, m_cancelled(false)
|
||||
{}
|
||||
|
||||
conditional_scoped_lambda(const conditional_scoped_lambda& other) = delete;
|
||||
|
||||
conditional_scoped_lambda(conditional_scoped_lambda&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<FUNC>::value)
|
||||
: m_func(std::forward<FUNC>(other.m_func))
|
||||
, m_uncaughtExceptionCount(other.m_uncaughtExceptionCount)
|
||||
, m_cancelled(other.m_cancelled)
|
||||
{
|
||||
other.cancel();
|
||||
}
|
||||
|
||||
~conditional_scoped_lambda() noexcept(CALL_ON_FAILURE || noexcept(std::declval<FUNC>()()))
|
||||
{
|
||||
if (!m_cancelled && (is_unwinding_due_to_exception() == CALL_ON_FAILURE))
|
||||
{
|
||||
m_func();
|
||||
}
|
||||
}
|
||||
|
||||
void cancel() noexcept
|
||||
{
|
||||
m_cancelled = true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool is_unwinding_due_to_exception() const noexcept
|
||||
{
|
||||
return std::uncaught_exceptions() > m_uncaughtExceptionCount;
|
||||
}
|
||||
|
||||
FUNC m_func;
|
||||
int m_uncaughtExceptionCount;
|
||||
bool m_cancelled;
|
||||
|
||||
};
|
||||
|
||||
/// Returns an object that calls the provided function when it goes out
|
||||
/// of scope either normally or due to an uncaught exception unwinding
|
||||
/// the stack.
|
||||
///
|
||||
/// \param func
|
||||
/// The function to call when the scope exits.
|
||||
/// The function must be noexcept.
|
||||
template<typename FUNC>
|
||||
auto on_scope_exit(FUNC&& func)
|
||||
{
|
||||
return scoped_lambda<FUNC>{ std::forward<FUNC>(func) };
|
||||
}
|
||||
|
||||
/// Returns an object that calls the provided function when it goes out
|
||||
/// of scope due to an uncaught exception unwinding the stack.
|
||||
///
|
||||
/// \param func
|
||||
/// The function to be called if unwinding due to an exception.
|
||||
/// The function must be noexcept.
|
||||
template<typename FUNC>
|
||||
auto on_scope_failure(FUNC&& func)
|
||||
{
|
||||
return conditional_scoped_lambda<FUNC, true>{ std::forward<FUNC>(func) };
|
||||
}
|
||||
|
||||
/// Returns an object that calls the provided function when it goes out
|
||||
/// of scope via normal execution (ie. not unwinding due to an exception).
|
||||
///
|
||||
/// \param func
|
||||
/// The function to call if the scope exits normally.
|
||||
/// The function does not necessarily need to be noexcept.
|
||||
template<typename FUNC>
|
||||
auto on_scope_success(FUNC&& func)
|
||||
{
|
||||
return conditional_scoped_lambda<FUNC, false>{ std::forward<FUNC>(func) };
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,59 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_READ_ONLY_FILE_HPP_INCLUDED
|
||||
#define CPPCORO_READ_ONLY_FILE_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/readable_file.hpp>
|
||||
#include <cppcoro/file_share_mode.hpp>
|
||||
#include <cppcoro/file_buffering_mode.hpp>
|
||||
|
||||
#include <experimental/filesystem>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class read_only_file : public readable_file
|
||||
{
|
||||
public:
|
||||
|
||||
/// Open a file for read-only access.
|
||||
///
|
||||
/// \param ioContext
|
||||
/// The I/O context to use when dispatching I/O completion events.
|
||||
/// When asynchronous read operations on this file complete the
|
||||
/// completion events will be dispatched to an I/O thread associated
|
||||
/// with the I/O context.
|
||||
///
|
||||
/// \param path
|
||||
/// Path of the file to open.
|
||||
///
|
||||
/// \param shareMode
|
||||
/// Specifies the access to be allowed on the file concurrently with this file access.
|
||||
///
|
||||
/// \param bufferingMode
|
||||
/// Specifies the modes/hints to provide to the OS that affects the behaviour
|
||||
/// of its file buffering.
|
||||
///
|
||||
/// \return
|
||||
/// An object that can be used to read from the file.
|
||||
///
|
||||
/// \throw std::system_error
|
||||
/// If the file could not be opened for read.
|
||||
[[nodiscard]]
|
||||
static read_only_file open(
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
file_share_mode shareMode = file_share_mode::read,
|
||||
file_buffering_mode bufferingMode = file_buffering_mode::default_);
|
||||
|
||||
protected:
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
read_only_file(detail::win32::safe_handle&& fileHandle) noexcept;
|
||||
#endif
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,66 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_READ_WRITE_FILE_HPP_INCLUDED
|
||||
#define CPPCORO_READ_WRITE_FILE_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/readable_file.hpp>
|
||||
#include <cppcoro/writable_file.hpp>
|
||||
#include <cppcoro/file_share_mode.hpp>
|
||||
#include <cppcoro/file_buffering_mode.hpp>
|
||||
#include <cppcoro/file_open_mode.hpp>
|
||||
|
||||
#include <experimental/filesystem>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class read_write_file : public readable_file, public writable_file
|
||||
{
|
||||
public:
|
||||
|
||||
/// Open a file for read-write access.
|
||||
///
|
||||
/// \param ioContext
|
||||
/// The I/O context to use when dispatching I/O completion events.
|
||||
/// When asynchronous write operations on this file complete the
|
||||
/// completion events will be dispatched to an I/O thread associated
|
||||
/// with the I/O context.
|
||||
///
|
||||
/// \param pathMode
|
||||
/// Path of the file to open.
|
||||
///
|
||||
/// \param openMode
|
||||
/// Specifies how the file should be opened and how to handle cases
|
||||
/// when the file exists or doesn't exist.
|
||||
///
|
||||
/// \param shareMode
|
||||
/// Specifies the access to be allowed on the file concurrently with this file access.
|
||||
///
|
||||
/// \param bufferingMode
|
||||
/// Specifies the modes/hints to provide to the OS that affects the behaviour
|
||||
/// of its file buffering.
|
||||
///
|
||||
/// \return
|
||||
/// An object that can be used to write to the file.
|
||||
///
|
||||
/// \throw std::system_error
|
||||
/// If the file could not be opened for write.
|
||||
[[nodiscard]]
|
||||
static read_write_file open(
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
file_open_mode openMode = file_open_mode::create_or_open,
|
||||
file_share_mode shareMode = file_share_mode::none,
|
||||
file_buffering_mode bufferingMode = file_buffering_mode::default_);
|
||||
|
||||
protected:
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
read_write_file(detail::win32::safe_handle&& fileHandle) noexcept;
|
||||
#endif
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,65 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_READABLE_FILE_HPP_INCLUDED
|
||||
#define CPPCORO_READABLE_FILE_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/file.hpp>
|
||||
#include <cppcoro/file_read_operation.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class readable_file : virtual public file
|
||||
{
|
||||
public:
|
||||
|
||||
/// Read some data from the file.
|
||||
///
|
||||
/// Reads \a byteCount bytes from the file starting at \a offset
|
||||
/// into the specified \a buffer.
|
||||
///
|
||||
/// \param offset
|
||||
/// The offset within the file to start reading from.
|
||||
/// If the file has been opened using file_buffering_mode::unbuffered
|
||||
/// then the offset must be a multiple of the file-system's sector size.
|
||||
///
|
||||
/// \param buffer
|
||||
/// The buffer to read the file contents into.
|
||||
/// If the file has been opened using file_buffering_mode::unbuffered
|
||||
/// then the address of the start of the buffer must be a multiple of
|
||||
/// the file-system's sector size.
|
||||
///
|
||||
/// \param byteCount
|
||||
/// The number of bytes to read from the file.
|
||||
/// If the file has been opeend using file_buffering_mode::unbuffered
|
||||
/// then the byteCount must be a multiple of the file-system's sector size.
|
||||
///
|
||||
/// \param ct
|
||||
/// An optional cancellation_token that can be used to cancel the
|
||||
/// read operation before it completes.
|
||||
///
|
||||
/// \return
|
||||
/// An object that represents the read-operation.
|
||||
/// This object must be co_await'ed to start the read operation.
|
||||
[[nodiscard]]
|
||||
file_read_operation read(
|
||||
std::uint64_t offset,
|
||||
void* buffer,
|
||||
std::size_t byteCount) const noexcept;
|
||||
[[nodiscard]]
|
||||
file_read_operation_cancellable read(
|
||||
std::uint64_t offset,
|
||||
void* buffer,
|
||||
std::size_t byteCount,
|
||||
cancellation_token ct) const noexcept;
|
||||
|
||||
protected:
|
||||
|
||||
using file::file;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include <cppcoro/generator.hpp>
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
|
@ -39,12 +39,12 @@ namespace cppcoro
|
|||
return recursive_generator<T>{ *this };
|
||||
}
|
||||
|
||||
std::experimental::suspend_always initial_suspend() noexcept
|
||||
std::suspend_always initial_suspend() noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::experimental::suspend_always final_suspend() noexcept
|
||||
std::suspend_always final_suspend() noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
@ -56,13 +56,13 @@ namespace cppcoro
|
|||
|
||||
void return_void() noexcept {}
|
||||
|
||||
std::experimental::suspend_always yield_value(T& value) noexcept
|
||||
std::suspend_always yield_value(T& value) noexcept
|
||||
{
|
||||
m_value = std::addressof(value);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::experimental::suspend_always yield_value(T&& value) noexcept
|
||||
std::suspend_always yield_value(T&& value) noexcept
|
||||
{
|
||||
m_value = std::addressof(value);
|
||||
return {};
|
||||
|
@ -87,7 +87,7 @@ namespace cppcoro
|
|||
return this->m_childPromise == nullptr;
|
||||
}
|
||||
|
||||
void await_suspend(std::experimental::coroutine_handle<promise_type>) noexcept
|
||||
void await_suspend(std::coroutine_handle<promise_type>) noexcept
|
||||
{}
|
||||
|
||||
void await_resume()
|
||||
|
@ -122,11 +122,11 @@ namespace cppcoro
|
|||
|
||||
// Don't allow any use of 'co_await' inside the recursive_generator coroutine.
|
||||
template<typename U>
|
||||
std::experimental::suspend_never await_transform(U&& value) = delete;
|
||||
std::suspend_never await_transform(U&& value) = delete;
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
std::experimental::coroutine_handle<promise_type>::from_promise(*this).destroy();
|
||||
std::coroutine_handle<promise_type>::from_promise(*this).destroy();
|
||||
}
|
||||
|
||||
void throw_if_exception()
|
||||
|
@ -139,7 +139,7 @@ namespace cppcoro
|
|||
|
||||
bool is_complete() noexcept
|
||||
{
|
||||
return std::experimental::coroutine_handle<promise_type>::from_promise(*this).done();
|
||||
return std::coroutine_handle<promise_type>::from_promise(*this).done();
|
||||
}
|
||||
|
||||
T& value() noexcept
|
||||
|
@ -167,7 +167,7 @@ namespace cppcoro
|
|||
|
||||
void resume() noexcept
|
||||
{
|
||||
std::experimental::coroutine_handle<promise_type>::from_promise(*this).resume();
|
||||
std::coroutine_handle<promise_type>::from_promise(*this).resume();
|
||||
}
|
||||
|
||||
std::add_pointer_t<T> m_value;
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_ROUND_ROBIN_SCHEDULER_HPP_INCLUDED
|
||||
#define CPPCORO_ROUND_ROBIN_SCHEDULER_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
#if CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER
|
||||
/// This is a scheduler class that schedules coroutines in a round-robin
|
||||
/// fashion once N coroutines have been scheduled to it.
|
||||
///
|
||||
/// Only supports access from a single thread at a time so
|
||||
///
|
||||
/// This implementation was inspired by Gor Nishanov's CppCon 2018 talk
|
||||
/// about nano-coroutines.
|
||||
///
|
||||
/// The implementation relies on symmetric transfer and noop_coroutine()
|
||||
/// and so only works with a relatively recent version of Clang and does
|
||||
/// not yet work with MSVC.
|
||||
template<size_t N>
|
||||
class round_robin_scheduler
|
||||
{
|
||||
static_assert(
|
||||
N >= 2,
|
||||
"Round robin scheduler must be configured to support at least two coroutines");
|
||||
|
||||
class schedule_operation
|
||||
{
|
||||
public:
|
||||
explicit schedule_operation(round_robin_scheduler& s) noexcept : m_scheduler(s) {}
|
||||
|
||||
bool await_ready() noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::experimental::coroutine_handle<> await_suspend(
|
||||
std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
return m_scheduler.exchange_next(awaitingCoroutine);
|
||||
}
|
||||
|
||||
void await_resume() noexcept {}
|
||||
|
||||
private:
|
||||
round_robin_scheduler& m_scheduler;
|
||||
};
|
||||
|
||||
friend class schedule_operation;
|
||||
|
||||
public:
|
||||
round_robin_scheduler() noexcept
|
||||
: m_index(0)
|
||||
, m_noop(std::experimental::noop_coroutine())
|
||||
{
|
||||
for (size_t i = 0; i < N - 1; ++i)
|
||||
{
|
||||
m_coroutines[i] = m_noop();
|
||||
}
|
||||
}
|
||||
|
||||
~round_robin_scheduler()
|
||||
{
|
||||
// All tasks should have been joined before calling destructor.
|
||||
assert(std::all_of(
|
||||
m_coroutines.begin(),
|
||||
m_coroutines.end(),
|
||||
[&](auto h) { return h == m_noop; }));
|
||||
}
|
||||
|
||||
schedule_operation schedule() noexcept
|
||||
{
|
||||
return schedule_operation{ *this };
|
||||
}
|
||||
|
||||
/// Resume any queued coroutines until there are no more coroutines.
|
||||
void drain() noexcept
|
||||
{
|
||||
size_t countRemaining = N - 1;
|
||||
do
|
||||
{
|
||||
auto nextToResume = exchange_next(m_noop);
|
||||
if (nextToResume != m_noop)
|
||||
{
|
||||
nextToResume.resume();
|
||||
countRemaining = N - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
--countRemaining;
|
||||
}
|
||||
} while (countRemaining > 0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::experimental::coroutine_handle exchange_next(
|
||||
std::experimental::coroutine_handle<> coroutine) noexcept
|
||||
{
|
||||
auto coroutineToResume = std::exchange(
|
||||
m_scheduler.m_coroutines[m_scheduler.m_index],
|
||||
awaitingCoroutine);
|
||||
m_scheduler.m_index = m_scheduler.m_index < (N - 2) ? m_scheduler.m_index + 1 : 0;
|
||||
return coroutineToResume;
|
||||
}
|
||||
|
||||
size_t m_index;
|
||||
const std::experimental::coroutine_handle<> m_noop;
|
||||
std::array<std::experimental::coroutine_handle<>, N - 1> m_coroutines;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
|
@ -15,7 +15,7 @@
|
|||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
|
@ -161,7 +161,7 @@ namespace cppcoro
|
|||
return !TRAITS::precedes(m_lastKnownPublished, m_targetSequence);
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
m_awaitingCoroutine = awaitingCoroutine;
|
||||
m_barrier.add_awaiter(this);
|
||||
|
@ -192,7 +192,7 @@ namespace cppcoro
|
|||
const SEQUENCE m_targetSequence;
|
||||
SEQUENCE m_lastKnownPublished;
|
||||
sequence_barrier_wait_operation_base* m_next;
|
||||
std::experimental::coroutine_handle<> m_awaitingCoroutine;
|
||||
std::coroutine_handle<> m_awaitingCoroutine;
|
||||
std::atomic<bool> m_readyToResume;
|
||||
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ namespace cppcoro
|
|||
{
|
||||
struct shared_task_waiter
|
||||
{
|
||||
std::experimental::coroutine_handle<> m_continuation;
|
||||
std::coroutine_handle<> m_continuation;
|
||||
shared_task_waiter* m_next;
|
||||
};
|
||||
|
||||
|
@ -41,7 +41,7 @@ namespace cppcoro
|
|||
bool await_ready() const noexcept { return false; }
|
||||
|
||||
template<typename PROMISE>
|
||||
void await_suspend(std::experimental::coroutine_handle<PROMISE> h) noexcept
|
||||
void await_suspend(std::coroutine_handle<PROMISE> h) noexcept
|
||||
{
|
||||
shared_task_promise_base& promise = h.promise();
|
||||
|
||||
|
@ -79,7 +79,7 @@ namespace cppcoro
|
|||
, m_exception(nullptr)
|
||||
{}
|
||||
|
||||
std::experimental::suspend_always initial_suspend() noexcept { return {}; }
|
||||
std::suspend_always initial_suspend() noexcept { return {}; }
|
||||
final_awaiter final_suspend() noexcept { return {}; }
|
||||
|
||||
void unhandled_exception() noexcept
|
||||
|
@ -124,7 +124,7 @@ namespace cppcoro
|
|||
/// waiter->m_coroutine will be resumed when the task completes.
|
||||
/// false if the coroutine was already completed and the awaiting
|
||||
/// coroutine can continue without suspending.
|
||||
bool try_await(shared_task_waiter* waiter, std::experimental::coroutine_handle<> coroutine)
|
||||
bool try_await(shared_task_waiter* waiter, std::coroutine_handle<> coroutine)
|
||||
{
|
||||
void* const valueReadyValue = this;
|
||||
void* const notStartedValue = &this->m_waiters;
|
||||
|
@ -304,10 +304,10 @@ namespace cppcoro
|
|||
|
||||
struct awaitable_base
|
||||
{
|
||||
std::experimental::coroutine_handle<promise_type> m_coroutine;
|
||||
std::coroutine_handle<promise_type> m_coroutine;
|
||||
detail::shared_task_waiter m_waiter;
|
||||
|
||||
awaitable_base(std::experimental::coroutine_handle<promise_type> coroutine) noexcept
|
||||
awaitable_base(std::coroutine_handle<promise_type> coroutine) noexcept
|
||||
: m_coroutine(coroutine)
|
||||
{}
|
||||
|
||||
|
@ -316,7 +316,7 @@ namespace cppcoro
|
|||
return !m_coroutine || m_coroutine.promise().is_ready();
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept
|
||||
bool await_suspend(std::coroutine_handle<> awaiter) noexcept
|
||||
{
|
||||
m_waiter.m_continuation = awaiter;
|
||||
return m_coroutine.promise().try_await(&m_waiter, m_coroutine);
|
||||
|
@ -329,7 +329,7 @@ namespace cppcoro
|
|||
: m_coroutine(nullptr)
|
||||
{}
|
||||
|
||||
explicit shared_task(std::experimental::coroutine_handle<promise_type> coroutine)
|
||||
explicit shared_task(std::coroutine_handle<promise_type> coroutine)
|
||||
: m_coroutine(coroutine)
|
||||
{
|
||||
// Don't increment the ref-count here since it has already been
|
||||
|
@ -452,7 +452,7 @@ namespace cppcoro
|
|||
}
|
||||
}
|
||||
|
||||
std::experimental::coroutine_handle<promise_type> m_coroutine;
|
||||
std::coroutine_handle<promise_type> m_coroutine;
|
||||
|
||||
};
|
||||
|
||||
|
@ -480,7 +480,7 @@ namespace cppcoro
|
|||
shared_task<T> shared_task_promise<T>::get_return_object() noexcept
|
||||
{
|
||||
return shared_task<T>{
|
||||
std::experimental::coroutine_handle<shared_task_promise>::from_promise(*this)
|
||||
std::coroutine_handle<shared_task_promise>::from_promise(*this)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -488,14 +488,14 @@ namespace cppcoro
|
|||
shared_task<T&> shared_task_promise<T&>::get_return_object() noexcept
|
||||
{
|
||||
return shared_task<T&>{
|
||||
std::experimental::coroutine_handle<shared_task_promise>::from_promise(*this)
|
||||
std::coroutine_handle<shared_task_promise>::from_promise(*this)
|
||||
};
|
||||
}
|
||||
|
||||
inline shared_task<void> shared_task_promise<void>::get_return_object() noexcept
|
||||
{
|
||||
return shared_task<void>{
|
||||
std::experimental::coroutine_handle<shared_task_promise>::from_promise(*this)
|
||||
std::coroutine_handle<shared_task_promise>::from_promise(*this)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef CPPCORO_SINGLE_CONSUMER_ASYNC_AUTO_RESET_EVENT_HPP_INCLUDED
|
||||
#define CPPCORO_SINGLE_CONSUMER_ASYNC_AUTO_RESET_EVENT_HPP_INCLUDED
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
@ -26,7 +26,7 @@ namespace cppcoro
|
|||
if (oldValue != nullptr && oldValue != this)
|
||||
{
|
||||
// There was a waiting coroutine that we now need to resume.
|
||||
auto handle = *static_cast<std::experimental::coroutine_handle<>*>(oldValue);
|
||||
auto handle = *static_cast<std::coroutine_handle<>*>(oldValue);
|
||||
|
||||
// We also need to transition the state back to 'not set' before
|
||||
// resuming the coroutine. This operation needs to be 'acquire'
|
||||
|
@ -54,7 +54,7 @@ namespace cppcoro
|
|||
|
||||
bool await_ready() const noexcept { return false; }
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
m_awaitingCoroutine = awaitingCoroutine;
|
||||
|
||||
|
@ -82,7 +82,7 @@ namespace cppcoro
|
|||
|
||||
private:
|
||||
const single_consumer_async_auto_reset_event& m_event;
|
||||
std::experimental::coroutine_handle<> m_awaitingCoroutine;
|
||||
std::coroutine_handle<> m_awaitingCoroutine;
|
||||
};
|
||||
|
||||
return awaiter{ *this };
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#define CPPCORO_SINGLE_CONSUMER_EVENT_HPP_INCLUDED
|
||||
|
||||
#include <atomic>
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
|
@ -84,7 +84,7 @@ namespace cppcoro
|
|||
return m_event.is_set();
|
||||
}
|
||||
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaiter)
|
||||
bool await_suspend(std::coroutine_handle<> awaiter)
|
||||
{
|
||||
m_event.m_awaiter = awaiter;
|
||||
|
||||
|
@ -120,7 +120,7 @@ namespace cppcoro
|
|||
// by encoding 'not_set' as 0 (nullptr), 'set' as 1 and
|
||||
// 'not_set_consumer_waiting' as a coroutine handle pointer.
|
||||
std::atomic<state> m_state;
|
||||
std::experimental::coroutine_handle<> m_awaiter;
|
||||
std::coroutine_handle<> m_awaiter;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ namespace cppcoro
|
|||
return m_consumerWaitOperation.await_ready();
|
||||
}
|
||||
|
||||
auto await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
auto await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
return m_consumerWaitOperation.await_suspend(awaitingCoroutine);
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ namespace cppcoro
|
|||
return m_consumerWaitOperation.await_ready();
|
||||
}
|
||||
|
||||
auto await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
auto await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
return m_consumerWaitOperation.await_suspend(awaitingCoroutine);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <thread>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
|
@ -38,7 +38,7 @@ namespace cppcoro
|
|||
schedule_operation(static_thread_pool* tp) noexcept : m_threadPool(tp) {}
|
||||
|
||||
bool await_ready() noexcept { return false; }
|
||||
void await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept;
|
||||
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept;
|
||||
void await_resume() noexcept {}
|
||||
|
||||
private:
|
||||
|
@ -46,7 +46,7 @@ namespace cppcoro
|
|||
friend class static_thread_pool;
|
||||
|
||||
static_thread_pool* m_threadPool;
|
||||
std::experimental::coroutine_handle<> m_awaitingCoroutine;
|
||||
std::coroutine_handle<> m_awaitingCoroutine;
|
||||
schedule_operation* m_next;
|
||||
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <coroutine>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
|
@ -36,8 +36,8 @@ namespace cppcoro
|
|||
|
||||
#if CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER
|
||||
template<typename PROMISE>
|
||||
std::experimental::coroutine_handle<> await_suspend(
|
||||
std::experimental::coroutine_handle<PROMISE> coro) noexcept
|
||||
std::coroutine_handle<> await_suspend(
|
||||
std::coroutine_handle<PROMISE> coro) noexcept
|
||||
{
|
||||
return coro.promise().m_continuation;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ namespace cppcoro
|
|||
// were crashing under x86 optimised builds.
|
||||
template<typename PROMISE>
|
||||
CPPCORO_NOINLINE
|
||||
void await_suspend(std::experimental::coroutine_handle<PROMISE> coroutine)
|
||||
void await_suspend(std::coroutine_handle<PROMISE> coroutine) noexcept
|
||||
{
|
||||
task_promise_base& promise = coroutine.promise();
|
||||
|
||||
|
@ -79,7 +79,7 @@ namespace cppcoro
|
|||
|
||||
auto initial_suspend() noexcept
|
||||
{
|
||||
return std::experimental::suspend_always{};
|
||||
return std::suspend_always{};
|
||||
}
|
||||
|
||||
auto final_suspend() noexcept
|
||||
|
@ -88,12 +88,12 @@ namespace cppcoro
|
|||
}
|
||||
|
||||
#if CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER
|
||||
void set_continuation(std::experimental::coroutine_handle<> continuation) noexcept
|
||||
void set_continuation(std::coroutine_handle<> continuation) noexcept
|
||||
{
|
||||
m_continuation = continuation;
|
||||
}
|
||||
#else
|
||||
bool try_set_continuation(std::experimental::coroutine_handle<> continuation)
|
||||
bool try_set_continuation(std::coroutine_handle<> continuation)
|
||||
{
|
||||
m_continuation = continuation;
|
||||
return !m_state.exchange(true, std::memory_order_acq_rel);
|
||||
|
@ -102,7 +102,7 @@ namespace cppcoro
|
|||
|
||||
private:
|
||||
|
||||
std::experimental::coroutine_handle<> m_continuation;
|
||||
std::coroutine_handle<> m_continuation;
|
||||
|
||||
#if !CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER
|
||||
// Initially false. Set to true when either a continuation is registered
|
||||
|
@ -292,9 +292,9 @@ namespace cppcoro
|
|||
|
||||
struct awaitable_base
|
||||
{
|
||||
std::experimental::coroutine_handle<promise_type> m_coroutine;
|
||||
std::coroutine_handle<promise_type> m_coroutine;
|
||||
|
||||
awaitable_base(std::experimental::coroutine_handle<promise_type> coroutine) noexcept
|
||||
awaitable_base(std::coroutine_handle<promise_type> coroutine) noexcept
|
||||
: m_coroutine(coroutine)
|
||||
{}
|
||||
|
||||
|
@ -304,14 +304,14 @@ namespace cppcoro
|
|||
}
|
||||
|
||||
#if CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER
|
||||
std::experimental::coroutine_handle<> await_suspend(
|
||||
std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
std::coroutine_handle<> await_suspend(
|
||||
std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
m_coroutine.promise().set_continuation(awaitingCoroutine);
|
||||
return m_coroutine;
|
||||
}
|
||||
#else
|
||||
bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
bool await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
// NOTE: We are using the bool-returning version of await_suspend() here
|
||||
// to work around a potential stack-overflow issue if a coroutine
|
||||
|
@ -341,7 +341,7 @@ namespace cppcoro
|
|||
: m_coroutine(nullptr)
|
||||
{}
|
||||
|
||||
explicit task(std::experimental::coroutine_handle<promise_type> coroutine)
|
||||
explicit task(std::coroutine_handle<promise_type> coroutine)
|
||||
: m_coroutine(coroutine)
|
||||
{}
|
||||
|
||||
|
@ -446,7 +446,7 @@ namespace cppcoro
|
|||
|
||||
private:
|
||||
|
||||
std::experimental::coroutine_handle<promise_type> m_coroutine;
|
||||
std::coroutine_handle<promise_type> m_coroutine;
|
||||
|
||||
};
|
||||
|
||||
|
@ -455,18 +455,18 @@ namespace cppcoro
|
|||
template<typename T>
|
||||
task<T> task_promise<T>::get_return_object() noexcept
|
||||
{
|
||||
return task<T>{ std::experimental::coroutine_handle<task_promise>::from_promise(*this) };
|
||||
return task<T>{ std::coroutine_handle<task_promise>::from_promise(*this) };
|
||||
}
|
||||
|
||||
inline task<void> task_promise<void>::get_return_object() noexcept
|
||||
{
|
||||
return task<void>{ std::experimental::coroutine_handle<task_promise>::from_promise(*this) };
|
||||
return task<void>{ std::coroutine_handle<task_promise>::from_promise(*this) };
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
task<T&> task_promise<T&>::get_return_object() noexcept
|
||||
{
|
||||
return task<T&>{ std::experimental::coroutine_handle<task_promise>::from_promise(*this) };
|
||||
return task<T&>{ std::coroutine_handle<task_promise>::from_promise(*this) };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_WRITABLE_FILE_HPP_INCLUDED
|
||||
#define CPPCORO_WRITABLE_FILE_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/file.hpp>
|
||||
#include <cppcoro/file_write_operation.hpp>
|
||||
#include <cppcoro/cancellation_token.hpp>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class writable_file : virtual public file
|
||||
{
|
||||
public:
|
||||
|
||||
/// Set the size of the file.
|
||||
///
|
||||
/// \param fileSize
|
||||
/// The new size of the file in bytes.
|
||||
void set_size(std::uint64_t fileSize);
|
||||
|
||||
/// Write some data to the file.
|
||||
///
|
||||
/// Writes \a byteCount bytes from the file starting at \a offset
|
||||
/// into the specified \a buffer.
|
||||
///
|
||||
/// \param offset
|
||||
/// The offset within the file to start writing from.
|
||||
/// If the file has been opened using file_buffering_mode::unbuffered
|
||||
/// then the offset must be a multiple of the file-system's sector size.
|
||||
///
|
||||
/// \param buffer
|
||||
/// The buffer containing the data to be written to the file.
|
||||
/// If the file has been opened using file_buffering_mode::unbuffered
|
||||
/// then the address of the start of the buffer must be a multiple of
|
||||
/// the file-system's sector size.
|
||||
///
|
||||
/// \param byteCount
|
||||
/// The number of bytes to write to the file.
|
||||
/// If the file has been opeend using file_buffering_mode::unbuffered
|
||||
/// then the byteCount must be a multiple of the file-system's sector size.
|
||||
///
|
||||
/// \param ct
|
||||
/// An optional cancellation_token that can be used to cancel the
|
||||
/// write operation before it completes.
|
||||
///
|
||||
/// \return
|
||||
/// An object that represents the write operation.
|
||||
/// This object must be co_await'ed to start the write operation.
|
||||
[[nodiscard]]
|
||||
file_write_operation write(
|
||||
std::uint64_t offset,
|
||||
const void* buffer,
|
||||
std::size_t byteCount) noexcept;
|
||||
[[nodiscard]]
|
||||
file_write_operation_cancellable write(
|
||||
std::uint64_t offset,
|
||||
const void* buffer,
|
||||
std::size_t byteCount,
|
||||
cancellation_token ct) noexcept;
|
||||
|
||||
protected:
|
||||
|
||||
using file::file;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,65 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_WRITE_ONLY_FILE_HPP_INCLUDED
|
||||
#define CPPCORO_WRITE_ONLY_FILE_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/writable_file.hpp>
|
||||
#include <cppcoro/file_share_mode.hpp>
|
||||
#include <cppcoro/file_buffering_mode.hpp>
|
||||
#include <cppcoro/file_open_mode.hpp>
|
||||
|
||||
#include <experimental/filesystem>
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
class write_only_file : public writable_file
|
||||
{
|
||||
public:
|
||||
|
||||
/// Open a file for write-only access.
|
||||
///
|
||||
/// \param ioContext
|
||||
/// The I/O context to use when dispatching I/O completion events.
|
||||
/// When asynchronous write operations on this file complete the
|
||||
/// completion events will be dispatched to an I/O thread associated
|
||||
/// with the I/O context.
|
||||
///
|
||||
/// \param pathMode
|
||||
/// Path of the file to open.
|
||||
///
|
||||
/// \param openMode
|
||||
/// Specifies how the file should be opened and how to handle cases
|
||||
/// when the file exists or doesn't exist.
|
||||
///
|
||||
/// \param shareMode
|
||||
/// Specifies the access to be allowed on the file concurrently with this file access.
|
||||
///
|
||||
/// \param bufferingMode
|
||||
/// Specifies the modes/hints to provide to the OS that affects the behaviour
|
||||
/// of its file buffering.
|
||||
///
|
||||
/// \return
|
||||
/// An object that can be used to write to the file.
|
||||
///
|
||||
/// \throw std::system_error
|
||||
/// If the file could not be opened for write.
|
||||
[[nodiscard]]
|
||||
static write_only_file open(
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
file_open_mode openMode = file_open_mode::create_or_open,
|
||||
file_share_mode shareMode = file_share_mode::none,
|
||||
file_buffering_mode bufferingMode = file_buffering_mode::default_);
|
||||
|
||||
protected:
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
write_only_file(detail::win32::safe_handle&& fileHandle) noexcept;
|
||||
#endif
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
7
init.sh
7
init.sh
|
@ -1,7 +0,0 @@
|
|||
#!/bin/sh
|
||||
export CAKE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/tools/cake/src/run.py"
|
||||
|
||||
function cake()
|
||||
{
|
||||
python2.7 "$CAKE" "$@"
|
||||
}
|
|
@ -241,7 +241,7 @@ cppcoro::async_auto_reset_event_operation::async_auto_reset_event_operation(
|
|||
{}
|
||||
|
||||
bool cppcoro::async_auto_reset_event_operation::await_suspend(
|
||||
std::experimental::coroutine_handle<> awaiter) noexcept
|
||||
std::coroutine_handle<> awaiter) noexcept
|
||||
{
|
||||
m_awaiter = awaiter;
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ bool cppcoro::async_manual_reset_event_operation::await_ready() const noexcept
|
|||
}
|
||||
|
||||
bool cppcoro::async_manual_reset_event_operation::await_suspend(
|
||||
std::experimental::coroutine_handle<> awaiter) noexcept
|
||||
std::coroutine_handle<> awaiter) noexcept
|
||||
{
|
||||
m_awaiter = awaiter;
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ void cppcoro::async_mutex::unlock()
|
|||
waitersHead->m_awaiter.resume();
|
||||
}
|
||||
|
||||
bool cppcoro::async_mutex_lock_operation::await_suspend(std::experimental::coroutine_handle<> awaiter) noexcept
|
||||
bool cppcoro::async_mutex_lock_operation::await_suspend(std::coroutine_handle<> awaiter) noexcept
|
||||
{
|
||||
m_awaiter = awaiter;
|
||||
|
||||
|
|
182
lib/build.cake
182
lib/build.cake
|
@ -1,182 +0,0 @@
|
|||
###############################################################################
|
||||
# Copyright Lewis Baker
|
||||
# Licenced under MIT license. See LICENSE.txt for details.
|
||||
###############################################################################
|
||||
|
||||
import cake.path
|
||||
|
||||
from cake.tools import compiler, script, env, project, variant
|
||||
|
||||
includes = cake.path.join(env.expand('${CPPCORO}'), 'include', 'cppcoro', [
|
||||
'awaitable_traits.hpp',
|
||||
'is_awaitable.hpp',
|
||||
'async_auto_reset_event.hpp',
|
||||
'async_manual_reset_event.hpp',
|
||||
'async_generator.hpp',
|
||||
'async_mutex.hpp',
|
||||
'async_latch.hpp',
|
||||
'async_scope.hpp',
|
||||
'broken_promise.hpp',
|
||||
'cancellation_registration.hpp',
|
||||
'cancellation_source.hpp',
|
||||
'cancellation_token.hpp',
|
||||
'task.hpp',
|
||||
'sequence_barrier.hpp',
|
||||
'sequence_traits.hpp',
|
||||
'single_producer_sequencer.hpp',
|
||||
'multi_producer_sequencer.hpp',
|
||||
'shared_task.hpp',
|
||||
'single_consumer_event.hpp',
|
||||
'single_consumer_async_auto_reset_event.hpp',
|
||||
'sync_wait.hpp',
|
||||
'task.hpp',
|
||||
'io_service.hpp',
|
||||
'config.hpp',
|
||||
'on_scope_exit.hpp',
|
||||
'file_share_mode.hpp',
|
||||
'file_open_mode.hpp',
|
||||
'file_buffering_mode.hpp',
|
||||
'file.hpp',
|
||||
'fmap.hpp',
|
||||
'when_all.hpp',
|
||||
'when_all_ready.hpp',
|
||||
'resume_on.hpp',
|
||||
'schedule_on.hpp',
|
||||
'generator.hpp',
|
||||
'readable_file.hpp',
|
||||
'recursive_generator.hpp',
|
||||
'writable_file.hpp',
|
||||
'read_only_file.hpp',
|
||||
'write_only_file.hpp',
|
||||
'read_write_file.hpp',
|
||||
'file_read_operation.hpp',
|
||||
'file_write_operation.hpp',
|
||||
'static_thread_pool.hpp',
|
||||
])
|
||||
|
||||
netIncludes = cake.path.join(env.expand('${CPPCORO}'), 'include', 'cppcoro', 'net', [
|
||||
'ip_address.hpp',
|
||||
'ip_endpoint.hpp',
|
||||
'ipv4_address.hpp',
|
||||
'ipv4_endpoint.hpp',
|
||||
'ipv6_address.hpp',
|
||||
'ipv6_endpoint.hpp',
|
||||
'socket.hpp',
|
||||
])
|
||||
|
||||
detailIncludes = cake.path.join(env.expand('${CPPCORO}'), 'include', 'cppcoro', 'detail', [
|
||||
'void_value.hpp',
|
||||
'when_all_ready_awaitable.hpp',
|
||||
'when_all_counter.hpp',
|
||||
'when_all_task.hpp',
|
||||
'get_awaiter.hpp',
|
||||
'is_awaiter.hpp',
|
||||
'any.hpp',
|
||||
'sync_wait_task.hpp',
|
||||
'unwrap_reference.hpp',
|
||||
'lightweight_manual_reset_event.hpp',
|
||||
])
|
||||
|
||||
privateHeaders = script.cwd([
|
||||
'cancellation_state.hpp',
|
||||
'socket_helpers.hpp',
|
||||
'auto_reset_event.hpp',
|
||||
'spin_wait.hpp',
|
||||
'spin_mutex.hpp',
|
||||
])
|
||||
|
||||
sources = script.cwd([
|
||||
'async_auto_reset_event.cpp',
|
||||
'async_manual_reset_event.cpp',
|
||||
'async_mutex.cpp',
|
||||
'cancellation_state.cpp',
|
||||
'cancellation_token.cpp',
|
||||
'cancellation_source.cpp',
|
||||
'cancellation_registration.cpp',
|
||||
'lightweight_manual_reset_event.cpp',
|
||||
'ip_address.cpp',
|
||||
'ip_endpoint.cpp',
|
||||
'ipv4_address.cpp',
|
||||
'ipv4_endpoint.cpp',
|
||||
'ipv6_address.cpp',
|
||||
'ipv6_endpoint.cpp',
|
||||
'static_thread_pool.cpp',
|
||||
'auto_reset_event.cpp',
|
||||
'spin_wait.cpp',
|
||||
'spin_mutex.cpp',
|
||||
])
|
||||
|
||||
extras = script.cwd([
|
||||
'build.cake',
|
||||
'use.cake',
|
||||
])
|
||||
|
||||
if variant.platform == "windows":
|
||||
detailIncludes.extend(cake.path.join(env.expand('${CPPCORO}'), 'include', 'cppcoro', 'detail', [
|
||||
'win32.hpp',
|
||||
'win32_overlapped_operation.hpp',
|
||||
]))
|
||||
netIncludes.extend(cake.path.join(env.expand('${CPPCORO}'), 'include', 'cppcoro', 'net', [
|
||||
'socket.hpp',
|
||||
'socket_accept_operation.hpp',
|
||||
'socket_connect_operation.hpp',
|
||||
'socket_disconnect_operation.hpp',
|
||||
'socket_recv_operation.hpp',
|
||||
'socket_recv_from_operation.hpp',
|
||||
'socket_send_operation.hpp',
|
||||
'socket_send_to_operation.hpp',
|
||||
]))
|
||||
sources.extend(script.cwd([
|
||||
'win32.cpp',
|
||||
'io_service.cpp',
|
||||
'file.cpp',
|
||||
'readable_file.cpp',
|
||||
'writable_file.cpp',
|
||||
'read_only_file.cpp',
|
||||
'write_only_file.cpp',
|
||||
'read_write_file.cpp',
|
||||
'file_read_operation.cpp',
|
||||
'file_write_operation.cpp',
|
||||
'socket_helpers.cpp',
|
||||
'socket.cpp',
|
||||
'socket_accept_operation.cpp',
|
||||
'socket_connect_operation.cpp',
|
||||
'socket_disconnect_operation.cpp',
|
||||
'socket_send_operation.cpp',
|
||||
'socket_send_to_operation.cpp',
|
||||
'socket_recv_operation.cpp',
|
||||
'socket_recv_from_operation.cpp',
|
||||
]))
|
||||
|
||||
buildDir = env.expand('${CPPCORO_BUILD}')
|
||||
|
||||
compiler.addIncludePath(env.expand('${CPPCORO}/include'))
|
||||
|
||||
objects = compiler.objects(
|
||||
targetDir=env.expand('${CPPCORO_BUILD}/obj'),
|
||||
sources=sources,
|
||||
)
|
||||
|
||||
lib = compiler.library(
|
||||
target=env.expand('${CPPCORO_LIB}/cppcoro'),
|
||||
sources=objects,
|
||||
)
|
||||
|
||||
vcproj = project.project(
|
||||
target=env.expand('${CPPCORO_PROJECT}/cppcoro'),
|
||||
items={
|
||||
'Include': {
|
||||
'Detail': detailIncludes,
|
||||
'Net': netIncludes,
|
||||
'': includes,
|
||||
},
|
||||
'Source': sources + privateHeaders,
|
||||
'': extras
|
||||
},
|
||||
output=lib,
|
||||
)
|
||||
|
||||
script.setResult(
|
||||
project=vcproj,
|
||||
library=lib,
|
||||
)
|
168
lib/file.cpp
168
lib/file.cpp
|
@ -1,168 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/file.hpp>
|
||||
#include <cppcoro/io_service.hpp>
|
||||
|
||||
#include <system_error>
|
||||
#include <cassert>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
cppcoro::file::~file()
|
||||
{}
|
||||
|
||||
std::uint64_t cppcoro::file::size() const
|
||||
{
|
||||
#if CPPCORO_OS_WINNT
|
||||
LARGE_INTEGER size;
|
||||
BOOL ok = ::GetFileSizeEx(m_fileHandle.handle(), &size);
|
||||
if (!ok)
|
||||
{
|
||||
DWORD errorCode = ::GetLastError();
|
||||
throw std::system_error
|
||||
{
|
||||
static_cast<int>(errorCode),
|
||||
std::system_category(),
|
||||
"error getting file size: GetFileSizeEx"
|
||||
};
|
||||
}
|
||||
|
||||
return size.QuadPart;
|
||||
#endif
|
||||
}
|
||||
|
||||
cppcoro::file::file(detail::win32::safe_handle&& fileHandle) noexcept
|
||||
: m_fileHandle(std::move(fileHandle))
|
||||
{
|
||||
}
|
||||
|
||||
cppcoro::detail::win32::safe_handle cppcoro::file::open(
|
||||
detail::win32::dword_t fileAccess,
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
file_open_mode openMode,
|
||||
file_share_mode shareMode,
|
||||
file_buffering_mode bufferingMode)
|
||||
{
|
||||
DWORD flags = FILE_FLAG_OVERLAPPED;
|
||||
if ((bufferingMode & file_buffering_mode::random_access) == file_buffering_mode::random_access)
|
||||
{
|
||||
flags |= FILE_FLAG_RANDOM_ACCESS;
|
||||
}
|
||||
if ((bufferingMode & file_buffering_mode::sequential) == file_buffering_mode::sequential)
|
||||
{
|
||||
flags |= FILE_FLAG_SEQUENTIAL_SCAN;
|
||||
}
|
||||
if ((bufferingMode & file_buffering_mode::write_through) == file_buffering_mode::write_through)
|
||||
{
|
||||
flags |= FILE_FLAG_WRITE_THROUGH;
|
||||
}
|
||||
if ((bufferingMode & file_buffering_mode::temporary) == file_buffering_mode::temporary)
|
||||
{
|
||||
flags |= FILE_ATTRIBUTE_TEMPORARY;
|
||||
}
|
||||
if ((bufferingMode & file_buffering_mode::unbuffered) == file_buffering_mode::unbuffered)
|
||||
{
|
||||
flags |= FILE_FLAG_NO_BUFFERING;
|
||||
}
|
||||
|
||||
DWORD shareFlags = 0;
|
||||
if ((shareMode & file_share_mode::read) == file_share_mode::read)
|
||||
{
|
||||
shareFlags |= FILE_SHARE_READ;
|
||||
}
|
||||
if ((shareMode & file_share_mode::write) == file_share_mode::write)
|
||||
{
|
||||
shareFlags |= FILE_SHARE_WRITE;
|
||||
}
|
||||
if ((shareMode & file_share_mode::delete_) == file_share_mode::delete_)
|
||||
{
|
||||
shareFlags |= FILE_SHARE_DELETE;
|
||||
}
|
||||
|
||||
DWORD creationDisposition = 0;
|
||||
switch (openMode)
|
||||
{
|
||||
case file_open_mode::create_or_open:
|
||||
creationDisposition = OPEN_ALWAYS;
|
||||
break;
|
||||
case file_open_mode::create_always:
|
||||
creationDisposition = CREATE_ALWAYS;
|
||||
break;
|
||||
case file_open_mode::create_new:
|
||||
creationDisposition = CREATE_NEW;
|
||||
break;
|
||||
case file_open_mode::open_existing:
|
||||
creationDisposition = OPEN_EXISTING;
|
||||
break;
|
||||
case file_open_mode::truncate_existing:
|
||||
creationDisposition = TRUNCATE_EXISTING;
|
||||
break;
|
||||
}
|
||||
|
||||
// Open the file
|
||||
detail::win32::safe_handle fileHandle(
|
||||
::CreateFileW(
|
||||
path.wstring().c_str(),
|
||||
fileAccess,
|
||||
shareFlags,
|
||||
nullptr,
|
||||
creationDisposition,
|
||||
flags,
|
||||
nullptr));
|
||||
if (fileHandle.handle() == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
const DWORD errorCode = ::GetLastError();
|
||||
throw std::system_error
|
||||
{
|
||||
static_cast<int>(errorCode),
|
||||
std::system_category(),
|
||||
"error opening file: CreateFileW"
|
||||
};
|
||||
}
|
||||
|
||||
// Associate with the I/O service's completion port.
|
||||
const HANDLE result = ::CreateIoCompletionPort(
|
||||
fileHandle.handle(),
|
||||
ioService.native_iocp_handle(),
|
||||
0,
|
||||
0);
|
||||
if (result == nullptr)
|
||||
{
|
||||
const DWORD errorCode = ::GetLastError();
|
||||
throw std::system_error
|
||||
{
|
||||
static_cast<int>(errorCode),
|
||||
std::system_category(),
|
||||
"error opening file: CreateIoCompletionPort"
|
||||
};
|
||||
}
|
||||
|
||||
// Configure I/O operations to avoid dispatching a completion event
|
||||
// to the I/O service if the operation completes synchronously.
|
||||
// This avoids unnecessary suspension/resuption of the awaiting coroutine.
|
||||
const BOOL ok = ::SetFileCompletionNotificationModes(
|
||||
fileHandle.handle(),
|
||||
FILE_SKIP_COMPLETION_PORT_ON_SUCCESS |
|
||||
FILE_SKIP_SET_EVENT_ON_HANDLE);
|
||||
if (!ok)
|
||||
{
|
||||
const DWORD errorCode = ::GetLastError();
|
||||
throw std::system_error
|
||||
{
|
||||
static_cast<int>(errorCode),
|
||||
std::system_category(),
|
||||
"error opening file: SetFileCompletionNotificationModes"
|
||||
};
|
||||
}
|
||||
|
||||
return std::move(fileHandle);
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/file_read_operation.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <Windows.h>
|
||||
|
||||
bool cppcoro::file_read_operation_impl::try_start(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
const DWORD numberOfBytesToRead =
|
||||
m_byteCount <= 0xFFFFFFFF ?
|
||||
static_cast<DWORD>(m_byteCount) : DWORD(0xFFFFFFFF);
|
||||
|
||||
DWORD numberOfBytesRead = 0;
|
||||
BOOL ok = ::ReadFile(
|
||||
m_fileHandle,
|
||||
m_buffer,
|
||||
numberOfBytesToRead,
|
||||
&numberOfBytesRead,
|
||||
operation.get_overlapped());
|
||||
const DWORD errorCode = ok ? ERROR_SUCCESS : ::GetLastError();
|
||||
if (errorCode != ERROR_IO_PENDING)
|
||||
{
|
||||
// Completed synchronously.
|
||||
//
|
||||
// We are assuming that the file-handle has been set to the
|
||||
// mode where synchronous completions do not post a completion
|
||||
// event to the I/O completion port and thus can return without
|
||||
// suspending here.
|
||||
|
||||
operation.m_errorCode = errorCode;
|
||||
operation.m_numberOfBytesTransferred = numberOfBytesRead;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cppcoro::file_read_operation_impl::cancel(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
(void)::CancelIoEx(m_fileHandle, operation.get_overlapped());
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
|
@ -1,53 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/file_write_operation.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <Windows.h>
|
||||
|
||||
bool cppcoro::file_write_operation_impl::try_start(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
const DWORD numberOfBytesToWrite =
|
||||
m_byteCount <= 0xFFFFFFFF ?
|
||||
static_cast<DWORD>(m_byteCount) : DWORD(0xFFFFFFFF);
|
||||
|
||||
DWORD numberOfBytesWritten = 0;
|
||||
BOOL ok = ::WriteFile(
|
||||
m_fileHandle,
|
||||
m_buffer,
|
||||
numberOfBytesToWrite,
|
||||
&numberOfBytesWritten,
|
||||
operation.get_overlapped());
|
||||
const DWORD errorCode = ok ? ERROR_SUCCESS : ::GetLastError();
|
||||
if (errorCode != ERROR_IO_PENDING)
|
||||
{
|
||||
// Completed synchronously.
|
||||
//
|
||||
// We are assuming that the file-handle has been set to the
|
||||
// mode where synchronous completions do not post a completion
|
||||
// event to the I/O completion port and thus can return without
|
||||
// suspending here.
|
||||
|
||||
operation.m_errorCode = errorCode;
|
||||
operation.m_numberOfBytesTransferred = numberOfBytesWritten;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cppcoro::file_write_operation_impl::cancel(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
(void)::CancelIoEx(m_fileHandle, operation.get_overlapped());
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
1020
lib/io_service.cpp
1020
lib/io_service.cpp
File diff suppressed because it is too large
Load Diff
|
@ -1,27 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/ip_address.hpp>
|
||||
|
||||
std::string cppcoro::net::ip_address::to_string() const
|
||||
{
|
||||
return is_ipv4() ? m_ipv4.to_string() : m_ipv6.to_string();
|
||||
}
|
||||
|
||||
std::optional<cppcoro::net::ip_address>
|
||||
cppcoro::net::ip_address::from_string(std::string_view string) noexcept
|
||||
{
|
||||
if (auto ipv4 = ipv4_address::from_string(string); ipv4)
|
||||
{
|
||||
return *ipv4;
|
||||
}
|
||||
|
||||
if (auto ipv6 = ipv6_address::from_string(string); ipv6)
|
||||
{
|
||||
return *ipv6;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/ip_endpoint.hpp>
|
||||
|
||||
std::string cppcoro::net::ip_endpoint::to_string() const
|
||||
{
|
||||
return is_ipv4() ? m_ipv4.to_string() : m_ipv6.to_string();
|
||||
}
|
||||
|
||||
std::optional<cppcoro::net::ip_endpoint>
|
||||
cppcoro::net::ip_endpoint::from_string(std::string_view string) noexcept
|
||||
{
|
||||
if (auto ipv4 = ipv4_endpoint::from_string(string); ipv4)
|
||||
{
|
||||
return *ipv4;
|
||||
}
|
||||
|
||||
if (auto ipv6 = ipv6_endpoint::from_string(string); ipv6)
|
||||
{
|
||||
return *ipv6;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/ipv4_address.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace local
|
||||
{
|
||||
constexpr bool is_digit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
constexpr std::uint8_t digit_value(char c)
|
||||
{
|
||||
return static_cast<std::uint8_t>(c - '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<cppcoro::net::ipv4_address>
|
||||
cppcoro::net::ipv4_address::from_string(std::string_view string) noexcept
|
||||
{
|
||||
if (string.empty()) return std::nullopt;
|
||||
|
||||
if (!local::is_digit(string[0]))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto length = string.length();
|
||||
|
||||
std::uint8_t partValues[4];
|
||||
|
||||
if (string[0] == '0' && length > 1)
|
||||
{
|
||||
if (local::is_digit(string[1]))
|
||||
{
|
||||
// Octal format (not supported)
|
||||
return std::nullopt;
|
||||
}
|
||||
else if (string[1] == 'x')
|
||||
{
|
||||
// Hexadecimal format (not supported)
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the first integer.
|
||||
// Could be a single 32-bit integer or first integer in a dotted decimal string.
|
||||
|
||||
std::size_t pos = 0;
|
||||
|
||||
{
|
||||
constexpr std::uint32_t maxValue = 0xFFFFFFFFu / 10;
|
||||
constexpr std::uint32_t maxDigit = 0xFFFFFFFFu % 10;
|
||||
|
||||
std::uint32_t partValue = local::digit_value(string[pos]);
|
||||
++pos;
|
||||
|
||||
while (pos < length && local::is_digit(string[pos]))
|
||||
{
|
||||
const auto digitValue = local::digit_value(string[pos]);
|
||||
++pos;
|
||||
|
||||
// Check if this digit would overflow the 32-bit integer
|
||||
if (partValue > maxValue || (partValue == maxValue && digitValue > maxDigit))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
partValue = (partValue * 10) + digitValue;
|
||||
}
|
||||
|
||||
if (pos == length)
|
||||
{
|
||||
// A single-integer string
|
||||
return ipv4_address{ partValue };
|
||||
}
|
||||
else if (partValue > 255)
|
||||
{
|
||||
// Not a valid first component of dotted decimal
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
partValues[0] = static_cast<std::uint8_t>(partValue);
|
||||
}
|
||||
|
||||
for (int part = 1; part < 4; ++part)
|
||||
{
|
||||
if ((pos + 1) >= length || string[pos] != '.' || !local::is_digit(string[pos + 1]))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Skip the '.'
|
||||
++pos;
|
||||
|
||||
// Check for an octal format (not yet supported)
|
||||
const bool isPartOctal =
|
||||
(pos + 1) < length &&
|
||||
string[pos] == '0' &&
|
||||
local::is_digit(string[pos + 1]);
|
||||
if (isPartOctal)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::uint32_t partValue = local::digit_value(string[pos]);
|
||||
++pos;
|
||||
if (pos < length && local::is_digit(string[pos]))
|
||||
{
|
||||
partValue = (partValue * 10) + local::digit_value(string[pos]);
|
||||
++pos;
|
||||
if (pos < length && local::is_digit(string[pos]))
|
||||
{
|
||||
partValue = (partValue * 10) + local::digit_value(string[pos]);
|
||||
if (partValue > 255)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
partValues[part] = static_cast<std::uint8_t>(partValue);
|
||||
}
|
||||
|
||||
if (pos < length)
|
||||
{
|
||||
// Extra chars after end of a valid IPv4 string
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return ipv4_address{ partValues };
|
||||
}
|
||||
|
||||
std::string cppcoro::net::ipv4_address::to_string() const
|
||||
{
|
||||
// Buffer is large enough to hold larges ip address
|
||||
// "xxx.xxx.xxx.xxx"
|
||||
char buffer[15];
|
||||
|
||||
char* c = &buffer[0];
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
*c++ = '.';
|
||||
}
|
||||
|
||||
if (m_bytes[i] >= 100)
|
||||
{
|
||||
*c++ = '0' + (m_bytes[i] / 100);
|
||||
*c++ = '0' + (m_bytes[i] % 100) / 10;
|
||||
*c++ = '0' + (m_bytes[i] % 10);
|
||||
}
|
||||
else if (m_bytes[i] >= 10)
|
||||
{
|
||||
*c++ = '0' + (m_bytes[i] / 10);
|
||||
*c++ = '0' + (m_bytes[i] % 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
*c++ = '0' + m_bytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return std::string{ &buffer[0], c };
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Kt C++ Library
|
||||
// Copyright (c) 2015 Lewis Baker
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/ipv4_endpoint.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace local
|
||||
{
|
||||
bool is_digit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
std::uint8_t digit_value(char c)
|
||||
{
|
||||
return static_cast<std::uint8_t>(c - '0');
|
||||
}
|
||||
|
||||
std::optional<std::uint16_t> parse_port(std::string_view string)
|
||||
{
|
||||
if (string.empty()) return std::nullopt;
|
||||
|
||||
std::uint32_t value = 0;
|
||||
for (auto c : string)
|
||||
{
|
||||
if (!is_digit(c)) return std::nullopt;
|
||||
value = value * 10 + digit_value(c);
|
||||
if (value > 0xFFFFu) return std::nullopt;
|
||||
}
|
||||
|
||||
return static_cast<std::uint16_t>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string cppcoro::net::ipv4_endpoint::to_string() const
|
||||
{
|
||||
auto s = m_address.to_string();
|
||||
s.push_back(':');
|
||||
s.append(std::to_string(m_port));
|
||||
return s;
|
||||
}
|
||||
|
||||
std::optional<cppcoro::net::ipv4_endpoint>
|
||||
cppcoro::net::ipv4_endpoint::from_string(std::string_view string) noexcept
|
||||
{
|
||||
auto colonPos = string.find(':');
|
||||
if (colonPos == std::string_view::npos)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto address = ipv4_address::from_string(string.substr(0, colonPos));
|
||||
if (!address)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto port = local::parse_port(string.substr(colonPos + 1));
|
||||
if (!port)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return ipv4_endpoint{ *address, *port };
|
||||
}
|
|
@ -1,362 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/ipv6_address.hpp>
|
||||
#include <cppcoro/config.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace local
|
||||
{
|
||||
constexpr bool is_digit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
constexpr std::uint8_t digit_value(char c)
|
||||
{
|
||||
return static_cast<std::uint8_t>(c - '0');
|
||||
}
|
||||
|
||||
std::optional<std::uint8_t> try_parse_hex_digit(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
return static_cast<std::uint8_t>(c - '0');
|
||||
}
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
{
|
||||
return static_cast<std::uint8_t>(c - 'a' + 10);
|
||||
}
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
{
|
||||
return static_cast<std::uint8_t>(c - 'A' + 10);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
char hex_char(std::uint8_t value)
|
||||
{
|
||||
return value < 10 ?
|
||||
static_cast<char>('0' + value) :
|
||||
static_cast<char>('a' + value - 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<cppcoro::net::ipv6_address>
|
||||
cppcoro::net::ipv6_address::from_string(std::string_view string) noexcept
|
||||
{
|
||||
// Longest possible valid IPv6 string is
|
||||
// "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:nnn.nnn.nnn.nnn"
|
||||
constexpr std::size_t maxLength = 45;
|
||||
|
||||
if (string.empty() || string.length() > maxLength)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::size_t length = string.length();
|
||||
|
||||
std::optional<int> doubleColonPos;
|
||||
|
||||
std::size_t pos = 0;
|
||||
|
||||
if (length >= 2 && string[0] == ':' && string[1] == ':')
|
||||
{
|
||||
doubleColonPos = 0;
|
||||
pos = 2;
|
||||
}
|
||||
|
||||
int partCount = 0;
|
||||
std::uint16_t parts[8] = { 0 };
|
||||
|
||||
while (pos < length && partCount < 8)
|
||||
{
|
||||
std::uint8_t digits[4];
|
||||
int digitCount = 0;
|
||||
auto digit = local::try_parse_hex_digit(string[pos]);
|
||||
if (!digit)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
digits[digitCount] = *digit;
|
||||
++digitCount;
|
||||
++pos;
|
||||
} while (digitCount < 4 && pos < length && (digit = local::try_parse_hex_digit(string[pos])));
|
||||
|
||||
// If we're not at the end of the string then there must either be a ':' or a '.' next
|
||||
// followed by the next part.
|
||||
if (pos < length)
|
||||
{
|
||||
// Check if there's room for anything after the separator.
|
||||
if ((pos + 1) == length)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (string[pos] == ':')
|
||||
{
|
||||
++pos;
|
||||
if (string[pos] == ':')
|
||||
{
|
||||
if (doubleColonPos)
|
||||
{
|
||||
// This is a second double-colon, which is invalid.
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
doubleColonPos = partCount + 1;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
else if (string[pos] == '.')
|
||||
{
|
||||
// Treat the current set of digits as decimal digits and parse
|
||||
// the remaining three groups as dotted decimal notation.
|
||||
|
||||
// Decimal notation produces two 16-bit parts.
|
||||
// If we already have more than 6 parts then we'll end up
|
||||
// with too many.
|
||||
if (partCount > 6)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Check for over-long or octal notation.
|
||||
if (digitCount > 3 || (digitCount > 1 && digits[0] == 0))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Check that digits are valid decimal digits
|
||||
if (digits[0] > 9 ||
|
||||
(digitCount > 1 && digits[1] > 9) ||
|
||||
(digitCount == 3 && digits[2] > 9))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::uint16_t decimalParts[4];
|
||||
|
||||
{
|
||||
decimalParts[0] = digits[0];
|
||||
for (int i = 1; i < digitCount; ++i)
|
||||
{
|
||||
decimalParts[0] *= 10;
|
||||
decimalParts[0] += digits[i];
|
||||
}
|
||||
|
||||
if (decimalParts[0] > 255)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
for (int decimalPart = 1; decimalPart < 4; ++decimalPart)
|
||||
{
|
||||
if (string[pos] != '.')
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
++pos;
|
||||
|
||||
if (pos == length || !local::is_digit(string[pos]))
|
||||
{
|
||||
// Expected a number after a dot.
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const bool hasLeadingZero = string[pos] == '0';
|
||||
|
||||
decimalParts[decimalPart] = local::digit_value(string[pos]);
|
||||
++pos;
|
||||
digitCount = 1;
|
||||
while (digitCount < 3 && pos < length && local::is_digit(string[pos]))
|
||||
{
|
||||
decimalParts[decimalPart] *= 10;
|
||||
decimalParts[decimalPart] += local::digit_value(string[pos]);
|
||||
++pos;
|
||||
++digitCount;
|
||||
}
|
||||
|
||||
if (decimalParts[decimalPart] > 255)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Detect octal-style number (redundant leading zero)
|
||||
if (digitCount > 1 && hasLeadingZero)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
parts[partCount] = (decimalParts[0] << 8) + decimalParts[1];
|
||||
parts[partCount + 1] = (decimalParts[2] << 8) + decimalParts[3];
|
||||
partCount += 2;
|
||||
|
||||
// Dotted decimal notation only appears at end.
|
||||
// Don't parse any more of the string.
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid separator.
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// Current part was made up of hex-digits.
|
||||
std::uint16_t partValue = digits[0];
|
||||
for (int i = 1; i < digitCount; ++i)
|
||||
{
|
||||
partValue = partValue * 16 + digits[i];
|
||||
}
|
||||
|
||||
parts[partCount] = partValue;
|
||||
++partCount;
|
||||
}
|
||||
|
||||
// Finished parsing the IPv6 address, we should have consumed all of the string.
|
||||
if (pos < length)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (partCount < 8)
|
||||
{
|
||||
if (!doubleColonPos)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const int preCount = *doubleColonPos;
|
||||
|
||||
//CPPCORO_ASSUME(preCount <= partCount);
|
||||
|
||||
const int postCount = partCount - preCount;
|
||||
const int zeroCount = 8 - preCount - postCount;
|
||||
|
||||
// Move parts after double colon down to the end.
|
||||
for (int i = 0; i < postCount; ++i)
|
||||
{
|
||||
parts[7 - i] = parts[7 - zeroCount - i];
|
||||
}
|
||||
|
||||
// Fill gap with zeroes.
|
||||
for (int i = 0; i < zeroCount; ++i)
|
||||
{
|
||||
parts[preCount + i] = 0;
|
||||
}
|
||||
}
|
||||
else if (doubleColonPos)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return ipv6_address{ parts };
|
||||
}
|
||||
|
||||
std::string cppcoro::net::ipv6_address::to_string() const
|
||||
{
|
||||
std::uint32_t longestZeroRunStart = 0;
|
||||
std::uint32_t longestZeroRunLength = 0;
|
||||
for (std::uint32_t i = 0; i < 8; )
|
||||
{
|
||||
if (m_bytes[2 * i] == 0 && m_bytes[2 * i + 1] == 0)
|
||||
{
|
||||
const std::uint32_t zeroRunStart = i;
|
||||
++i;
|
||||
while (i < 8 && m_bytes[2 * i] == 0 && m_bytes[2 * i + 1] == 0)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
std::uint32_t zeroRunLength = i - zeroRunStart;
|
||||
if (zeroRunLength > longestZeroRunLength)
|
||||
{
|
||||
longestZeroRunLength = zeroRunLength;
|
||||
longestZeroRunStart = zeroRunStart;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Longest string will be 8 x 4 digits + 7 ':' separators
|
||||
char buffer[40];
|
||||
|
||||
char* c = &buffer[0];
|
||||
|
||||
auto appendPart = [&](std::uint32_t index)
|
||||
{
|
||||
const std::uint8_t highByte = m_bytes[index * 2];
|
||||
const std::uint8_t lowByte = m_bytes[index * 2 + 1];
|
||||
|
||||
// Don't output leading zero hex digits in the part string.
|
||||
if (highByte > 0 || lowByte > 15)
|
||||
{
|
||||
if (highByte > 0)
|
||||
{
|
||||
if (highByte > 15)
|
||||
{
|
||||
*c++ = local::hex_char(highByte >> 4);
|
||||
}
|
||||
*c++ = local::hex_char(highByte & 0xF);
|
||||
}
|
||||
*c++ = local::hex_char(lowByte >> 4);
|
||||
}
|
||||
*c++ = local::hex_char(lowByte & 0xF);
|
||||
};
|
||||
|
||||
if (longestZeroRunLength >= 2)
|
||||
{
|
||||
for (std::uint32_t i = 0; i < longestZeroRunStart; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
*c++ = ':';
|
||||
}
|
||||
|
||||
appendPart(i);
|
||||
}
|
||||
|
||||
*c++ = ':';
|
||||
*c++ = ':';
|
||||
|
||||
for (std::uint32_t i = longestZeroRunStart + longestZeroRunLength; i < 8; ++i)
|
||||
{
|
||||
appendPart(i);
|
||||
|
||||
if (i < 7)
|
||||
{
|
||||
*c++ = ':';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
appendPart(0);
|
||||
for (std::uint32_t i = 1; i < 8; ++i)
|
||||
{
|
||||
*c++ = ':';
|
||||
appendPart(i);
|
||||
}
|
||||
}
|
||||
|
||||
assert((c - &buffer[0]) <= sizeof(buffer));
|
||||
|
||||
return std::string{ &buffer[0], c };
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Kt C++ Library
|
||||
// Copyright (c) 2015 Lewis Baker
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/ipv6_endpoint.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace local
|
||||
{
|
||||
bool is_digit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
std::uint8_t digit_value(char c)
|
||||
{
|
||||
return static_cast<std::uint8_t>(c - '0');
|
||||
}
|
||||
|
||||
std::optional<std::uint16_t> parse_port(std::string_view string)
|
||||
{
|
||||
if (string.empty()) return std::nullopt;
|
||||
|
||||
std::uint32_t value = 0;
|
||||
for (auto c : string)
|
||||
{
|
||||
if (!is_digit(c)) return std::nullopt;
|
||||
value = value * 10 + digit_value(c);
|
||||
if (value > 0xFFFFu) return std::nullopt;
|
||||
}
|
||||
|
||||
return static_cast<std::uint16_t>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string cppcoro::net::ipv6_endpoint::to_string() const
|
||||
{
|
||||
std::string result;
|
||||
result.push_back('[');
|
||||
result += m_address.to_string();
|
||||
result += "]:";
|
||||
result += std::to_string(m_port);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<cppcoro::net::ipv6_endpoint>
|
||||
cppcoro::net::ipv6_endpoint::from_string(std::string_view string) noexcept
|
||||
{
|
||||
// Shortest valid endpoint is "[::]:0"
|
||||
if (string.size() < 6)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (string[0] != '[')
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto closeBracketPos = string.find("]:", 1);
|
||||
if (closeBracketPos == std::string_view::npos)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto address = ipv6_address::from_string(string.substr(1, closeBracketPos - 1));
|
||||
if (!address)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto port = local::parse_port(string.substr(closeBracketPos + 2));
|
||||
if (!port)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return ipv6_endpoint{ *address, *port };
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro\read_only_file.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <Windows.h>
|
||||
|
||||
cppcoro::read_only_file cppcoro::read_only_file::open(
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
file_share_mode shareMode,
|
||||
file_buffering_mode bufferingMode)
|
||||
{
|
||||
return read_only_file(file::open(
|
||||
GENERIC_READ,
|
||||
ioService,
|
||||
path,
|
||||
file_open_mode::open_existing,
|
||||
shareMode,
|
||||
bufferingMode));
|
||||
}
|
||||
|
||||
cppcoro::read_only_file::read_only_file(
|
||||
detail::win32::safe_handle&& fileHandle) noexcept
|
||||
: file(std::move(fileHandle))
|
||||
, readable_file(detail::win32::safe_handle{})
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,38 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro\read_write_file.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <Windows.h>
|
||||
|
||||
cppcoro::read_write_file cppcoro::read_write_file::open(
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
file_open_mode openMode,
|
||||
file_share_mode shareMode,
|
||||
file_buffering_mode bufferingMode)
|
||||
{
|
||||
return read_write_file(file::open(
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
ioService,
|
||||
path,
|
||||
openMode,
|
||||
shareMode,
|
||||
bufferingMode));
|
||||
}
|
||||
|
||||
cppcoro::read_write_file::read_write_file(
|
||||
detail::win32::safe_handle&& fileHandle) noexcept
|
||||
: file(std::move(fileHandle))
|
||||
, readable_file(detail::win32::safe_handle{})
|
||||
, writable_file(detail::win32::safe_handle{})
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,36 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/readable_file.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
|
||||
cppcoro::file_read_operation cppcoro::readable_file::read(
|
||||
std::uint64_t offset,
|
||||
void* buffer,
|
||||
std::size_t byteCount) const noexcept
|
||||
{
|
||||
return file_read_operation(
|
||||
m_fileHandle.handle(),
|
||||
offset,
|
||||
buffer,
|
||||
byteCount);
|
||||
}
|
||||
|
||||
cppcoro::file_read_operation_cancellable cppcoro::readable_file::read(
|
||||
std::uint64_t offset,
|
||||
void* buffer,
|
||||
std::size_t byteCount,
|
||||
cancellation_token ct) const noexcept
|
||||
{
|
||||
return file_read_operation_cancellable(
|
||||
m_fileHandle.handle(),
|
||||
offset,
|
||||
buffer,
|
||||
byteCount,
|
||||
std::move(ct));
|
||||
}
|
||||
|
||||
#endif
|
493
lib/socket.cpp
493
lib/socket.cpp
|
@ -1,493 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/socket.hpp>
|
||||
|
||||
#include <cppcoro/net/socket_accept_operation.hpp>
|
||||
#include <cppcoro/net/socket_connect_operation.hpp>
|
||||
#include <cppcoro/net/socket_disconnect_operation.hpp>
|
||||
#include <cppcoro/net/socket_recv_operation.hpp>
|
||||
#include <cppcoro/net/socket_send_operation.hpp>
|
||||
|
||||
#include <cppcoro/io_service.hpp>
|
||||
#include <cppcoro/on_scope_exit.hpp>
|
||||
|
||||
#include "socket_helpers.hpp"
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <WinSock2.h>
|
||||
# include <WS2tcpip.h>
|
||||
# include <MSWSock.h>
|
||||
# include <Windows.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace local
|
||||
{
|
||||
std::tuple<SOCKET, bool> create_socket(
|
||||
int addressFamily,
|
||||
int socketType,
|
||||
int protocol,
|
||||
HANDLE ioCompletionPort)
|
||||
{
|
||||
// Enumerate available protocol providers for the specified socket type.
|
||||
|
||||
WSAPROTOCOL_INFOW stackInfos[4];
|
||||
std::unique_ptr<WSAPROTOCOL_INFOW[]> heapInfos;
|
||||
WSAPROTOCOL_INFOW* selectedProtocolInfo = nullptr;
|
||||
|
||||
{
|
||||
INT protocols[] = { protocol, 0 };
|
||||
DWORD bufferSize = sizeof(stackInfos);
|
||||
WSAPROTOCOL_INFOW* infos = stackInfos;
|
||||
|
||||
int protocolCount = ::WSAEnumProtocolsW(protocols, infos, &bufferSize);
|
||||
if (protocolCount == SOCKET_ERROR)
|
||||
{
|
||||
int errorCode = ::WSAGetLastError();
|
||||
if (errorCode == WSAENOBUFS)
|
||||
{
|
||||
DWORD requiredElementCount = bufferSize / sizeof(WSAPROTOCOL_INFOW);
|
||||
heapInfos = std::make_unique<WSAPROTOCOL_INFOW[]>(requiredElementCount);
|
||||
bufferSize = requiredElementCount * sizeof(WSAPROTOCOL_INFOW);
|
||||
infos = heapInfos.get();
|
||||
protocolCount = ::WSAEnumProtocolsW(protocols, infos, &bufferSize);
|
||||
if (protocolCount == SOCKET_ERROR)
|
||||
{
|
||||
errorCode = ::WSAGetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolCount == SOCKET_ERROR)
|
||||
{
|
||||
throw std::system_error(
|
||||
errorCode,
|
||||
std::system_category(),
|
||||
"Error creating socket: WSAEnumProtocolsW");
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolCount == 0)
|
||||
{
|
||||
throw std::system_error(
|
||||
std::make_error_code(std::errc::protocol_not_supported));
|
||||
}
|
||||
|
||||
for (int i = 0; i < protocolCount; ++i)
|
||||
{
|
||||
auto& info = infos[i];
|
||||
if (info.iAddressFamily == addressFamily && info.iProtocol == protocol && info.iSocketType == socketType)
|
||||
{
|
||||
selectedProtocolInfo = &info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedProtocolInfo == nullptr)
|
||||
{
|
||||
throw std::system_error(
|
||||
std::make_error_code(std::errc::address_family_not_supported));
|
||||
}
|
||||
}
|
||||
|
||||
// WSA_FLAG_NO_HANDLE_INHERIT for SDKs earlier than Windows 7.
|
||||
constexpr DWORD flagNoInherit = 0x80;
|
||||
|
||||
const DWORD flags = WSA_FLAG_OVERLAPPED | flagNoInherit;
|
||||
|
||||
const SOCKET socketHandle = ::WSASocketW(
|
||||
addressFamily, socketType, protocol, selectedProtocolInfo, 0, flags);
|
||||
if (socketHandle == INVALID_SOCKET)
|
||||
{
|
||||
const int errorCode = ::WSAGetLastError();
|
||||
throw std::system_error(
|
||||
errorCode,
|
||||
std::system_category(),
|
||||
"Error creating socket: WSASocketW");
|
||||
}
|
||||
|
||||
auto closeSocketOnFailure = cppcoro::on_scope_failure([&]
|
||||
{
|
||||
::closesocket(socketHandle);
|
||||
});
|
||||
|
||||
// This is needed on operating systems earlier than Windows 7 to prevent
|
||||
// socket handles from being inherited. On Windows 7 or later this is
|
||||
// redundant as the WSA_FLAG_NO_HANDLE_INHERIT flag passed to creation
|
||||
// above causes the socket to be atomically created with this flag cleared.
|
||||
if (!::SetHandleInformation((HANDLE)socketHandle, HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
const DWORD errorCode = ::GetLastError();
|
||||
throw std::system_error(
|
||||
errorCode,
|
||||
std::system_category(),
|
||||
"Error creating socket: SetHandleInformation");
|
||||
}
|
||||
|
||||
// Associate the socket with the I/O completion port.
|
||||
{
|
||||
const HANDLE result = ::CreateIoCompletionPort(
|
||||
(HANDLE)socketHandle,
|
||||
ioCompletionPort,
|
||||
ULONG_PTR(0),
|
||||
DWORD(0));
|
||||
if (result == nullptr)
|
||||
{
|
||||
const DWORD errorCode = ::GetLastError();
|
||||
throw std::system_error(
|
||||
static_cast<int>(errorCode),
|
||||
std::system_category(),
|
||||
"Error creating socket: CreateIoCompletionPort");
|
||||
}
|
||||
}
|
||||
|
||||
const bool skipCompletionPortOnSuccess =
|
||||
(selectedProtocolInfo->dwServiceFlags1 & XP1_IFS_HANDLES) != 0;
|
||||
|
||||
{
|
||||
UCHAR completionModeFlags = FILE_SKIP_SET_EVENT_ON_HANDLE;
|
||||
if (skipCompletionPortOnSuccess)
|
||||
{
|
||||
completionModeFlags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
|
||||
}
|
||||
|
||||
const BOOL ok = ::SetFileCompletionNotificationModes(
|
||||
(HANDLE)socketHandle,
|
||||
completionModeFlags);
|
||||
if (!ok)
|
||||
{
|
||||
const DWORD errorCode = ::GetLastError();
|
||||
throw std::system_error(
|
||||
static_cast<int>(errorCode),
|
||||
std::system_category(),
|
||||
"Error creating socket: SetFileCompletionNotificationModes");
|
||||
}
|
||||
}
|
||||
|
||||
if (socketType == SOCK_STREAM)
|
||||
{
|
||||
// Turn off linger so that the destructor doesn't block while closing
|
||||
// the socket or silently continue to flush remaining data in the
|
||||
// background after ::closesocket() is called, which could fail and
|
||||
// we'd never know about it.
|
||||
// We expect clients to call Disconnect() or use CloseSend() to cleanly
|
||||
// shut-down connections instead.
|
||||
BOOL value = TRUE;
|
||||
const int result = ::setsockopt(socketHandle,
|
||||
SOL_SOCKET,
|
||||
SO_DONTLINGER,
|
||||
reinterpret_cast<const char*>(&value),
|
||||
sizeof(value));
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
const int errorCode = ::WSAGetLastError();
|
||||
throw std::system_error(
|
||||
errorCode,
|
||||
std::system_category(),
|
||||
"Error creating socket: setsockopt(SO_DONTLINGER)");
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_tuple(socketHandle, skipCompletionPortOnSuccess);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cppcoro::net::socket cppcoro::net::socket::create_tcpv4(io_service& ioSvc)
|
||||
{
|
||||
ioSvc.ensure_winsock_initialised();
|
||||
|
||||
auto[socketHandle, skipCompletionPortOnSuccess] = local::create_socket(
|
||||
AF_INET, SOCK_STREAM, IPPROTO_TCP, ioSvc.native_iocp_handle());
|
||||
|
||||
socket result(socketHandle, skipCompletionPortOnSuccess);
|
||||
result.m_localEndPoint = ipv4_endpoint();
|
||||
result.m_remoteEndPoint = ipv4_endpoint();
|
||||
return result;
|
||||
}
|
||||
|
||||
cppcoro::net::socket cppcoro::net::socket::create_tcpv6(io_service& ioSvc)
|
||||
{
|
||||
ioSvc.ensure_winsock_initialised();
|
||||
|
||||
auto[socketHandle, skipCompletionPortOnSuccess] = local::create_socket(
|
||||
AF_INET6, SOCK_STREAM, IPPROTO_TCP, ioSvc.native_iocp_handle());
|
||||
|
||||
socket result(socketHandle, skipCompletionPortOnSuccess);
|
||||
result.m_localEndPoint = ipv6_endpoint();
|
||||
result.m_remoteEndPoint = ipv6_endpoint();
|
||||
return result;
|
||||
}
|
||||
|
||||
cppcoro::net::socket cppcoro::net::socket::create_udpv4(io_service& ioSvc)
|
||||
{
|
||||
ioSvc.ensure_winsock_initialised();
|
||||
|
||||
auto[socketHandle, skipCompletionPortOnSuccess] = local::create_socket(
|
||||
AF_INET, SOCK_DGRAM, IPPROTO_UDP, ioSvc.native_iocp_handle());
|
||||
|
||||
socket result(socketHandle, skipCompletionPortOnSuccess);
|
||||
result.m_localEndPoint = ipv4_endpoint();
|
||||
result.m_remoteEndPoint = ipv4_endpoint();
|
||||
return result;
|
||||
}
|
||||
|
||||
cppcoro::net::socket cppcoro::net::socket::create_udpv6(io_service& ioSvc)
|
||||
{
|
||||
ioSvc.ensure_winsock_initialised();
|
||||
|
||||
auto[socketHandle, skipCompletionPortOnSuccess] = local::create_socket(
|
||||
AF_INET6, SOCK_DGRAM, IPPROTO_UDP, ioSvc.native_iocp_handle());
|
||||
|
||||
socket result(socketHandle, skipCompletionPortOnSuccess);
|
||||
result.m_localEndPoint = ipv6_endpoint();
|
||||
result.m_remoteEndPoint = ipv6_endpoint();
|
||||
return result;
|
||||
}
|
||||
|
||||
cppcoro::net::socket::socket(socket&& other) noexcept
|
||||
: m_handle(std::exchange(other.m_handle, INVALID_SOCKET))
|
||||
, m_skipCompletionOnSuccess(other.m_skipCompletionOnSuccess)
|
||||
, m_localEndPoint(std::move(other.m_localEndPoint))
|
||||
, m_remoteEndPoint(std::move(other.m_remoteEndPoint))
|
||||
{}
|
||||
|
||||
cppcoro::net::socket::~socket()
|
||||
{
|
||||
if (m_handle != INVALID_SOCKET)
|
||||
{
|
||||
::closesocket(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
cppcoro::net::socket&
|
||||
cppcoro::net::socket::operator=(socket&& other) noexcept
|
||||
{
|
||||
auto handle = std::exchange(other.m_handle, INVALID_SOCKET);
|
||||
if (m_handle != INVALID_SOCKET)
|
||||
{
|
||||
::closesocket(m_handle);
|
||||
}
|
||||
|
||||
m_handle = handle;
|
||||
m_skipCompletionOnSuccess = other.m_skipCompletionOnSuccess;
|
||||
m_localEndPoint = other.m_localEndPoint;
|
||||
m_remoteEndPoint = other.m_remoteEndPoint;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void cppcoro::net::socket::bind(const ip_endpoint& localEndPoint)
|
||||
{
|
||||
SOCKADDR_STORAGE sockaddrStorage = { 0 };
|
||||
SOCKADDR* sockaddr = reinterpret_cast<SOCKADDR*>(&sockaddrStorage);
|
||||
if (localEndPoint.is_ipv4())
|
||||
{
|
||||
SOCKADDR_IN& ipv4Sockaddr = *reinterpret_cast<SOCKADDR_IN*>(sockaddr);
|
||||
ipv4Sockaddr.sin_family = AF_INET;
|
||||
std::memcpy(&ipv4Sockaddr.sin_addr, localEndPoint.to_ipv4().address().bytes(), 4);
|
||||
ipv4Sockaddr.sin_port = localEndPoint.to_ipv4().port();
|
||||
}
|
||||
else
|
||||
{
|
||||
SOCKADDR_IN6& ipv6Sockaddr = *reinterpret_cast<SOCKADDR_IN6*>(sockaddr);
|
||||
ipv6Sockaddr.sin6_family = AF_INET6;
|
||||
std::memcpy(&ipv6Sockaddr.sin6_addr, localEndPoint.to_ipv6().address().bytes(), 16);
|
||||
ipv6Sockaddr.sin6_port = localEndPoint.to_ipv6().port();
|
||||
}
|
||||
|
||||
int result = ::bind(m_handle, sockaddr, sizeof(sockaddrStorage));
|
||||
if (result != 0)
|
||||
{
|
||||
// WSANOTINITIALISED: WSAStartup not called
|
||||
// WSAENETDOWN: network subsystem failed
|
||||
// WSAEACCES: access denied
|
||||
// WSAEADDRINUSE: port in use
|
||||
// WSAEADDRNOTAVAIL: address is not an address that can be bound to
|
||||
// WSAEFAULT: invalid pointer passed to bind()
|
||||
// WSAEINPROGRESS: a callback is in progress
|
||||
// WSAEINVAL: socket already bound
|
||||
// WSAENOBUFS: system failed to allocate memory
|
||||
// WSAENOTSOCK: socket was not a valid socket.
|
||||
int errorCode = ::WSAGetLastError();
|
||||
throw std::system_error(
|
||||
errorCode,
|
||||
std::system_category(),
|
||||
"Error binding to endpoint: bind()");
|
||||
}
|
||||
|
||||
int sockaddrLen = sizeof(sockaddrStorage);
|
||||
result = ::getsockname(m_handle, sockaddr, &sockaddrLen);
|
||||
if (result == 0)
|
||||
{
|
||||
m_localEndPoint = cppcoro::net::detail::sockaddr_to_ip_endpoint(*sockaddr);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_localEndPoint = localEndPoint;
|
||||
}
|
||||
}
|
||||
|
||||
void cppcoro::net::socket::listen()
|
||||
{
|
||||
int result = ::listen(m_handle, SOMAXCONN);
|
||||
if (result != 0)
|
||||
{
|
||||
int errorCode = ::WSAGetLastError();
|
||||
throw std::system_error(
|
||||
errorCode,
|
||||
std::system_category(),
|
||||
"Failed to start listening on bound endpoint: listen");
|
||||
}
|
||||
}
|
||||
|
||||
void cppcoro::net::socket::listen(std::uint32_t backlog)
|
||||
{
|
||||
if (backlog > 0x7FFFFFFF)
|
||||
{
|
||||
backlog = 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
int result = ::listen(m_handle, (int)backlog);
|
||||
if (result != 0)
|
||||
{
|
||||
// WSANOTINITIALISED: WSAStartup not called
|
||||
// WSAENETDOWN: network subsystem failed
|
||||
// WSAEADDRINUSE: port in use
|
||||
// WSAEINPROGRESS: a callback is in progress
|
||||
// WSAEINVAL: socket not yet bound
|
||||
// WSAEISCONN: socket already connected
|
||||
// WSAEMFILE: no more socket descriptors available
|
||||
// WSAENOBUFS: system failed to allocate memory
|
||||
// WSAENOTSOCK: socket was not a valid socket.
|
||||
// WSAEOPNOTSUPP: The socket does not support listening
|
||||
|
||||
int errorCode = ::WSAGetLastError();
|
||||
throw std::system_error(
|
||||
errorCode,
|
||||
std::system_category(),
|
||||
"Failed to start listening on bound endpoint: listen");
|
||||
}
|
||||
}
|
||||
|
||||
cppcoro::net::socket_accept_operation
|
||||
cppcoro::net::socket::accept(socket& acceptingSocket) noexcept
|
||||
{
|
||||
return socket_accept_operation{ *this, acceptingSocket };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_accept_operation_cancellable
|
||||
cppcoro::net::socket::accept(socket& acceptingSocket, cancellation_token ct) noexcept
|
||||
{
|
||||
return socket_accept_operation_cancellable{ *this, acceptingSocket, std::move(ct) };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_connect_operation
|
||||
cppcoro::net::socket::connect(const ip_endpoint& remoteEndPoint) noexcept
|
||||
{
|
||||
return socket_connect_operation{ *this, remoteEndPoint };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_connect_operation_cancellable
|
||||
cppcoro::net::socket::connect(const ip_endpoint& remoteEndPoint, cancellation_token ct) noexcept
|
||||
{
|
||||
return socket_connect_operation_cancellable{ *this, remoteEndPoint, std::move(ct) };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_disconnect_operation
|
||||
cppcoro::net::socket::disconnect() noexcept
|
||||
{
|
||||
return socket_disconnect_operation(*this);
|
||||
}
|
||||
|
||||
cppcoro::net::socket_disconnect_operation_cancellable
|
||||
cppcoro::net::socket::disconnect(cancellation_token ct) noexcept
|
||||
{
|
||||
return socket_disconnect_operation_cancellable{ *this, std::move(ct) };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_send_operation
|
||||
cppcoro::net::socket::send(const void* buffer, std::size_t byteCount) noexcept
|
||||
{
|
||||
return socket_send_operation{ *this, buffer, byteCount };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_send_operation_cancellable
|
||||
cppcoro::net::socket::send(const void* buffer, std::size_t byteCount, cancellation_token ct) noexcept
|
||||
{
|
||||
return socket_send_operation_cancellable{ *this, buffer, byteCount, std::move(ct) };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_recv_operation
|
||||
cppcoro::net::socket::recv(void* buffer, std::size_t byteCount) noexcept
|
||||
{
|
||||
return socket_recv_operation{ *this, buffer, byteCount };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_recv_operation_cancellable
|
||||
cppcoro::net::socket::recv(void* buffer, std::size_t byteCount, cancellation_token ct) noexcept
|
||||
{
|
||||
return socket_recv_operation_cancellable{ *this, buffer, byteCount, std::move(ct) };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_recv_from_operation
|
||||
cppcoro::net::socket::recv_from(void* buffer, std::size_t byteCount) noexcept
|
||||
{
|
||||
return socket_recv_from_operation{ *this, buffer, byteCount };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_recv_from_operation_cancellable
|
||||
cppcoro::net::socket::recv_from(void* buffer, std::size_t byteCount, cancellation_token ct) noexcept
|
||||
{
|
||||
return socket_recv_from_operation_cancellable{ *this, buffer, byteCount, std::move(ct) };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_send_to_operation
|
||||
cppcoro::net::socket::send_to(const ip_endpoint& destination, const void* buffer, std::size_t byteCount) noexcept
|
||||
{
|
||||
return socket_send_to_operation{ *this, destination, buffer, byteCount };
|
||||
}
|
||||
|
||||
cppcoro::net::socket_send_to_operation_cancellable
|
||||
cppcoro::net::socket::send_to(const ip_endpoint& destination, const void* buffer, std::size_t byteCount, cancellation_token ct) noexcept
|
||||
{
|
||||
return socket_send_to_operation_cancellable{ *this, destination, buffer, byteCount, std::move(ct) };
|
||||
}
|
||||
|
||||
void cppcoro::net::socket::close_send()
|
||||
{
|
||||
int result = ::shutdown(m_handle, SD_SEND);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
int errorCode = ::WSAGetLastError();
|
||||
throw std::system_error(
|
||||
errorCode,
|
||||
std::system_category(),
|
||||
"failed to close socket send stream: shutdown(SD_SEND)");
|
||||
}
|
||||
}
|
||||
|
||||
void cppcoro::net::socket::close_recv()
|
||||
{
|
||||
int result = ::shutdown(m_handle, SD_RECEIVE);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
int errorCode = ::WSAGetLastError();
|
||||
throw std::system_error(
|
||||
errorCode,
|
||||
std::system_category(),
|
||||
"failed to close socket receive stream: shutdown(SD_RECEIVE)");
|
||||
}
|
||||
}
|
||||
|
||||
cppcoro::net::socket::socket(
|
||||
cppcoro::detail::win32::socket_t handle,
|
||||
bool skipCompletionOnSuccess) noexcept
|
||||
: m_handle(handle)
|
||||
, m_skipCompletionOnSuccess(skipCompletionOnSuccess)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,129 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/socket_accept_operation.hpp>
|
||||
#include <cppcoro/net/socket.hpp>
|
||||
|
||||
#include "socket_helpers.hpp"
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <WinSock2.h>
|
||||
# include <WS2tcpip.h>
|
||||
# include <MSWSock.h>
|
||||
# include <Windows.h>
|
||||
|
||||
// TODO: Eliminate duplication of implementation between socket_accept_operation
|
||||
// and socket_accept_operation_cancellable.
|
||||
|
||||
bool cppcoro::net::socket_accept_operation_impl::try_start(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
static_assert(
|
||||
(sizeof(m_addressBuffer) / 2) >= (16 + sizeof(SOCKADDR_IN)) &&
|
||||
(sizeof(m_addressBuffer) / 2) >= (16 + sizeof(SOCKADDR_IN6)),
|
||||
"AcceptEx requires address buffer to be at least 16 bytes more than largest address.");
|
||||
|
||||
// Need to read this flag before starting the operation, otherwise
|
||||
// it may be possible that the operation will complete immediately
|
||||
// on another thread and then destroy the socket before we get a
|
||||
// chance to read it.
|
||||
const bool skipCompletionOnSuccess = m_listeningSocket.skip_completion_on_success();
|
||||
|
||||
DWORD bytesReceived = 0;
|
||||
BOOL ok = ::AcceptEx(
|
||||
m_listeningSocket.native_handle(),
|
||||
m_acceptingSocket.native_handle(),
|
||||
m_addressBuffer,
|
||||
0,
|
||||
sizeof(m_addressBuffer) / 2,
|
||||
sizeof(m_addressBuffer) / 2,
|
||||
&bytesReceived,
|
||||
operation.get_overlapped());
|
||||
if (!ok)
|
||||
{
|
||||
int errorCode = ::WSAGetLastError();
|
||||
if (errorCode != ERROR_IO_PENDING)
|
||||
{
|
||||
operation.m_errorCode = static_cast<DWORD>(errorCode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (skipCompletionOnSuccess)
|
||||
{
|
||||
operation.m_errorCode = ERROR_SUCCESS;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cppcoro::net::socket_accept_operation_impl::cancel(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
(void)::CancelIoEx(
|
||||
reinterpret_cast<HANDLE>(m_listeningSocket.native_handle()),
|
||||
operation.get_overlapped());
|
||||
}
|
||||
|
||||
void cppcoro::net::socket_accept_operation_impl::get_result(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation)
|
||||
{
|
||||
if (operation.m_errorCode != ERROR_SUCCESS)
|
||||
{
|
||||
throw std::system_error{
|
||||
static_cast<int>(operation.m_errorCode),
|
||||
std::system_category(),
|
||||
"Accepting a connection failed: AcceptEx"
|
||||
};
|
||||
}
|
||||
|
||||
sockaddr* localSockaddr = nullptr;
|
||||
sockaddr* remoteSockaddr = nullptr;
|
||||
|
||||
INT localSockaddrLength;
|
||||
INT remoteSockaddrLength;
|
||||
|
||||
::GetAcceptExSockaddrs(
|
||||
m_addressBuffer,
|
||||
0,
|
||||
sizeof(m_addressBuffer) / 2,
|
||||
sizeof(m_addressBuffer) / 2,
|
||||
&localSockaddr,
|
||||
&localSockaddrLength,
|
||||
&remoteSockaddr,
|
||||
&remoteSockaddrLength);
|
||||
|
||||
m_acceptingSocket.m_localEndPoint =
|
||||
detail::sockaddr_to_ip_endpoint(*localSockaddr);
|
||||
|
||||
m_acceptingSocket.m_remoteEndPoint =
|
||||
detail::sockaddr_to_ip_endpoint(*remoteSockaddr);
|
||||
|
||||
{
|
||||
// Need to set SO_UPDATE_ACCEPT_CONTEXT after the accept completes
|
||||
// to ensure that ::shutdown() and ::setsockopt() calls work on the
|
||||
// accepted socket.
|
||||
SOCKET listenSocket = m_listeningSocket.native_handle();
|
||||
const int result = ::setsockopt(
|
||||
m_acceptingSocket.native_handle(),
|
||||
SOL_SOCKET,
|
||||
SO_UPDATE_ACCEPT_CONTEXT,
|
||||
(const char*)&listenSocket,
|
||||
sizeof(SOCKET));
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
const int errorCode = ::WSAGetLastError();
|
||||
throw std::system_error{
|
||||
errorCode,
|
||||
std::system_category(),
|
||||
"Socket accept operation failed: setsockopt(SO_UPDATE_ACCEPT_CONTEXT)"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,178 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/socket_connect_operation.hpp>
|
||||
#include <cppcoro/net/socket.hpp>
|
||||
|
||||
#include <cppcoro/operation_cancelled.hpp>
|
||||
|
||||
#include "socket_helpers.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <system_error>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <WinSock2.h>
|
||||
# include <WS2tcpip.h>
|
||||
# include <MSWSock.h>
|
||||
# include <Windows.h>
|
||||
|
||||
bool cppcoro::net::socket_connect_operation_impl::try_start(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
// Lookup the address of the ConnectEx function pointer for this socket.
|
||||
LPFN_CONNECTEX connectExPtr;
|
||||
{
|
||||
GUID connectExGuid = WSAID_CONNECTEX;
|
||||
DWORD byteCount = 0;
|
||||
int result = ::WSAIoctl(
|
||||
m_socket.native_handle(),
|
||||
SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
static_cast<void*>(&connectExGuid),
|
||||
sizeof(connectExGuid),
|
||||
static_cast<void*>(&connectExPtr),
|
||||
sizeof(connectExPtr),
|
||||
&byteCount,
|
||||
nullptr,
|
||||
nullptr);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
operation.m_errorCode = ::WSAGetLastError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to read this flag before starting the operation, otherwise
|
||||
// it may be possible that the operation will complete immediately
|
||||
// on another thread and then destroy the socket before we get a
|
||||
// chance to read it.
|
||||
const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success();
|
||||
|
||||
SOCKADDR_STORAGE remoteSockaddrStorage;
|
||||
const int sockaddrNameLength = cppcoro::net::detail::ip_endpoint_to_sockaddr(
|
||||
m_remoteEndPoint,
|
||||
std::ref(remoteSockaddrStorage));
|
||||
|
||||
DWORD bytesSent = 0;
|
||||
const BOOL ok = connectExPtr(
|
||||
m_socket.native_handle(),
|
||||
reinterpret_cast<const SOCKADDR*>(&remoteSockaddrStorage),
|
||||
sockaddrNameLength,
|
||||
nullptr, // send buffer
|
||||
0, // size of send buffer
|
||||
&bytesSent,
|
||||
operation.get_overlapped());
|
||||
if (!ok)
|
||||
{
|
||||
const int errorCode = ::WSAGetLastError();
|
||||
if (errorCode != ERROR_IO_PENDING)
|
||||
{
|
||||
// Failed synchronously.
|
||||
operation.m_errorCode = static_cast<DWORD>(errorCode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (skipCompletionOnSuccess)
|
||||
{
|
||||
// Successfully completed synchronously and no completion event
|
||||
// will be posted to an I/O thread so we can return without suspending.
|
||||
operation.m_errorCode = ERROR_SUCCESS;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cppcoro::net::socket_connect_operation_impl::cancel(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
(void)::CancelIoEx(
|
||||
reinterpret_cast<HANDLE>(m_socket.native_handle()),
|
||||
operation.get_overlapped());
|
||||
}
|
||||
|
||||
void cppcoro::net::socket_connect_operation_impl::get_result(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation)
|
||||
{
|
||||
if (operation.m_errorCode != ERROR_SUCCESS)
|
||||
{
|
||||
if (operation.m_errorCode == ERROR_OPERATION_ABORTED)
|
||||
{
|
||||
throw operation_cancelled{};
|
||||
}
|
||||
|
||||
throw std::system_error{
|
||||
static_cast<int>(operation.m_errorCode),
|
||||
std::system_category(),
|
||||
"Connect operation failed: ConnectEx"
|
||||
};
|
||||
}
|
||||
|
||||
// We need to call setsockopt() to update the socket state with information
|
||||
// about the connection now that it has been successfully connected.
|
||||
{
|
||||
const int result = ::setsockopt(
|
||||
m_socket.native_handle(),
|
||||
SOL_SOCKET,
|
||||
SO_UPDATE_CONNECT_CONTEXT,
|
||||
nullptr,
|
||||
0);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
// This shouldn't fail, but just in case it does we fall back to
|
||||
// setting the remote address as specified in the call to Connect().
|
||||
//
|
||||
// Don't really want to throw an exception here since the connection
|
||||
// has actually been established.
|
||||
m_socket.m_remoteEndPoint = m_remoteEndPoint;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
SOCKADDR_STORAGE localSockaddr;
|
||||
int nameLength = sizeof(localSockaddr);
|
||||
const int result = ::getsockname(
|
||||
m_socket.native_handle(),
|
||||
reinterpret_cast<SOCKADDR*>(&localSockaddr),
|
||||
&nameLength);
|
||||
if (result == 0)
|
||||
{
|
||||
m_socket.m_localEndPoint = cppcoro::net::detail::sockaddr_to_ip_endpoint(
|
||||
*reinterpret_cast<const SOCKADDR*>(&localSockaddr));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failed to get the updated local-end-point
|
||||
// Just leave m_localEndPoint set to whatever bind() left it as.
|
||||
//
|
||||
// TODO: Should we be throwing an exception here instead?
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
SOCKADDR_STORAGE remoteSockaddr;
|
||||
int nameLength = sizeof(remoteSockaddr);
|
||||
const int result = ::getpeername(
|
||||
m_socket.native_handle(),
|
||||
reinterpret_cast<SOCKADDR*>(&remoteSockaddr),
|
||||
&nameLength);
|
||||
if (result == 0)
|
||||
{
|
||||
m_socket.m_remoteEndPoint = cppcoro::net::detail::sockaddr_to_ip_endpoint(
|
||||
*reinterpret_cast<const SOCKADDR*>(&remoteSockaddr));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failed to get the actual remote end-point so just fall back to
|
||||
// remembering the actual end-point that was passed to connect().
|
||||
//
|
||||
// TODO: Should we be throwing an exception here instead?
|
||||
m_socket.m_remoteEndPoint = m_remoteEndPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,107 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/socket_disconnect_operation.hpp>
|
||||
#include <cppcoro/net/socket.hpp>
|
||||
|
||||
#include "socket_helpers.hpp"
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <WinSock2.h>
|
||||
# include <WS2tcpip.h>
|
||||
# include <MSWSock.h>
|
||||
# include <Windows.h>
|
||||
|
||||
bool cppcoro::net::socket_disconnect_operation_impl::try_start(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
// Lookup the address of the DisconnectEx function pointer for this socket.
|
||||
LPFN_DISCONNECTEX disconnectExPtr;
|
||||
{
|
||||
GUID disconnectExGuid = WSAID_DISCONNECTEX;
|
||||
DWORD byteCount = 0;
|
||||
const int result = ::WSAIoctl(
|
||||
m_socket.native_handle(),
|
||||
SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
static_cast<void*>(&disconnectExGuid),
|
||||
sizeof(disconnectExGuid),
|
||||
static_cast<void*>(&disconnectExPtr),
|
||||
sizeof(disconnectExPtr),
|
||||
&byteCount,
|
||||
nullptr,
|
||||
nullptr);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
operation.m_errorCode = static_cast<DWORD>(::WSAGetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to read this flag before starting the operation, otherwise
|
||||
// it may be possible that the operation will complete immediately
|
||||
// on another thread and then destroy the socket before we get a
|
||||
// chance to read it.
|
||||
const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success();
|
||||
|
||||
// Need to add TF_REUSE_SOCKET to these flags if we want to allow reusing
|
||||
// a socket for subsequent connections once the disconnect operation
|
||||
// completes.
|
||||
const DWORD flags = 0;
|
||||
|
||||
const BOOL ok = disconnectExPtr(
|
||||
m_socket.native_handle(),
|
||||
operation.get_overlapped(),
|
||||
flags,
|
||||
0);
|
||||
if (!ok)
|
||||
{
|
||||
const int errorCode = ::WSAGetLastError();
|
||||
if (errorCode != ERROR_IO_PENDING)
|
||||
{
|
||||
// Failed synchronously.
|
||||
operation.m_errorCode = static_cast<DWORD>(errorCode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (skipCompletionOnSuccess)
|
||||
{
|
||||
// Successfully completed synchronously and no completion event
|
||||
// will be posted to an I/O thread so we can return without suspending.
|
||||
operation.m_errorCode = ERROR_SUCCESS;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cppcoro::net::socket_disconnect_operation_impl::cancel(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
(void)::CancelIoEx(
|
||||
reinterpret_cast<HANDLE>(m_socket.native_handle()),
|
||||
operation.get_overlapped());
|
||||
}
|
||||
|
||||
void cppcoro::net::socket_disconnect_operation_impl::get_result(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation)
|
||||
{
|
||||
if (operation.m_errorCode != ERROR_SUCCESS)
|
||||
{
|
||||
if (operation.m_errorCode == ERROR_OPERATION_ABORTED)
|
||||
{
|
||||
throw operation_cancelled{};
|
||||
}
|
||||
|
||||
throw std::system_error{
|
||||
static_cast<int>(operation.m_errorCode),
|
||||
std::system_category(),
|
||||
"Disconnect operation failed: DisconnectEx"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,85 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "socket_helpers.hpp"
|
||||
|
||||
#include <cppcoro/net/ip_endpoint.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#include <MSWSock.h>
|
||||
#include <Windows.h>
|
||||
|
||||
|
||||
cppcoro::net::ip_endpoint
|
||||
cppcoro::net::detail::sockaddr_to_ip_endpoint(const sockaddr& address) noexcept
|
||||
{
|
||||
if (address.sa_family == AF_INET)
|
||||
{
|
||||
SOCKADDR_IN ipv4Address;
|
||||
std::memcpy(&ipv4Address, &address, sizeof(ipv4Address));
|
||||
|
||||
std::uint8_t addressBytes[4];
|
||||
std::memcpy(addressBytes, &ipv4Address.sin_addr, 4);
|
||||
|
||||
return ipv4_endpoint{
|
||||
ipv4_address{ addressBytes },
|
||||
ntohs(ipv4Address.sin_port)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(address.sa_family == AF_INET6);
|
||||
|
||||
SOCKADDR_IN6 ipv6Address;
|
||||
std::memcpy(&ipv6Address, &address, sizeof(ipv6Address));
|
||||
|
||||
return ipv6_endpoint{
|
||||
ipv6_address{ ipv6Address.sin6_addr.u.Byte },
|
||||
ntohs(ipv6Address.sin6_port)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
int cppcoro::net::detail::ip_endpoint_to_sockaddr(
|
||||
const ip_endpoint& endPoint,
|
||||
std::reference_wrapper<sockaddr_storage> address) noexcept
|
||||
{
|
||||
if (endPoint.is_ipv4())
|
||||
{
|
||||
const auto& ipv4EndPoint = endPoint.to_ipv4();
|
||||
|
||||
SOCKADDR_IN ipv4Address;
|
||||
ipv4Address.sin_family = AF_INET;
|
||||
std::memcpy(&ipv4Address.sin_addr, ipv4EndPoint.address().bytes(), 4);
|
||||
ipv4Address.sin_port = htons(ipv4EndPoint.port());
|
||||
std::memset(&ipv4Address.sin_zero, 0, sizeof(ipv4Address.sin_zero));
|
||||
|
||||
std::memcpy(&address.get(), &ipv4Address, sizeof(ipv4Address));
|
||||
|
||||
return sizeof(SOCKADDR_IN);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& ipv6EndPoint = endPoint.to_ipv6();
|
||||
|
||||
SOCKADDR_IN6 ipv6Address;
|
||||
ipv6Address.sin6_family = AF_INET6;
|
||||
std::memcpy(&ipv6Address.sin6_addr, ipv6EndPoint.address().bytes(), 16);
|
||||
ipv6Address.sin6_port = htons(ipv6EndPoint.port());
|
||||
ipv6Address.sin6_flowinfo = 0;
|
||||
ipv6Address.sin6_scope_struct = SCOPEID_UNSPECIFIED_INIT;
|
||||
|
||||
std::memcpy(&address.get(), &ipv6Address, sizeof(ipv6Address));
|
||||
|
||||
return sizeof(SOCKADDR_IN6);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CPPCORO_OS_WINNT
|
|
@ -1,47 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef CPPCORO_PRIVATE_SOCKET_HELPERS_HPP_INCLUDED
|
||||
#define CPPCORO_PRIVATE_SOCKET_HELPERS_HPP_INCLUDED
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <cppcoro/detail/win32.hpp>
|
||||
struct sockaddr;
|
||||
struct sockaddr_storage;
|
||||
#endif
|
||||
|
||||
namespace cppcoro
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
class ip_endpoint;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
#if CPPCORO_OS_WINNT
|
||||
/// Convert a sockaddr to an IP endpoint.
|
||||
ip_endpoint sockaddr_to_ip_endpoint(const sockaddr& address) noexcept;
|
||||
|
||||
/// Converts an ip_endpoint to a sockaddr structure.
|
||||
///
|
||||
/// \param endPoint
|
||||
/// The IP endpoint to convert to a sockaddr structure.
|
||||
///
|
||||
/// \param address
|
||||
/// The sockaddr structure to populate.
|
||||
///
|
||||
/// \return
|
||||
/// The length of the sockaddr structure that was populated.
|
||||
int ip_endpoint_to_sockaddr(
|
||||
const ip_endpoint& endPoint,
|
||||
std::reference_wrapper<sockaddr_storage> address) noexcept;
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,96 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/socket_recv_from_operation.hpp>
|
||||
#include <cppcoro/net/socket.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include "socket_helpers.hpp"
|
||||
|
||||
# include <WinSock2.h>
|
||||
# include <WS2tcpip.h>
|
||||
# include <MSWSock.h>
|
||||
# include <Windows.h>
|
||||
|
||||
bool cppcoro::net::socket_recv_from_operation_impl::try_start(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
static_assert(
|
||||
sizeof(m_sourceSockaddrStorage) >= sizeof(SOCKADDR_IN) &&
|
||||
sizeof(m_sourceSockaddrStorage) >= sizeof(SOCKADDR_IN6));
|
||||
static_assert(
|
||||
sockaddrStorageAlignment >= alignof(SOCKADDR_IN) &&
|
||||
sockaddrStorageAlignment >= alignof(SOCKADDR_IN6));
|
||||
|
||||
// Need to read this flag before starting the operation, otherwise
|
||||
// it may be possible that the operation will complete immediately
|
||||
// on another thread, resume the coroutine and then destroy the
|
||||
// socket before we get a chance to read it.
|
||||
const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success();
|
||||
|
||||
m_sourceSockaddrLength = sizeof(m_sourceSockaddrStorage);
|
||||
|
||||
DWORD numberOfBytesReceived = 0;
|
||||
DWORD flags = 0;
|
||||
int result = ::WSARecvFrom(
|
||||
m_socket.native_handle(),
|
||||
reinterpret_cast<WSABUF*>(&m_buffer),
|
||||
1, // buffer count
|
||||
&numberOfBytesReceived,
|
||||
&flags,
|
||||
reinterpret_cast<sockaddr*>(&m_sourceSockaddrStorage),
|
||||
&m_sourceSockaddrLength,
|
||||
operation.get_overlapped(),
|
||||
nullptr);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
int errorCode = ::WSAGetLastError();
|
||||
if (errorCode != WSA_IO_PENDING)
|
||||
{
|
||||
// Failed synchronously.
|
||||
operation.m_errorCode = static_cast<DWORD>(errorCode);
|
||||
operation.m_numberOfBytesTransferred = numberOfBytesReceived;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (skipCompletionOnSuccess)
|
||||
{
|
||||
// Completed synchronously, no completion event will be posted to the IOCP.
|
||||
operation.m_errorCode = ERROR_SUCCESS;
|
||||
operation.m_numberOfBytesTransferred = numberOfBytesReceived;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Operation will complete asynchronously.
|
||||
return true;
|
||||
}
|
||||
|
||||
void cppcoro::net::socket_recv_from_operation_impl::cancel(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
(void)::CancelIoEx(
|
||||
reinterpret_cast<HANDLE>(m_socket.native_handle()),
|
||||
operation.get_overlapped());
|
||||
}
|
||||
|
||||
std::tuple<std::size_t, cppcoro::net::ip_endpoint>
|
||||
cppcoro::net::socket_recv_from_operation_impl::get_result(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation)
|
||||
{
|
||||
if (operation.m_errorCode != ERROR_SUCCESS)
|
||||
{
|
||||
throw std::system_error(
|
||||
static_cast<int>(operation.m_errorCode),
|
||||
std::system_category(),
|
||||
"Error receiving message on socket: WSARecvFrom");
|
||||
}
|
||||
|
||||
return std::make_tuple(
|
||||
static_cast<std::size_t>(operation.m_numberOfBytesTransferred),
|
||||
detail::sockaddr_to_ip_endpoint(
|
||||
*reinterpret_cast<SOCKADDR*>(&m_sourceSockaddrStorage)));
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,66 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/socket_recv_operation.hpp>
|
||||
#include <cppcoro/net/socket.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <WinSock2.h>
|
||||
# include <WS2tcpip.h>
|
||||
# include <MSWSock.h>
|
||||
# include <Windows.h>
|
||||
|
||||
bool cppcoro::net::socket_recv_operation_impl::try_start(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
// Need to read this flag before starting the operation, otherwise
|
||||
// it may be possible that the operation will complete immediately
|
||||
// on another thread and then destroy the socket before we get a
|
||||
// chance to read it.
|
||||
const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success();
|
||||
|
||||
DWORD numberOfBytesReceived = 0;
|
||||
DWORD flags = 0;
|
||||
int result = ::WSARecv(
|
||||
m_socket.native_handle(),
|
||||
reinterpret_cast<WSABUF*>(&m_buffer),
|
||||
1, // buffer count
|
||||
&numberOfBytesReceived,
|
||||
&flags,
|
||||
operation.get_overlapped(),
|
||||
nullptr);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
int errorCode = ::WSAGetLastError();
|
||||
if (errorCode != WSA_IO_PENDING)
|
||||
{
|
||||
// Failed synchronously.
|
||||
operation.m_errorCode = static_cast<DWORD>(errorCode);
|
||||
operation.m_numberOfBytesTransferred = numberOfBytesReceived;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (skipCompletionOnSuccess)
|
||||
{
|
||||
// Completed synchronously, no completion event will be posted to the IOCP.
|
||||
operation.m_errorCode = ERROR_SUCCESS;
|
||||
operation.m_numberOfBytesTransferred = numberOfBytesReceived;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Operation will complete asynchronously.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void cppcoro::net::socket_recv_operation_impl::cancel(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
(void)::CancelIoEx(
|
||||
reinterpret_cast<HANDLE>(m_socket.native_handle()),
|
||||
operation.get_overlapped());
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,64 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/socket_send_operation.hpp>
|
||||
#include <cppcoro/net/socket.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include <WinSock2.h>
|
||||
# include <WS2tcpip.h>
|
||||
# include <MSWSock.h>
|
||||
# include <Windows.h>
|
||||
|
||||
bool cppcoro::net::socket_send_operation_impl::try_start(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
// Need to read this flag before starting the operation, otherwise
|
||||
// it may be possible that the operation will complete immediately
|
||||
// on another thread and then destroy the socket before we get a
|
||||
// chance to read it.
|
||||
const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success();
|
||||
|
||||
DWORD numberOfBytesSent = 0;
|
||||
int result = ::WSASend(
|
||||
m_socket.native_handle(),
|
||||
reinterpret_cast<WSABUF*>(&m_buffer),
|
||||
1, // buffer count
|
||||
&numberOfBytesSent,
|
||||
0, // flags
|
||||
operation.get_overlapped(),
|
||||
nullptr);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
int errorCode = ::WSAGetLastError();
|
||||
if (errorCode != WSA_IO_PENDING)
|
||||
{
|
||||
// Failed synchronously.
|
||||
operation.m_errorCode = static_cast<DWORD>(errorCode);
|
||||
operation.m_numberOfBytesTransferred = numberOfBytesSent;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (skipCompletionOnSuccess)
|
||||
{
|
||||
// Completed synchronously, no completion event will be posted to the IOCP.
|
||||
operation.m_errorCode = ERROR_SUCCESS;
|
||||
operation.m_numberOfBytesTransferred = numberOfBytesSent;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Operation will complete asynchronously.
|
||||
return true;
|
||||
}
|
||||
|
||||
void cppcoro::net::socket_send_operation_impl::cancel(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
(void)::CancelIoEx(
|
||||
reinterpret_cast<HANDLE>(m_socket.native_handle()),
|
||||
operation.get_overlapped());
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,72 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/net/socket_send_to_operation.hpp>
|
||||
#include <cppcoro/net/socket.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# include "socket_helpers.hpp"
|
||||
|
||||
# include <WinSock2.h>
|
||||
# include <WS2tcpip.h>
|
||||
# include <MSWSock.h>
|
||||
# include <Windows.h>
|
||||
|
||||
bool cppcoro::net::socket_send_to_operation_impl::try_start(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
// Need to read this flag before starting the operation, otherwise
|
||||
// it may be possible that the operation will complete immediately
|
||||
// on another thread and then destroy the socket before we get a
|
||||
// chance to read it.
|
||||
const bool skipCompletionOnSuccess = m_socket.skip_completion_on_success();
|
||||
|
||||
SOCKADDR_STORAGE destinationAddress;
|
||||
const int destinationLength = detail::ip_endpoint_to_sockaddr(
|
||||
m_destination, std::ref(destinationAddress));
|
||||
|
||||
DWORD numberOfBytesSent = 0;
|
||||
int result = ::WSASendTo(
|
||||
m_socket.native_handle(),
|
||||
reinterpret_cast<WSABUF*>(&m_buffer),
|
||||
1, // buffer count
|
||||
&numberOfBytesSent,
|
||||
0, // flags
|
||||
reinterpret_cast<const SOCKADDR*>(&destinationAddress),
|
||||
destinationLength,
|
||||
operation.get_overlapped(),
|
||||
nullptr);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
int errorCode = ::WSAGetLastError();
|
||||
if (errorCode != WSA_IO_PENDING)
|
||||
{
|
||||
// Failed synchronously.
|
||||
operation.m_errorCode = static_cast<DWORD>(errorCode);
|
||||
operation.m_numberOfBytesTransferred = numberOfBytesSent;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (skipCompletionOnSuccess)
|
||||
{
|
||||
// Completed synchronously, no completion event will be posted to the IOCP.
|
||||
operation.m_errorCode = ERROR_SUCCESS;
|
||||
operation.m_numberOfBytesTransferred = numberOfBytesSent;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Operation will complete asynchronously.
|
||||
return true;
|
||||
}
|
||||
|
||||
void cppcoro::net::socket_send_to_operation_impl::cancel(
|
||||
cppcoro::detail::win32_overlapped_operation_base& operation) noexcept
|
||||
{
|
||||
(void)::CancelIoEx(
|
||||
reinterpret_cast<HANDLE>(m_socket.native_handle()),
|
||||
operation.get_overlapped());
|
||||
}
|
||||
|
||||
#endif
|
|
@ -12,6 +12,7 @@
|
|||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -319,7 +320,7 @@ namespace cppcoro
|
|||
};
|
||||
|
||||
void static_thread_pool::schedule_operation::await_suspend(
|
||||
std::experimental::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
std::coroutine_handle<> awaitingCoroutine) noexcept
|
||||
{
|
||||
m_awaitingCoroutine = awaitingCoroutine;
|
||||
m_threadPool->schedule_impl(this);
|
||||
|
|
20
lib/use.cake
20
lib/use.cake
|
@ -1,20 +0,0 @@
|
|||
###############################################################################
|
||||
# Copyright (c) Lewis Baker
|
||||
# Licenced under MIT license. See LICENSE.txt for details.
|
||||
###############################################################################
|
||||
|
||||
import cake.path
|
||||
|
||||
from cake.tools import script, env, compiler, variant
|
||||
|
||||
compiler.addIncludePath(env.expand('${CPPCORO}/include'))
|
||||
|
||||
buildScript = script.get(script.cwd('build.cake'))
|
||||
compiler.addLibrary(buildScript.getResult('library'))
|
||||
|
||||
if variant.platform == "windows":
|
||||
compiler.addLibrary("Synchronization")
|
||||
compiler.addLibrary("kernel32")
|
||||
compiler.addLibrary("WS2_32")
|
||||
compiler.addLibrary("Mswsock")
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
|
||||
#include <cppcoro/detail/win32.hpp>
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
|
@ -18,3 +20,5 @@ void cppcoro::detail::win32::safe_handle::close() noexcept
|
|||
m_handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,75 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/writable_file.hpp>
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <Windows.h>
|
||||
|
||||
void cppcoro::writable_file::set_size(
|
||||
std::uint64_t fileSize)
|
||||
{
|
||||
LARGE_INTEGER position;
|
||||
position.QuadPart = fileSize;
|
||||
|
||||
BOOL ok = ::SetFilePointerEx(m_fileHandle.handle(), position, nullptr, FILE_BEGIN);
|
||||
if (!ok)
|
||||
{
|
||||
DWORD errorCode = ::GetLastError();
|
||||
throw std::system_error
|
||||
{
|
||||
static_cast<int>(errorCode),
|
||||
std::system_category(),
|
||||
"error setting file size: SetFilePointerEx"
|
||||
};
|
||||
}
|
||||
|
||||
ok = ::SetEndOfFile(m_fileHandle.handle());
|
||||
if (!ok)
|
||||
{
|
||||
DWORD errorCode = ::GetLastError();
|
||||
throw std::system_error
|
||||
{
|
||||
static_cast<int>(errorCode),
|
||||
std::system_category(),
|
||||
"error setting file size: SetEndOfFile"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
cppcoro::file_write_operation cppcoro::writable_file::write(
|
||||
std::uint64_t offset,
|
||||
const void* buffer,
|
||||
std::size_t byteCount) noexcept
|
||||
{
|
||||
return file_write_operation{
|
||||
m_fileHandle.handle(),
|
||||
offset,
|
||||
buffer,
|
||||
byteCount
|
||||
};
|
||||
}
|
||||
|
||||
cppcoro::file_write_operation_cancellable cppcoro::writable_file::write(
|
||||
std::uint64_t offset,
|
||||
const void* buffer,
|
||||
std::size_t byteCount,
|
||||
cancellation_token ct) noexcept
|
||||
{
|
||||
return file_write_operation_cancellable{
|
||||
m_fileHandle.handle(),
|
||||
offset,
|
||||
buffer,
|
||||
byteCount,
|
||||
std::move(ct)
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,37 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro\write_only_file.hpp>
|
||||
|
||||
#if CPPCORO_OS_WINNT
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <Windows.h>
|
||||
|
||||
cppcoro::write_only_file cppcoro::write_only_file::open(
|
||||
io_service& ioService,
|
||||
const std::experimental::filesystem::path& path,
|
||||
file_open_mode openMode,
|
||||
file_share_mode shareMode,
|
||||
file_buffering_mode bufferingMode)
|
||||
{
|
||||
return write_only_file(file::open(
|
||||
GENERIC_WRITE,
|
||||
ioService,
|
||||
path,
|
||||
openMode,
|
||||
shareMode,
|
||||
bufferingMode));
|
||||
}
|
||||
|
||||
cppcoro::write_only_file::write_only_file(
|
||||
detail::win32::safe_handle&& fileHandle) noexcept
|
||||
: file(std::move(fileHandle))
|
||||
, writable_file(detail::win32::safe_handle{})
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,140 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Lewis Baker
|
||||
// Licenced under MIT license. See LICENSE.txt for details.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cppcoro/async_auto_reset_event.hpp>
|
||||
|
||||
#include <cppcoro/config.hpp>
|
||||
#include <cppcoro/task.hpp>
|
||||
#include <cppcoro/sync_wait.hpp>
|
||||
#include <cppcoro/when_all.hpp>
|
||||
#include <cppcoro/when_all_ready.hpp>
|
||||
#include <cppcoro/on_scope_exit.hpp>
|
||||
#include <cppcoro/static_thread_pool.hpp>
|
||||
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include <ostream>
|
||||
#include "doctest/doctest.h"
|
||||
|
||||
TEST_SUITE_BEGIN("async_auto_reset_event");
|
||||
|
||||
TEST_CASE("single waiter")
|
||||
{
|
||||
cppcoro::async_auto_reset_event event;
|
||||
|
||||
bool started = false;
|
||||
bool finished = false;
|
||||
auto run = [&]() -> cppcoro::task<>
|
||||
{
|
||||
started = true;
|
||||
co_await event;
|
||||
finished = true;
|
||||
};
|
||||
|
||||
auto check = [&]() -> cppcoro::task<>
|
||||
{
|
||||
CHECK(started);
|
||||
CHECK(!finished);
|
||||
|
||||
event.set();
|
||||
|
||||
CHECK(finished);
|
||||
|
||||
co_return;
|
||||
};
|
||||
|
||||
cppcoro::sync_wait(cppcoro::when_all_ready(run(), check()));
|
||||
}
|
||||
|
||||
TEST_CASE("multiple waiters")
|
||||
{
|
||||
cppcoro::async_auto_reset_event event;
|
||||
|
||||
|
||||
auto run = [&](bool& flag) -> cppcoro::task<>
|
||||
{
|
||||
co_await event;
|
||||
flag = true;
|
||||
};
|
||||
|
||||
bool completed1 = false;
|
||||
bool completed2 = false;
|
||||
|
||||
auto check = [&]() -> cppcoro::task<>
|
||||
{
|
||||
CHECK(!completed1);
|
||||
CHECK(!completed2);
|
||||
|
||||
event.set();
|
||||
|
||||
CHECK(completed1);
|
||||
CHECK(!completed2);
|
||||
|
||||
event.set();
|
||||
|
||||
CHECK(completed2);
|
||||
|
||||
co_return;
|
||||
};
|
||||
|
||||
cppcoro::sync_wait(cppcoro::when_all_ready(
|
||||
run(completed1),
|
||||
run(completed2),
|
||||
check()));
|
||||
}
|
||||
|
||||
TEST_CASE("multi-threaded")
|
||||
{
|
||||
cppcoro::static_thread_pool tp{ 3 };
|
||||
|
||||
auto run = [&]() -> cppcoro::task<>
|
||||
{
|
||||
cppcoro::async_auto_reset_event event;
|
||||
|
||||
int value = 0;
|
||||
|
||||
auto startWaiter = [&]() -> cppcoro::task<>
|
||||
{
|
||||
co_await tp.schedule();
|
||||
co_await event;
|
||||
++value;
|
||||
event.set();
|
||||
};
|
||||
|
||||
auto startSignaller = [&]() -> cppcoro::task<>
|
||||
{
|
||||
co_await tp.schedule();
|
||||
value = 5;
|
||||
event.set();
|
||||
};
|
||||
|
||||
std::vector<cppcoro::task<>> tasks;
|
||||
|
||||
tasks.emplace_back(startSignaller());
|
||||
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
tasks.emplace_back(startWaiter());
|
||||
}
|
||||
|
||||
co_await cppcoro::when_all(std::move(tasks));
|
||||
|
||||
// NOTE: Can't use CHECK() here because it's not thread-safe
|
||||
assert(value == 1005);
|
||||
};
|
||||
|
||||
std::vector<cppcoro::task<>> tasks;
|
||||
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
tasks.emplace_back(run());
|
||||
}
|
||||
|
||||
cppcoro::sync_wait(cppcoro::when_all(std::move(tasks)));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue