Add lite branch

This commit is contained in:
Andrey Rodionov 2023-07-08 22:28:28 +03:00
parent 391215262b
commit d6cd7b3d18
135 changed files with 177 additions and 20319 deletions

View File

@ -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

View File

@ -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

3
.gitignore vendored
View File

@ -1,3 +0,0 @@
build
*.cakec
*.pyc

4
.gitmodules vendored
View File

@ -1,4 +0,0 @@
[submodule "cake"]
path = tools/cake
url = https://github.com/lewissbaker/cake.git
branch = clang

View File

@ -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

18
CMakeLists.txt Normal file
View File

@ -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}
)

View File

@ -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.

View File

@ -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"

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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,
)

View File

@ -1 +0,0 @@
@python %~dp0tools\cake\src\run.py %*

View File

@ -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)

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;
};
}

View File

@ -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;
};

View File

@ -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

View File

@ -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<>>()))>>
{};
}
}

View File

@ -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)

View File

@ -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;
};
}

View File

@ -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)
{

View File

@ -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{};
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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) };
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;
};

View File

@ -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)
};
}
}

View File

@ -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 };

View File

@ -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;
};
}

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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) };
}
}

View File

@ -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

View File

@ -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

View File

@ -1,7 +0,0 @@
#!/bin/sh
export CAKE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/tools/cake/src/run.py"
function cake()
{
python2.7 "$CAKE" "$@"
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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,
)

View File

@ -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);
}

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 };
}

View File

@ -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 };
}

View File

@ -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 };
}

View File

@ -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 };
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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