[
  {
    "path": ".github/workflows/cmake.yml",
    "content": "name: CMake\n\non: [push]\n\nenv:\n  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)\n  BUILD_TYPE: Release\n\njobs:\n  build:\n    # The CMake configure and build commands are platform agnostic and should work equally\n    # well on Windows or Mac.  You can convert this to a matrix build if you need\n    # cross-platform coverage.\n    # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n\n    - name: Create Build Environment\n      # Some projects don't allow in-source building, so create a separate build directory\n      # We'll use this as our working directory for all subsequent commands\n      run: cmake -E make_directory ${{github.workspace}}/build\n\n    - name: Configure CMake\n      # Use a bash shell so we can use the same syntax for environment variable\n      # access regardless of the host operating system\n      shell: bash\n      working-directory: ${{github.workspace}}/build\n      # Note the current convention is to use the -S and -B options here to specify source \n      # and build directories, but this is only available with CMake 3.13 and higher.  \n      # The CMake binaries on the Github Actions machines are (as of this writing) 3.12\n      run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE\n\n    - name: Build\n      working-directory: ${{github.workspace}}/build\n      shell: bash\n      # Execute the build.  You can specify a specific target with \"--target <NAME>\"\n      run: cmake --build . --config $BUILD_TYPE\n\n    #- name: Test\n    #  working-directory: ${{github.workspace}}/build\n    #  shell: bash\n      # Execute tests defined by the CMake configuration.  \n      # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail\n    #  run: ctest -C $BUILD_TYPE\n"
  },
  {
    "path": ".gitignore",
    "content": "﻿################################################################################\r\n# This .gitignore file was automatically created by Microsoft(R) Visual Studio.\r\n################################################################################\r\n\r\n/vs_proj/x64\r\n/vs_proj/.vs\r\n/vs_proj/librf.vcxproj.user\r\n/.vs\r\n/vs_proj/Release\r\n/vs_proj/Debug\r\nprivate\r\nbin\r\nout\r\n/CMakeSettings.json\r\ninstall\r\nlib\r\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"modern_cb\"]\n\tpath = modern_cb\n\turl = https://github.com/tearshark/modern_cb.git\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "﻿cmake_minimum_required(VERSION 3.10)\r\nproject(librf VERSION 3.0)\r\n\r\nset(CMAKE_CXX_STANDARD 20)\r\nset(CMAKE_CXX_STANDARD_REQUIRED true)\r\n\r\nset(LIBRF_COMPILER_SETTING )\r\nif(WIN32)\r\n\tif(\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\r\n\t\tset(LIBRF_COMPILER_SETTING \"clang_on_msvc\")\r\n\telseif(\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"MSVC\")\r\n\t\tset(LIBRF_COMPILER_SETTING \"msvc\")\r\n\telse()\r\n\t\tset(LIBRF_COMPILER_SETTING \"gcc\")\r\n\tendif()\r\nelseif(APPLE)\r\n\tif(\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"AppleClang\")\r\n\t\tset(LIBRF_COMPILER_SETTING \"clang\")\r\n\telse()\r\n\t\tset(LIBRF_COMPILER_SETTING \"gcc\")\r\n\tendif()\r\nelseif(UNIX)\r\n\tif(\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\r\n\t\tset(LIBRF_COMPILER_SETTING \"clang\")\r\n\telse()\r\n\t\tset(LIBRF_COMPILER_SETTING \"gcc\")\r\n\tendif()\r\nelse()\r\n\tset(LIBRF_COMPILER_SETTING \"clang\")\r\nendif()\r\n\r\nmessage(STATUS \"LIBRF_COMPILER_SETTING=${LIBRF_COMPILER_SETTING}\")\r\nmessage(STATUS \"CMAKE_CXX_COMPILER_VERSION=${CMAKE_CXX_COMPILER_VERSION}\")\r\n\r\nif(${LIBRF_COMPILER_SETTING} STREQUAL \"msvc\")\r\n\tif (${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER_EQUAL \"19.30.0.0\")\r\n\t\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} /std:c++20 /EHsc\")\t\t#VS2022\r\n\telse()\r\n\t\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} /std:c++latest /EHsc /await\")\t#VS2019\r\n\tendif()\r\nelseif (\"${LIBRF_COMPILER_SETTING}\" STREQUAL \"clang_on_msvc\")\r\n\tif (${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER_EQUAL \"12.0.0\")\r\n\t\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} /std:c++20 /EHsc -Wno-unused-private-field\")\t\t#VS2022\r\n\telse()\r\n\t\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} /std:c++latest /EHsc -Wno-unused-private-field\")\t#VS2019\r\n\tendif()\r\nelseif (\"${LIBRF_COMPILER_SETTING}\" STREQUAL \"clang\")\r\n\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++2a -fcoroutines-ts -stdlib=libstdc++\")\r\nelseif (\"${LIBRF_COMPILER_SETTING}\" STREQUAL \"gcc\")\r\n\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++2a -fcoroutines -fconcepts-diagnostics-depth=8\")\r\nendif()\r\n\r\n\r\noption(LIBRF_DEBUG_COUNTER \"Debug objects count\" OFF)\r\noption(LIBRF_KEEP_REAL_SIZE \"Keep real size in queue\" OFF)\r\noption(LIBRF_DISABLE_MULT_THREAD \"Disable multi-threaded scheduler\" OFF)\r\noption(LIBRF_USE_MIMALLOC \"Use mimalloc\" OFF)\r\noption(LIBRF_DYNAMIC_LIBRARY \"Use shared library\" ON)\r\noption(CMAKE_ENABLE_UNIT_TEST \"Enable unit test\" OFF)\r\n\r\nif (UNIX)\r\n\tif(LIBRF_USE_MIMALLOC)\r\n\t\tfind_package(mimalloc 1.4 REQUIRED)\r\n\tendif()\r\n\r\n\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fPIC -pthread\")\r\nendif()\r\n\r\nif(${LIBRF_COMPILER_SETTING} STREQUAL \"msvc\")\r\n\tset(CMAKE_CXX_FLAGS_MINSIZEREL \"/W3 /WX /MP /GS- /Gm- /Ox /Ob2 /Oy /Oi /Os /GT /EHsc /Zc:inline\")\r\n\tset(CMAKE_CXX_FLAGS_RELEASE    \"/W3 /WX /MP /GS- /Gm- /Ox /Ob2 /Oy /Oi /Os /GT /EHsc /Zc:inline\")\r\nelseif (\"${LIBRF_COMPILER_SETTING}\" STREQUAL \"clang_on_msvc\")\r\n\tset(CMAKE_CXX_FLAGS_MINSIZEREL \"/W3 /GS- /Ox /Ob2 /Oy /Oi /Os /EHsc /Zc:inline\")\r\n\tset(CMAKE_CXX_FLAGS_RELEASE    \"/W3 /GS- /Ox /Ob2 /Oy /Oi /Os /EHsc /Zc:inline\")\r\nelseif()\r\n\tif(CMAKE_BUILD_TYPE STREQUAL \"Debug\")\r\n\t\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -g -ggdb\")\r\n\telse()\r\n\t\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -O3 -DNDEBUG\")\r\n\tendif()\r\nendif()\r\n\r\n\r\nmessage(STATUS \"C++ flags: ${CMAKE_CXX_FLAGS}\")\r\n\r\n#set(RESUMEF_USE_CUSTOM_SPINLOCK \"std::mutex\")\r\n\r\nif(LIBRF_DEBUG_COUNTER)\r\n\tset(RESUMEF_DEBUG_COUNTER 1)\r\nendif()\r\nif(LIBRF_KEEP_REAL_SIZE)\r\n\tset(_WITH_LOCK_FREE_Q_KEEP_REAL_SIZE 1)\r\nendif()\r\nif(LIBRF_DISABLE_MULT_THREAD)\r\n\tset(RESUMEF_DISABLE_MULT_THREAD 1)\r\nendif()\r\n\r\nconfigure_file(\r\n\t${CMAKE_CURRENT_SOURCE_DIR}/config.h.in\r\n\t${CMAKE_CURRENT_SOURCE_DIR}/include/librf/src/config.h\r\n)\r\n\r\n\r\nfile(GLOB_RECURSE HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/*.*)\r\nfile(GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/source/*.*)\r\n\r\nset(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)\r\nset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)\r\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)\r\n\r\nif(LIBRF_DYNAMIC_LIBRARY)\r\n\tadd_library(${PROJECT_NAME} SHARED\r\n\t\t${HEADER_FILES}\r\n\t\t${SOURCE_FILES}\r\n\t)\r\n\ttarget_compile_definitions(${PROJECT_NAME}\r\n\t\tPRIVATE LIBRF_DYNAMIC_EXPORTS=1\r\n\t)\r\nelse()\r\n\tadd_library(${PROJECT_NAME} STATIC\r\n\t\t${HEADER_FILES}\r\n\t\t${SOURCE_FILES}\r\n\t)\r\n\ttarget_compile_definitions(${PROJECT_NAME}\r\n\t\tPRIVATE LIBRF_USE_STATIC_LIBRARY=1\r\n\t)\r\nendif()\r\n\r\ntarget_include_directories(${PROJECT_NAME}\r\n\tPUBLIC\r\n\t${CMAKE_CURRENT_SOURCE_DIR}/include\r\n\t${CMAKE_CURRENT_SOURCE_DIR}/modern_cb\r\n)\r\n\r\nif(UNIX)\r\n    set_target_properties(${PROJECT_NAME} PROPERTIES INSTALL_RPATH \"$ORIGIN/\")\r\nendif(UNIX)\r\n\r\nif(LIBRF_USE_MIMALLOC)\r\n\tset(LIB_MIMALLOC, \"mimalloc\")\r\nelse()\r\n\tset(LIB_MIMALLOC, \"\")\r\nendif()\r\n\r\nif(CMAKE_ENABLE_UNIT_TEST)\r\n\tadd_subdirectory(tutorial)\r\n\r\n\taux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/tutorial ALL_TUTORIAL_FILES)\r\n\tadd_executable(test_librf\r\n\t\t${CMAKE_CURRENT_SOURCE_DIR}/test_librf.cpp\r\n\t\t${CMAKE_CURRENT_SOURCE_DIR}/benchmark/benchmark_async_mem.cpp\r\n\t\t${CMAKE_CURRENT_SOURCE_DIR}/benchmark/benchmark_channel_passing_next.cpp\r\n\t\t${ALL_TUTORIAL_FILES})\r\n\ttarget_link_libraries(test_librf PUBLIC librf)\r\n\tif(UNIX)\r\n\t\tset_target_properties(test_librf PROPERTIES INSTALL_RPATH \"$ORIGIN/\")\r\n\tendif(UNIX)\r\n\r\n\tadd_subdirectory(benchmark)\r\nendif()\r\n\r\n\r\ninclude(${CMAKE_SOURCE_DIR}/cmake/install.cmake)\r\ninstall(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/librf DESTINATION include)\r\n"
  },
  {
    "path": "Config.cmake.in",
    "content": "include(${CMAKE_CURRENT_LIST_DIR}/SelectDynamicLibrary.cmake)\nselect_dynamic_library(librf librf/librf.h)\n"
  },
  {
    "path": "Doxyfile",
    "content": "# Doxyfile 1.8.17\r\n\r\n# This file describes the settings to be used by the documentation system\r\n# doxygen (www.doxygen.org) for a project.\r\n#\r\n# All text after a double hash (##) is considered a comment and is placed in\r\n# front of the TAG it is preceding.\r\n#\r\n# All text after a single hash (#) is considered a comment and will be ignored.\r\n# The format is:\r\n# TAG = value [value, ...]\r\n# For lists, items can also be appended using:\r\n# TAG += value [value, ...]\r\n# Values that contain spaces should be placed between quotes (\\\" \\\").\r\n\r\n#---------------------------------------------------------------------------\r\n# Project related configuration options\r\n#---------------------------------------------------------------------------\r\n\r\n# This tag specifies the encoding used for all characters in the configuration\r\n# file that follow. The default is UTF-8 which is also the encoding used for all\r\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\r\n# iconv built into libc) for the transcoding. See\r\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\r\n# The default value is: UTF-8.\r\n\r\nDOXYFILE_ENCODING      = UTF-8\r\n\r\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\r\n# double-quotes, unless you are using Doxywizard) that should identify the\r\n# project for which the documentation is generated. This name is used in the\r\n# title of most generated pages and in a few other places.\r\n# The default value is: My Project.\r\n\r\nPROJECT_NAME           = librf\r\n\r\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\r\n# could be handy for archiving the generated documentation or if some version\r\n# control system is used.\r\n\r\nPROJECT_NUMBER         =\r\n\r\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\r\n# for a project that appears at the top of each page and should give viewer a\r\n# quick idea about the purpose of the project. Keep the description short.\r\n\r\nPROJECT_BRIEF          =\r\n\r\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\r\n# in the documentation. The maximum height of the logo should not exceed 55\r\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\r\n# the logo to the output directory.\r\n\r\nPROJECT_LOGO           =\r\n\r\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\r\n# into which the generated documentation will be written. If a relative path is\r\n# entered, it will be relative to the location where doxygen was started. If\r\n# left blank the current directory will be used.\r\n\r\nOUTPUT_DIRECTORY       = .\\doxygen\r\n\r\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\r\n# directories (in 2 levels) under the output directory of each output format and\r\n# will distribute the generated files over these directories. Enabling this\r\n# option can be useful when feeding doxygen a huge amount of source files, where\r\n# putting all generated files in the same directory would otherwise causes\r\n# performance problems for the file system.\r\n# The default value is: NO.\r\n\r\nCREATE_SUBDIRS         = NO\r\n\r\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\r\n# characters to appear in the names of generated files. If set to NO, non-ASCII\r\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\r\n# U+3044.\r\n# The default value is: NO.\r\n\r\nALLOW_UNICODE_NAMES    = NO\r\n\r\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\r\n# documentation generated by doxygen is written. Doxygen will use this\r\n# information to generate all constant output in the proper language.\r\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\r\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\r\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\r\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\r\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\r\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\r\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\r\n# Ukrainian and Vietnamese.\r\n# The default value is: English.\r\n\r\nOUTPUT_LANGUAGE        = Chinese\r\n\r\n# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all\r\n# documentation generated by doxygen is written. Doxygen will use this\r\n# information to generate all generated output in the proper direction.\r\n# Possible values are: None, LTR, RTL and Context.\r\n# The default value is: None.\r\n\r\nOUTPUT_TEXT_DIRECTION  = None\r\n\r\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\r\n# descriptions after the members that are listed in the file and class\r\n# documentation (similar to Javadoc). Set to NO to disable this.\r\n# The default value is: YES.\r\n\r\nBRIEF_MEMBER_DESC      = YES\r\n\r\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\r\n# description of a member or function before the detailed description\r\n#\r\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\r\n# brief descriptions will be completely suppressed.\r\n# The default value is: YES.\r\n\r\nREPEAT_BRIEF           = YES\r\n\r\n# This tag implements a quasi-intelligent brief description abbreviator that is\r\n# used to form the text in various listings. Each string in this list, if found\r\n# as the leading text of the brief description, will be stripped from the text\r\n# and the result, after processing the whole list, is used as the annotated\r\n# text. Otherwise, the brief description is used as-is. If left blank, the\r\n# following values are used ($name is automatically replaced with the name of\r\n# the entity):The $name class, The $name widget, The $name file, is, provides,\r\n# specifies, contains, represents, a, an and the.\r\n\r\nABBREVIATE_BRIEF       = \"The $name class\" \\\r\n                         \"The $name widget\" \\\r\n                         \"The $name file\" \\\r\n                         is \\\r\n                         provides \\\r\n                         specifies \\\r\n                         contains \\\r\n                         represents \\\r\n                         a \\\r\n                         an \\\r\n                         the\r\n\r\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\r\n# doxygen will generate a detailed section even if there is only a brief\r\n# description.\r\n# The default value is: NO.\r\n\r\nALWAYS_DETAILED_SEC    = NO\r\n\r\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\r\n# inherited members of a class in the documentation of that class as if those\r\n# members were ordinary class members. Constructors, destructors and assignment\r\n# operators of the base classes will not be shown.\r\n# The default value is: NO.\r\n\r\nINLINE_INHERITED_MEMB  = NO\r\n\r\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\r\n# before files name in the file list and in the header files. If set to NO the\r\n# shortest path that makes the file name unique will be used\r\n# The default value is: YES.\r\n\r\nFULL_PATH_NAMES        = YES\r\n\r\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\r\n# Stripping is only done if one of the specified strings matches the left-hand\r\n# part of the path. The tag can be used to show relative paths in the file list.\r\n# If left blank the directory from which doxygen is run is used as the path to\r\n# strip.\r\n#\r\n# Note that you can specify absolute paths here, but also relative paths, which\r\n# will be relative from the directory where doxygen is started.\r\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\r\n\r\nSTRIP_FROM_PATH        =\r\n\r\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\r\n# path mentioned in the documentation of a class, which tells the reader which\r\n# header file to include in order to use a class. If left blank only the name of\r\n# the header file containing the class definition is used. Otherwise one should\r\n# specify the list of include paths that are normally passed to the compiler\r\n# using the -I flag.\r\n\r\nSTRIP_FROM_INC_PATH    =\r\n\r\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\r\n# less readable) file names. This can be useful is your file systems doesn't\r\n# support long names like on DOS, Mac, or CD-ROM.\r\n# The default value is: NO.\r\n\r\nSHORT_NAMES            = NO\r\n\r\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\r\n# first line (until the first dot) of a Javadoc-style comment as the brief\r\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\r\n# style comments (thus requiring an explicit @brief command for a brief\r\n# description.)\r\n# The default value is: NO.\r\n\r\nJAVADOC_AUTOBRIEF      = NO\r\n\r\n# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line\r\n# such as\r\n# /***************\r\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\r\n# Javadoc-style will behave just like regular comments and it will not be\r\n# interpreted by doxygen.\r\n# The default value is: NO.\r\n\r\nJAVADOC_BANNER         = NO\r\n\r\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\r\n# line (until the first dot) of a Qt-style comment as the brief description. If\r\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\r\n# requiring an explicit \\brief command for a brief description.)\r\n# The default value is: NO.\r\n\r\nQT_AUTOBRIEF           = NO\r\n\r\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\r\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\r\n# a brief description. This used to be the default behavior. The new default is\r\n# to treat a multi-line C++ comment block as a detailed description. Set this\r\n# tag to YES if you prefer the old behavior instead.\r\n#\r\n# Note that setting this tag to YES also means that rational rose comments are\r\n# not recognized any more.\r\n# The default value is: NO.\r\n\r\nMULTILINE_CPP_IS_BRIEF = NO\r\n\r\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\r\n# documentation from any documented member that it re-implements.\r\n# The default value is: YES.\r\n\r\nINHERIT_DOCS           = YES\r\n\r\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\r\n# page for each member. If set to NO, the documentation of a member will be part\r\n# of the file/class/namespace that contains it.\r\n# The default value is: NO.\r\n\r\nSEPARATE_MEMBER_PAGES  = NO\r\n\r\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\r\n# uses this value to replace tabs by spaces in code fragments.\r\n# Minimum value: 1, maximum value: 16, default value: 4.\r\n\r\nTAB_SIZE               = 4\r\n\r\n# This tag can be used to specify a number of aliases that act as commands in\r\n# the documentation. An alias has the form:\r\n# name=value\r\n# For example adding\r\n# \"sideeffect=@par Side Effects:\\n\"\r\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\r\n# documentation, which will result in a user-defined paragraph with heading\r\n# \"Side Effects:\". You can put \\n's in the value part of an alias to insert\r\n# newlines (in the resulting output). You can put ^^ in the value part of an\r\n# alias to insert a newline as if a physical newline was in the original file.\r\n# When you need a literal { or } or , in the value part of an alias you have to\r\n# escape them by means of a backslash (\\), this can lead to conflicts with the\r\n# commands \\{ and \\} for these it is advised to use the version @{ and @} or use\r\n# a double escape (\\\\{ and \\\\})\r\n\r\nALIASES                =\r\n\r\n# This tag can be used to specify a number of word-keyword mappings (TCL only).\r\n# A mapping has the form \"name=value\". For example adding \"class=itcl::class\"\r\n# will allow you to use the command class in the itcl::class meaning.\r\n\r\nTCL_SUBST              =\r\n\r\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\r\n# only. Doxygen will then generate output that is more tailored for C. For\r\n# instance, some of the names that are used will be different. The list of all\r\n# members will be omitted, etc.\r\n# The default value is: NO.\r\n\r\nOPTIMIZE_OUTPUT_FOR_C  = NO\r\n\r\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\r\n# Python sources only. Doxygen will then generate output that is more tailored\r\n# for that language. For instance, namespaces will be presented as packages,\r\n# qualified scopes will look different, etc.\r\n# The default value is: NO.\r\n\r\nOPTIMIZE_OUTPUT_JAVA   = NO\r\n\r\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\r\n# sources. Doxygen will then generate output that is tailored for Fortran.\r\n# The default value is: NO.\r\n\r\nOPTIMIZE_FOR_FORTRAN   = NO\r\n\r\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\r\n# sources. Doxygen will then generate output that is tailored for VHDL.\r\n# The default value is: NO.\r\n\r\nOPTIMIZE_OUTPUT_VHDL   = NO\r\n\r\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\r\n# sources only. Doxygen will then generate output that is more tailored for that\r\n# language. For instance, namespaces will be presented as modules, types will be\r\n# separated into more groups, etc.\r\n# The default value is: NO.\r\n\r\nOPTIMIZE_OUTPUT_SLICE  = NO\r\n\r\n# Doxygen selects the parser to use depending on the extension of the files it\r\n# parses. With this tag you can assign which parser to use for a given\r\n# extension. Doxygen has a built-in mapping, but you can override or extend it\r\n# using this tag. The format is ext=language, where ext is a file extension, and\r\n# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,\r\n# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,\r\n# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\r\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\r\n# tries to guess whether the code is fixed or free formatted code, this is the\r\n# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat\r\n# .inc files as Fortran files (default is PHP), and .f files as C (default is\r\n# Fortran), use: inc=Fortran f=C.\r\n#\r\n# Note: For files without extension you can use no_extension as a placeholder.\r\n#\r\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\r\n# the files are not read by doxygen.\r\n\r\nEXTENSION_MAPPING      =\r\n\r\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\r\n# according to the Markdown format, which allows for more readable\r\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\r\n# The output of markdown processing is further processed by doxygen, so you can\r\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\r\n# case of backward compatibilities issues.\r\n# The default value is: YES.\r\n\r\nMARKDOWN_SUPPORT       = YES\r\n\r\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\r\n# to that level are automatically included in the table of contents, even if\r\n# they do not have an id attribute.\r\n# Note: This feature currently applies only to Markdown headings.\r\n# Minimum value: 0, maximum value: 99, default value: 5.\r\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\r\n\r\nTOC_INCLUDE_HEADINGS   = 5\r\n\r\n# When enabled doxygen tries to link words that correspond to documented\r\n# classes, or namespaces to their corresponding documentation. Such a link can\r\n# be prevented in individual cases by putting a % sign in front of the word or\r\n# globally by setting AUTOLINK_SUPPORT to NO.\r\n# The default value is: YES.\r\n\r\nAUTOLINK_SUPPORT       = YES\r\n\r\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\r\n# to include (a tag file for) the STL sources as input, then you should set this\r\n# tag to YES in order to let doxygen match functions declarations and\r\n# definitions whose arguments contain STL classes (e.g. func(std::string);\r\n# versus func(std::string) {}). This also make the inheritance and collaboration\r\n# diagrams that involve STL classes more complete and accurate.\r\n# The default value is: NO.\r\n\r\nBUILTIN_STL_SUPPORT    = YES\r\n\r\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\r\n# enable parsing support.\r\n# The default value is: NO.\r\n\r\nCPP_CLI_SUPPORT        = NO\r\n\r\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\r\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\r\n# will parse them like normal C++ but will assume all classes use public instead\r\n# of private inheritance when no explicit protection keyword is present.\r\n# The default value is: NO.\r\n\r\nSIP_SUPPORT            = NO\r\n\r\n# For Microsoft's IDL there are propget and propput attributes to indicate\r\n# getter and setter methods for a property. Setting this option to YES will make\r\n# doxygen to replace the get and set methods by a property in the documentation.\r\n# This will only work if the methods are indeed getting or setting a simple\r\n# type. If this is not the case, or you want to show the methods anyway, you\r\n# should set this option to NO.\r\n# The default value is: YES.\r\n\r\nIDL_PROPERTY_SUPPORT   = YES\r\n\r\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\r\n# tag is set to YES then doxygen will reuse the documentation of the first\r\n# member in the group (if any) for the other members of the group. By default\r\n# all members of a group must be documented explicitly.\r\n# The default value is: NO.\r\n\r\nDISTRIBUTE_GROUP_DOC   = NO\r\n\r\n# If one adds a struct or class to a group and this option is enabled, then also\r\n# any nested class or struct is added to the same group. By default this option\r\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\r\n# The default value is: NO.\r\n\r\nGROUP_NESTED_COMPOUNDS = NO\r\n\r\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\r\n# (for instance a group of public functions) to be put as a subgroup of that\r\n# type (e.g. under the Public Functions section). Set it to NO to prevent\r\n# subgrouping. Alternatively, this can be done per class using the\r\n# \\nosubgrouping command.\r\n# The default value is: YES.\r\n\r\nSUBGROUPING            = YES\r\n\r\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\r\n# are shown inside the group in which they are included (e.g. using \\ingroup)\r\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\r\n# and RTF).\r\n#\r\n# Note that this feature does not work in combination with\r\n# SEPARATE_MEMBER_PAGES.\r\n# The default value is: NO.\r\n\r\nINLINE_GROUPED_CLASSES = NO\r\n\r\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\r\n# with only public data fields or simple typedef fields will be shown inline in\r\n# the documentation of the scope in which they are defined (i.e. file,\r\n# namespace, or group documentation), provided this scope is documented. If set\r\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\r\n# Man pages) or section (for LaTeX and RTF).\r\n# The default value is: NO.\r\n\r\nINLINE_SIMPLE_STRUCTS  = NO\r\n\r\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\r\n# enum is documented as struct, union, or enum with the name of the typedef. So\r\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\r\n# with name TypeT. When disabled the typedef will appear as a member of a file,\r\n# namespace, or class. And the struct will be named TypeS. This can typically be\r\n# useful for C code in case the coding convention dictates that all compound\r\n# types are typedef'ed and only the typedef is referenced, never the tag name.\r\n# The default value is: NO.\r\n\r\nTYPEDEF_HIDES_STRUCT   = NO\r\n\r\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\r\n# cache is used to resolve symbols given their name and scope. Since this can be\r\n# an expensive process and often the same symbol appears multiple times in the\r\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\r\n# doxygen will become slower. If the cache is too large, memory is wasted. The\r\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\r\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\r\n# symbols. At the end of a run doxygen will report the cache usage and suggest\r\n# the optimal cache size from a speed point of view.\r\n# Minimum value: 0, maximum value: 9, default value: 0.\r\n\r\nLOOKUP_CACHE_SIZE      = 0\r\n\r\n#---------------------------------------------------------------------------\r\n# Build related configuration options\r\n#---------------------------------------------------------------------------\r\n\r\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\r\n# documentation are documented, even if no documentation was available. Private\r\n# class members and static file members will be hidden unless the\r\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\r\n# Note: This will also disable the warnings about undocumented members that are\r\n# normally produced when WARNINGS is set to YES.\r\n# The default value is: NO.\r\n\r\nEXTRACT_ALL            = NO\r\n\r\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\r\n# be included in the documentation.\r\n# The default value is: NO.\r\n\r\nEXTRACT_PRIVATE        = NO\r\n\r\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\r\n# methods of a class will be included in the documentation.\r\n# The default value is: NO.\r\n\r\nEXTRACT_PRIV_VIRTUAL   = NO\r\n\r\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\r\n# scope will be included in the documentation.\r\n# The default value is: NO.\r\n\r\nEXTRACT_PACKAGE        = NO\r\n\r\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\r\n# included in the documentation.\r\n# The default value is: NO.\r\n\r\nEXTRACT_STATIC         = YES\r\n\r\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\r\n# locally in source files will be included in the documentation. If set to NO,\r\n# only classes defined in header files are included. Does not have any effect\r\n# for Java sources.\r\n# The default value is: YES.\r\n\r\nEXTRACT_LOCAL_CLASSES  = YES\r\n\r\n# This flag is only useful for Objective-C code. If set to YES, local methods,\r\n# which are defined in the implementation section but not in the interface are\r\n# included in the documentation. If set to NO, only methods in the interface are\r\n# included.\r\n# The default value is: NO.\r\n\r\nEXTRACT_LOCAL_METHODS  = YES\r\n\r\n# If this flag is set to YES, the members of anonymous namespaces will be\r\n# extracted and appear in the documentation as a namespace called\r\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\r\n# the file that contains the anonymous namespace. By default anonymous namespace\r\n# are hidden.\r\n# The default value is: NO.\r\n\r\nEXTRACT_ANON_NSPACES   = YES\r\n\r\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\r\n# undocumented members inside documented classes or files. If set to NO these\r\n# members will be included in the various overviews, but no documentation\r\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\r\n# The default value is: NO.\r\n\r\nHIDE_UNDOC_MEMBERS     = YES\r\n\r\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\r\n# undocumented classes that are normally visible in the class hierarchy. If set\r\n# to NO, these classes will be included in the various overviews. This option\r\n# has no effect if EXTRACT_ALL is enabled.\r\n# The default value is: NO.\r\n\r\nHIDE_UNDOC_CLASSES     = YES\r\n\r\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\r\n# declarations. If set to NO, these declarations will be included in the\r\n# documentation.\r\n# The default value is: NO.\r\n\r\nHIDE_FRIEND_COMPOUNDS  = YES\r\n\r\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\r\n# documentation blocks found inside the body of a function. If set to NO, these\r\n# blocks will be appended to the function's detailed documentation block.\r\n# The default value is: NO.\r\n\r\nHIDE_IN_BODY_DOCS      = NO\r\n\r\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\r\n# \\internal command is included. If the tag is set to NO then the documentation\r\n# will be excluded. Set it to YES to include the internal documentation.\r\n# The default value is: NO.\r\n\r\nINTERNAL_DOCS          = NO\r\n\r\n# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\r\n# names in lower-case letters. If set to YES, upper-case letters are also\r\n# allowed. This is useful if you have classes or files whose names only differ\r\n# in case and if your file system supports case sensitive file names. Windows\r\n# (including Cygwin) ands Mac users are advised to set this option to NO.\r\n# The default value is: system dependent.\r\n\r\nCASE_SENSE_NAMES       = NO\r\n\r\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\r\n# their full class and namespace scopes in the documentation. If set to YES, the\r\n# scope will be hidden.\r\n# The default value is: NO.\r\n\r\nHIDE_SCOPE_NAMES       = NO\r\n\r\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\r\n# append additional text to a page's title, such as Class Reference. If set to\r\n# YES the compound reference will be hidden.\r\n# The default value is: NO.\r\n\r\nHIDE_COMPOUND_REFERENCE= NO\r\n\r\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\r\n# the files that are included by a file in the documentation of that file.\r\n# The default value is: YES.\r\n\r\nSHOW_INCLUDE_FILES     = NO\r\n\r\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\r\n# grouped member an include statement to the documentation, telling the reader\r\n# which file to include in order to use the member.\r\n# The default value is: NO.\r\n\r\nSHOW_GROUPED_MEMB_INC  = NO\r\n\r\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\r\n# files with double quotes in the documentation rather than with sharp brackets.\r\n# The default value is: NO.\r\n\r\nFORCE_LOCAL_INCLUDES   = NO\r\n\r\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\r\n# documentation for inline members.\r\n# The default value is: YES.\r\n\r\nINLINE_INFO            = YES\r\n\r\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\r\n# (detailed) documentation of file and class members alphabetically by member\r\n# name. If set to NO, the members will appear in declaration order.\r\n# The default value is: YES.\r\n\r\nSORT_MEMBER_DOCS       = YES\r\n\r\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\r\n# descriptions of file, namespace and class members alphabetically by member\r\n# name. If set to NO, the members will appear in declaration order. Note that\r\n# this will also influence the order of the classes in the class list.\r\n# The default value is: NO.\r\n\r\nSORT_BRIEF_DOCS        = NO\r\n\r\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\r\n# (brief and detailed) documentation of class members so that constructors and\r\n# destructors are listed first. If set to NO the constructors will appear in the\r\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\r\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\r\n# member documentation.\r\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\r\n# detailed member documentation.\r\n# The default value is: NO.\r\n\r\nSORT_MEMBERS_CTORS_1ST = NO\r\n\r\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\r\n# of group names into alphabetical order. If set to NO the group names will\r\n# appear in their defined order.\r\n# The default value is: NO.\r\n\r\nSORT_GROUP_NAMES       = NO\r\n\r\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\r\n# fully-qualified names, including namespaces. If set to NO, the class list will\r\n# be sorted only by class name, not including the namespace part.\r\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\r\n# Note: This option applies only to the class list, not to the alphabetical\r\n# list.\r\n# The default value is: NO.\r\n\r\nSORT_BY_SCOPE_NAME     = NO\r\n\r\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\r\n# type resolution of all parameters of a function it will reject a match between\r\n# the prototype and the implementation of a member function even if there is\r\n# only one candidate or it is obvious which candidate to choose by doing a\r\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\r\n# accept a match between prototype and implementation in such cases.\r\n# The default value is: NO.\r\n\r\nSTRICT_PROTO_MATCHING  = NO\r\n\r\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\r\n# list. This list is created by putting \\todo commands in the documentation.\r\n# The default value is: YES.\r\n\r\nGENERATE_TODOLIST      = YES\r\n\r\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\r\n# list. This list is created by putting \\test commands in the documentation.\r\n# The default value is: YES.\r\n\r\nGENERATE_TESTLIST      = YES\r\n\r\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\r\n# list. This list is created by putting \\bug commands in the documentation.\r\n# The default value is: YES.\r\n\r\nGENERATE_BUGLIST       = YES\r\n\r\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\r\n# the deprecated list. This list is created by putting \\deprecated commands in\r\n# the documentation.\r\n# The default value is: YES.\r\n\r\nGENERATE_DEPRECATEDLIST= YES\r\n\r\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\r\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\r\n# ... \\endcond blocks.\r\n\r\nENABLED_SECTIONS       =\r\n\r\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\r\n# initial value of a variable or macro / define can have for it to appear in the\r\n# documentation. If the initializer consists of more lines than specified here\r\n# it will be hidden. Use a value of 0 to hide initializers completely. The\r\n# appearance of the value of individual variables and macros / defines can be\r\n# controlled using \\showinitializer or \\hideinitializer command in the\r\n# documentation regardless of this setting.\r\n# Minimum value: 0, maximum value: 10000, default value: 30.\r\n\r\nMAX_INITIALIZER_LINES  = 30\r\n\r\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\r\n# the bottom of the documentation of classes and structs. If set to YES, the\r\n# list will mention the files that were used to generate the documentation.\r\n# The default value is: YES.\r\n\r\nSHOW_USED_FILES        = YES\r\n\r\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\r\n# will remove the Files entry from the Quick Index and from the Folder Tree View\r\n# (if specified).\r\n# The default value is: YES.\r\n\r\nSHOW_FILES             = YES\r\n\r\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\r\n# page. This will remove the Namespaces entry from the Quick Index and from the\r\n# Folder Tree View (if specified).\r\n# The default value is: YES.\r\n\r\nSHOW_NAMESPACES        = YES\r\n\r\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\r\n# doxygen should invoke to get the current version for each file (typically from\r\n# the version control system). Doxygen will invoke the program by executing (via\r\n# popen()) the command command input-file, where command is the value of the\r\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\r\n# by doxygen. Whatever the program writes to standard output is used as the file\r\n# version. For an example see the documentation.\r\n\r\nFILE_VERSION_FILTER    =\r\n\r\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\r\n# by doxygen. The layout file controls the global structure of the generated\r\n# output files in an output format independent way. To create the layout file\r\n# that represents doxygen's defaults, run doxygen with the -l option. You can\r\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\r\n# will be used as the name of the layout file.\r\n#\r\n# Note that if you run doxygen from a directory containing a file called\r\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\r\n# tag is left empty.\r\n\r\nLAYOUT_FILE            =\r\n\r\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\r\n# the reference definitions. This must be a list of .bib files. The .bib\r\n# extension is automatically appended if omitted. This requires the bibtex tool\r\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\r\n# For LaTeX the style of the bibliography can be controlled using\r\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\r\n# search path. See also \\cite for info how to create references.\r\n\r\nCITE_BIB_FILES         =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to warning and progress messages\r\n#---------------------------------------------------------------------------\r\n\r\n# The QUIET tag can be used to turn on/off the messages that are generated to\r\n# standard output by doxygen. If QUIET is set to YES this implies that the\r\n# messages are off.\r\n# The default value is: NO.\r\n\r\nQUIET                  = NO\r\n\r\n# The WARNINGS tag can be used to turn on/off the warning messages that are\r\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\r\n# this implies that the warnings are on.\r\n#\r\n# Tip: Turn warnings on while writing the documentation.\r\n# The default value is: YES.\r\n\r\nWARNINGS               = YES\r\n\r\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\r\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\r\n# will automatically be disabled.\r\n# The default value is: YES.\r\n\r\nWARN_IF_UNDOCUMENTED   = YES\r\n\r\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\r\n# potential errors in the documentation, such as not documenting some parameters\r\n# in a documented function, or documenting parameters that don't exist or using\r\n# markup commands wrongly.\r\n# The default value is: YES.\r\n\r\nWARN_IF_DOC_ERROR      = YES\r\n\r\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\r\n# are documented, but have no documentation for their parameters or return\r\n# value. If set to NO, doxygen will only warn about wrong or incomplete\r\n# parameter documentation, but not about the absence of documentation. If\r\n# EXTRACT_ALL is set to YES then this flag will automatically be disabled.\r\n# The default value is: NO.\r\n\r\nWARN_NO_PARAMDOC       = NO\r\n\r\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\r\n# a warning is encountered.\r\n# The default value is: NO.\r\n\r\nWARN_AS_ERROR          = NO\r\n\r\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\r\n# can produce. The string should contain the $file, $line, and $text tags, which\r\n# will be replaced by the file and line number from which the warning originated\r\n# and the warning text. Optionally the format may contain $version, which will\r\n# be replaced by the version of the file (if it could be obtained via\r\n# FILE_VERSION_FILTER)\r\n# The default value is: $file:$line: $text.\r\n\r\nWARN_FORMAT            = \"$file:$line: $text\"\r\n\r\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\r\n# messages should be written. If left blank the output is written to standard\r\n# error (stderr).\r\n\r\nWARN_LOGFILE           =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the input files\r\n#---------------------------------------------------------------------------\r\n\r\n# The INPUT tag is used to specify the files and/or directories that contain\r\n# documented source files. You may enter file names like myfile.cpp or\r\n# directories like /usr/src/myproject. Separate the files or directories with\r\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\r\n# Note: If this tag is empty the current directory is searched.\r\n\r\nINPUT                  = .\\librf\\src\r\n\r\n# This tag can be used to specify the character encoding of the source files\r\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\r\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\r\n# documentation (see: https://www.gnu.org/software/libiconv/) for the list of\r\n# possible encodings.\r\n# The default value is: UTF-8.\r\n\r\nINPUT_ENCODING         = UTF-8\r\n\r\n# If the value of the INPUT tag contains directories, you can use the\r\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\r\n# *.h) to filter out the source-files in the directories.\r\n#\r\n# Note that for custom extensions or not directly supported extensions you also\r\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\r\n# read by doxygen.\r\n#\r\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\r\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\r\n# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,\r\n# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),\r\n# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen\r\n# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd,\r\n# *.vhdl, *.ucf, *.qsf and *.ice.\r\n\r\nFILE_PATTERNS          = *.java \\\r\n                         *.idl \\\r\n                         *.ddl \\\r\n                         *.odl \\\r\n                         *.h \\\r\n                         *.cs \\\r\n                         *.d \\\r\n                         *.php \\\r\n                         *.php4 \\\r\n                         *.php5 \\\r\n                         *.phtml \\\r\n                         *.inc \\\r\n                         *.m \\\r\n                         *.markdown \\\r\n                         *.md \\\r\n                         *.mm \\\r\n                         *.dox \\\r\n                         *.doc \\\r\n                         *.txt \\\r\n                         *.py \\\r\n                         *.pyw \\\r\n                         *.f90 \\\r\n                         *.f95 \\\r\n                         *.f03 \\\r\n                         *.f08 \\\r\n                         *.f \\\r\n                         *.for \\\r\n                         *.tcl \\\r\n                         *.vhd \\\r\n                         *.vhdl \\\r\n                         *.ucf \\\r\n                         *.qsf \\\r\n                         *.ice \\\r\n                         *.inl\r\n\r\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\r\n# be searched for input files as well.\r\n# The default value is: NO.\r\n\r\nRECURSIVE              = NO\r\n\r\n# The EXCLUDE tag can be used to specify files and/or directories that should be\r\n# excluded from the INPUT source files. This way you can easily exclude a\r\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\r\n#\r\n# Note that relative paths are relative to the directory from which doxygen is\r\n# run.\r\n\r\nEXCLUDE                = channel_v1.h \\\r\n                         event_v1.h \\\r\n                         mutex_v1.h \\\r\n                         ring_queue.h \\\r\n                         ring_queue_lockfree.h \\\r\n                         ring_queue_spinlock.h \\\r\n                         intrusive_link_queue.h\r\n\r\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\r\n# directories that are symbolic links (a Unix file system feature) are excluded\r\n# from the input.\r\n# The default value is: NO.\r\n\r\nEXCLUDE_SYMLINKS       = NO\r\n\r\n# If the value of the INPUT tag contains directories, you can use the\r\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\r\n# certain files from those directories.\r\n#\r\n# Note that the wildcards are matched against the file with absolute path, so to\r\n# exclude all test directories for example use the pattern */test/*\r\n\r\nEXCLUDE_PATTERNS       =\r\n\r\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\r\n# (namespaces, classes, functions, etc.) that should be excluded from the\r\n# output. The symbol name can be a fully qualified name, a word, or if the\r\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\r\n# AClass::ANamespace, ANamespace::*Test\r\n#\r\n# Note that the wildcards are matched against the file with absolute path, so to\r\n# exclude all test directories use the pattern */test/*\r\n\r\nEXCLUDE_SYMBOLS        =\r\n\r\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\r\n# that contain example code fragments that are included (see the \\include\r\n# command).\r\n\r\nEXAMPLE_PATH           =\r\n\r\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\r\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\r\n# *.h) to filter out the source-files in the directories. If left blank all\r\n# files are included.\r\n\r\nEXAMPLE_PATTERNS       = *\r\n\r\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\r\n# searched for input files to be used with the \\include or \\dontinclude commands\r\n# irrespective of the value of the RECURSIVE tag.\r\n# The default value is: NO.\r\n\r\nEXAMPLE_RECURSIVE      = NO\r\n\r\n# The IMAGE_PATH tag can be used to specify one or more files or directories\r\n# that contain images that are to be included in the documentation (see the\r\n# \\image command).\r\n\r\nIMAGE_PATH             =\r\n\r\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\r\n# invoke to filter for each input file. Doxygen will invoke the filter program\r\n# by executing (via popen()) the command:\r\n#\r\n# <filter> <input-file>\r\n#\r\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\r\n# name of an input file. Doxygen will then use the output that the filter\r\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\r\n# will be ignored.\r\n#\r\n# Note that the filter must not add or remove lines; it is applied before the\r\n# code is scanned, but not when the output code is generated. If lines are added\r\n# or removed, the anchors will not be placed correctly.\r\n#\r\n# Note that for custom extensions or not directly supported extensions you also\r\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\r\n# properly processed by doxygen.\r\n\r\nINPUT_FILTER           =\r\n\r\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\r\n# basis. Doxygen will compare the file name with each pattern and apply the\r\n# filter if there is a match. The filters are a list of the form: pattern=filter\r\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\r\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\r\n# patterns match the file name, INPUT_FILTER is applied.\r\n#\r\n# Note that for custom extensions or not directly supported extensions you also\r\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\r\n# properly processed by doxygen.\r\n\r\nFILTER_PATTERNS        =\r\n\r\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\r\n# INPUT_FILTER) will also be used to filter the input files that are used for\r\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\r\n# The default value is: NO.\r\n\r\nFILTER_SOURCE_FILES    = NO\r\n\r\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\r\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\r\n# it is also possible to disable source filtering for a specific pattern using\r\n# *.ext= (so without naming a filter).\r\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\r\n\r\nFILTER_SOURCE_PATTERNS =\r\n\r\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\r\n# is part of the input, its contents will be placed on the main page\r\n# (index.html). This can be useful if you have a project on for instance GitHub\r\n# and want to reuse the introduction page also for the doxygen output.\r\n\r\nUSE_MDFILE_AS_MAINPAGE =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to source browsing\r\n#---------------------------------------------------------------------------\r\n\r\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\r\n# generated. Documented entities will be cross-referenced with these sources.\r\n#\r\n# Note: To get rid of all source code in the generated output, make sure that\r\n# also VERBATIM_HEADERS is set to NO.\r\n# The default value is: NO.\r\n\r\nSOURCE_BROWSER         = NO\r\n\r\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\r\n# classes and enums directly into the documentation.\r\n# The default value is: NO.\r\n\r\nINLINE_SOURCES         = NO\r\n\r\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\r\n# special comment blocks from generated source code fragments. Normal C, C++ and\r\n# Fortran comments will always remain visible.\r\n# The default value is: YES.\r\n\r\nSTRIP_CODE_COMMENTS    = YES\r\n\r\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\r\n# entity all documented functions referencing it will be listed.\r\n# The default value is: NO.\r\n\r\nREFERENCED_BY_RELATION = NO\r\n\r\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\r\n# all documented entities called/used by that function will be listed.\r\n# The default value is: NO.\r\n\r\nREFERENCES_RELATION    = NO\r\n\r\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\r\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\r\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\r\n# link to the documentation.\r\n# The default value is: YES.\r\n\r\nREFERENCES_LINK_SOURCE = YES\r\n\r\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\r\n# source code will show a tooltip with additional information such as prototype,\r\n# brief description and links to the definition and documentation. Since this\r\n# will make the HTML file larger and loading of large files a bit slower, you\r\n# can opt to disable this feature.\r\n# The default value is: YES.\r\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\r\n\r\nSOURCE_TOOLTIPS        = YES\r\n\r\n# If the USE_HTAGS tag is set to YES then the references to source code will\r\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\r\n# source browser. The htags tool is part of GNU's global source tagging system\r\n# (see https://www.gnu.org/software/global/global.html). You will need version\r\n# 4.8.6 or higher.\r\n#\r\n# To use it do the following:\r\n# - Install the latest version of global\r\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\r\n# - Make sure the INPUT points to the root of the source tree\r\n# - Run doxygen as normal\r\n#\r\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\r\n# tools must be available from the command line (i.e. in the search path).\r\n#\r\n# The result: instead of the source browser generated by doxygen, the links to\r\n# source code will now point to the output of htags.\r\n# The default value is: NO.\r\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\r\n\r\nUSE_HTAGS              = NO\r\n\r\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\r\n# verbatim copy of the header file for each class for which an include is\r\n# specified. Set to NO to disable this.\r\n# See also: Section \\class.\r\n# The default value is: YES.\r\n\r\nVERBATIM_HEADERS       = YES\r\n\r\n# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the\r\n# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the\r\n# cost of reduced performance. This can be particularly helpful with template\r\n# rich C++ code for which doxygen's built-in parser lacks the necessary type\r\n# information.\r\n# Note: The availability of this option depends on whether or not doxygen was\r\n# generated with the -Duse_libclang=ON option for CMake.\r\n# The default value is: NO.\r\n\r\nCLANG_ASSISTED_PARSING = NO\r\n\r\n# If clang assisted parsing is enabled you can provide the compiler with command\r\n# line options that you would normally use when invoking the compiler. Note that\r\n# the include paths will already be set by doxygen for the files and directories\r\n# specified with INPUT and INCLUDE_PATH.\r\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\r\n\r\nCLANG_OPTIONS          =\r\n\r\n# If clang assisted parsing is enabled you can provide the clang parser with the\r\n# path to the compilation database (see:\r\n# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files\r\n# were built. This is equivalent to specifying the \"-p\" option to a clang tool,\r\n# such as clang-check. These options will then be passed to the parser.\r\n# Note: The availability of this option depends on whether or not doxygen was\r\n# generated with the -Duse_libclang=ON option for CMake.\r\n\r\nCLANG_DATABASE_PATH    =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the alphabetical class index\r\n#---------------------------------------------------------------------------\r\n\r\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\r\n# compounds will be generated. Enable this if the project contains a lot of\r\n# classes, structs, unions or interfaces.\r\n# The default value is: YES.\r\n\r\nALPHABETICAL_INDEX     = YES\r\n\r\n# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\r\n# which the alphabetical index list will be split.\r\n# Minimum value: 1, maximum value: 20, default value: 5.\r\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\r\n\r\nCOLS_IN_ALPHA_INDEX    = 5\r\n\r\n# In case all classes in a project start with a common prefix, all classes will\r\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\r\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\r\n# while generating the index headers.\r\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\r\n\r\nIGNORE_PREFIX          =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the HTML output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\r\n# The default value is: YES.\r\n\r\nGENERATE_HTML          = YES\r\n\r\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\r\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\r\n# it.\r\n# The default directory is: html.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_OUTPUT            = html\r\n\r\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\r\n# generated HTML page (for example: .htm, .php, .asp).\r\n# The default value is: .html.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_FILE_EXTENSION    = .html\r\n\r\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\r\n# each generated HTML page. If the tag is left blank doxygen will generate a\r\n# standard header.\r\n#\r\n# To get valid HTML the header file that includes any scripts and style sheets\r\n# that doxygen needs, which is dependent on the configuration options used (e.g.\r\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\r\n# default header using\r\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\r\n# YourConfigFile\r\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\r\n# for information on how to generate the default header that doxygen normally\r\n# uses.\r\n# Note: The header is subject to change so you typically have to regenerate the\r\n# default header when upgrading to a newer version of doxygen. For a description\r\n# of the possible markers and block names see the documentation.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_HEADER            =\r\n\r\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\r\n# generated HTML page. If the tag is left blank doxygen will generate a standard\r\n# footer. See HTML_HEADER for more information on how to generate a default\r\n# footer and what special commands can be used inside the footer. See also\r\n# section \"Doxygen usage\" for information on how to generate the default footer\r\n# that doxygen normally uses.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_FOOTER            =\r\n\r\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\r\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\r\n# the HTML output. If left blank doxygen will generate a default style sheet.\r\n# See also section \"Doxygen usage\" for information on how to generate the style\r\n# sheet that doxygen normally uses.\r\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\r\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\r\n# obsolete.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_STYLESHEET        =\r\n\r\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\r\n# cascading style sheets that are included after the standard style sheets\r\n# created by doxygen. Using this option one can overrule certain style aspects.\r\n# This is preferred over using HTML_STYLESHEET since it does not replace the\r\n# standard style sheet and is therefore more robust against future updates.\r\n# Doxygen will copy the style sheet files to the output directory.\r\n# Note: The order of the extra style sheet files is of importance (e.g. the last\r\n# style sheet in the list overrules the setting of the previous ones in the\r\n# list). For an example see the documentation.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_EXTRA_STYLESHEET  =\r\n\r\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\r\n# other source files which should be copied to the HTML output directory. Note\r\n# that these files will be copied to the base HTML output directory. Use the\r\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\r\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\r\n# files will be copied as-is; there are no commands or markers available.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_EXTRA_FILES       =\r\n\r\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\r\n# will adjust the colors in the style sheet and background images according to\r\n# this color. Hue is specified as an angle on a colorwheel, see\r\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\r\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\r\n# purple, and 360 is red again.\r\n# Minimum value: 0, maximum value: 359, default value: 220.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_COLORSTYLE_HUE    = 220\r\n\r\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\r\n# in the HTML output. For a value of 0 the output will use grayscales only. A\r\n# value of 255 will produce the most vivid colors.\r\n# Minimum value: 0, maximum value: 255, default value: 100.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_COLORSTYLE_SAT    = 100\r\n\r\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\r\n# luminance component of the colors in the HTML output. Values below 100\r\n# gradually make the output lighter, whereas values above 100 make the output\r\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\r\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\r\n# change the gamma.\r\n# Minimum value: 40, maximum value: 240, default value: 80.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_COLORSTYLE_GAMMA  = 80\r\n\r\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\r\n# page will contain the date and time when the page was generated. Setting this\r\n# to YES can help to show when doxygen was last run and thus if the\r\n# documentation is up to date.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_TIMESTAMP         = NO\r\n\r\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\r\n# documentation will contain a main index with vertical navigation menus that\r\n# are dynamically created via JavaScript. If disabled, the navigation index will\r\n# consists of multiple levels of tabs that are statically embedded in every HTML\r\n# page. Disable this option to support browsers that do not have JavaScript,\r\n# like the Qt help browser.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_DYNAMIC_MENUS     = YES\r\n\r\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\r\n# documentation will contain sections that can be hidden and shown after the\r\n# page has loaded.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_DYNAMIC_SECTIONS  = NO\r\n\r\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\r\n# shown in the various tree structured indices initially; the user can expand\r\n# and collapse entries dynamically later on. Doxygen will expand the tree to\r\n# such a level that at most the specified number of entries are visible (unless\r\n# a fully collapsed tree already exceeds this amount). So setting the number of\r\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\r\n# representing an infinite number of entries and will result in a full expanded\r\n# tree by default.\r\n# Minimum value: 0, maximum value: 9999, default value: 100.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nHTML_INDEX_NUM_ENTRIES = 100\r\n\r\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\r\n# generated that can be used as input for Apple's Xcode 3 integrated development\r\n# environment (see: https://developer.apple.com/xcode/), introduced with OSX\r\n# 10.5 (Leopard). To create a documentation set, doxygen will generate a\r\n# Makefile in the HTML output directory. Running make will produce the docset in\r\n# that directory and running make install will install the docset in\r\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\r\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\r\n# genXcode/_index.html for more information.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nGENERATE_DOCSET        = NO\r\n\r\n# This tag determines the name of the docset feed. A documentation feed provides\r\n# an umbrella under which multiple documentation sets from a single provider\r\n# (such as a company or product suite) can be grouped.\r\n# The default value is: Doxygen generated docs.\r\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\r\n\r\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\r\n\r\n# This tag specifies a string that should uniquely identify the documentation\r\n# set bundle. This should be a reverse domain-name style string, e.g.\r\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\r\n# The default value is: org.doxygen.Project.\r\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\r\n\r\nDOCSET_BUNDLE_ID       = org.doxygen.Project\r\n\r\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\r\n# the documentation publisher. This should be a reverse domain-name style\r\n# string, e.g. com.mycompany.MyDocSet.documentation.\r\n# The default value is: org.doxygen.Publisher.\r\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\r\n\r\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\r\n\r\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\r\n# The default value is: Publisher.\r\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\r\n\r\nDOCSET_PUBLISHER_NAME  = Publisher\r\n\r\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\r\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\r\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\r\n# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on\r\n# Windows.\r\n#\r\n# The HTML Help Workshop contains a compiler that can convert all HTML output\r\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\r\n# files are now used as the Windows 98 help format, and will replace the old\r\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\r\n# HTML files also contain an index, a table of contents, and you can search for\r\n# words in the documentation. The HTML workshop also contains a viewer for\r\n# compressed HTML files.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nGENERATE_HTMLHELP      = NO\r\n\r\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\r\n# file. You can add a path in front of the file if the result should not be\r\n# written to the html output directory.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nCHM_FILE               =\r\n\r\n# The HHC_LOCATION tag can be used to specify the location (absolute path\r\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\r\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\r\n# The file has to be specified with full path.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nHHC_LOCATION           =\r\n\r\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\r\n# (YES) or that it should be included in the master .chm file (NO).\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nGENERATE_CHI           = NO\r\n\r\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\r\n# and project file content.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nCHM_INDEX_ENCODING     =\r\n\r\n# The BINARY_TOC flag controls whether a binary table of contents is generated\r\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\r\n# enables the Previous and Next buttons.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nBINARY_TOC             = NO\r\n\r\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\r\n# the table of contents of the HTML help documentation and to the tree view.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\r\n\r\nTOC_EXPAND             = NO\r\n\r\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\r\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\r\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\r\n# (.qch) of the generated HTML documentation.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nGENERATE_QHP           = NO\r\n\r\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\r\n# the file name of the resulting .qch file. The path specified is relative to\r\n# the HTML output folder.\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQCH_FILE               =\r\n\r\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\r\n# Project output. For more information please see Qt Help Project / Namespace\r\n# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\r\n# The default value is: org.doxygen.Project.\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHP_NAMESPACE          = org.doxygen.Project\r\n\r\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\r\n# Help Project output. For more information please see Qt Help Project / Virtual\r\n# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-\r\n# folders).\r\n# The default value is: doc.\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHP_VIRTUAL_FOLDER     = doc\r\n\r\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\r\n# filter to add. For more information please see Qt Help Project / Custom\r\n# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-\r\n# filters).\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHP_CUST_FILTER_NAME   =\r\n\r\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\r\n# custom filter to add. For more information please see Qt Help Project / Custom\r\n# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-\r\n# filters).\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHP_CUST_FILTER_ATTRS  =\r\n\r\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\r\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\r\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHP_SECT_FILTER_ATTRS  =\r\n\r\n# The QHG_LOCATION tag can be used to specify the location of Qt's\r\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\r\n# generated .qhp file.\r\n# This tag requires that the tag GENERATE_QHP is set to YES.\r\n\r\nQHG_LOCATION           =\r\n\r\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\r\n# generated, together with the HTML files, they form an Eclipse help plugin. To\r\n# install this plugin and make it available under the help contents menu in\r\n# Eclipse, the contents of the directory containing the HTML and XML files needs\r\n# to be copied into the plugins directory of eclipse. The name of the directory\r\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\r\n# After copying Eclipse needs to be restarted before the help appears.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nGENERATE_ECLIPSEHELP   = NO\r\n\r\n# A unique identifier for the Eclipse help plugin. When installing the plugin\r\n# the directory name containing the HTML and XML files should also have this\r\n# name. Each documentation set should have its own identifier.\r\n# The default value is: org.doxygen.Project.\r\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\r\n\r\nECLIPSE_DOC_ID         = org.doxygen.Project\r\n\r\n# If you want full control over the layout of the generated HTML pages it might\r\n# be necessary to disable the index and replace it with your own. The\r\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\r\n# of each HTML page. A value of NO enables the index and the value YES disables\r\n# it. Since the tabs in the index contain the same information as the navigation\r\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nDISABLE_INDEX          = NO\r\n\r\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\r\n# structure should be generated to display hierarchical information. If the tag\r\n# value is set to YES, a side panel will be generated containing a tree-like\r\n# index structure (just like the one that is generated for HTML Help). For this\r\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\r\n# (i.e. any modern browser). Windows users are probably better off using the\r\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\r\n# further fine-tune the look of the index. As an example, the default style\r\n# sheet generated by doxygen has an example that shows how to put an image at\r\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\r\n# the same information as the tab index, you could consider setting\r\n# DISABLE_INDEX to YES when enabling this option.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nGENERATE_TREEVIEW      = NO\r\n\r\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\r\n# doxygen will group on one line in the generated HTML documentation.\r\n#\r\n# Note that a value of 0 will completely suppress the enum values from appearing\r\n# in the overview section.\r\n# Minimum value: 0, maximum value: 20, default value: 4.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nENUM_VALUES_PER_LINE   = 4\r\n\r\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\r\n# to set the initial width (in pixels) of the frame in which the tree is shown.\r\n# Minimum value: 0, maximum value: 1500, default value: 250.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nTREEVIEW_WIDTH         = 250\r\n\r\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\r\n# external symbols imported via tag files in a separate window.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nEXT_LINKS_IN_WINDOW    = NO\r\n\r\n# Use this tag to change the font size of LaTeX formulas included as images in\r\n# the HTML documentation. When you change the font size after a successful\r\n# doxygen run you need to manually remove any form_*.png images from the HTML\r\n# output directory to force them to be regenerated.\r\n# Minimum value: 8, maximum value: 50, default value: 10.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nFORMULA_FONTSIZE       = 10\r\n\r\n# Use the FORMULA_TRANSPARENT tag to determine whether or not the images\r\n# generated for formulas are transparent PNGs. Transparent PNGs are not\r\n# supported properly for IE 6.0, but are supported on all modern browsers.\r\n#\r\n# Note that when changing this option you need to delete any form_*.png files in\r\n# the HTML output directory before the changes have effect.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nFORMULA_TRANSPARENT    = YES\r\n\r\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\r\n# to create new LaTeX commands to be used in formulas as building blocks. See\r\n# the section \"Including formulas\" for details.\r\n\r\nFORMULA_MACROFILE      =\r\n\r\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\r\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\r\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\r\n# installed or if you want to formulas look prettier in the HTML output. When\r\n# enabled you may also need to install MathJax separately and configure the path\r\n# to it using the MATHJAX_RELPATH option.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nUSE_MATHJAX            = NO\r\n\r\n# When MathJax is enabled you can set the default output format to be used for\r\n# the MathJax output. See the MathJax site (see:\r\n# http://docs.mathjax.org/en/latest/output.html) for more details.\r\n# Possible values are: HTML-CSS (which is slower, but has the best\r\n# compatibility), NativeMML (i.e. MathML) and SVG.\r\n# The default value is: HTML-CSS.\r\n# This tag requires that the tag USE_MATHJAX is set to YES.\r\n\r\nMATHJAX_FORMAT         = HTML-CSS\r\n\r\n# When MathJax is enabled you need to specify the location relative to the HTML\r\n# output directory using the MATHJAX_RELPATH option. The destination directory\r\n# should contain the MathJax.js script. For instance, if the mathjax directory\r\n# is located at the same level as the HTML output directory, then\r\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\r\n# Content Delivery Network so you can quickly see the result without installing\r\n# MathJax. However, it is strongly recommended to install a local copy of\r\n# MathJax from https://www.mathjax.org before deployment.\r\n# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.\r\n# This tag requires that the tag USE_MATHJAX is set to YES.\r\n\r\nMATHJAX_RELPATH        = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/\r\n\r\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\r\n# extension names that should be enabled during MathJax rendering. For example\r\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\r\n# This tag requires that the tag USE_MATHJAX is set to YES.\r\n\r\nMATHJAX_EXTENSIONS     =\r\n\r\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\r\n# of code that will be used on startup of the MathJax code. See the MathJax site\r\n# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an\r\n# example see the documentation.\r\n# This tag requires that the tag USE_MATHJAX is set to YES.\r\n\r\nMATHJAX_CODEFILE       =\r\n\r\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\r\n# the HTML output. The underlying search engine uses javascript and DHTML and\r\n# should work on any modern browser. Note that when using HTML help\r\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\r\n# there is already a search function so this one should typically be disabled.\r\n# For large projects the javascript based search engine can be slow, then\r\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\r\n# search using the keyboard; to jump to the search box use <access key> + S\r\n# (what the <access key> is depends on the OS and browser, but it is typically\r\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\r\n# key> to jump into the search results window, the results can be navigated\r\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\r\n# the search. The filter options can be selected when the cursor is inside the\r\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\r\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\r\n# option.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_HTML is set to YES.\r\n\r\nSEARCHENGINE           = YES\r\n\r\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\r\n# implemented using a web server instead of a web client using JavaScript. There\r\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\r\n# setting. When disabled, doxygen will generate a PHP script for searching and\r\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\r\n# and searching needs to be provided by external tools. See the section\r\n# \"External Indexing and Searching\" for details.\r\n# The default value is: NO.\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nSERVER_BASED_SEARCH    = NO\r\n\r\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\r\n# script for searching. Instead the search results are written to an XML file\r\n# which needs to be processed by an external indexer. Doxygen will invoke an\r\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\r\n# search results.\r\n#\r\n# Doxygen ships with an example indexer (doxyindexer) and search engine\r\n# (doxysearch.cgi) which are based on the open source search engine library\r\n# Xapian (see: https://xapian.org/).\r\n#\r\n# See the section \"External Indexing and Searching\" for details.\r\n# The default value is: NO.\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nEXTERNAL_SEARCH        = NO\r\n\r\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\r\n# which will return the search results when EXTERNAL_SEARCH is enabled.\r\n#\r\n# Doxygen ships with an example indexer (doxyindexer) and search engine\r\n# (doxysearch.cgi) which are based on the open source search engine library\r\n# Xapian (see: https://xapian.org/). See the section \"External Indexing and\r\n# Searching\" for details.\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nSEARCHENGINE_URL       =\r\n\r\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\r\n# search data is written to a file for indexing by an external tool. With the\r\n# SEARCHDATA_FILE tag the name of this file can be specified.\r\n# The default file is: searchdata.xml.\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nSEARCHDATA_FILE        = searchdata.xml\r\n\r\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\r\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\r\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\r\n# projects and redirect the results back to the right project.\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nEXTERNAL_SEARCH_ID     =\r\n\r\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\r\n# projects other than the one defined by this configuration file, but that are\r\n# all added to the same external search index. Each project needs to have a\r\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\r\n# to a relative location where the documentation can be found. The format is:\r\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\r\n# This tag requires that the tag SEARCHENGINE is set to YES.\r\n\r\nEXTRA_SEARCH_MAPPINGS  =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the LaTeX output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\r\n# The default value is: YES.\r\n\r\nGENERATE_LATEX         = NO\r\n\r\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\r\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\r\n# it.\r\n# The default directory is: latex.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_OUTPUT           = latex\r\n\r\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\r\n# invoked.\r\n#\r\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\r\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\r\n# chosen this is overwritten by pdflatex. For specific output languages the\r\n# default can have been set differently, this depends on the implementation of\r\n# the output language.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_CMD_NAME         =\r\n\r\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\r\n# index for LaTeX.\r\n# Note: This tag is used in the Makefile / make.bat.\r\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\r\n# (.tex).\r\n# The default file is: makeindex.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nMAKEINDEX_CMD_NAME     = makeindex\r\n\r\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\r\n# generate index for LaTeX. In case there is no backslash (\\) as first character\r\n# it will be automatically added in the LaTeX code.\r\n# Note: This tag is used in the generated output file (.tex).\r\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\r\n# The default value is: makeindex.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_MAKEINDEX_CMD    = makeindex\r\n\r\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\r\n# documents. This may be useful for small projects and may help to save some\r\n# trees in general.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nCOMPACT_LATEX          = NO\r\n\r\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\r\n# printer.\r\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\r\n# 14 inches) and executive (7.25 x 10.5 inches).\r\n# The default value is: a4.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nPAPER_TYPE             = a4\r\n\r\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\r\n# that should be included in the LaTeX output. The package can be specified just\r\n# by its name or with the correct syntax as to be used with the LaTeX\r\n# \\usepackage command. To get the times font for instance you can specify :\r\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\r\n# To use the option intlimits with the amsmath package you can specify:\r\n# EXTRA_PACKAGES=[intlimits]{amsmath}\r\n# If left blank no extra packages will be included.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nEXTRA_PACKAGES         =\r\n\r\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the\r\n# generated LaTeX document. The header should contain everything until the first\r\n# chapter. If it is left blank doxygen will generate a standard header. See\r\n# section \"Doxygen usage\" for information on how to let doxygen write the\r\n# default header to a separate file.\r\n#\r\n# Note: Only use a user-defined header if you know what you are doing! The\r\n# following commands have a special meaning inside the header: $title,\r\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber,\r\n# $projectbrief, $projectlogo. Doxygen will replace $title with the empty\r\n# string, for the replacement values of the other commands the user is referred\r\n# to HTML_HEADER.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_HEADER           =\r\n\r\n# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the\r\n# generated LaTeX document. The footer should contain everything after the last\r\n# chapter. If it is left blank doxygen will generate a standard footer. See\r\n# LATEX_HEADER for more information on how to generate a default footer and what\r\n# special commands can be used inside the footer.\r\n#\r\n# Note: Only use a user-defined footer if you know what you are doing!\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_FOOTER           =\r\n\r\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\r\n# LaTeX style sheets that are included after the standard style sheets created\r\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\r\n# will copy the style sheet files to the output directory.\r\n# Note: The order of the extra style sheet files is of importance (e.g. the last\r\n# style sheet in the list overrules the setting of the previous ones in the\r\n# list).\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_EXTRA_STYLESHEET =\r\n\r\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\r\n# other source files which should be copied to the LATEX_OUTPUT output\r\n# directory. Note that the files will be copied as-is; there are no commands or\r\n# markers available.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_EXTRA_FILES      =\r\n\r\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\r\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\r\n# contain links (just like the HTML output) instead of page references. This\r\n# makes the output suitable for online browsing using a PDF viewer.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nPDF_HYPERLINKS         = YES\r\n\r\n# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate\r\n# the PDF file directly from the LaTeX files. Set this option to YES, to get a\r\n# higher quality PDF documentation.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nUSE_PDFLATEX           = YES\r\n\r\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\r\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\r\n# if errors occur, instead of asking the user for help. This option is also used\r\n# when generating formulas in HTML.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_BATCHMODE        = NO\r\n\r\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\r\n# index chapters (such as File Index, Compound Index, etc.) in the output.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_HIDE_INDICES     = NO\r\n\r\n# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\r\n# code with syntax highlighting in the LaTeX output.\r\n#\r\n# Note that which sources are shown also depends on other settings such as\r\n# SOURCE_BROWSER.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_SOURCE_CODE      = NO\r\n\r\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\r\n# bibliography, e.g. plainnat, or ieeetr. See\r\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\r\n# The default value is: plain.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_BIB_STYLE        = plain\r\n\r\n# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated\r\n# page will contain the date and time when the page was generated. Setting this\r\n# to NO can help when comparing the output of multiple runs.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_TIMESTAMP        = NO\r\n\r\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\r\n# path from which the emoji images will be read. If a relative path is entered,\r\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\r\n# LATEX_OUTPUT directory will be used.\r\n# This tag requires that the tag GENERATE_LATEX is set to YES.\r\n\r\nLATEX_EMOJI_DIRECTORY  =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the RTF output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\r\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\r\n# readers/editors.\r\n# The default value is: NO.\r\n\r\nGENERATE_RTF           = NO\r\n\r\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\r\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\r\n# it.\r\n# The default directory is: rtf.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nRTF_OUTPUT             = rtf\r\n\r\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\r\n# documents. This may be useful for small projects and may help to save some\r\n# trees in general.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nCOMPACT_RTF            = NO\r\n\r\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\r\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\r\n# output) instead of page references. This makes the output suitable for online\r\n# browsing using Word or some other Word compatible readers that support those\r\n# fields.\r\n#\r\n# Note: WordPad (write) and others do not support links.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nRTF_HYPERLINKS         = NO\r\n\r\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\r\n# configuration file, i.e. a series of assignments. You only have to provide\r\n# replacements, missing definitions are set to their default value.\r\n#\r\n# See also section \"Doxygen usage\" for information on how to generate the\r\n# default style sheet that doxygen normally uses.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nRTF_STYLESHEET_FILE    =\r\n\r\n# Set optional variables used in the generation of an RTF document. Syntax is\r\n# similar to doxygen's configuration file. A template extensions file can be\r\n# generated using doxygen -e rtf extensionFile.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nRTF_EXTENSIONS_FILE    =\r\n\r\n# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code\r\n# with syntax highlighting in the RTF output.\r\n#\r\n# Note that which sources are shown also depends on other settings such as\r\n# SOURCE_BROWSER.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_RTF is set to YES.\r\n\r\nRTF_SOURCE_CODE        = NO\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the man page output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\r\n# classes and files.\r\n# The default value is: NO.\r\n\r\nGENERATE_MAN           = NO\r\n\r\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\r\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\r\n# it. A directory man3 will be created inside the directory specified by\r\n# MAN_OUTPUT.\r\n# The default directory is: man.\r\n# This tag requires that the tag GENERATE_MAN is set to YES.\r\n\r\nMAN_OUTPUT             = man\r\n\r\n# The MAN_EXTENSION tag determines the extension that is added to the generated\r\n# man pages. In case the manual section does not start with a number, the number\r\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\r\n# optional.\r\n# The default value is: .3.\r\n# This tag requires that the tag GENERATE_MAN is set to YES.\r\n\r\nMAN_EXTENSION          = .3\r\n\r\n# The MAN_SUBDIR tag determines the name of the directory created within\r\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\r\n# MAN_EXTENSION with the initial . removed.\r\n# This tag requires that the tag GENERATE_MAN is set to YES.\r\n\r\nMAN_SUBDIR             =\r\n\r\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\r\n# will generate one additional man file for each entity documented in the real\r\n# man page(s). These additional files only source the real man page, but without\r\n# them the man command would be unable to find the correct page.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_MAN is set to YES.\r\n\r\nMAN_LINKS              = NO\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the XML output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\r\n# captures the structure of the code including all documentation.\r\n# The default value is: NO.\r\n\r\nGENERATE_XML           = NO\r\n\r\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\r\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\r\n# it.\r\n# The default directory is: xml.\r\n# This tag requires that the tag GENERATE_XML is set to YES.\r\n\r\nXML_OUTPUT             = xml\r\n\r\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\r\n# listings (including syntax highlighting and cross-referencing information) to\r\n# the XML output. Note that enabling this will significantly increase the size\r\n# of the XML output.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_XML is set to YES.\r\n\r\nXML_PROGRAMLISTING     = YES\r\n\r\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\r\n# namespace members in file scope as well, matching the HTML output.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_XML is set to YES.\r\n\r\nXML_NS_MEMB_FILE_SCOPE = NO\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the DOCBOOK output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\r\n# that can be used to generate PDF.\r\n# The default value is: NO.\r\n\r\nGENERATE_DOCBOOK       = NO\r\n\r\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\r\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\r\n# front of it.\r\n# The default directory is: docbook.\r\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\r\n\r\nDOCBOOK_OUTPUT         = docbook\r\n\r\n# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the\r\n# program listings (including syntax highlighting and cross-referencing\r\n# information) to the DOCBOOK output. Note that enabling this will significantly\r\n# increase the size of the DOCBOOK output.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\r\n\r\nDOCBOOK_PROGRAMLISTING = NO\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options for the AutoGen Definitions output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\r\n# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures\r\n# the structure of the code including all documentation. Note that this feature\r\n# is still experimental and incomplete at the moment.\r\n# The default value is: NO.\r\n\r\nGENERATE_AUTOGEN_DEF   = NO\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the Perl module output\r\n#---------------------------------------------------------------------------\r\n\r\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\r\n# file that captures the structure of the code including all documentation.\r\n#\r\n# Note that this feature is still experimental and incomplete at the moment.\r\n# The default value is: NO.\r\n\r\nGENERATE_PERLMOD       = NO\r\n\r\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\r\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\r\n# output from the Perl module output.\r\n# The default value is: NO.\r\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\r\n\r\nPERLMOD_LATEX          = NO\r\n\r\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\r\n# formatted so it can be parsed by a human reader. This is useful if you want to\r\n# understand what is going on. On the other hand, if this tag is set to NO, the\r\n# size of the Perl module output will be much smaller and Perl will parse it\r\n# just the same.\r\n# The default value is: YES.\r\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\r\n\r\nPERLMOD_PRETTY         = YES\r\n\r\n# The names of the make variables in the generated doxyrules.make file are\r\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\r\n# so different doxyrules.make files included by the same Makefile don't\r\n# overwrite each other's variables.\r\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\r\n\r\nPERLMOD_MAKEVAR_PREFIX =\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the preprocessor\r\n#---------------------------------------------------------------------------\r\n\r\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\r\n# C-preprocessor directives found in the sources and include files.\r\n# The default value is: YES.\r\n\r\nENABLE_PREPROCESSING   = YES\r\n\r\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\r\n# in the source code. If set to NO, only conditional compilation will be\r\n# performed. Macro expansion can be done in a controlled way by setting\r\n# EXPAND_ONLY_PREDEF to YES.\r\n# The default value is: NO.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nMACRO_EXPANSION        = NO\r\n\r\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\r\n# the macro expansion is limited to the macros specified with the PREDEFINED and\r\n# EXPAND_AS_DEFINED tags.\r\n# The default value is: NO.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nEXPAND_ONLY_PREDEF     = NO\r\n\r\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\r\n# INCLUDE_PATH will be searched if a #include is found.\r\n# The default value is: YES.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nSEARCH_INCLUDES        = YES\r\n\r\n# The INCLUDE_PATH tag can be used to specify one or more directories that\r\n# contain include files that are not input files but should be processed by the\r\n# preprocessor.\r\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\r\n\r\nINCLUDE_PATH           =\r\n\r\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\r\n# patterns (like *.h and *.hpp) to filter out the header-files in the\r\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\r\n# used.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nINCLUDE_FILE_PATTERNS  =\r\n\r\n# The PREDEFINED tag can be used to specify one or more macro names that are\r\n# defined before the preprocessor is started (similar to the -D option of e.g.\r\n# gcc). The argument of the tag is a list of macros of the form: name or\r\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\r\n# is assumed. To prevent a macro definition from being undefined via #undef or\r\n# recursively expanded use the := operator instead of the = operator.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nPREDEFINED             = DOXYGEN_SKIP_PROPERTY\r\n\r\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\r\n# tag can be used to specify a list of macro names that should be expanded. The\r\n# macro definition that is found in the sources will be used. Use the PREDEFINED\r\n# tag if you want to use a different macro definition that overrules the\r\n# definition found in the source code.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nEXPAND_AS_DEFINED      =\r\n\r\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\r\n# remove all references to function-like macros that are alone on a line, have\r\n# an all uppercase name, and do not end with a semicolon. Such function macros\r\n# are typically used for boiler-plate code, and will confuse the parser if not\r\n# removed.\r\n# The default value is: YES.\r\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\r\n\r\nSKIP_FUNCTION_MACROS   = YES\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to external references\r\n#---------------------------------------------------------------------------\r\n\r\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\r\n# file the location of the external documentation should be added. The format of\r\n# a tag file without this location is as follows:\r\n# TAGFILES = file1 file2 ...\r\n# Adding location for the tag files is done as follows:\r\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\r\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\r\n# section \"Linking to external documentation\" for more information about the use\r\n# of tag files.\r\n# Note: Each tag file must have a unique name (where the name does NOT include\r\n# the path). If a tag file is not located in the directory in which doxygen is\r\n# run, you must also specify the path to the tagfile here.\r\n\r\nTAGFILES               =\r\n\r\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\r\n# tag file that is based on the input files it reads. See section \"Linking to\r\n# external documentation\" for more information about the usage of tag files.\r\n\r\nGENERATE_TAGFILE       =\r\n\r\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\r\n# the class index. If set to NO, only the inherited external classes will be\r\n# listed.\r\n# The default value is: NO.\r\n\r\nALLEXTERNALS           = NO\r\n\r\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\r\n# in the modules index. If set to NO, only the current project's groups will be\r\n# listed.\r\n# The default value is: YES.\r\n\r\nEXTERNAL_GROUPS        = YES\r\n\r\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\r\n# the related pages index. If set to NO, only the current project's pages will\r\n# be listed.\r\n# The default value is: YES.\r\n\r\nEXTERNAL_PAGES         = YES\r\n\r\n#---------------------------------------------------------------------------\r\n# Configuration options related to the dot tool\r\n#---------------------------------------------------------------------------\r\n\r\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\r\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\r\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\r\n# disabled, but it is recommended to install and use dot, since it yields more\r\n# powerful graphs.\r\n# The default value is: YES.\r\n\r\nCLASS_DIAGRAMS         = YES\r\n\r\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\r\n# then run dia to produce the diagram and insert it in the documentation. The\r\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\r\n# If left empty dia is assumed to be found in the default search path.\r\n\r\nDIA_PATH               =\r\n\r\n# If set to YES the inheritance and collaboration graphs will hide inheritance\r\n# and usage relations if the target is undocumented or is not a class.\r\n# The default value is: YES.\r\n\r\nHIDE_UNDOC_RELATIONS   = YES\r\n\r\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\r\n# available from the path. This tool is part of Graphviz (see:\r\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\r\n# Bell Labs. The other options in this section have no effect if this option is\r\n# set to NO\r\n# The default value is: NO.\r\n\r\nHAVE_DOT               = NO\r\n\r\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\r\n# to run in parallel. When set to 0 doxygen will base this on the number of\r\n# processors available in the system. You can set it explicitly to a value\r\n# larger than 0 to get control over the balance between CPU load and processing\r\n# speed.\r\n# Minimum value: 0, maximum value: 32, default value: 0.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_NUM_THREADS        = 0\r\n\r\n# When you want a differently looking font in the dot files that doxygen\r\n# generates you can specify the font name using DOT_FONTNAME. You need to make\r\n# sure dot is able to find the font, which can be done by putting it in a\r\n# standard location or by setting the DOTFONTPATH environment variable or by\r\n# setting DOT_FONTPATH to the directory containing the font.\r\n# The default value is: Helvetica.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_FONTNAME           = Helvetica\r\n\r\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\r\n# dot graphs.\r\n# Minimum value: 4, maximum value: 24, default value: 10.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_FONTSIZE           = 10\r\n\r\n# By default doxygen will tell dot to use the default font as specified with\r\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\r\n# the path where dot can find it using this tag.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_FONTPATH           =\r\n\r\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\r\n# each documented class showing the direct and indirect inheritance relations.\r\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nCLASS_GRAPH            = YES\r\n\r\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\r\n# graph for each documented class showing the direct and indirect implementation\r\n# dependencies (inheritance, containment, and class references variables) of the\r\n# class with other documented classes.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nCOLLABORATION_GRAPH    = YES\r\n\r\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\r\n# groups, showing the direct groups dependencies.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nGROUP_GRAPHS           = YES\r\n\r\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\r\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\r\n# Language.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nUML_LOOK               = NO\r\n\r\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\r\n# class node. If there are many fields or methods and many nodes the graph may\r\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\r\n# number of items for each type to make the size more manageable. Set this to 0\r\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\r\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\r\n# but if the number exceeds 15, the total amount of fields shown is limited to\r\n# 10.\r\n# Minimum value: 0, maximum value: 100, default value: 10.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nUML_LIMIT_NUM_FIELDS   = 10\r\n\r\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\r\n# collaboration graphs will show the relations between templates and their\r\n# instances.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nTEMPLATE_RELATIONS     = NO\r\n\r\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\r\n# YES then doxygen will generate a graph for each documented file showing the\r\n# direct and indirect include dependencies of the file with other documented\r\n# files.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nINCLUDE_GRAPH          = YES\r\n\r\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\r\n# set to YES then doxygen will generate a graph for each documented file showing\r\n# the direct and indirect include dependencies of the file with other documented\r\n# files.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nINCLUDED_BY_GRAPH      = YES\r\n\r\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\r\n# dependency graph for every global function or class method.\r\n#\r\n# Note that enabling this option will significantly increase the time of a run.\r\n# So in most cases it will be better to enable call graphs for selected\r\n# functions only using the \\callgraph command. Disabling a call graph can be\r\n# accomplished by means of the command \\hidecallgraph.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nCALL_GRAPH             = NO\r\n\r\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\r\n# dependency graph for every global function or class method.\r\n#\r\n# Note that enabling this option will significantly increase the time of a run.\r\n# So in most cases it will be better to enable caller graphs for selected\r\n# functions only using the \\callergraph command. Disabling a caller graph can be\r\n# accomplished by means of the command \\hidecallergraph.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nCALLER_GRAPH           = NO\r\n\r\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\r\n# hierarchy of all classes instead of a textual one.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nGRAPHICAL_HIERARCHY    = YES\r\n\r\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\r\n# dependencies a directory has on other directories in a graphical way. The\r\n# dependency relations are determined by the #include relations between the\r\n# files in the directories.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDIRECTORY_GRAPH        = YES\r\n\r\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\r\n# generated by dot. For an explanation of the image formats see the section\r\n# output formats in the documentation of the dot tool (Graphviz (see:\r\n# http://www.graphviz.org/)).\r\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\r\n# to make the SVG files visible in IE 9+ (other browsers do not have this\r\n# requirement).\r\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\r\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\r\n# png:gdiplus:gdiplus.\r\n# The default value is: png.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_IMAGE_FORMAT       = png\r\n\r\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\r\n# enable generation of interactive SVG images that allow zooming and panning.\r\n#\r\n# Note that this requires a modern browser other than Internet Explorer. Tested\r\n# and working are Firefox, Chrome, Safari, and Opera.\r\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\r\n# the SVG files visible. Older versions of IE do not have SVG support.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nINTERACTIVE_SVG        = NO\r\n\r\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\r\n# found. If left blank, it is assumed the dot tool can be found in the path.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_PATH               =\r\n\r\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\r\n# contain dot files that are included in the documentation (see the \\dotfile\r\n# command).\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOTFILE_DIRS           =\r\n\r\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\r\n# contain msc files that are included in the documentation (see the \\mscfile\r\n# command).\r\n\r\nMSCFILE_DIRS           =\r\n\r\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\r\n# contain dia files that are included in the documentation (see the \\diafile\r\n# command).\r\n\r\nDIAFILE_DIRS           =\r\n\r\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\r\n# path where java can find the plantuml.jar file. If left blank, it is assumed\r\n# PlantUML is not used or called during a preprocessing step. Doxygen will\r\n# generate a warning when it encounters a \\startuml command in this case and\r\n# will not generate output for the diagram.\r\n\r\nPLANTUML_JAR_PATH      =\r\n\r\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\r\n# configuration file for plantuml.\r\n\r\nPLANTUML_CFG_FILE      =\r\n\r\n# When using plantuml, the specified paths are searched for files specified by\r\n# the !include statement in a plantuml block.\r\n\r\nPLANTUML_INCLUDE_PATH  =\r\n\r\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\r\n# that will be shown in the graph. If the number of nodes in a graph becomes\r\n# larger than this value, doxygen will truncate the graph, which is visualized\r\n# by representing a node as a red box. Note that doxygen if the number of direct\r\n# children of the root node in a graph is already larger than\r\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\r\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\r\n# Minimum value: 0, maximum value: 10000, default value: 50.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_GRAPH_MAX_NODES    = 50\r\n\r\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\r\n# generated by dot. A depth value of 3 means that only nodes reachable from the\r\n# root by following a path via at most 3 edges will be shown. Nodes that lay\r\n# further from the root node will be omitted. Note that setting this option to 1\r\n# or 2 may greatly reduce the computation time needed for large code bases. Also\r\n# note that the size of a graph can be further restricted by\r\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\r\n# Minimum value: 0, maximum value: 1000, default value: 0.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nMAX_DOT_GRAPH_DEPTH    = 0\r\n\r\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\r\n# background. This is disabled by default, because dot on Windows does not seem\r\n# to support this out of the box.\r\n#\r\n# Warning: Depending on the platform used, enabling this option may lead to\r\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\r\n# read).\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_TRANSPARENT        = NO\r\n\r\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\r\n# files in one run (i.e. multiple -o and -T options on the command line). This\r\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\r\n# this, this feature is disabled by default.\r\n# The default value is: NO.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_MULTI_TARGETS      = NO\r\n\r\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\r\n# explaining the meaning of the various boxes and arrows in the dot generated\r\n# graphs.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nGENERATE_LEGEND        = YES\r\n\r\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot\r\n# files that are used to generate the various graphs.\r\n# The default value is: YES.\r\n# This tag requires that the tag HAVE_DOT is set to YES.\r\n\r\nDOT_CLEANUP            = YES\r\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2017 lanzhengpeng\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# librf 2.9.10\n\n### librf  - 协程库\n * librf是一个基于C++20 coroutines编写的无栈协程库。\n\n支持以下平台和编译器:\n\n\tWindows: 使用VS2017/VS2019编译(clang在兼容msvc模式下不支持异常，不再支持clang)\n\tAndroid: 使用NDK 20.1 自带的clang编译\n\tMac: 使用XCode 11.2.1 自带的apple-clang编译\n\tUbuntu: 使用GCC 11.2/clang 14 编译（2020-04-18：mutex ranged lock failed）\n\t\n\t注意：GCC 10.0.1在C++ Coroutines上依然存在很多BUG。\n\t当前阶段不推荐使用GCC(version 11.2以下)。\n\n<br>\n\n * 性能测试\n\n   ​\t本来无意搞性能测试的，因为librf的初衷和设计目标，就不是为了性能。然而，随着C++ Coroutines进入C++ 20标准，类似的协程库也越来越多。得益于C++ Coroutines的优良设计，他们的这些协程库都把性能放在首要位置进行宣传。裹挟着我也不得不进行一番性能调优，以及测试。\n\n   以下数据全是平局每个操作的时间开销。\n\n   | 编译器 | 操作系统     | [mimalloc](https://github.com/microsoft/mimalloc) | 多线程 | 创建*1 | 切换*1 | 创建*1000 | 切换*1000 | 创建*30000 | 切换*30000 |\n   | :----: | :----------- | :-----------------------------------------------: | :----: | :----: | :----: | :-------: | :-------: | :--------: | :--------: |\n   | clang  | Windows10    |                        ON                         |  OFF   | 500 ns | 17 ns  |  101 ns   | **11 ns** |   90 ns    |   12 ns    |\n   | clang  | Windows10    |                      **ON**                       | **ON** | 500 ns | 37 ns  |  116 ns   | **18 ns** |   103 ns   |   20 ns    |\n   | clang  | Windows10    |                        OFF                        |  OFF   | 800 ns | 16 ns  |  208 ns   | **11 ns** |   186 ns   |   15 ns    |\n   | clang  | Windows10    |                        OFF                        |   ON   | 900 ns | 37 ns  |  240 ns   | **18 ns** |   203 ns   |   23 ns    |\n   | clang  | Ubuntu 20.04 |                        ON                         |  OFF   | 400 ns | 15 ns  |  112 ns   | **11 ns** |   108 ns   |   12 ns    |\n   | clang  | Ubuntu 20.04 |                      **ON**                       | **ON** | 500 ns | 31 ns  |  111 ns   | **16 ns** |   109 ns   |   17 ns    |\n   | clang  | Ubuntu 20.04 |                        OFF                        |  OFF   | 400 ns | 15 ns  |  133 ns   | **13 ns** |   149 ns   |   15 ns    |\n   | clang  | Ubuntu 20.04 |                        OFF                        |   ON   | 500 ns | 30 ns  |  140 ns   | **17 ns** |   141 ns   |   19 ns    |\n\n   (测试环境：I7 8700K OC 4.3GHz，频率由于酷睿原因不是特别稳定)\n\n   多线程：调度器是否开启多线程支持。由于调度器里频繁用到自旋锁，导致在这种测试中数据较为难看。\n\n   创建*1：创建 1 个协程的平均开销；\n\n   切换*1：在只有 1 个协程的时候，切换 3000000 次的平均开销；\n\n   创建*1000：创建 1000 个协程的平均开销；\n\n   切换*1000：在具有 1000 个协程的时候，切换 3000000 次的平均开销；\n\n   创建*30000：创建 30000 个协程的平均开销；\n\n   切换*30000：在具有 30000 个协程的时候，切换 3000000 次的平均开销；\n\n   加粗的字，表示那一项应该是常规状态下的状态，具有较大参考价值。\n\n   <br>\n\n* librf有以下特点：\n\n```\n1.基于C++20提案'Stackless Resumable Functions'编写的非对称stackless协程库，可以以同步的方式编写简单的代码，同时获得异步的性能\n2.理论上支持海量协程, 创建 **10,000,000** 个协程只需使用 **2.2G** 物理内存(使用clang编译)\n3.拥有极小的协程调度开销，在I7 8100 3.6GHz的CPU上，**1000** 个协程的平均切换开销是 **27** 纳秒(使用clang编译)\n4.提供协程锁(mutex), 定时器, channel, event等特性, 帮助用户更加容易地编写程序\n5.可以很好的跟asio, libuv等库结合，能跟现有的callback范式的异步/延迟代码很好的结合\n6.目前已处于较为完善状态，已经小规模在生产项目中使用。不出意外，2.8以上版本就是C++20 Coroutines对应的版本\n```\n\n<br>\n\n* 如果你发现了任何bug、有好的建议、或使用上有不明之处，可以提交到issue，也可以直接联系作者:\n\n\temail: tearshark@163.net\n\tQQ交流群: 296561497\n\n<br>\n\n* 更新日志：\n\n2020-06-20 更新：\n\n```\n性能调优和测试数据\n```\n\n2020-03-31 更新：\n\n\t使用Doxygen自动生成文档，并完善文档内容。\n\t支持cmake。(目前仅VS2019测试通过)。\n\n2020-03-26 更新：\n\n\t兼容xcode 11.2.1。\n2020-03-18 更新：\n\n\t更新event/mutex/when_all/when_any实现。至此，2.x版本完整恢复1.x版本的所有功能。\n\t版本号提升至 2.8.0。\n\t3.0之前，只打算做修复BUG相关的工作。\n\t3.0的目标，是根据executor的设计，重写scheduler代码。\n2020-03-08 更新：\n\n\t更新channel实现，效率提高了近三倍。\n\tchannel的新的实现方法，为event/mutex指明了新的修改方向。\n2020-02-16 更新：\n\n\t更新调度器算法，深入应用Coroutines的特性，以期获得更高调度性能。\n\t不再支持C++14。\n\n\n\n\n * 注一：doc目录下有作者搜集的一些关于C++协程的资料\n * 注二：tutorial目录下有针对每个特性的范例代码，让用户可以循序渐进的了解librf库的特性\n"
  },
  {
    "path": "asio/asio_task.h",
    "content": "﻿#pragma once\n\n#if ASIO_VERSION >= 101202\n#include \"asio_task_1.12.2.inl\"\n#elif ASIO_VERSION >= 101200\n#include \"asio_task_1.12.0.inl\"\n#else\n#include \"asio_task_1.10.0.inl\"\n#endif\n"
  },
  {
    "path": "asio/asio_task_1.10.0.inl",
    "content": "﻿#include <memory>\n\n#include \"asio/detail/push_options.hpp\"\n\nnamespace asio {\n\n\ttemplate<typename Allocator = std::allocator<int> >\n\tclass rf_task_t\n\t{\n\tpublic:\n\t\ttypedef Allocator allocator_type;\n\t\tconstexpr rf_task_t() {}\n\t\texplicit rf_task_t(const Allocator& allocator) : allocator_(allocator) {}\n\n\t\ttemplate<typename OtherAllocator>\n\t\trf_task_t<OtherAllocator> operator[](const OtherAllocator& allocator) const {\n\t\t\treturn rf_task_t<OtherAllocator>(allocator);\n\t\t}\n\n\t\tallocator_type get_allocator() const { return allocator_; }\n\tprivate:\n\t\tAllocator allocator_;\n\t};\n\tconstexpr rf_task_t<> rf_task;\n\n\tnamespace detail {\n\n\t\ttemplate<typename T>\n\t\tclass promise_handler\n\t\t{\n\t\tpublic:\n\t\t\tusing result_type_t = T;\n\t\t\tusing state_type = librf::state_t<result_type_t>;\n\n\t\t\ttemplate<typename Allocator>\n\t\t\tpromise_handler(const rf_task_t<Allocator> &)\n\t\t\t\t: state_(state_type::typename _Alloc_state<state_type>(true))\n\t\t\t{\n\t\t\t}\n\n\t\t\tvoid operator()(T t) const\n\t\t\t{\n\t\t\t\tstate_->set_value(std::move(t));\n\t\t\t}\n\n\t\t\tvoid operator()(const asio::error_code& ec, T t) const\n\t\t\t{\n\t\t\t\tif (!ec)\n\t\t\t\t{\n\t\t\t\t\tstate_->set_value(std::move(t));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstate_->set_exception(std::make_exception_ptr(asio::system_error(ec)));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlibrf::counted_ptr<state_type> state_;\n\t\t};\n\n\t\ttemplate<>\n\t\tclass promise_handler<void>\n\t\t{\n\t\tpublic:\n\t\t\tusing result_type_t = void;\n\t\t\tusing state_type = librf::state_t<result_type_t>;\n\n\t\t\ttemplate<typename Allocator>\n\t\t\tpromise_handler(const rf_task_t<Allocator> &)\n\t\t\t\t: state_(state_type::typename _Alloc_state<state_type>(true))\n\t\t\t{\n\t\t\t}\n\n\t\t\tvoid operator()() const\n\t\t\t{\n\t\t\t\tstate_->set_value();\n\t\t\t}\n\n\t\t\tvoid operator()(const asio::error_code& ec) const\n\t\t\t{\n\t\t\t\tif (!ec)\n\t\t\t\t{\n\t\t\t\t\tstate_->set_value();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstate_->set_exception(std::make_exception_ptr(asio::system_error(ec)));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlibrf::counted_ptr<state_type> state_;\n\t\t};\n\n\t}  // namespace detail\n\n\ttemplate<typename T>\n\tclass async_result<detail::promise_handler<T> >\n\t{\n\tpublic:\n\t\ttypedef librf::future_t<T> type;\n\n\t\texplicit async_result(detail::promise_handler<T> & h)\n\t\t\t: task_(std::move(h.state_))\n\t\t{ }\n\n\t\tlibrf::future_t<T> get() { return std::move(task_); }\n\tprivate:\n\t\tlibrf::future_t<T> task_;\n\t};\n\n\t// Handler type specialisation for zero arg.\n\ttemplate<typename Allocator, typename ReturnType>\n\tstruct handler_type<rf_task_t<Allocator>, ReturnType()> {\n\t\ttypedef detail::promise_handler<void> type;\n\t};\n\n\t// Handler type specialisation for one arg.\n\ttemplate<typename Allocator, typename ReturnType, typename Arg1>\n\tstruct handler_type<rf_task_t<Allocator>, ReturnType(Arg1)> {\n\t\ttypedef detail::promise_handler<Arg1> type;\n\t};\n\n\t// Handler type specialisation for two arg.\n\ttemplate<typename Allocator, typename ReturnType, typename Arg2>\n\tstruct handler_type<rf_task_t<Allocator>, ReturnType(asio::error_code, Arg2)> {\n\t\ttypedef detail::promise_handler<Arg2> type;\n\t};\n\n\ttemplate<typename Allocator, typename ReturnType>\n\tstruct handler_type<rf_task_t<Allocator>, ReturnType(asio::error_code)> {\n\t\ttypedef detail::promise_handler<void> type;\n\t};\n\n}  // namespace asio\n\n#include \"asio/detail/pop_options.hpp\"\n"
  },
  {
    "path": "asio/asio_task_1.12.0.inl",
    "content": "﻿#include <memory>\n\n#include \"asio/detail/push_options.hpp\"\n\nnamespace asio {\n\n\t/**\n\t * @brief 用于指示asio相关异步函数，返回librf::future_t<>的类型，从而变成支持 librf 的协程函数。\n\t */\n\ttemplate <typename Executor = executor>\n\tstruct rf_task_t\n\t{\n\t\tASIO_CONSTEXPR rf_task_t() {}\n\t};\n\n\t/**\n\t * @brief 用于指示asio相关异步函数，返回librf::future_t<>的常量，从而变成支持 librf 的协程函数。\n\t */\n\tconstexpr rf_task_t<> rf_task;\n\n\tnamespace librf {\n\n\t\ttemplate <typename Executor, typename T>\n\t\tstruct promise_handler_base\n\t\t{\n\t\tpublic:\n\t\t\ttypedef T result_type;\n\t\t\ttypedef librf::state_t<result_type> state_type;\n\n\t\t\tpromise_handler_base()\n\t\t\t\t: state_(librf::state_future_t::_Alloc_state<state_type>(true))\n\t\t\t{\n\t\t\t}\n\n\t\t\tlibrf::counted_ptr<state_type> state_;\n\t\t\tpromise_handler_base(promise_handler_base &&) = default;\n\t\t\tpromise_handler_base(const promise_handler_base &) = default;\n\t\t\tpromise_handler_base & operator = (promise_handler_base &&) = default;\n\t\t\tpromise_handler_base & operator = (const promise_handler_base &) = default;\n\t\t};\n\n\t\ttemplate <typename, typename...>\n\t\tstruct promise_handler;\n\n\t\ttemplate <typename Executor>\n\t\tstruct promise_handler<Executor, void> : public promise_handler_base<Executor, void>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, void>::promise_handler_base;\n\n\t\t\tvoid operator()() const\n\t\t\t{\n\t\t\t\tthis->state_->set_value();\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor>\n\t\tstruct promise_handler<Executor, asio::error_code> : public promise_handler_base<Executor, void>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, void>::promise_handler_base;\n\n\t\t\tvoid operator()(const asio::error_code& ec) const\n\t\t\t{\n\t\t\t\tif (!ec)\n\t\t\t\t\tthis->state_->set_value();\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(std::make_exception_ptr(asio::system_error(ec)));\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor>\n\t\tstruct promise_handler<Executor, std::exception_ptr> : public promise_handler_base<Executor, void>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, void>::promise_handler_base;\n\n\t\t\tvoid operator()(std::exception_ptr ex) const\n\t\t\t{\n\t\t\t\tif (!ex)\n\t\t\t\t\tthis->state_->set_value();\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(ex);\n\t\t\t}\n\t\t};\n\n\n\n\t\ttemplate <typename Executor, typename T>\n\t\tstruct promise_handler<Executor, T> : public promise_handler_base<Executor, T>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, T>::promise_handler_base;\n\n\t\t\ttemplate <typename Arg>\n\t\t\tvoid operator()(Arg&& arg) const\n\t\t\t{\n\t\t\t\tthis->state_->set_value(std::forward<Arg>(arg));\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor, typename T>\n\t\tstruct promise_handler<Executor, asio::error_code, T> : public promise_handler_base<Executor, T>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, T>::promise_handler_base;\n\n\t\t\ttemplate <typename Arg>\n\t\t\tvoid operator()(const asio::error_code& ec, Arg&& arg) const\n\t\t\t{\n\t\t\t\tif (!ec)\n\t\t\t\t\tthis->state_->set_value(std::forward<Arg>(arg));\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(std::make_exception_ptr(asio::system_error(ec)));\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor, typename T>\n\t\tstruct promise_handler<Executor, std::exception_ptr, T> : public promise_handler_base<Executor, T>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, T>::promise_handler_base;\n\n\t\t\ttemplate <typename Arg>\n\t\t\tvoid operator()(std::exception_ptr ex, Arg&& arg) const\n\t\t\t{\n\t\t\t\tif (!ex)\n\t\t\t\t\tthis->state_->set_value(std::forward<Arg>(arg));\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(ex);\n\t\t\t}\n\t\t};\n\n\n\n\t\ttemplate <typename Executor, typename... Ts>\n\t\tstruct promise_handler : public promise_handler_base<Executor, std::tuple<Ts...>>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, std::tuple<Ts...>>::promise_handler_base;\n\n\t\t\ttemplate <typename... Args>\n\t\t\tvoid operator()(Args&&... args) const\n\t\t\t{\n\t\t\t\tthis->state_->set_value(std::make_tuple(std::forward<Args>(args)...));\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor, typename... Ts>\n\t\tstruct promise_handler<Executor, asio::error_code, Ts...> : public promise_handler_base<Executor, std::tuple<Ts...>>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, std::tuple<Ts...>>::promise_handler_base;\n\n\t\t\ttemplate <typename... Args>\n\t\t\tvoid operator()(const asio::error_code& ec, Args&&... args) const\n\t\t\t{\n\t\t\t\tif (!ec)\n\t\t\t\t\tthis->state_->set_value(std::make_tuple(std::forward<Args>(args)...));\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(std::make_exception_ptr(asio::system_error(ec)));\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor, typename... Ts>\n\t\tstruct promise_handler<Executor, std::exception_ptr, Ts...> : public promise_handler_base<Executor, std::tuple<Ts...>>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, std::tuple<Ts...>>::promise_handler_base;\n\n\t\t\ttemplate <typename... Args>\n\t\t\tvoid operator()(std::exception_ptr ex, Args&&... args) const\n\t\t\t{\n\t\t\t\tif (!ex)\n\t\t\t\t\tthis->state_->set_value(std::make_tuple(std::forward<Args>(args)...));\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(ex);\n\t\t\t}\n\t\t};\n\n\t}  // namespace librf\n\n\ttemplate <typename Executor, typename R, typename... Args>\n\tclass async_result<rf_task_t<Executor>, R(Args...)>\n\t{\n\tpublic:\n\t\ttypedef librf::promise_handler<Executor, Args...> handler_type;\n\t\ttypedef typename handler_type::result_type result_type;\n\t\ttypedef librf::future_t<result_type> return_type;\n\n\t\ttemplate <typename Initiation, typename... InitArgs>\n\t\tstatic return_type initiate(ASIO_MOVE_ARG(Initiation) initiation,\n\t\t\trf_task_t<Executor>, ASIO_MOVE_ARG(InitArgs)... args)\n\t\t{\n\t\t\thandler_type handler{};\n\t\t\treturn_type future{ handler.state_ };\n\n\t\t\tstd::move(initiation)(std::move(handler), std::move(args)...);\n\t\t\treturn std::move(future);\n\t\t}\n\t};\n\n}  // namespace asio\n\n#include \"asio/detail/pop_options.hpp\"\n"
  },
  {
    "path": "asio/asio_task_1.12.2.inl",
    "content": "﻿#include <memory>\n\n#include \"asio/detail/push_options.hpp\"\n\nnamespace asio {\n\n\t/**\n\t * @brief 用于指示asio相关异步函数，返回librf::future_t<>的类型，从而变成支持 librf 的协程函数。\n\t */\n\ttemplate <typename Executor = executor>\n\tstruct rf_task_t\n\t{\n\t\tASIO_CONSTEXPR rf_task_t() {}\n\t};\n\n\t/**\n\t * @brief 用于指示asio相关异步函数，返回librf::future_t<>的常量，从而变成支持 librf 的协程函数。\n\t */\n\tconstexpr rf_task_t<> rf_task;\n\n\tnamespace librf {\n\n\t\ttemplate <typename Executor, typename _Result>\n\t\tstruct promise_handler_base\n\t\t{\n\t\tpublic:\n\t\t\ttypedef _Result result_type;\n\t\t\ttypedef ::librf::state_t<result_type> state_type;\n\n\t\t\tpromise_handler_base(const rf_task_t<Executor>&)\n\t\t\t\t: state_(::librf::state_future_t::_Alloc_state<state_type>(true))\n\t\t\t{\n\t\t\t}\n\n\t\t\t::librf::counted_ptr<state_type> state_;\n\t\t\tpromise_handler_base(promise_handler_base &&) = default;\n\t\t\tpromise_handler_base(const promise_handler_base &) = default;\n\t\t\tpromise_handler_base & operator = (promise_handler_base &&) = default;\n\t\t\tpromise_handler_base & operator = (const promise_handler_base &) = default;\n\t\t};\n\n\t\ttemplate <typename, typename...>\n\t\tstruct promise_handler;\n\n\t\ttemplate <typename Executor>\n\t\tstruct promise_handler<Executor, void> : public promise_handler_base<Executor, void>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, void>::promise_handler_base;\n\n\t\t\tvoid operator()() const\n\t\t\t{\n\t\t\t\tthis->state_->set_value();\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor>\n\t\tstruct promise_handler<Executor, asio::error_code> : public promise_handler_base<Executor, void>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, void>::promise_handler_base;\n\n\t\t\tvoid operator()(const asio::error_code& ec) const\n\t\t\t{\n\t\t\t\tif (!ec)\n\t\t\t\t\tthis->state_->set_value();\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(std::make_exception_ptr(asio::system_error(ec)));\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor>\n\t\tstruct promise_handler<Executor, std::exception_ptr> : public promise_handler_base<Executor, void>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, void>::promise_handler_base;\n\n\t\t\tvoid operator()(std::exception_ptr ex) const\n\t\t\t{\n\t\t\t\tif (!ex)\n\t\t\t\t\tthis->state_->set_value();\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(ex);\n\t\t\t}\n\t\t};\n\n\n\n\t\ttemplate <typename Executor, typename T>\n\t\tstruct promise_handler<Executor, T> : public promise_handler_base<Executor, T>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, T>::promise_handler_base;\n\n\t\t\ttemplate <typename Arg>\n\t\t\tvoid operator()(Arg&& arg) const\n\t\t\t{\n\t\t\t\tthis->state_->set_value(std::forward<Arg>(arg));\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor, typename T>\n\t\tstruct promise_handler<Executor, asio::error_code, T> : public promise_handler_base<Executor, T>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, T>::promise_handler_base;\n\n\t\t\ttemplate <typename Arg>\n\t\t\tvoid operator()(const asio::error_code& ec, Arg&& arg) const\n\t\t\t{\n\t\t\t\tif (!ec)\n\t\t\t\t\tthis->state_->set_value(std::forward<Arg>(arg));\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(std::make_exception_ptr(asio::system_error(ec)));\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor, typename T>\n\t\tstruct promise_handler<Executor, std::exception_ptr, T> : public promise_handler_base<Executor, T>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, T>::promise_handler_base;\n\n\t\t\ttemplate <typename Arg>\n\t\t\tvoid operator()(std::exception_ptr ex, Arg&& arg) const\n\t\t\t{\n\t\t\t\tif (!ex)\n\t\t\t\t\tthis->state_->set_value(std::forward<Arg>(arg));\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(ex);\n\t\t\t}\n\t\t};\n\n\n\n\t\ttemplate <typename Executor, typename... Ts>\n\t\tstruct promise_handler : public promise_handler_base<Executor, std::tuple<Ts...>>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, std::tuple<Ts...>>::promise_handler_base;\n\n\t\t\ttemplate <typename... Args>\n\t\t\tvoid operator()(Args&&... args) const\n\t\t\t{\n\t\t\t\tthis->state_->set_value(std::make_tuple(std::forward<Args>(args)...));\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor, typename... Ts>\n\t\tstruct promise_handler<Executor, asio::error_code, Ts...> : public promise_handler_base<Executor, std::tuple<Ts...>>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, std::tuple<Ts...>>::promise_handler_base;\n\n\t\t\ttemplate <typename... Args>\n\t\t\tvoid operator()(const asio::error_code& ec, Args&&... args) const\n\t\t\t{\n\t\t\t\tif (!ec)\n\t\t\t\t\tthis->state_->set_value(std::make_tuple(std::forward<Args>(args)...));\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(std::make_exception_ptr(asio::system_error(ec)));\n\t\t\t}\n\t\t};\n\n\t\ttemplate <typename Executor, typename... Ts>\n\t\tstruct promise_handler<Executor, std::exception_ptr, Ts...> : public promise_handler_base<Executor, std::tuple<Ts...>>\n\t\t{\n\t\t\tusing promise_handler_base<Executor, std::tuple<Ts...>>::promise_handler_base;\n\n\t\t\ttemplate <typename... Args>\n\t\t\tvoid operator()(std::exception_ptr ex, Args&&... args) const\n\t\t\t{\n\t\t\t\tif (!ex)\n\t\t\t\t\tthis->state_->set_value(std::make_tuple(std::forward<Args>(args)...));\n\t\t\t\telse\n\t\t\t\t\tthis->state_->set_exception(ex);\n\t\t\t}\n\t\t};\n\n\t}  // namespace librf\n\n\ttemplate <typename Executor, typename R, typename... Args>\n\tclass async_result<rf_task_t<Executor>, R(Args...)>\n\t{\n\tpublic:\n\t\ttypedef librf::promise_handler<Executor, Args...> completion_handler_type;\n\t\ttypedef typename completion_handler_type::result_type result_type;\n\n\t\ttypedef ::librf::state_t<result_type> state_type;\n\t\ttypedef ::librf::future_t<result_type> return_type;\n\n\t\tasync_result(completion_handler_type& hander)\n\t\t\t: state_(hander.state_)\n\t\t{}\n\n\t\treturn_type get() const noexcept\n\t\t{\n\t\t\treturn this->state_;\n\t\t}\n\tprivate:\n\t\t::librf::counted_ptr<state_type> state_;\n\t};\n\n}  // namespace asio\n\n#include \"asio/detail/pop_options.hpp\"\n"
  },
  {
    "path": "benchmark/CMakeLists.txt",
    "content": "﻿set(BENCHMARK_FILES \"\")\naux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} BENCHMARK_FILES)\n\nfind_package(asio REQUIRED)\n\nforeach(BENCHMARK_FILE_PATH ${BENCHMARK_FILES})\n    string(REGEX REPLACE \".+[/\\]([^/\\.]+)\\\\.cpp\" \"\\\\1\" BENCHMARK_FILE_NAME ${BENCHMARK_FILE_PATH})\n    message(STATUS \"Generating benchmark target: ${BENCHMARK_FILE_NAME}\")\n\n    add_executable(${BENCHMARK_FILE_NAME} ${BENCHMARK_FILE_PATH})\n    target_link_libraries(${BENCHMARK_FILE_NAME} PUBLIC librf asio asio::asio)\n    target_compile_definitions(${BENCHMARK_FILE_NAME}\n        PRIVATE LIBRF_TUTORIAL_STAND_ALONE=1 ASIO_STANDALONE\n    )\nendforeach(BENCHMARK_FILE_PATH)\n"
  },
  {
    "path": "benchmark/benchmark_asio_echo.cpp",
    "content": "﻿\r\n#include <iostream>\r\n\r\n#include \"librf/librf.h\"\r\n#include <asio.hpp>\r\n#include \"../asio/asio_task.h\"\r\n\r\n#pragma warning(disable : 4834)\r\n\r\nusing namespace asio::ip;\r\nusing namespace librf;\r\n\r\ntemplate<class _Ty, size_t _Size>\r\nunion uarray\r\n{\r\n\tstd::array<_Ty, _Size>\t\tc;\r\n\r\n\ttemplate<class... Args>\r\n\tuarray(Args&... args)\r\n\t{\r\n\t\tfor (auto & v : c)\r\n\t\t\tnew(&v) _Ty(args...);\r\n\t}\r\n\t~uarray()\r\n\t{\r\n\t\tfor (auto & v : c)\r\n\t\t\tv.~_Ty();\r\n\t}\r\n};\r\n\r\n#define BUF_SIZE 1024\r\n\r\nstd::atomic<intptr_t> g_echo_count = 0;\r\n\r\nfuture_t<> RunEchoSession(tcp::socket socket)\r\n{\r\n\tstd::size_t bytes_transferred = 0;\r\n\tstd::array<char, BUF_SIZE> buffer;\r\n\tfor(;;)\r\n\t{\r\n\t\ttry\r\n\t\t{\r\n\t\t\tbytes_transferred += co_await socket.async_read_some(\r\n\t\t\t\tasio::buffer(buffer.data() + bytes_transferred, buffer.size() - bytes_transferred), asio::rf_task);\r\n\t\t\tif (bytes_transferred >= buffer.size())\r\n\t\t\t{\r\n\t\t\t\tco_await asio::async_write(socket, asio::buffer(buffer, buffer.size()), asio::rf_task);\r\n\t\t\t\tbytes_transferred = 0;\r\n\r\n\t\t\t\tg_echo_count.fetch_add(1, std::memory_order_release);\r\n\t\t\t}\r\n\t\t}\r\n\t\tcatch (std::exception & e)\r\n\t\t{\r\n\t\t\tstd::cerr << e.what() << std::endl;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n}\r\n\r\ntemplate<size_t _N>\r\nvoid AcceptConnections(tcp::acceptor & acceptor, uarray<tcp::socket, _N> & socketes)\r\n{\r\n\ttry\r\n\t{\r\n\t\tfor (size_t idx = 0; idx < socketes.c.size(); ++idx)\r\n\t\t{\r\n\t\t\tgo[&, idx]() -> future_t<>\r\n\t\t\t{\r\n\t\t\t\tfor (;;)\r\n\t\t\t\t{\r\n\t\t\t\t\ttry\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tco_await acceptor.async_accept(socketes.c[idx], asio::rf_task);\r\n\t\t\t\t\t\tgo RunEchoSession(std::move(socketes.c[idx]));\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcatch (std::exception & e)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tstd::cerr << e.what() << std::endl;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n\tcatch (std::exception & e)\r\n\t{\r\n\t\tstd::cerr << e.what() << std::endl;\r\n\t}\r\n}\r\n\r\nvoid StartPrintEchoCount()\r\n{\r\n\tusing namespace std::literals;\r\n\r\n\tGO\r\n\t{\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\tg_echo_count.exchange(0, std::memory_order_release);\r\n\t\t\tco_await 1s;\r\n\t\t\tstd::cout << g_echo_count.load(std::memory_order_acquire) << std::endl;\r\n\t\t}\r\n\t};\r\n}\r\n\r\nvoid RunOneBenchmark(bool bMain)\r\n{\r\n\tlibrf::local_scheduler_t ls;\r\n\r\n\tasio::io_service io_service;\r\n\ttcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 3456));\r\n\tuarray<tcp::socket, 16> socketes(io_service);\r\n\r\n\tAcceptConnections(acceptor, socketes);\r\n\tif (bMain) StartPrintEchoCount();\r\n\r\n\tfor (;;)\r\n\t{\r\n\t\tio_service.poll();\r\n\t\tthis_scheduler()->run_one_batch();\r\n\t}\r\n}\r\n\r\nvoid resumable_main_benchmark_asio_server()\r\n{\r\n\tstd::array<std::thread, 2> thds;\r\n\tfor (size_t i = 0; i < thds.size(); ++i)\r\n\t{\r\n\t\tthds[i] = std::thread(&RunOneBenchmark, i == 0);\r\n\t}\r\n\r\n\tfor (auto & t : thds)\r\n\t\tt.join();\r\n}\r\n\r\n//----------------------------------------------------------------------------------------------------------------------\r\n\r\nfuture_t<> RunPipelineEchoClient(asio::io_service & ios, tcp::resolver::iterator ep)\r\n{\r\n\tstd::shared_ptr<tcp::socket> sptr = std::make_shared<tcp::socket>(ios);\r\n\r\n\ttry\r\n\t{\r\n\t\tco_await asio::async_connect(*sptr, ep, asio::rf_task);\r\n\r\n\t\tGO\r\n\t\t{\r\n\t\t\tstd::array<char, BUF_SIZE> write_buff_;\r\n\t\t\tfor (auto & c : write_buff_)\r\n\t\t\t\tc = 'A' + rand() % 52;\r\n\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tfor (;;)\r\n\t\t\t\t{\r\n\t\t\t\t\tco_await asio::async_write(*sptr, asio::buffer(write_buff_), asio::rf_task);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tcatch (std::exception & e)\r\n\t\t\t{\r\n\t\t\t\tstd::cerr << e.what() << std::endl;\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tGO\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tstd::array<char, BUF_SIZE> read_buff_;\r\n\t\t\t\tfor (;;)\r\n\t\t\t\t{\r\n\t\t\t\t\tco_await sptr->async_read_some(asio::buffer(read_buff_), asio::rf_task);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tcatch (std::exception & e)\r\n\t\t\t{\r\n\t\t\t\tstd::cerr << e.what() << std::endl;\r\n\t\t\t}\r\n\t\t};\r\n\t}\r\n\tcatch (std::exception & e)\r\n\t{\r\n\t\tstd::cerr << e.what() << std::endl;\r\n\t}\r\n}\r\n\r\n#if _HAS_CXX17\r\n\r\nfuture_t<> RunPingPongEchoClient(asio::io_service & ios, tcp::resolver::iterator ep)\r\n{\r\n\ttcp::socket socket_{ ios };\r\n\r\n\tstd::array<char, BUF_SIZE> read_buff_;\r\n\tstd::array<char, BUF_SIZE> write_buff_;\r\n\r\n\ttry\r\n\t{\r\n\t\tco_await asio::async_connect(socket_, ep, asio::rf_task);\r\n\r\n\t\tfor (auto & c : write_buff_)\r\n\t\t\tc = 'A' + rand() % 52;\r\n\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\tco_await when_all(\r\n\t\t\t\tasio::async_write(socket_, asio::buffer(write_buff_), asio::rf_task),\r\n\t\t\t\tsocket_.async_read_some(asio::buffer(read_buff_), asio::rf_task)\r\n\t\t\t);\r\n\t\t}\r\n\t}\r\n\tcatch (std::exception & e)\r\n\t{\r\n\t\tstd::cerr << e.what() << std::endl;\r\n\t}\r\n}\r\n\r\nvoid resumable_main_benchmark_asio_client_with_rf(intptr_t nNum)\r\n{\r\n\tnNum = std::max((intptr_t)1, nNum);\r\n\r\n\ttry\r\n\t{\r\n\t\tasio::io_service ios;\r\n\r\n\t\tasio::ip::tcp::resolver resolver_(ios);\r\n\t\tasio::ip::tcp::resolver::query query_(\"localhost\", \"3456\");\r\n\t\ttcp::resolver::iterator iter = resolver_.resolve(query_);\r\n\r\n\t\tfor (intptr_t i = 0; i < nNum; ++i)\r\n\t\t{\r\n\t\t\tgo RunPingPongEchoClient(ios, iter);\r\n\t\t}\r\n\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\tios.poll();\r\n\t\t\tthis_scheduler()->run_one_batch();\r\n\t\t}\r\n\t}\r\n\tcatch (std::exception & e)\r\n\t{\r\n\t\tstd::cout << e.what() << std::endl;\r\n\t}\r\n}\r\n#endif\r\n\r\nclass chat_session : public std::enable_shared_from_this<chat_session>\r\n{\r\npublic:\r\n\tchat_session(asio::io_service & ios, tcp::resolver::iterator ep)\r\n\t\t: socket_(ios)\r\n\t\t, endpoint_(ep)\r\n\t{\r\n\t}\r\n\r\n\tvoid start()\r\n\t{\r\n\t\tdo_connect();\r\n\t}\r\n\r\nprivate:\r\n\tvoid do_connect()\r\n\t{\r\n\t\tauto self = this->shared_from_this();\r\n\t\tasio::async_connect(socket_, endpoint_,\r\n\t\t\t[this, self](std::error_code ec, tcp::resolver::iterator )\r\n\t\t\t{\r\n\t\t\t\tif (!ec)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (auto & c : write_buff_)\r\n\t\t\t\t\t\tc = 'A' + rand() % 52;\r\n\r\n\t\t\t\t\tdo_write();\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tstd::cerr << ec.message() << std::endl;\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t}\r\n\r\n\tvoid do_read()\r\n\t{\r\n\t\tauto self(shared_from_this());\r\n\t\tsocket_.async_read_some(asio::buffer(read_buff_),\r\n\t\t\t[this, self](const asio::error_code& ec, std::size_t )\r\n\t\t\t{\r\n\t\t\t\tif (!ec)\r\n\t\t\t\t{\r\n\t\t\t\t\tdo_write();\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tstd::cerr << ec.message() << std::endl;\r\n\t\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\tvoid do_write()\r\n\t{\r\n\t\tauto self(shared_from_this());\r\n\t\tasio::async_write(socket_,\r\n\t\t\tasio::buffer(write_buff_),\r\n\t\t\t[this, self](std::error_code ec, std::size_t)\r\n\t\t\t{\r\n\t\t\t\tif (!ec)\r\n\t\t\t\t{\r\n\t\t\t\t\tdo_read();\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tstd::cerr << ec.message() << std::endl;\r\n\t\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\ttcp::socket socket_;\r\n\ttcp::resolver::iterator endpoint_;\r\n\r\n\tstd::array<char, BUF_SIZE> read_buff_;\r\n\tstd::array<char, BUF_SIZE> write_buff_;\r\n};\r\n\r\nvoid resumable_main_benchmark_asio_client_with_callback(intptr_t nNum)\r\n{\r\n\tnNum = std::max((intptr_t)1, nNum);\r\n\r\n\ttry\r\n\t{\r\n\t\tasio::io_service ios;\r\n\r\n\t\tasio::ip::tcp::resolver resolver_(ios);\r\n\t\tasio::ip::tcp::resolver::query query_(\"127.0.0.1\", \"3456\");\r\n\t\ttcp::resolver::iterator iter = resolver_.resolve(query_);\r\n\r\n\t\tfor (intptr_t i = 0; i < nNum; ++i)\r\n\t\t{\r\n\t\t\tauto chat = std::make_shared<chat_session>(ios, iter);\r\n\t\t\tchat->start();\r\n\t\t}\r\n\r\n\t\tios.run();\r\n\t}\r\n\tcatch (std::exception & e)\r\n\t{\r\n\t\tstd::cout << \"Exception: \" << e.what() << \"\\n\";\r\n\t}\r\n}\r\n\r\nvoid resumable_main_benchmark_asio_client(intptr_t nNum)\r\n{\r\n\tresumable_main_benchmark_asio_client_with_callback(nNum);\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main(int argc, const char* argv[])\r\n{\r\n\tif (argc > 1)\r\n\t\tresumable_main_benchmark_asio_client(atoi(argv[1]));\r\n\telse\r\n\t\tresumable_main_benchmark_asio_server();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "benchmark/benchmark_async_mem.cpp",
    "content": "﻿\r\n#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nconst size_t N = 10'000'000;\r\nconst size_t LOOP_COUNT = 50;\r\n\r\nstd::atomic<size_t> globalValue{0};\r\n\r\nvoid resumable_main_benchmark_mem(bool wait_key)\r\n{\r\n\tusing namespace std::chrono;\r\n\t\r\n\tfor (size_t i = 0; i < N; ++i)\r\n\t{\r\n\t\tgo[=]()->librf::generator_t<size_t>\r\n\t\t{\r\n\t\t\tfor (size_t k = 0; k < LOOP_COUNT; ++k)\r\n\t\t\t{\r\n\t\t\t\tglobalValue += i * k;\r\n\t\t\t\tco_yield k;\r\n\t\t\t}\r\n\t\t\tco_return 0;\r\n\t\t};\r\n\t}\r\n\r\n\tlibrf::this_scheduler()->run_until_notask();\r\n\tif (wait_key)\r\n\t{\r\n\t\tstd::cout << \"press any key to continue.\" << std::endl;\r\n\t\t(void)getchar();\r\n\t}\r\n}\r\n\r\n//clang : \r\n//\tx64: 平均 256 字节, operator new: size = 48, state size = 32\r\n//\tx86: 平均 121 字节, operator new: size = 40, state size = 16\r\n//msvc : 平均 304 字节(vs2022,17.7.4)\r\n//\tx64: 平均 304 字节, operator new: size = 144, state size = 32\r\n//\tx86: 平均 153 字节, operator new: size = 72, state size = 16\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_benchmark_mem(false);\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "benchmark/benchmark_channel_passing_next.cpp",
    "content": "﻿\n#include <chrono>\n#include <iostream>\n#include <string>\n#include <thread>\n#include <deque>\n#include <mutex>\n\n#include \"librf/librf.h\"\n\nusing namespace librf;\nusing namespace std::chrono;\nusing namespace std::literals;\n\nconst static auto MaxNum = 100000;\nconst static auto LoopCount = 100;\n\nusing int_channel_ptr = std::shared_ptr<channel_t<intptr_t>>;\n\nstatic future_t<> passing_next(channel_t<intptr_t> rd, channel_t<intptr_t> wr)\n{\n\tfor (int i = 0; i < LoopCount; ++i)\n\t{\n\t\tintptr_t value = co_await rd;\n\t\tco_await wr.write(value + 1);\n\t}\n}\n\n#if defined(__GNUC__)\nstatic future_t<> passing_loop_all(channel_t<intptr_t> head, channel_t<intptr_t> tail)\n{\n\tfor (int i = 0; i < LoopCount; ++i)\n\t{\n\t\tauto tstart = high_resolution_clock::now();\n\n\t\tco_await(head << 0);\n\t\tintptr_t value = co_await tail;\n\n\t\tauto dt = duration_cast<duration<double>>(high_resolution_clock::now() - tstart).count();\n\t\tstd::cout << value << \" cost time \" << dt << \"s\" << std::endl;\n\t}\n}\n#endif\n\nvoid benchmark_main_channel_passing_next()\n{\n\tchannel_t<intptr_t> head{1};\n\tchannel_t<intptr_t> in = head;\n\tchannel_t<intptr_t> tail{0};\n\n\tfor (int i = 0; i < MaxNum; ++i)\n\t{\n\t\ttail = channel_t<intptr_t>{ 1 };\n\t\tgo passing_next(in, tail);\n\t\tin = tail;\n\t}\n\n#if defined(__GNUC__)\n\tgo passing_loop_all(head, tail);\n#else\n\tGO\n\t{\n\t\tfor (int i = 0; i < LoopCount; ++i)\n\t\t{\n\t\t\tauto tstart = high_resolution_clock::now();\n\n\t\t\tco_await (head << 0);\n\t\t\tintptr_t value = co_await tail;\n\n\t\t\tauto dt = duration_cast<duration<double>>(high_resolution_clock::now() - tstart).count();\n\t\t\tstd::cout << value << \" cost time \" << dt << \"s\" << std::endl;\n\t\t}\n\t};\n#endif\n\n\tthis_scheduler()->run_until_notask();\n}\n\n#if LIBRF_TUTORIAL_STAND_ALONE\nint main()\n{\n\tbenchmark_main_channel_passing_next();\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "cmake/SelectDynamicLibrary.cmake",
    "content": "include(SelectLibraryConfigurations)\n\nif(NOT DEFINED CMAKE_PROCESSOR_ALIAS)\n    MESSAGE(STATUS \"operation system is ${CMAKE_CXX_PLATFORM_ID}\")\n    MESSAGE(STATUS \"processor is ${CMAKE_HOST_SYSTEM_PROCESSOR}\")\n\n\tif((${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL \"i386\") OR (${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL \"AMD64\") OR (${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL \"x86_64\"))\n\t\tif(CMAKE_SIZEOF_VOID_P EQUAL 4)\n\t\t\tset(CMAKE_PROCESSOR_ALIAS \"x86\" CACHE STRING \"Alias of processor\")\n\t\telseif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\t\t\tset(CMAKE_PROCESSOR_ALIAS \"x64\" CACHE STRING \"Alias of processor\")\n\t\telse()\n\t\t\tmessage(FATAL_ERROR \"Unknown processor\")\n\t\tendif()\n\telseif(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL \"aarch64\")\n\t\tif(CMAKE_SIZEOF_VOID_P EQUAL 4)\n\t\t\tset(CMAKE_PROCESSOR_ALIAS \"arm\" CACHE STRING \"Alias of processor\")\n\t\telseif(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\t\t\tset(CMAKE_PROCESSOR_ALIAS \"arm64\" CACHE STRING \"Alias of processor\")\n\t\telse()\n\t\t\tmessage(FATAL_ERROR \"Unknown processor\")\n\t\tendif()\n\telse()\n\t\tmessage(FATAL_ERROR \"Unknown processor\")\n\tendif()\n\n    message(STATUS \"CMAKE_PROCESSOR_ALIAS=${CMAKE_PROCESSOR_ALIAS}\")\nendif()\n\n\nmacro(_acl_copy_dynamic_library_build_type basename build_type)\n\n    if(${build_type} STREQUAL \"Debug\")\n        set(_acl_build_type_dir \"Debug\")\n        set(_acl_runtime_output_dir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG})\n    elseif(${build_type} STREQUAL \"Release\")\n        set(_acl_build_type_dir \"Release\")\n        set(_acl_runtime_output_dir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE})\n    elseif(${build_type} STREQUAL \"MinSizeRel\")\n        set(_acl_build_type_dir \"Release\")\n        set(_acl_runtime_output_dir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL})\n    elseif(${build_type} STREQUAL \"RelWithDebInfo\")\n        set(_acl_build_type_dir \"Release\")\n        set(_acl_runtime_output_dir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO})\n    endif()\n    \n    #message(STATUS \"_acl_build_type_dir=${CMAKE_CURRENT_LIST_DIR}/../lib/${CMAKE_CXX_PLATFORM_ID}/${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}-${_acl_build_type_dir}\")\n    #message(STATUS \"_acl_runtime_output_dir=${_acl_runtime_output_dir}\")\n\n    find_file(_acl_${basename}_dynamic_binary\n        NAMES \"Acl.${basename}.dll\" \"Acl.${basename}.so\"\n        PATHS\n        ${CMAKE_CURRENT_LIST_DIR}/../bin/${CMAKE_CXX_PLATFORM_ID}/${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}-${_acl_build_type_dir}\n        NO_DEFAULT_PATH\n    )\n    if(NOT _acl_${basename}_dynamic_binary)\n        find_file(_acl_${basename}_dynamic_binary\n            NAMES \"${basename}.dll\" \"${basename}.so\"\n            PATHS\n            ${CMAKE_CURRENT_LIST_DIR}/../bin/${CMAKE_CXX_PLATFORM_ID}/${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}-${_acl_build_type_dir}\n            NO_DEFAULT_PATH\n        )\n    endif()\n\n    #message(STATUS \"_acl_runtime_dynamic_binary=${_acl_runtime_dynamic_binary}\")\n\n    if(_acl_${basename}_dynamic_binary)\n        file(INSTALL ${_acl_${basename}_dynamic_binary} DESTINATION ${_acl_runtime_output_dir})\n    endif()\n\n    unset(_acl_build_type_dir)\n    unset(_acl_runtime_output_dir)\n    unset(_acl_${basename}_dynamic_binary CACHE)\nendmacro(_acl_copy_dynamic_library_build_type)\n\nmacro(_acl_copy_dynamic_library basename)\n\n    if(DEFINED CMAKE_CONFIGURATION_TYPES)\n        foreach(build_type ${CMAKE_CONFIGURATION_TYPES})\n            _acl_copy_dynamic_library_build_type(${basename} ${build_type})\n        endforeach(build_type)\n    elseif(DEFINED CMAKE_BUILD_TYPE)\n        _acl_copy_dynamic_library_build_type(${basename} ${CMAKE_BUILD_TYPE})\n    else()\n        _acl_copy_dynamic_library_build_type(${basename} \"Release\")\n    endif()\n\nendmacro(_acl_copy_dynamic_library)\n\n\nmacro(select_dynamic_library basename header)\n    #message(STATUS \"basename=${basename}\")\n    #message(STATUS \"header=${header}\")\n\n    # Ѿҵ basename ָģ飬ֻʱĶ̬Ĺ\n    #message(STATUS \"${basename}_FOUND=${${basename}_FOUND}\")\n    if(${basename}_FOUND)\n        _acl_copy_dynamic_library(${basename})\n        return()\n    endif()\n\n\n\n    # ͷļڵ·\n    find_path(${basename}_INCLUDE_DIR ${header}\n        ${CMAKE_CURRENT_LIST_DIR}/../include\n        NO_DEFAULT_PATH\n    )\n\n    # ҵ԰汾Ŀļ·\n    find_library(\"${basename}_LIBRARY_DEBUG\"\n        NAMES \"Acl.${basename}\"\n        PATHS\n        ${CMAKE_CURRENT_LIST_DIR}/../lib/${CMAKE_CXX_PLATFORM_ID}/${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}-Debug\n        NO_DEFAULT_PATH\n    )\n    if(NOT ${basename}_LIBRARY_DEBUG)\n        find_library(\"${basename}_LIBRARY_DEBUG\"\n            NAMES \"${basename}\"\n            PATHS\n            ${CMAKE_CURRENT_LIST_DIR}/../lib/${CMAKE_CXX_PLATFORM_ID}/${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}-Debug\n            NO_DEFAULT_PATH\n        )\n    endif()\n\n    # ҷа汾Ŀļ·\n    find_library(\"${basename}_LIBRARY_RELEASE\"\n        NAMES \"Acl.${basename}\"\n        PATHS\n        ${CMAKE_CURRENT_LIST_DIR}/../lib/${CMAKE_CXX_PLATFORM_ID}/${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}-Release\n        NO_DEFAULT_PATH\n    )\n    if(NOT ${basename}_LIBRARY_RELEASE)\n        find_library(\"${basename}_LIBRARY_RELEASE\"\n            NAMES \"${basename}\"\n            PATHS\n            ${CMAKE_CURRENT_LIST_DIR}/../lib/${CMAKE_CXX_PLATFORM_ID}/${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}-Release\n            NO_DEFAULT_PATH\n        )\n    endif()\n\n    # ʹcmakeõ select_library_configurations  ${basename}_LIBRARY ֶ\n    select_library_configurations(${basename})\n\n\n\n    set(${basename}_FOUND FALSE)\n    if(${basename}_LIBRARY AND ${basename}_INCLUDE_DIR)\n\n        set(${basename}_FOUND TRUE)\n        if(NOT ${basename}_DIR)\n            set(${basename}_DIR ${CMAKE_CURRENT_LIST_DIR})\n        endif()\n\n        # ʱĶ̬⵽ĿĿ¼\n        _acl_copy_dynamic_library(${basename})\n\n        #  basename ָĵӿģ\n        if(NOT TARGET Acl::${basename})\n\n            add_library(Acl::${basename} UNKNOWN IMPORTED)\n            set_target_properties(Acl::${basename} PROPERTIES\n              INTERFACE_INCLUDE_DIRECTORIES \"${${basename}_INCLUDE_DIR}\")\n\n            set_property(TARGET Acl::${basename} APPEND PROPERTY\n                IMPORTED_CONFIGURATIONS RELEASE)\n            set_target_properties(Acl::${basename} PROPERTIES\n                IMPORTED_LOCATION_RELEASE \"${${basename}_LIBRARY_RELEASE}\")\n\n            set_property(TARGET Acl::${basename} APPEND PROPERTY\n                IMPORTED_CONFIGURATIONS DEBUG)\n            set_target_properties(Acl::${basename} PROPERTIES\n                IMPORTED_LOCATION_DEBUG \"${${basename}_LIBRARY_DEBUG}\")\n        endif()\n    endif()\n\n\n\n    mark_as_advanced(${basename}_DIR)\n    mark_as_advanced(${basename}_LIBRARY)\n    mark_as_advanced(${basename}_INCLUDE_DIR)\n\n    #message(STATUS \"${basename}_DIR=${${basename}_DIR}\")\n    #message(STATUS \"${basename}_LIBRARY=${${basename}_LIBRARY}\")\n    #message(STATUS \"${basename}_LIBRARY_DEBUG=${${basename}_LIBRARY_DEBUG}\")\n    #message(STATUS \"${basename}_LIBRARY_RELEASE=${${basename}_LIBRARY_RELEASE}\")\n    #message(STATUS \"${basename}_INCLUDE_DIR=${${basename}_INCLUDE_DIR}\")\n\nendmacro(select_dynamic_library)\n"
  },
  {
    "path": "cmake/install.cmake",
    "content": "﻿\n# Configuration\n# Used by cmake to find_package(xxx)\nset(PROJECT_CONFIG \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake\")\n\n# Add definitions for targets\n# Values:\n#   * Debug: -Dxxx_DEBUG=1\n#   * Release: -Dxxx_DEBUG=0\n#   * other: -Dxxx_DEBUG=0\ntarget_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_DEBUG=$<CONFIG:Debug>)\n\n# Include module with function 'write_basic_package_version_file'\n# Configure 'xxxConfigVersion.cmake'\ninclude(CMakePackageConfigHelpers)\nwrite_basic_package_version_file(${VERSION_CONFIG} VERSION ${PACKAGE_VERSION}\n        COMPATIBILITY SameMajorVersion)\n\n# Configure 'xxxConfig.cmake'\nconfigure_package_config_file(Config.cmake.in ${PROJECT_CONFIG}\n        INSTALL_DESTINATION cmake/${PROJECT_NAME})\n\n# Targets:\n#   * <prefix>/lib/Windows/x64-Debug/xxx.lib\n#   * <prefix>/bin/Windows/x64-Debug/xxx.dll\nset(INSTALL_TARGET_PREFIX \"${CMAKE_CXX_PLATFORM_ID}/${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}\")\ninstall(TARGETS ${PROJECT_NAME}\n        CONFIGURATIONS Debug\n        LIBRARY DESTINATION \"lib/${INSTALL_TARGET_PREFIX}-Debug\"\n        ARCHIVE DESTINATION \"lib/${INSTALL_TARGET_PREFIX}-Debug\"\n        RUNTIME DESTINATION \"bin/${INSTALL_TARGET_PREFIX}-Debug\"\n        )\n#   * <prefix>/lib/Windows/x64-Release/xxx.lib\n#   * <prefix>/bin/Windows/x64-Release/xxx.dll\ninstall(TARGETS ${PROJECT_NAME}\n        CONFIGURATIONS Release RelWithDebInfo MinSizeRel\n        LIBRARY DESTINATION \"lib/${INSTALL_TARGET_PREFIX}-Release\"\n        ARCHIVE DESTINATION \"lib/${INSTALL_TARGET_PREFIX}-Release\"\n        RUNTIME DESTINATION \"bin/${INSTALL_TARGET_PREFIX}-Release\"\n        )\n\n# Config\n#   * <prefix>/cmake/xxxConfig.cmake\n#   * <prefix>/cmake/xxxConfigVersion.cmake\ninstall(FILES ${PROJECT_CONFIG} DESTINATION cmake)\n"
  },
  {
    "path": "config.h.in",
    "content": "#pragma once\r\n\r\n#ifndef RESUMEF_DEBUG_COUNTER\r\n#cmakedefine RESUMEF_DEBUG_COUNTER @RESUMEF_DEBUG_COUNTER@\r\n#endif\t//RESUMEF_DEBUG_COUNTER\r\n\r\n#ifndef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\r\n#cmakedefine _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE @_WITH_LOCK_FREE_Q_KEEP_REAL_SIZE@\r\n#endif\t//_WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\r\n\r\n#ifndef RESUMEF_DISABLE_MULT_THREAD\r\n#cmakedefine RESUMEF_DISABLE_MULT_THREAD @RESUMEF_DISABLE_MULT_THREAD@\r\n#endif\t//RESUMEF_DISABLE_MULT_THREAD\r\n\r\n#cmakedefine RESUMEF_USE_CUSTOM_SPINLOCK @RESUMEF_USE_CUSTOM_SPINLOCK@\r\n\r\n#cmakedefine RESUMEF_USE_SHARD_LIBRARY @RESUMEF_USE_SHARD_LIBRARY@\r\n"
  },
  {
    "path": "gcc_bugs.cpp",
    "content": "﻿/*\ng++ --version:\n\ng++ (Ubuntu 10 - 20200416 - 0ubuntu1~18.04) 10.0.1 20200416 (experimental)[master revision 3c3f12e2a76:dcee354ce56:44b326839d864fc10c459916abcc97f35a9ac3de]\nCopyright(C) 2020 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions.There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n*/\n#include <iostream>\n#include \"librf.h\"\nusing namespace librf;\n\n#define GCC_FIX_BUGS\t1\n\nstatic future_t<> gcc_bugs_if_await(event_t e)\n{\n#if GCC_FIX_BUGS\n\tauto result = co_await e.wait();\n\tif (result == false)\n#else\n\tif (co_await e.wait() == false)\t//internal compiler error: in fold_convert_loc, at fold-const.c:2435\n#endif\n\t\tstd::cout << \"time out!\" << std::endl;\n\telse\n\t\tstd::cout << \"event signal!\" << std::endl;\n}\n\n\nstatic future_t<> gcc_bugs_while_await(mutex_t lock)\n{\n#if GCC_FIX_BUGS\n\tfor (;;)\n\t{\n\t\tauto result = co_await lock.try_lock();\n\t\tif (result) break;\n\t}\n#else\n\twhile (!co_await lock.try_lock());\t//internal compiler error: in fold_convert_loc, at fold-const.c:2435\n#endif\n\tstd::cout << \"OK.\" << std::endl;\n}\n\n\n\n#if GCC_FIX_BUGS\nstatic future_t<> gcc_bugs_lambda_coroutines_fixed(std::thread& other, channel_t<bool> c_done)\n{\n\tco_await c_done;\n\tstd::cout << \"other thread = \" << other.get_id();\n\tco_await c_done;\n}\n#endif\nstatic void gcc_bugs_lambda_coroutines()\n{\n\tchannel_t<bool> c_done{ 1 };\n\tstd::thread other;\n\n#if GCC_FIX_BUGS\n\tgo gcc_bugs_lambda_coroutines_fixed(other, c_done);\n#else\n\tgo[&other, c_done]()->future_t<>\n\t{\n\t\tco_await c_done;\n\t\tstd::cout << \"other thread = \" << other.get_id();\n\t\tco_await c_done;\n\t}; //internal compiler error: in captures_temporary, at cp/coroutines.cc:2716\n#endif\n}\n\n\n\n#if GCC_FIX_BUGS\nstatic future_t<> gcc_bugs_lambda_coroutines2_fixed(channel_t<intptr_t> head, channel_t<intptr_t> tail)\n{\n\tfor (int i = 0; i < 100; ++i)\n\t{\n\t\tco_await(head << 0);\n\t\tco_await tail;\n\t}\n}\n#endif\nstatic void gcc_bugs_lambda_coroutines2()\n{\n\tchannel_t<intptr_t> head{ 1 };\n\tchannel_t<intptr_t> tail{ 0 };\n\n#if GCC_FIX_BUGS\n\tgo gcc_bugs_lambda_coroutines2_fixed(head, tail);\n#else\n\tGO\n\t{\n\t\tfor (int i = 0; i < 100; ++i)\n\t\t{\n\t\t\tco_await(head << 0);\n\t\t\tintptr_t value = co_await tail;\n\t\t\t(void)value;\n\t\t}\n\t};\t//internal compiler error: in captures_temporary, at cp/coroutines.cc:2716\n#endif\n}\n\n\n\ntemplate<class... _Mtxs>\nstatic future_t<> gcc_bugs_nameless_args(adopt_manual_unlock_t\n#if GCC_FIX_BUGS\n\tnameless\n#endif\n\t, _Mtxs&... mtxs)\n{\n#if GCC_FIX_BUGS\n\t(void)nameless;\n#endif\n\tco_await mutex_t::lock(adopt_manual_unlock, mtxs...);\n}\t//internal compiler error: Segmentation fault\n\n\n\n\nvoid gcc_bugs()\n{\n\tevent_t e;\n\tgo gcc_bugs_if_await(e);\n\n\tmutex_t mtx;\n\tgo gcc_bugs_while_await(mtx);\n\n\tmutex_t a, b, c;\n\tgo gcc_bugs_nameless_args(adopt_manual_unlock, a, b, c);\n}\n"
  },
  {
    "path": "include/librf/librf.h",
    "content": "﻿/*\n *Copyright 2017~2020 lanzhengpeng\n *\n *Licensed under the Apache License, Version 2.0 (the \"License\");\n *you may not use this file except in compliance with the License.\n *You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n *Unless required by applicable law or agreed to in writing, software\n *distributed under the License is distributed on an \"AS IS\" BASIS,\n *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *See the License for the specific language governing permissions and\n *limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <cstddef>\n#include <type_traits>\n#include <atomic>\n#include <chrono>\n#include <array>\n#include <vector>\n#include <deque>\n#include <mutex>\n#include <map>\n#include <list>\n#include <any>\n#include <unordered_map>\n#include <functional>\n#include <optional>\n#include <thread>\n#include <cassert>\n#include <utility>\n\n#if __cpp_impl_coroutine\n#include <coroutine>\n#ifdef _MSC_VER\n#ifndef __clang__\nextern \"C\" size_t _coro_frame_size();\nextern \"C\" void* _coro_frame_ptr();\nextern \"C\" void _coro_init_block();\nextern \"C\" void* _coro_resume_addr();\nextern \"C\" void _coro_init_frame(void*);\nextern \"C\" void _coro_save(size_t);\nextern \"C\" void _coro_suspend(size_t);\nextern \"C\" void _coro_cancel();\nextern \"C\" void _coro_resume_block();\n\n#pragma intrinsic(_coro_frame_size)\n#pragma intrinsic(_coro_frame_ptr)\n#pragma intrinsic(_coro_init_block)\n#pragma intrinsic(_coro_resume_addr)\n#pragma intrinsic(_coro_init_frame)\n#pragma intrinsic(_coro_save)\n#pragma intrinsic(_coro_suspend)\n#pragma intrinsic(_coro_cancel)\n#pragma intrinsic(_coro_resume_block)\n#else\n#include \"src/unix/clang_builtin.h\"\n#endif\n#endif\n#elif defined(__clang__)\n#include \"src/unix/coroutine.h\"\n#elif __has_include(<experimental/coroutine>)\n#include <experimental/coroutine>\n#else\n#include \"src/unix/coroutine.h\"\n#endif\n\n#include \"src/stop_token.h\"\n\n#include \"src/config.h\"\n\n#if RESUMEF_DEBUG_COUNTER\n#include <iostream>\n#endif\n\n#include \"src/def.h\"\n#include \"src/macro_def.inl\"\n#include \"src/exception.inl\"\n#include \"src/counted_ptr.h\"\n#include \"src/type_traits.inl\"\n#include \"src/type_concept.inl\"\n#include \"src/spinlock.h\"\n#include \"src/state.h\"\n#include \"src/future.h\"\n#include \"src/promise.h\"\n#include \"src/awaitable.h\"\n#include \"src/generator.h\"\n\n#include \"src/rf_task.h\"\n#include \"src/timer.h\"\n#include \"src/scheduler.h\"\n\n#include \"src/promise.inl\"\n#include \"src/state.inl\"\n\n#include \"src/switch_scheduler.h\"\n#include \"src/current_scheduler.h\"\n\n#include \"src/yield.h\"\n#include \"src/sleep.h\"\n#include \"src/when.h\"\n\n#include \"src/_awaker.h\"\n#include \"src/ring_queue.h\"\n#include \"src/intrusive_link_queue.h\"\n#include \"src/channel.h\"\n#include \"src/event.h\"\n#include \"src/mutex.h\"\n\nnamespace resumef = librf;\n"
  },
  {
    "path": "include/librf/librf_macro.h",
    "content": "﻿#include \"src/macro_def.inl\""
  },
  {
    "path": "include/librf/src/_awaker.h",
    "content": "﻿#pragma once\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\nnamespace librf\n{\n\tnamespace detail\n\t{\n\t\ttemplate<class _Ety, class... _Types>\n\t\tstruct _awaker\n\t\t{\n\t\t\t//如果超时\n\t\t\t//\t\te 始终为nullptr\n\t\t\t//\t\t不关心返回值\n\t\t\t//如果不是超时，\n\t\t\t//\t\te 指向当前触发的事件，用于实现wait_any\n\t\t\t//\t\t返回true表示成功触发了事件，event内部减小一次事件计数，并删除此awaker\n\t\t\t//\t\t返回false表示此事件已经无效，event内部只删除此awaker\n\t\t\ttypedef std::function<bool(_Ety * e, _Types...)> callee_type;\n\t\tprivate:\n\t\t\ttypedef spinlock lock_type;\n\t\t\t//typedef std::recursive_mutex lock_type;\n\n\t\t\tlock_type _lock;\n\t\t\tcallee_type _callee;\n\t\t\tstd::atomic<intptr_t> _counter;\n\t\tpublic:\n\t\t\t_awaker(callee_type && callee_, intptr_t init_count_ = 0)\n\t\t\t\t: _callee(std::forward<callee_type>(callee_))\n\t\t\t\t, _counter(init_count_)\n\t\t\t{\n\t\t\t}\n\n\t\t\t//调用一次后，_callee就被置nullptr，下次再调用，必然返回false\n\t\t\t//第一次调用，返回调用_callee的返回值\n\t\t\t//超时通过传入nullptr来调用\n\t\t\tbool awake(_Ety * e, intptr_t count_, const _Types&... args)\n\t\t\t{\n\t\t\t\tassert(count_ > 0);\n\t\t\t\tscoped_lock<lock_type> lock_(this->_lock);\n\n\t\t\t\tif ((this->_counter.fetch_sub(count_) - count_) <= 0)\n\t\t\t\t{\n\t\t\t\t\tif (this->_callee)\n\t\t\t\t\t{\n\t\t\t\t\t\tcallee_type callee_ = std::move(this->_callee);\n\t\t\t\t\t\treturn callee_(e, args...);\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\tprivate:\n\t\t\t_awaker(const _awaker &) = delete;\n\t\t\t_awaker(_awaker &&) = delete;\n\t\t\t_awaker & operator = (const _awaker &) = delete;\n\t\t\t_awaker & operator = (_awaker &&) = delete;\n\t\t};\n\t}\n}\n\n#endif\t//DOXYGEN_SKIP_PROPERTY\n "
  },
  {
    "path": "include/librf/src/awaitable.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\n\t/**\n\t * @brief awaitable_t<>的公共实现部分，用于减少awaitable_t<>的重复代码。\n\t * @param _Ty 可等待函数(awaitable function)的返回类型。\n\t * @see 参见awaitable_t<>类的说明。\n\t */\n\ttemplate<class _Ty>\n\tstruct awaitable_impl_t\n\t{\n\t\tusing value_type = _Ty;\n\t\tusing state_type = state_t<value_type>;\n\t\tusing future_type = future_t<value_type>;\n\t\tusing lock_type = typename state_type::lock_type;\n\t\tusing _Alloc_char = typename state_type::_Alloc_char;\n\n\t\tawaitable_impl_t() noexcept = default;\n\t\tawaitable_impl_t(const awaitable_impl_t&) noexcept = default;\n\t\tawaitable_impl_t(awaitable_impl_t&&) noexcept = default;\n\n\t\tawaitable_impl_t& operator = (const awaitable_impl_t&) noexcept = default;\n\t\tawaitable_impl_t& operator = (awaitable_impl_t&&) = default;\n\n\t\t/**\n\t\t * @brief 发生了异常后，设置异常。\n\t\t * @attention 与set_value()互斥。调用了set_exception(e)后，不能再调用set_value()。\n\t\t */\n\t\tvoid set_exception(std::exception_ptr e) const\n\t\t{\n\t\t\tcounted_ptr<state_type> cp(std::move(this->_state));\n\t\t\tcp->set_exception(std::move(e));\n\t\t}\n\n\t\t/**\n\t\t * @brief 在协程内部，重新抛出之前设置的异常。\n\t\t */\n\t\ttemplate<class _Exp>\n\t\tvoid throw_exception(_Exp e) const\n\t\t{\n\t\t\tset_exception(std::make_exception_ptr(std::move(e)));\n\t\t}\n\n\t\t/**\n\t\t * @brief 获得与之关联的future_t<>对象，作为可等待函数(awaitable function)的返回值。\n\t\t */\n\t\tfuture_type get_future() const noexcept\n\t\t{\n\t\t\treturn future_type{ this->_state };\n\t\t}\n\n\t\texplicit operator bool() const noexcept\n\t\t{\n\t\t\treturn _state.get() != nullptr;\n\t\t}\n\n\t\t/**\n\t\t * @brief 管理的state_t<>对象。\n\t\t */\n\t\tmutable counted_ptr<state_type> _state = state_future_t::_Alloc_state<state_type>(true);\n\t};\n\n\t/**\n\t * @brief 用于包装‘异步函数’为‘可等待函数(awaitable function)’。\n\t * @details 通过返回一个‘可等待对象(awaitor)’，符合C++ coroutines的co_await所需的接口，来达成‘可等待函数(awaitable function)’。\\n\n\t * 这是扩展异步函数支持协程的重要手段。\\n\n\t * \\n\n\t * 典型用法是申明一个 awaitable_t<>局部变量 awaitable，\\n\n\t * 在已经获得结果的情况下，直接调用 awaitable.set_value(value)设置返回值，使得可等待函数立即获得结果。\\n\n\t * 在不能立即获得结果的情况下，通过在异步的回调lambda里，捕获awaitable局部变量，\\n\n\t * 根据异步结果，要么调用 awaitable.set_value(value)设置结果值，要么调用 awaitable.set_exception(e)设置异常。\\n\n\t * 在设置值或者异常后，调用可等待函数的协程将得以继续执行。\\n\n\t * 此可等待函数通过 awaitable.get_future()返回与之关联的 future_t<>对象，作为协程的可等待对象。\\n\n\t * \\n\n\t * @param _Ty 可等待函数(awaitable function)的返回类型。\\n\n\t * 要求至少支持移动构造和移动赋值。\\n\n\t * _Ty 支持特化为 _Ty&，以及 void。\n\t */\n\ttemplate<class _Ty>\n\tstruct [[nodiscard]] awaitable_t : public awaitable_impl_t<_Ty>\n\t{\n\t\tusing typename awaitable_impl_t<_Ty>::value_type;\n\t\tusing typename awaitable_impl_t<_Ty>::state_type;\n\t\tusing awaitable_impl_t<_Ty>::awaitable_impl_t;\n\n\t\t/**\n\t\t * @brief 设置可等待函数的返回值。\n\t\t * @details _Ty的void特化版本，则是不带参数的set_value()函数。\n\t\t * @param value 返回值。必须支持通过value构造出_Ty类型。\n\t\t * @attention 与set_exception()互斥。调用了set_value(value)后，不能再调用set_exception(e)。\n\t\t */\n\t\ttemplate<class U>\n\t\tvoid set_value(U&& value) const\n\t\t{\n\t\t\tcounted_ptr<state_type> cp(std::move(this->_state));\n\t\t\tcp->set_value(std::forward<U>(value));\n\t\t}\n\t};\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\ttemplate<class _Ty>\n\tstruct [[nodiscard]] awaitable_t<_Ty&> : public awaitable_impl_t<_Ty&>\n\t{\n\t\tusing typename awaitable_impl_t<_Ty&>::value_type;\n\t\tusing typename awaitable_impl_t<_Ty&>::state_type;\n\t\tusing awaitable_impl_t<_Ty&>::awaitable_impl_t;\n\n\t\tvoid set_value(_Ty& value) const\n\t\t{\n\t\t\tcounted_ptr<state_type> cp(std::move(this->_state));\n\t\t\tcp->set_value(value);\n\t\t}\n\t};\n\n\ttemplate<>\n\tstruct [[nodiscard]] awaitable_t<void> : public awaitable_impl_t<void>\n\t{\n\t\tusing awaitable_impl_t<void>::state_type;\n\t\tusing awaitable_impl_t<void>::awaitable_impl_t;\n\n\t\tvoid set_value() const\n\t\t{\n\t\t\tcounted_ptr<state_type> cp(std::move(this->_state));\n\t\t\tcp->set_value();\n\t\t}\n\t};\n#endif\t//DOXYGEN_SKIP_PROPERTY\n}\n"
  },
  {
    "path": "include/librf/src/channel.h",
    "content": "﻿#pragma once\n\n#include \"channel_v2.h\"\n#include \"channel_v2.inl\"\n"
  },
  {
    "path": "include/librf/src/channel_v2.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n#ifndef DOXYGEN_SKIP_PROPERTY\nnamespace detail\n{\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tstruct channel_impl_v2;\n}\t//namespace detail\n\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\n\t/**\n\t * @brief 可传递数据的模板信号量。\n\t * @remarks 不支持数据类型为void的特例化。\n\t * @param _Ty 传递的数据类型。要求此类型至少支持移动构造和移动赋值。\n\t * @param _Optional 内部是否采用std::optional<>来存数据。\\n\n\t * 默认不是POD类型则采用std::optional<>。如果channel缓存的元素不能凭空产生，或者产生代价较大，则推荐将此参数设置为true，从而减小不必要的开销。\n\t * @param _OptimizationThread 针对多线程优化。目前此算法提升效率不稳定，需要自行根据实际情况决定。\n\t */\n\ttemplate<class _Ty = bool, bool _Optional = !std::is_trivial_v<_Ty>, bool _OptimizationThread = false>\n\tstruct channel_t\n\t{\n\t\tstatic_assert((std::is_copy_constructible_v<_Ty>&& std::is_copy_assignable_v<_Ty>) ||\n\t\t\t(std::is_move_constructible_v<_Ty> && std::is_move_assignable_v<_Ty>));\n\n\t\tstruct [[nodiscard]] read_awaiter;\n\t\tstruct [[nodiscard]] write_awaiter;\n\n\t\t/**\n\t\t * @brief 构造函数。\n\t\t * @param cache_size 缓存的数量。0 表示内部无缓存。\n\t\t */\n\t\tchannel_t(size_t cache_size = 1);\n\n\t\t/**\n\t\t * @brief 获得缓存数量。\n\t\t */\n\t\tsize_t capacity() const noexcept;\n\n\t\t/**\n\t\t * @brief 在协程中从channel_t里读取一个数据。\n\t\t * @see 参考read()函数\n\t\t * @return [co_await] value_type\n\t\t */\n\t\tread_awaiter operator co_await() const noexcept;\n\n\t\t/**\n\t\t * @brief 在协程中从channel_t里读取一个数据\n\t\t * @details 如果没有写入数据，则会阻塞协程。\n\t\t * @remarks 无缓冲的时候，先读后写，不再抛channel_exception异常。这是跟channel_v1的区别。\\n\n\t\t * 在非协程中也可以使用。如果不能立即读取成功，则会阻塞线程。\\n\n\t\t * 但如此用法并不能获得读取的结果，仅仅用作同步手段。\n\t\t * @return [co_await] value_type\n\t\t */\n\t\tread_awaiter read() const noexcept;\n\n\t\t/**\n\t\t * @brief 在协程中向channel_t里写入一个数据。\n\t\t * @see 参考write()函数\n\t\t */\n\t\ttemplate<class U>\n\t\trequires(std::is_constructible_v<_Ty, U&&>)\n\t\twrite_awaiter operator << (U&& val) const noexcept(std::is_nothrow_move_constructible_v<U>);\n\n\t\t/**\n\t\t * @brief 在协程中向channel_t里写入一个数据。\n\t\t * @details 在没有读操作等待时，且数据缓冲区满的情况下，则会阻塞协程。\n\t\t * @remarks 在非协程中也可以使用。如果不能立即写入成功，则会阻塞线程。\n\t\t * @param val 写入的数据。必须是可以成功构造_Ty(val)的类型。\n\t\t * @return [co_await] void\n\t\t */\n\t\ttemplate<class U>\n\t\trequires(std::is_constructible_v<_Ty, U&&>)\n\t\twrite_awaiter write(U&& val) const noexcept(std::is_nothrow_move_constructible_v<U>);\n\n\n\t\t/**\n\t\t\t* @brief 构造一个无效的信号量。\n\t\t\t* @details 如果用于后续保存另外一个信号量，则应当使用此构造函数，便于节省一次不必要的内部初始化。\n\t\t\t*/\n\t\tchannel_t(std::adopt_lock_t);\n\n\t\t/**\n\t\t\t* @brief 判断是不是一个无效的信号量。\n\t\t\t*/\n\t\tbool valid() const noexcept;\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\t\tusing value_type = _Ty;\n\n\t\tstatic constexpr bool use_optional = _Optional;\n\t\tstatic constexpr bool optimization_for_multithreading = _OptimizationThread;\n\n\t\tusing optional_type = std::conditional_t<use_optional, std::optional<value_type>, value_type>;\n\t\tusing channel_type = detail::channel_impl_v2<value_type, use_optional, optimization_for_multithreading>;\n\t\tusing lock_type = typename channel_type::lock_type;\n\n\t\tchannel_t(const channel_t&) = default;\n\t\tchannel_t(channel_t&&) = default;\n\t\tchannel_t& operator = (const channel_t&) = default;\n\t\tchannel_t& operator = (channel_t&&) = default;\n\tprivate:\n\t\tstd::shared_ptr<channel_type> _chan;\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\t};\n\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\t//不支持channel_t<void>\n\ttemplate<bool _Option, bool _OptimizationThread>\n\tstruct channel_t<void, _Option, _OptimizationThread>\n\t{\n\t};\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\n\t/**\n\t * @brief 利用channel_t重定义的信号量。\n\t */\n\tusing semaphore_t = channel_t<bool, false, true>;\n\n}\t//namespace librf\n"
  },
  {
    "path": "include/librf/src/channel_v2.inl",
    "content": "﻿#pragma once\n\nnamespace librf\n{\nnamespace detail\n{\n\ttemplate<class _Ty, class _Chty>\n\tstruct state_channel_t : public state_base_t\n\t\t\t\t\t\t   , public intrusive_link_node<state_channel_t<_Ty, _Chty>>\n\t{\n\t\tusing value_type = _Ty;\n\n\t\tstate_channel_t(std::shared_ptr<_Chty> ch, value_type& val) noexcept\n\t\t\t: _channel(std::move(ch))\n\t\t\t, _value(std::addressof(val))\n\t\t{\n\t\t}\n\n\t\tvirtual void resume() override\n\t\t{\n\t\t\tcoroutine_handle<> handler = _coro;\n\t\t\tif (handler)\n\t\t\t{\n\t\t\t\t_coro = nullptr;\n\t\t\t\t_scheduler->del_final(this);\n\t\t\t\thandler.resume();\n\t\t\t}\n\t\t}\n\n\t\tvirtual bool has_handler() const  noexcept override\n\t\t{\n\t\t\treturn (bool)_coro;\n\t\t}\n\n\t\tvoid on_notify()\n\t\t{\n\t\t\tassert(this->_scheduler != nullptr);\n\t\t\tif (this->_coro)\n\t\t\t\tthis->_scheduler->add_generator(this);\n\t\t}\n\n\t\tvoid on_cancel() noexcept\n\t\t{\n\t\t\tthis->_coro = nullptr;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tvoid on_await_suspend(coroutine_handle<_PromiseT> handler) noexcept\n\t\t{\n\t\t\t_PromiseT& promise = handler.promise();\n\t\t\tauto* parent_state = promise.get_state();\n\t\t\tscheduler_t* sch = parent_state->get_scheduler();\n\n\t\t\tthis->_scheduler = sch;\n\t\t\tthis->_coro = handler;\n\t\t}\n\n\t\tfriend _Chty;\n\tprotected:\n\t\t//co_await产生的临时awaitor会引用state，管理state的生命周期\n\t\t//state再引用channel\n\t\t//从而在co_await channel.xxx()这条语句内，awaitor/state/channel均处于正确的生命周期\n\t\tstd::shared_ptr<_Chty> _channel;\n\tprotected:\n\t\tvalue_type* _value;\n\t};\n\n\n\n//-----------------------------------------------------------------------------------------------------------------------------------------\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tstruct channel_impl_v2 : public std::enable_shared_from_this<channel_impl_v2<_Ty, _Optional, _OptimizationThread>>\n\t{\n\t\tusing value_type = _Ty;\n\t\tusing optional_type = std::conditional_t<_Optional, std::optional<value_type>, value_type>;\n\t\tusing this_type = channel_impl_v2<value_type, _Optional, _OptimizationThread>;\n\n\t\tusing state_read_t = state_channel_t<optional_type, this_type>;\n\t\tusing state_write_t = state_channel_t<value_type, this_type>;\n\n\t\tchannel_impl_v2(size_t cache_size);\n\n\t\tbool try_read(optional_type& val);\n\t\tbool try_read_nolock(optional_type& val);\n\t\tvoid add_read_list_nolock(state_read_t* state);\n\n\t\tbool try_write(value_type& val);\n\t\tbool try_write_nolock(value_type& val);\n\t\tvoid add_write_list_nolock(state_write_t* state);\n\n\t\tsize_t capacity() const noexcept\n\t\t{\n\t\t\treturn _max_counter;\n\t\t}\n\tprivate:\n\t\tauto try_pop_reader_()->state_read_t*;\n\t\tauto try_pop_writer_()->state_write_t*;\n\t\tvoid awake_one_reader_();\n\t\tbool awake_one_reader_(value_type& val);\n\n\t\tvoid awake_one_writer_();\n\t\tbool awake_one_writer_(optional_type& val);\n\n\t\tchannel_impl_v2(const channel_impl_v2&) = delete;\n\t\tchannel_impl_v2(channel_impl_v2&&) = delete;\n\t\tchannel_impl_v2& operator = (const channel_impl_v2&) = delete;\n\t\tchannel_impl_v2& operator = (channel_impl_v2&&) = delete;\n\n\t\tstatic constexpr bool USE_SPINLOCK = !_OptimizationThread;\n\t\tstatic constexpr bool USE_RING_QUEUE = true;\n\t\tstatic constexpr bool USE_LINK_QUEUE = true;\n\n\t\tusing queue_type = std::conditional_t<USE_RING_QUEUE, ring_queue<value_type, _Optional, uint32_t>, std::deque<value_type>>;\n\t\tusing read_queue_type = std::conditional_t<USE_LINK_QUEUE, intrusive_link_queue<state_read_t>, std::list<state_read_t*>>;\n\t\tusing write_queue_type = std::conditional_t<USE_LINK_QUEUE, intrusive_link_queue<state_write_t>, std::list<state_write_t*>>;\n\n\t\tconst size_t _max_counter;\t\t\t\t\t\t\t//数据队列的容量上限\n\tpublic:\n\t\tusing lock_type = std::conditional_t<USE_SPINLOCK, spinlock, std::mutex>;\n\t\tlock_type _lock;\t\t\t\t\t\t\t\t\t//保证访问本对象是线程安全的\n\tprivate:\n\t\tqueue_type _values;\t\t\t\t\t\t\t\t\t//数据队列\n\t\tread_queue_type _read_awakes;\t\t\t\t\t\t//读队列\n\t\twrite_queue_type _write_awakes;\t\t\t\t\t\t//写队列\n\t};\n\n\n\n//-----------------------------------------------------------------------------------------------------------------------------------------\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tchannel_impl_v2<_Ty, _Optional, _OptimizationThread>::channel_impl_v2(size_t cache_size)\n\t\t: _max_counter(cache_size)\n\t\t, _values(USE_RING_QUEUE ? cache_size : 0)\n\t{\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tinline bool channel_impl_v2<_Ty, _Optional, _OptimizationThread>::try_read(optional_type& val)\n\t{\n\t\tscoped_lock<lock_type> lock_(this->_lock);\n\t\treturn try_read_nolock(val);\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tbool channel_impl_v2<_Ty, _Optional, _OptimizationThread>::try_read_nolock(optional_type& val)\n\t{\n\t\tif constexpr (USE_RING_QUEUE)\n\t\t{\n\t\t\tif (_values.try_pop(val))\n\t\t\t{\n\t\t\t\tawake_one_writer_();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (_values.size() > 0)\n\t\t\t{\n\t\t\t\tval = std::move(_values.front());\n\t\t\t\t_values.pop_front();\n\t\t\t\tawake_one_writer_();\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn awake_one_writer_(val);\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tinline void channel_impl_v2<_Ty, _Optional, _OptimizationThread>::add_read_list_nolock(state_read_t* state)\n\t{\n\t\tassert(state != nullptr);\n\t\t_read_awakes.push_back(state);\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tinline bool channel_impl_v2<_Ty, _Optional, _OptimizationThread>::try_write(value_type& val)\n\t{\n\t\tscoped_lock<lock_type> lock_(this->_lock);\n\t\treturn try_write_nolock(val);\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tbool channel_impl_v2<_Ty, _Optional, _OptimizationThread>::try_write_nolock(value_type& val)\n\t{\n\t\tif constexpr (USE_RING_QUEUE)\n\t\t{\n\t\t\tif (_values.try_push(std::move(val)))\n\t\t\t{\n\t\t\t\tawake_one_reader_();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (_values.size() < _max_counter)\n\t\t\t{\n\t\t\t\t_values.push_back(std::move(val));\n\t\t\t\tawake_one_reader_();\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn awake_one_reader_(val);\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tinline void channel_impl_v2<_Ty, _Optional, _OptimizationThread>::add_write_list_nolock(state_write_t* state)\n\t{\n\t\tassert(state != nullptr);\n\t\t_write_awakes.push_back(state);\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tauto channel_impl_v2<_Ty, _Optional, _OptimizationThread>::try_pop_reader_()->state_read_t*\n\t{\n\t\tif constexpr (USE_LINK_QUEUE)\n\t\t{\n\t\t\treturn _read_awakes.try_pop();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!_read_awakes.empty())\n\t\t\t{\n\t\t\t\tstate_read_t* state = _read_awakes.front();\n\t\t\t\t_read_awakes.pop_front();\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn nullptr;\n\t\t}\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tauto channel_impl_v2<_Ty, _Optional, _OptimizationThread>::try_pop_writer_()->state_write_t*\n\t{\n\t\tif constexpr (USE_LINK_QUEUE)\n\t\t{\n\t\t\treturn _write_awakes.try_pop();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!_write_awakes.empty())\n\t\t\t{\n\t\t\t\tstate_write_t* state = _write_awakes.front();\n\t\t\t\t_write_awakes.pop_front();\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn nullptr;\n\t\t}\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tvoid channel_impl_v2<_Ty, _Optional, _OptimizationThread>::awake_one_reader_()\n\t{\n\t\tstate_read_t* state = try_pop_reader_();\n\t\tif (state != nullptr)\n\t\t{\n\t\t\tassert(!_values.empty());\n\n\t\t\tif constexpr (USE_RING_QUEUE)\n\t\t\t{\n\t\t\t\t_values.try_pop(*state->_value);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t*state->_value = std::move(_values.front());\n\t\t\t\t_values.pop_front();\n\t\t\t}\n\n\t\t\tstate->on_notify();\n\t\t}\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tbool channel_impl_v2<_Ty, _Optional, _OptimizationThread>::awake_one_reader_(value_type& val)\n\t{\n\t\tstate_read_t* state = try_pop_reader_();\n\t\tif (state != nullptr)\n\t\t{\n\t\t\t*state->_value = std::move(val);\n\n\t\t\tstate->on_notify();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tvoid channel_impl_v2<_Ty, _Optional, _OptimizationThread>::awake_one_writer_()\n\t{\n\t\tstate_write_t* state = try_pop_writer_();\n\t\tif (state != nullptr)\n\t\t{\n\t\t\tif constexpr (USE_RING_QUEUE)\n\t\t\t{\n\t\t\t\tbool ret = _values.try_push(std::move(*state->_value));\n\t\t\t\t(void)ret;\n\t\t\t\tassert(ret);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tassert(_values.size() < _max_counter);\n\t\t\t\t_values.push_back(std::move(*state->_value));\n\t\t\t}\n\n\t\t\tstate->on_notify();\n\t\t}\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tbool channel_impl_v2<_Ty, _Optional, _OptimizationThread>::awake_one_writer_(optional_type& val)\n\t{\n\t\tstate_write_t* writer = try_pop_writer_();\n\t\tif (writer != nullptr)\n\t\t{\n\t\t\tval = std::move(*writer->_value);\n\n\t\t\twriter->on_notify();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n}\t//namespace detail\n\n\n\n\n//-----------------------------------------------------------------------------------------------------------------------------------------\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tstruct [[nodiscard]] channel_t<_Ty, _Optional, _OptimizationThread>::read_awaiter\n\t{\n\t\tusing state_type = typename channel_type::state_read_t;\n\n\t\tread_awaiter(std::shared_ptr<channel_type> ch) noexcept\n\t\t\t: _channel(std::move(ch))\n\t\t\t, _value()\n\t\t{}\n\n\t\tread_awaiter(const read_awaiter&) = delete;\n\t\tread_awaiter(read_awaiter&&) = default;\n\t\tread_awaiter& operator=(const read_awaiter&) = delete;\n\t\tread_awaiter& operator=(read_awaiter&&) = default;\n\n\t\t~read_awaiter()\n\t\t{//为了不在协程中也能正常使用\n\t\t\tif (_channel != nullptr)\n\t\t\t{\n\t\t\t\twhile(!_channel->try_read(_value));\n\t\t\t}\n\t\t}\n\n\t\tbool await_ready()\n\t\t{\n\t\t\t//在多线程竞争较为多的时候，先检查是否可用，可以稍微提高点效率\n\t\t\tif constexpr (optimization_for_multithreading)\n\t\t\t{\n\t\t\t\tscoped_lock<lock_type> lock_(_channel->_lock);\n\n\t\t\t\tif (_channel->try_read_nolock(_value))\n\t\t\t\t{\n\t\t\t\t\t_channel = nullptr;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\tscoped_lock<lock_type> lock_(_channel->_lock);\n\n\t\t\tif (_channel->try_read_nolock(_value))\n\t\t\t{\n\t\t\t\t_channel = nullptr;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t_state = new state_type(_channel, _value);\n\t\t\t_state->on_await_suspend(handler);\n\t\t\t_channel->add_read_list_nolock(_state.get());\n\t\t\t_channel = nullptr;\n\n\t\t\treturn true;\n\t\t}\n\t\tvalue_type await_resume()\n\t\t{\n\t\t\tif constexpr (use_optional)\n\t\t\t\treturn std::move(_value).value();\n\t\t\telse\n\t\t\t\treturn std::move(_value);\n\t\t}\n\tprivate:\n\t\tstd::shared_ptr<channel_type> _channel;\n\t\tcounted_ptr<state_type> _state;\t//延迟到await_suspend()里创建，减小不必要的内存申请\n\t\tmutable optional_type _value;\n\t};\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tstruct [[nodiscard]] channel_t<_Ty, _Optional, _OptimizationThread>::write_awaiter\n\t{\n\t\tusing state_type = typename channel_type::state_write_t;\n\n\t\ttemplate<class U>\n\t\twrite_awaiter(std::shared_ptr<channel_type> ch, U&& val) noexcept(std::is_move_constructible_v<value_type>)\n\t\t\t: _channel(std::move(ch))\n\t\t\t, _value(std::forward<U>(val))\n\t\t{}\n\n\t\twrite_awaiter(const write_awaiter&) = delete;\n\t\twrite_awaiter(write_awaiter&&) = default;\n\t\twrite_awaiter& operator=(const write_awaiter&) = delete;\n\t\twrite_awaiter& operator=(write_awaiter&&) = default;\n\n\t\t~write_awaiter()\n\t\t{//为了不在协程中也能正常使用\n\t\t\tif (_channel != nullptr)\n\t\t\t{\n\t\t\t\twhile(!_channel->try_write(_value));\n\t\t\t}\n\t\t}\n\n\t\tbool await_ready()\n\t\t{\n\t\t\t//在多线程竞争较为多的时候，先检查是否可用，可以稍微提高点效率\n\t\t\tif constexpr (optimization_for_multithreading)\n\t\t\t{\n\t\t\t\tscoped_lock<lock_type> lock_(_channel->_lock);\n\n\t\t\t\tif (_channel->try_write_nolock(_value))\n\t\t\t\t{\n\t\t\t\t\t_channel = nullptr;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\tscoped_lock<lock_type> lock_(_channel->_lock);\n\n\t\t\tif (_channel->try_write_nolock(_value))\n\t\t\t{\n\t\t\t\t_channel = nullptr;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t_state = new state_type(_channel, _value);\n\t\t\t_state->on_await_suspend(handler);\n\t\t\t_channel->add_write_list_nolock(_state.get());\n\t\t\t_channel = nullptr;\n\n\t\t\treturn true;\n\t\t}\n\t\tvoid await_resume()\n\t\t{\n\t\t}\n\tprivate:\n\t\tstd::shared_ptr<channel_type> _channel;\n\t\tcounted_ptr<state_type> _state;\t//延迟到await_suspend()里创建，减小不必要的内存申请\n\t\tmutable value_type _value;\n\t};\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tchannel_t<_Ty, _Optional, _OptimizationThread>::channel_t(size_t cache_size)\n\t\t:_chan(std::make_shared<channel_type>(cache_size))\n\t{\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tchannel_t<_Ty, _Optional, _OptimizationThread>::channel_t(std::adopt_lock_t)\n\t{\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tsize_t channel_t<_Ty, _Optional, _OptimizationThread>::capacity() const noexcept\n\t{\n\t\treturn _chan->capacity();\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\ttypename channel_t<_Ty, _Optional, _OptimizationThread>::read_awaiter\n\t\tchannel_t<_Ty, _Optional, _OptimizationThread>::operator co_await() const noexcept\n\t{\n\t\treturn { _chan };\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\ttypename channel_t<_Ty, _Optional, _OptimizationThread>::read_awaiter\n\t\tchannel_t<_Ty, _Optional, _OptimizationThread>::read() const noexcept\n\t{\n\t\treturn { _chan };\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\ttemplate<class U> requires(std::is_constructible_v<_Ty, U&&>)\n\ttypename channel_t<_Ty, _Optional, _OptimizationThread>::write_awaiter\n\t\tchannel_t<_Ty, _Optional, _OptimizationThread>::write(U&& val) const noexcept(std::is_nothrow_move_constructible_v<U>)\n\t{\n\t\treturn write_awaiter{ _chan, std::forward<U>(val) };\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\ttemplate<class U> requires(std::is_constructible_v<_Ty, U&&>)\n\ttypename channel_t<_Ty, _Optional, _OptimizationThread>::write_awaiter\n\t\tchannel_t<_Ty, _Optional, _OptimizationThread>::operator << (U&& val) const noexcept(std::is_nothrow_move_constructible_v<U>)\n\t{\n\t\treturn write_awaiter{ _chan, std::forward<U>(val) };\n\t}\n\n\ttemplate<class _Ty, bool _Optional, bool _OptimizationThread>\n\tbool channel_t<_Ty, _Optional, _OptimizationThread>::valid() const noexcept\n\t{\n\t\treturn (bool)_chan;\n\t}\n\n}\t//namespace librf\n"
  },
  {
    "path": "include/librf/src/config.h",
    "content": "#pragma once\n\n#ifndef RESUMEF_DEBUG_COUNTER\n/* #undef RESUMEF_DEBUG_COUNTER */\n#endif\t//RESUMEF_DEBUG_COUNTER\n\n#ifndef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n/* #undef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE */\n#endif\t//_WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\n#ifndef RESUMEF_DISABLE_MULT_THREAD\n/* #undef RESUMEF_DISABLE_MULT_THREAD */\n#endif\t//RESUMEF_DISABLE_MULT_THREAD\n\n/* #undef RESUMEF_USE_CUSTOM_SPINLOCK */\n\n/* #undef RESUMEF_USE_SHARD_LIBRARY */\n"
  },
  {
    "path": "include/librf/src/counted_ptr.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\t/**\n\t * @brief 专用于state的智能计数指针，通过管理state内嵌的引用计数来管理state的生存期。\n\t */\n\ttemplate <typename T>\n\tstruct counted_ptr\n\t{\n\t\t/**\n\t\t * @brief 构造一个无内容的计数指针。\n\t\t */\n\t\tcounted_ptr() noexcept = default;\n\n\t\t/**\n\t\t * @brief 拷贝构造函数。\n\t\t */\n\t\tcounted_ptr(const counted_ptr& cp) noexcept : _p(cp._p)\n\t\t{\n\t\t\t_lock();\n\t\t}\n\n\t\t/**\n\t\t * @brief 通过裸指针构造一个计数指针。\n\t\t */\n\t\tcounted_ptr(T* p) noexcept : _p(p)\n\t\t{\n\t\t\t_lock();\n\t\t}\n\n\t\t/**\n\t\t * @brief 移动构造函数。\n\t\t */\n\t\tcounted_ptr(counted_ptr&& cp) noexcept : _p(std::exchange(cp._p, nullptr))\n\t\t{\n\t\t}\n\n\t\t/**\n\t\t * @brief 拷贝赋值函数。\n\t\t */\n\t\tcounted_ptr& operator=(const counted_ptr& cp)\n\t\t{\n\t\t\tif (&cp != this)\n\t\t\t{\n\t\t\t\tcounted_ptr t(cp);\n\t\t\t\tstd::swap(_p, t._p);\n\t\t\t}\n\t\t\treturn *this;\n\t\t}\n\n\t\t/**\n\t\t * @brief 移动赋值函数。\n\t\t */\n\t\tcounted_ptr& operator=(counted_ptr&& cp)\n\t\t{\n\t\t\tif (&cp != this)\n\t\t\t{\n\t\t\t\tstd::swap(_p, cp._p);\n\t\t\t\tcp._unlock();\n\t\t\t}\n\t\t\treturn *this;\n\t\t}\n\n\t\tvoid swap(counted_ptr& cp) noexcept\n\t\t{\n\t\t\tstd::swap(_p, cp._p);\n\t\t}\n\n\t\t/**\n\t\t * @brief 析构函数中自动做一个计数减一操作。计数减为0，则删除state对象。\n\t\t */\n\t\t~counted_ptr()\n\t\t{\n\t\t\t_unlock();\n\t\t}\n\n\t\t/**\n\t\t * @brief 重载指针操作符。\n\t\t */\n\t\tT* operator->() const noexcept\n\t\t{\n\t\t\treturn _p;\n\t\t}\n\t\toperator T* () const noexcept\n\t\t{\n\t\t\treturn _p;\n\t\t}\n\n\t\t/**\n\t\t * @brief 获得管理的state指针。\n\t\t */\n\t\tT* get() const noexcept\n\t\t{\n\t\t\treturn _p;\n\t\t}\n\n\t\t/**\n\t\t * @brief 重置为空指针。\n\t\t */\n\t\tvoid reset()\n\t\t{\n\t\t\t_unlock();\n\t\t}\n\tprivate:\n\t\tvoid _unlock()\n\t\t{\n\t\t\tif (likely(_p != nullptr))\n\t\t\t{\n\t\t\t\tauto t = _p;\n\t\t\t\t_p = nullptr;\n\t\t\t\tt->unlock();\n\t\t\t}\n\t\t}\n\t\tvoid _lock(T* p) noexcept\n\t\t{\n\t\t\tif (p != nullptr)\n\t\t\t\tp->lock();\n\t\t\t_p = p;\n\t\t}\n\t\tvoid _lock() noexcept\n\t\t{\n\t\t\tif (_p != nullptr)\n\t\t\t\t_p->lock();\n\t\t}\n\t\tT* _p = nullptr;\n\t};\n\n\ttemplate <typename T, typename U>\n\tinline bool operator == (const counted_ptr<T>& _Left, const counted_ptr<U>& _Right)\n\t{\n\t\treturn _Left.get() == _Right.get();\n\t}\n\ttemplate <typename T>\n\tinline bool operator == (const counted_ptr<T>& _Left, std::nullptr_t)\n\t{\n\t\treturn _Left.get() == nullptr;\n\t}\n\ttemplate <typename T>\n\tinline bool operator == (std::nullptr_t, const counted_ptr<T>& _Left)\n\t{\n\t\treturn _Left.get() == nullptr;\n\t}\n\ttemplate <typename T>\n\tinline bool operator != (const counted_ptr<T>& _Left, std::nullptr_t)\n\t{\n\t\treturn _Left.get() != nullptr;\n\t}\n\ttemplate <typename T>\n\tinline bool operator != (std::nullptr_t, const counted_ptr<T>& _Left)\n\t{\n\t\treturn _Left.get() != nullptr;\n\t}\n}\n\nnamespace std\n{\n\ttemplate<typename T>\n\tinline void swap(librf::counted_ptr<T>& a, librf::counted_ptr<T>& b) noexcept\n\t{\n\t\ta.swap(b);\n\t}\n}\n"
  },
  {
    "path": "include/librf/src/current_scheduler.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\t/**\n\t * @brief 获得本协程绑定的调度器的可等待对象。\n\t */\n\tstruct get_current_scheduler_awaitor\n\t{\n\t\tbool await_ready() const noexcept\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\t_PromiseT& promise = handler.promise();\n\t\t\tauto* state = promise.get_state();\n\t\t\tthis->_scheduler = state->get_scheduler();\n\n\t\t\treturn false;\n\t\t}\n\n\t\tscheduler_t* await_resume() const noexcept\n\t\t{\n\t\t\treturn _scheduler;\n\t\t}\n\tprivate:\n\t\tscheduler_t* _scheduler;\n#ifdef DOXYGEN_SKIP_PROPERTY\n\tpublic:\n\t\t/**\n\t\t * @brief 获得当前协程绑定的调度器。\n\t\t * @details 立即返回，没有协程切换和等待。\\n\n\t\t * 推荐使用 librf_current_scheduler() 宏替代 co_await get_current_scheduler()。\n\t\t * @return [co_await] scheduler_t*\n\t\t * @note 本函数是librf名字空间下的全局函数。由于doxygen使用上的问题，将之归纳到 get_current_scheduler_awaitor 类下。\n\t\t */\n\t\tstatic get_current_scheduler_awaitor get_current_scheduler() noexcept;\n\n\t\t/**\n\t\t * @brief 获得当前协程绑定的调度器。\n\t\t * @details 立即返回，没有协程切换和等待。\\n\n\t\t * 这是一条宏函数，等同于 co_await get_current_scheduler()。\n\t\t * @return scheduler_t*\n\t\t * @note 由于doxygen使用上的问题，将之归纳到 get_current_scheduler_awaitor 类下。\n\t\t */\n\t\tstatic scheduler_t* librf_current_scheduler() noexcept;\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\t};\n\n\t/**\n\t * @brief 获得当前协程绑定的调度器。\n\t * @details 立即返回，没有协程切换和等待。\\n\n\t * 推荐使用 librf_current_scheduler() 宏替代 co_await get_current_scheduler()。\n\t * @return [co_await] scheduler_t*\n\t */\n\tinline get_current_scheduler_awaitor get_current_scheduler() noexcept\n\t{\n\t\treturn {};\n\t}\n\n\n\t/**\n\t * @brief 获得本协程绑定的跟state指针的可等待对象。\n\t */\n\tstruct get_root_state_awaitor\n\t{\n\t\tbool await_ready() const noexcept\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\t_PromiseT& promise = handler.promise();\n\t\t\tauto* parent = promise.get_state();\n\t\t\tthis->_state = parent->get_root();\n\n\t\t\treturn false;\n\t\t}\n\n\t\tstate_base_t* await_resume() const noexcept\n\t\t{\n\t\t\treturn _state;\n\t\t}\n\tprivate:\n\t\tstate_base_t* _state;\n#ifdef DOXYGEN_SKIP_PROPERTY\n\tpublic:\n\t\t/**\n\t\t * @brief 获得当前协程的跟state指针。\n\t\t * @details 立即返回，没有协程切换和等待。\\n\n\t\t * 推荐使用 librf_root_state() 宏替代 co_await get_root_state()。\n\t\t * @return [co_await] state_base_t*\n\t\t * @note 本函数是librf名字空间下的全局函数。由于doxygen使用上的问题，将之归纳到 get_root_state_awaitor 类下。\n\t\t */\n\t\tstatic get_root_state_awaitor get_root_state() noexcept;\n\n\t\t/**\n\t\t * @brief 获得当前协程的跟state指针。\n\t\t * @details 立即返回，没有协程切换和等待。\n\t\t * 这是一条宏函数，等同于 co_await get_root_state()。\n\t\t * @return state_base_t*\n\t\t * @note 由于doxygen使用上的问题，将之归纳到 get_root_state_awaitor 类下。\n\t\t */\n\t\tstatic state_base_t* librf_root_state() noexcept;\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\t};\n\n\t/**\n\t * @brief 获得当前协程的跟state指针。\n\t * @details 立即返回，没有协程切换和等待。\n\t * 推荐使用 librf_root_state() 宏替代 co_await get_root_state()。\n\t * @return [co_await] state_base_t*\n\t */\n\tinline get_root_state_awaitor get_root_state() noexcept\n\t{\n\t\treturn {};\n\t}\n\n\t/**\n\t * @brief 获得本协程的task_t对象。\n\t */\n\tstruct get_current_task_awaitor\n\t{\n\t\tbool await_ready() const noexcept\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\t_PromiseT& promise = handler.promise();\n\t\t\tauto* parent = promise.get_state();\n\t\t\tstate_base_t * state = parent->get_root();\n\t\t\tscheduler_t* sch = state->get_scheduler();\n\n\t\t\tthis->_task = sch->find_task(state);\n\n\t\t\treturn false;\n\t\t}\n\n\t\ttask_t* await_resume() const noexcept\n\t\t{\n\t\t\treturn _task;\n\t\t}\n\tprivate:\n\t\ttask_t* _task;\n#ifdef DOXYGEN_SKIP_PROPERTY\n\tpublic:\n\t\t/**\n\t\t * @brief 获得当前协程的task_t指针。\n\t\t * @details 立即返回，没有协程切换和等待。\n\t\t * 推荐使用 librf_current_task() 宏替代 co_await get_current_task()。\n\t\t * @return [co_await] task_t*\n\t\t * @note 本函数是librf名字空间下的全局函数。由于doxygen使用上的问题，将之归纳到 get_current_task_awaitor 类下。\n\t\t */\n\t\tstatic get_root_state_awaitor get_current_task() noexcept;\n\n\t\t/**\n\t\t * @brief 获得当前协程的task_t指针。\n\t\t * @details 立即返回，没有协程切换和等待。\n\t\t * 这是一条宏函数，等同于 co_await get_current_task()。\n\t\t * @return task_t*\n\t\t * @note 由于doxygen使用上的问题，将之归纳到 get_current_task_awaitor 类下。\n\t\t */\n\t\tstatic task_t* librf_current_task() noexcept;\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\t};\n\n\t/**\n\t * @brief 获得当前协程的task_t指针。\n\t * @details 立即返回，没有协程切换和等待。\n\t * 推荐使用 librf_current_task() 宏替代 co_await get_current_task()。\n\t * @return [co_await] task_t*\n\t */\n\tinline get_current_task_awaitor get_current_task() noexcept\n\t{\n\t\treturn {};\n\t}\n}\n"
  },
  {
    "path": "include/librf/src/def.h",
    "content": "﻿#pragma once\n\n#define LIB_RESUMEF_VERSION 30000 // 3.0.0\n\n#ifndef __cpp_impl_coroutine\nnamespace std\n{\n\tusing experimental::coroutine_traits;\n\tusing experimental::coroutine_handle;\n\t//using experimental::suspend_if;\n\tusing experimental::suspend_always;\n\tusing experimental::suspend_never;\n}\n#endif\n\nnamespace librf\n{\n#ifndef DOXYGEN_SKIP_PROPERTY\n\tstruct scheduler_t;\n\n\ttemplate<class _Ty = void>\n\tstruct future_t;\n\n\ttemplate <typename _Ty = std::nullptr_t, typename _Alloc = std::allocator<char>>\n\tstruct generator_t;\n\n\ttemplate<class _Ty = void>\n\tstruct promise_t;\n\n\ttemplate<class _Ty = void>\n\tstruct awaitable_t;\n\n\tstruct state_base_t;\n\n\tstruct switch_scheduler_t;\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\n\ttemplate<typename _PromiseT = void>\n\tusing coroutine_handle = std::coroutine_handle<_PromiseT>;\n\tusing suspend_always = std::suspend_always;\n\tusing suspend_never = std::suspend_never;\n\n\ttemplate<class... _Mutexes>\n\tusing scoped_lock = std::scoped_lock<_Mutexes...>;\n\n\tusing stop_source = milk::concurrency::stop_source;\n\tusing stop_token = milk::concurrency::stop_token;\n\ttemplate<typename Callback>\n\tusing stop_callback = milk::concurrency::stop_callback<Callback>;\n\tusing milk::concurrency::nostopstate;\n\n\t/**\n\t * @brief 版本号。\n\t */\n\tconstexpr size_t _Version = LIB_RESUMEF_VERSION;\n}\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\n#if RESUMEF_DEBUG_COUNTER\nextern std::mutex g_resumef_cout_mutex;\nextern std::atomic<intptr_t> g_resumef_state_count;\nextern std::atomic<intptr_t> g_resumef_task_count;\nextern std::atomic<intptr_t> g_resumef_evtctx_count;\nextern std::atomic<intptr_t> g_resumef_state_id;\n#endif\n\nnamespace librf\n{\n\ttemplate<class T>\n\tstruct remove_cvref\n\t{\n\t\ttypedef std::remove_cv_t<std::remove_reference_t<T>> type;\n\t};\n\ttemplate<class T>\n\tusing remove_cvref_t = typename remove_cvref<T>::type;\n\n\n\ttemplate<class _Ty>\n\tconstexpr size_t _Align_size()\n\t{\n\t\tconstexpr size_t _ALIGN_REQ = sizeof(void*) * 2;\n\t\treturn std::is_empty_v<_Ty> ? 0 :\n\t\t\t(sizeof(_Ty) + _ALIGN_REQ - 1) & ~(_ALIGN_REQ - 1);\n\t}\n\n\ttemplate<class _Callable>\n\tauto make_stop_callback(const stop_token& token, _Callable&& cb) ->std::unique_ptr<stop_callback<_Callable>>\n\t{\n\t\treturn std::make_unique<stop_callback<_Callable>>(token, std::forward<_Callable>(cb));\n\t}\n\ttemplate<class _Callable>\n\tauto make_stop_callback(stop_token&& token, _Callable&& cb) ->std::unique_ptr<stop_callback<_Callable>>\n\t{\n\t\treturn std::make_unique<stop_callback<_Callable>>(std::move(token), std::forward<_Callable>(cb));\n\t}\n}\n\n#endif\t//DOXYGEN_SKIP_PROPERTY\n"
  },
  {
    "path": "include/librf/src/event.h",
    "content": "﻿#pragma once\n\n#include \"event_v2.h\"\n#include \"event_v2.inl\"\n"
  },
  {
    "path": "include/librf/src/event_v2.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n#ifndef DOXYGEN_SKIP_PROPERTY\n\tnamespace detail\n\t{\n\t\tstruct event_v2_impl;\n\t}\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\n\t/**\n\t\t* @brief 用于协程的事件。\n\t\t* @details 用于同步不同线程里运行的协程。\n\t\t*/\n\tstruct event_t\n\t{\n\t\tusing event_impl_ptr = std::shared_ptr<detail::event_v2_impl>;\n\t\tusing clock_type = std::chrono::system_clock;\n\n\t\t/**\n\t\t\t* @brief 构造一个事件。\n\t\t\t* @param initially 初始是否触发一次信号。\n\t\t\t*/\n\t\tLIBRF_API event_t(bool initially = false);\n\n\t\t/**\n\t\t\t* @brief 构造一个无效的事件。\n\t\t\t* @details 如果用于后续保存另外一个事件，则应当使用此构造函数，便于节省一次不必要的内部初始化。\n\t\t\t*/\n\t\tLIBRF_API event_t(std::adopt_lock_t);\n\n\t\t/**\n\t\t\t* @brief 采用shared_ptr<>来保存内部的事件实现。故不必担心正在被等待的协程，因为事件提前销毁而出现异常。\n\t\t\t*/\n\t\tLIBRF_API ~event_t();\n\n\t\t/**\n\t\t\t* @brief 向所有正在等待的协程触发一次信号。\n\t\t\t* @attention 非协程中也可以使用。\n\t\t\t*/\n\t\tvoid signal_all() const noexcept;\n\n\t\t/**\n\t\t\t* @brief 触发一次信号。\n\t\t\t* @details 如果有正在等待的协程，则最先等待的协程会被唤醒。\\n\n\t\t\t* 如果没有正在等待的协程，则信号触发次数加一。之后有协程调用wait()，则会直接返回。\n\t\t\t* @attention 非协程中也可以使用。\n\t\t\t*/\n\t\tvoid signal() const noexcept;\n\n\t\t/**\n\t\t\t* @brief 重置信号。\n\t\t\t* @attention 非协程中也可以使用。\n\t\t\t*/\n\t\tvoid reset() const noexcept;\n\n\t\tstruct [[nodiscard]] awaiter;\n\n\t\t/**\n\t\t\t* @brief 在协程中等待信号触发。\n\t\t\t* @see 等同于co_await wait()。\n\t\t\t* @attention 只能在协程中调用。\n\t\t\t*/\n\t\tawaiter operator co_await() const noexcept;\n\n\t\t/**\n\t\t\t* @brief 在协程中等待信号触发。\n\t\t\t* @details 如果信号已经触发，则立即返回true。\\n\n\t\t\t* 否则，当前协程被阻塞，直到信号被触发后唤醒。\n\t\t\t* 消耗一次信号触发次数。\n\t\t\t* @retval bool [co_await] 返回是否等到了信号\n\t\t\t* @attention 只能在协程中调用。\n\t\t\t*/\n\t\tawaiter wait() const noexcept;\n\n\t\ttemplate<class _Btype>\n\t\tstruct timeout_awaitor_impl;\n\n\t\tstruct [[nodiscard]] timeout_awaiter;\n\n\t\t/**\n\t\t\t* @brief 在协程中等待信号触发，直到超时。\n\t\t\t* @details 如果信号已经触发，则立即返回true。\\n\n\t\t\t* 否则，当前协程被阻塞，直到信号被触发后，或者超时后唤醒。\n\t\t\t* 如果等到了信号，则消耗一次信号触发次数。\n\t\t\t* @param dt 超时时长\n\t\t\t* @retval bool [co_await] 等到了信号返回true，超时了返回false。\n\t\t\t* @attention 只能在协程中调用。\n\t\t\t*/\n\t\ttemplate<class _Rep, class _Period>\n\t\ttimeout_awaiter wait_for(const std::chrono::duration<_Rep, _Period>& dt) const noexcept;\t\t\t\t\t\t//test OK\n\n\t\t/**\n\t\t\t* @brief 在协程中等待信号触发，直到超时。\n\t\t\t* @details 如果信号已经触发，则立即返回true。\\n\n\t\t\t* 否则，当前协程被阻塞，直到信号被触发后，或者超时后唤醒。\n\t\t\t* 如果等到了信号，则消耗一次信号触发次数。\n\t\t\t* @param tp 超时时刻\n\t\t\t* @retval bool [co_await] 等到了信号返回true，超时了返回false。\n\t\t\t* @attention 只能在协程中调用。\n\t\t\t*/\n\t\ttemplate<class _Clock, class _Duration>\n\t\ttimeout_awaiter wait_until(const std::chrono::time_point<_Clock, _Duration>& tp) const noexcept;\t\t\t\t//test OK\n\n\n\t\ttemplate<class _Iter>\n\t\tstruct [[nodiscard]] any_awaiter;\n\n\t\t/**\n\t\t\t* @brief 在协程中等待任意一个信号触发。\n\t\t\t* @details 如果已经有信号触发，则立即返回第一个触发信号的索引。\\n\n\t\t\t* 否则，当前协程被阻塞，直到信号被触发后唤醒。\n\t\t\t* 至少消耗一次信号触发次数。\n\t\t\t* @param begin_ 容纳信号的容器的首迭代器\n\t\t\t* @param end_ 容纳信号的容器的尾迭代器（不包含有效信号）\n\t\t\t* @retval intptr_t [co_await] 返回第一个触发信号的索引\n\t\t\t* @attention 只能在协程中调用。\n\t\t\t*/\n\t\ttemplate<class _Iter>\n\t\trequires(_IteratorOfT<_Iter, event_t>)\n\t\tstatic auto wait_any(_Iter begin_, _Iter end_)->any_awaiter<_Iter>;\n\n\t\t/**\n\t\t\t* @brief 在协程中等待任意一个信号触发。\n\t\t\t* @details 如果已经有信号触发，则立即返回第一个触发信号的索引。\\n\n\t\t\t* 否则，当前协程被阻塞，直到信号被触发后唤醒。\n\t\t\t* 至少消耗一次信号触发次数。\n\t\t\t* @param cnt_ 容纳信号的容器，需要支持std::begin(cnt_)和std::end(cnt_)\n\t\t\t* @retval intptr_t [co_await] 返回第一个触发信号的索引\n\t\t\t* @attention 只能在协程中调用。\n\t\t\t*/\n\t\ttemplate<class _Cont>\n\t\trequires(_ContainerOfT<_Cont, event_t>)\n\t\tstatic auto wait_any(const _Cont& cnt_)->any_awaiter<decltype(std::begin(cnt_))>;\n\n\t\ttemplate<class _Iter>\n\t\tstruct [[nodiscard]] timeout_any_awaiter;\n\n\t\t/**\n\t\t\t* @brief 在协程中等待任意一个信号触发，直到超时。\n\t\t\t* @details 如果已经有信号触发，则立即返回第一个触发信号的索引。\\n\n\t\t\t* 否则，当前协程被阻塞，直到信号被触发后，或者超时后唤醒。\n\t\t\t* 如果等到了信号，则至少消耗一次信号触发次数。\n\t\t\t* @param dt 超时时长\n\t\t\t* @param begin_ 容纳信号的容器的首迭代器\n\t\t\t* @param end_ 容纳信号的容器的尾迭代器（不包含有效信号）\n\t\t\t* @retval intptr_t [co_await] 如果等到了任意一个信号，返回其索引（相对于begin_的距离）；否则，返回-1\n\t\t\t* @attention 只能在协程中调用。\n\t\t\t*/\n\t\ttemplate<class _Rep, class _Period, class _Iter>\n\t\trequires(_IteratorOfT<_Iter, event_t>)\n\t\tstatic auto wait_any_for(const std::chrono::duration<_Rep, _Period>& dt, _Iter begin_, _Iter end_)\n\t\t\t->timeout_any_awaiter<_Iter>;\n\n\t\t/**\n\t\t\t* @brief 在协程中等待任意一个信号触发，直到超时。\n\t\t\t* @details 如果已经有信号触发，则立即返回第一个触发信号的索引。\\n\n\t\t\t* 否则，当前协程被阻塞，直到信号被触发后，或者超时后唤醒。\n\t\t\t* 如果等到了信号，则至少消耗一次信号触发次数。\n\t\t\t* @param dt 超时时长\n\t\t\t* @param cnt_ 容纳信号的容器，需要支持std::begin(cnt_)和std::end(cnt_)\n\t\t\t* @retval intptr_t [co_await] 如果等到了任意一个信号，返回其索引（相对于begin_的距离）；否则，返回-1\n\t\t\t* @attention 只能在协程中调用。\n\t\t\t*/\n\t\ttemplate<class _Rep, class _Period, class _Cont>\n\t\trequires(_ContainerOfT<_Cont, event_t>)\n\t\tstatic auto wait_any_for(const std::chrono::duration<_Rep, _Period>& dt, const _Cont& cnt_)\n\t\t\t->timeout_any_awaiter<decltype(std::begin(cnt_))>;\n\n\n\n\t\ttemplate<class _Iter>\n\t\tstruct [[nodiscard]] all_awaiter;\n\n\t\ttemplate<class _Iter>\n\t\trequires(_IteratorOfT<_Iter, event_t>)\n\t\tstatic auto wait_all(_Iter begin_, _Iter end_)\n\t\t\t->all_awaiter<_Iter>;\n\n\t\ttemplate<class _Cont>\n\t\trequires(_ContainerOfT<_Cont, event_t>)\n\t\tstatic auto wait_all(const _Cont& cnt_)\n\t\t\t->all_awaiter<decltype(std::begin(cnt_))>;\n\n\n\t\ttemplate<class _Iter>\n\t\tstruct [[nodiscard]] timeout_all_awaiter;\n\n\t\ttemplate<class _Rep, class _Period, class _Iter>\n\t\trequires(_IteratorOfT<_Iter, event_t>)\n\t\tstatic auto wait_all_for(const std::chrono::duration<_Rep, _Period>& dt, _Iter begin_, _Iter end_)\n\t\t\t->timeout_all_awaiter<_Iter>;\n\n\t\ttemplate<class _Rep, class _Period, class _Cont>\n\t\trequires(_ContainerOfT<_Cont, event_t>)\n\t\tstatic auto wait_all_for(const std::chrono::duration<_Rep, _Period>& dt, const _Cont& cnt_)\n\t\t\t->timeout_all_awaiter<decltype(std::begin(cnt_))>;\n\n\t\tevent_t(const event_t&) = default;\n\t\tevent_t(event_t&&) = default;\n\t\tevent_t& operator = (const event_t&) = default;\n\t\tevent_t& operator = (event_t&&) = default;\n\tprivate:\n\t\tevent_impl_ptr _event;\n\t};\n}\n"
  },
  {
    "path": "include/librf/src/event_v2.inl",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\tnamespace detail\n\t{\n\t\tstruct state_event_base_t;\n\n\t\tstruct event_v2_impl : public std::enable_shared_from_this<event_v2_impl>\n\t\t{\n\t\t\tLIBRF_API event_v2_impl(bool initially) noexcept;\n\t\t\tLIBRF_API ~event_v2_impl();\n\n\t\t\tbool is_signaled() const noexcept\n\t\t\t{\n\t\t\t\treturn _counter.load(std::memory_order_acquire) > 0;\n\t\t\t}\n\t\t\tLIBRF_API void signal_all() noexcept;\n\t\t\tLIBRF_API void signal() noexcept;\n\t\t\tLIBRF_API void reset() noexcept;\n\n\t\t\tLIBRF_API void add_wait_list(state_event_base_t* state);\n\t\t\tLIBRF_API void remove_wait_list(state_event_base_t* state);\n\t\tpublic:\n\t\t\tstatic constexpr bool USE_SPINLOCK = true;\n\n\t\t\tusing lock_type = std::conditional_t<USE_SPINLOCK, spinlock, std::recursive_mutex>;\n\t\t\tusing state_event_ptr = counted_ptr<state_event_base_t>;\n\t\t\tusing wait_queue_type = intrusive_link_queue<state_event_base_t, state_event_ptr>;\n\n\t\t\tbool try_wait_one() noexcept\n\t\t\t{\n\t\t\t\tif (_counter.load(std::memory_order_consume) > 0)\n\t\t\t\t{\n\t\t\t\t\tif (_counter.fetch_add(-1, std::memory_order_acq_rel) > 0)\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t_counter.fetch_add(1);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tlock_type _lock;\t\t\t\t\t\t\t\t\t//保证访问本对象是线程安全的\n\t\tprivate:\n\t\t\tstd::atomic<intptr_t> _counter;\n\t\t\twait_queue_type _wait_awakes;\t\t\t\t\t\t//等待队列\n\n\t\t\t// No copying/moving\n\t\t\tevent_v2_impl(const event_v2_impl&) = delete;\n\t\t\tevent_v2_impl(event_v2_impl&&) = delete;\n\t\t\tevent_v2_impl& operator=(const event_v2_impl&) = delete;\n\t\t\tevent_v2_impl& operator=(event_v2_impl&&) = delete;\n\t\t};\n\n\t\tstruct state_event_base_t : public state_base_t\n\t\t\t\t\t\t\t\t  , public intrusive_link_node<state_event_base_t, counted_ptr<state_event_base_t>>\n\t\t{\n\t\t\tvirtual void on_cancel() noexcept = 0;\n\t\t\tvirtual bool on_notify(event_v2_impl* eptr) = 0;\n\t\t\tvirtual bool on_timeout() = 0;\n\n\t\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\t\tscheduler_t* on_await_suspend(coroutine_handle<_PromiseT> handler) noexcept\n\t\t\t{\n\t\t\t\t_PromiseT& promise = handler.promise();\n\t\t\t\tauto* parent = promise.get_state();\n\t\t\t\tscheduler_t* sch = parent->get_scheduler();\n\n\t\t\t\tthis->_scheduler = sch;\n\t\t\t\tthis->_coro = handler;\n\n\t\t\t\treturn sch;\n\t\t\t}\n\n\t\t\tinline void add_timeout_timer(std::chrono::system_clock::time_point tp)\n\t\t\t{\n\t\t\t\tthis->_thandler = this->_scheduler->timer()->add_handler(tp,\n\t\t\t\t\t[st = counted_ptr<state_event_base_t>{ this }](bool canceld)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!canceld)\n\t\t\t\t\t\t\tst->on_timeout();\n\t\t\t\t\t});\n\t\t\t}\n\n\t\tprotected:\n\t\t\ttimer_handler _thandler;\n\t\t};\n\n\n\t\tstruct state_event_t : public state_event_base_t\n\t\t{\n\t\t\tstate_event_t(event_v2_impl*& val)\n\t\t\t\t: _value(&val)\n\t\t\t{}\n\n\t\t\tLIBRF_API virtual void on_cancel() noexcept override;\n\t\t\tLIBRF_API virtual bool on_notify(event_v2_impl* eptr) override;\n\t\t\tLIBRF_API virtual bool on_timeout() override;\n\t\tprotected:\n\t\t\t//_value引用awaitor保存的值，这样可以尽可能减少创建state的可能。而不必进入没有state就没有value实体被用于返回。\n\t\t\t//在调用on_notify()或on_timeout()任意之一后，置为nullptr。\n\t\t\t//这样来保证要么超时了，要么响应了signal的通知了。\n\t\t\t//这个指针在on_notify()和on_timeout()里，当作一个互斥的锁来防止同时进入两个函数\n\t\t\tstd::atomic<event_v2_impl**> _value;\n\t\t};\n\n\t\tstruct state_event_all_t : public state_base_t\n\t\t{\n\t\t\tusing sub_state_t = std::pair<counted_ptr<state_event_base_t>, detail::event_v2_impl*>;\n\n\t\t\tstate_event_all_t(intptr_t count, bool& val)\n\t\t\t\t: _counter(count)\n\t\t\t\t, _result(&val)\n\t\t\t{\n\t\t\t\t_values.resize(count, sub_state_t{nullptr, nullptr});\n\t\t\t}\n\n\t\t\tLIBRF_API void on_cancel(intptr_t idx);\n\t\t\tLIBRF_API bool on_notify(event_v2_impl* eptr, intptr_t idx);\n\t\t\tLIBRF_API bool on_timeout();\n\n\t\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\t\tscheduler_t* on_await_suspend(coroutine_handle<_PromiseT> handler) noexcept\n\t\t\t{\n\t\t\t\t_PromiseT& promise = handler.promise();\n\t\t\t\tauto* parent = promise.get_state();\n\t\t\t\tscheduler_t* sch = parent->get_scheduler();\n\n\t\t\t\tthis->_scheduler = sch;\n\t\t\t\tthis->_coro = handler;\n\n\t\t\t\treturn sch;\n\t\t\t}\n\n\t\t\tinline void add_timeout_timer(std::chrono::system_clock::time_point tp)\n\t\t\t{\n\t\t\t\tthis->_thandler = this->_scheduler->timer()->add_handler(tp,\n\t\t\t\t\t[st = counted_ptr<state_event_all_t>{ this }](bool canceld)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!canceld)\n\t\t\t\t\t\t\tst->on_timeout();\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\tstd::vector<sub_state_t> _values;\n\t\t\tintptr_t _counter;\n\t\t\tevent_v2_impl::lock_type _lock;\n\t\tprotected:\n\t\t\ttimer_handler _thandler;\n\t\t\tbool* _result;\n\t\t};\n\n\t\ttemplate<class _StateT>\n\t\tstruct state_event_proxy_t : public state_event_base_t\n\t\t{\n\t\t\tstate_event_proxy_t(_StateT* sta, intptr_t idx)\n\t\t\t\t: _state(sta)\n\t\t\t\t, _index(idx)\n\t\t\t{\n\t\t\t\tassert(sta != nullptr);\n\t\t\t\tassert(idx >= 0);\n\t\t\t}\n\n\t\t\tvoid on_cancel() noexcept override { return _state->on_cancel(_index); }\n\t\t\tbool on_notify(event_v2_impl* eptr) override { return _state->on_notify(eptr, _index); }\n\t\t\tbool on_timeout() override { assert(false); return false; }\n\t\tprivate:\n\t\t\t_StateT* _state;\n\t\t\tintptr_t _index;\n\t\t};\n\t}\n\n\tinline void event_t::signal_all() const noexcept\n\t{\n\t\t_event->signal_all();\n\t}\n\n\tinline void event_t::signal() const noexcept\n\t{\n\t\t_event->signal();\n\t}\n\n\tinline void event_t::reset() const noexcept\n\t{\n\t\t_event->reset();\n\t}\n\n\tstruct [[nodiscard]] event_t::awaiter\n\t{\n\t\tawaiter(detail::event_v2_impl* evt) noexcept\n\t\t\t: _event(evt)\n\t\t{\n\t\t}\n\t\t~awaiter()\n\t\t{\n\t\t\tif (_event != nullptr && _state != nullptr)\n\t\t\t\t_event->remove_wait_list(_state.get());\n\t\t}\n\n\t\tbool await_ready() noexcept\n\t\t{\n\t\t\tscoped_lock<detail::event_v2_impl::lock_type> lock_(_event->_lock);\n\t\t\treturn _event->try_wait_one();\n\t\t}\n\n\t\ttemplate<class _PromiseT, class _Timeout> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend2(coroutine_handle<_PromiseT> handler, const _Timeout& cb)\n\t\t{\n\t\t\t(void)cb;\n\t\t\tdetail::event_v2_impl* evt = _event;\n\t\t\tscoped_lock<detail::event_v2_impl::lock_type> lock_(evt->_lock);\n\n\t\t\tif (evt->try_wait_one())\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t_state = new detail::state_event_t(_event);\n\t\t\t(void)_state->on_await_suspend(handler);\n\n\t\t\tif constexpr (!std::is_same_v<std::remove_reference_t<_Timeout>, std::nullptr_t>)\n\t\t\t{\n\t\t\t\tcb();\n\t\t\t}\n\n\t\t\tevt->add_wait_list(_state.get());\n\n\t\t\treturn true;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\treturn await_suspend2(handler, nullptr);\n\t\t}\n\n\t\tbool await_resume() noexcept\n\t\t{\n\t\t\treturn _event != nullptr;\n\t\t}\n\n\tprotected:\n\t\tdetail::event_v2_impl* _event;\n\t\tcounted_ptr<detail::state_event_t> _state;\n\t};\n\n\tinline event_t::awaiter event_t::operator co_await() const noexcept\n\t{\n\t\treturn { _event.get() };\n\t}\n\n\tinline event_t::awaiter event_t::wait() const noexcept\n\t{\n\t\treturn { _event.get() };\n\t}\n\n\ttemplate<class _Btype>\n\tstruct event_t::timeout_awaitor_impl : public _Btype\n\t{\n\t\ttemplate<class... Args>\n\t\ttimeout_awaitor_impl(clock_type::time_point tp, Args&&... args) noexcept(std::is_nothrow_constructible_v<_Btype, Args&&...>)\n\t\t\t: _Btype(std::forward<Args>(args)...)\n\t\t\t, _tp(tp)\n\t\t{}\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\tif (!_Btype::await_suspend2(handler, [this]{ this->_state->add_timeout_timer(_tp);}))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\tprotected:\n\t\tclock_type::time_point _tp;\n\t};\n\n\tstruct [[nodiscard]] event_t::timeout_awaiter : timeout_awaitor_impl<awaiter>\n\t{\n\t\tusing timeout_awaitor_impl<awaiter>::timeout_awaitor_impl;\n\t};\n\n\ttemplate<class _Rep, class _Period>\n\tinline event_t::timeout_awaiter event_t::wait_for(const std::chrono::duration<_Rep, _Period>& dt) const noexcept\n\t{\n\t\tclock_type::time_point tp2 = clock_type::now() + std::chrono::duration_cast<clock_type::duration>(dt);\n\t\treturn { tp2, _event.get() };\n\t}\n\n\ttemplate<class _Clock, class _Duration>\n\tinline event_t::timeout_awaiter event_t::wait_until(const std::chrono::time_point<_Clock, _Duration>& tp) const noexcept\n\t{\n\t\tclock_type::time_point tp2 = std::chrono::time_point_cast<clock_type::duration>(tp);\n\t\treturn { tp2, _event.get() };\n\t}\n\n\n\ttemplate<class _Iter>\n\tstruct [[nodiscard]] event_t::any_awaiter\n\t{\n\t\tany_awaiter(_Iter begin, _Iter end) noexcept\n\t\t\t: _begin(begin)\n\t\t\t, _end(end)\n\t\t{\n\t\t}\n\n\t\tbool await_ready() noexcept\n\t\t{\n\t\t\treturn _begin == _end;\n\t\t}\n\n\t\ttemplate<class _PromiseT, class _Timeout> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend2(coroutine_handle<_PromiseT> handler, const _Timeout& cb)\n\t\t{\n\t\t\t(void)cb;\n\t\t\tusing ref_lock_type = std::reference_wrapper<detail::event_v2_impl::lock_type>;\n\t\t\tstd::vector<ref_lock_type> lockes;\n\t\t\tlockes.reserve(std::distance(_begin, _end));\n\n\t\t\tfor (auto iter = _begin; iter != _end; ++iter)\n\t\t\t{\n\t\t\t\tdetail::event_v2_impl* evt = (*iter)._event.get();\n\t\t\t\tlockes.emplace_back(std::ref(evt->_lock));\n\t\t\t}\n\n\t\t\tbatch_lock_t<ref_lock_type> lock_(lockes);\n\n\t\t\tfor (auto iter = _begin; iter != _end; ++iter)\n\t\t\t{\n\t\t\t\tdetail::event_v2_impl* evt = (*iter)._event.get();\n\t\t\t\tif (evt->try_wait_one())\n\t\t\t\t{\n\t\t\t\t\t_event = evt;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_state = new detail::state_event_t(_event);\n\t\t\t(void)_state->on_await_suspend(handler);\n\n\t\t\tif constexpr (!std::is_same_v<std::remove_reference_t<_Timeout>, std::nullptr_t>)\n\t\t\t{\n\t\t\t\tcb();\n\t\t\t}\n\n\t\t\tfor (auto iter = _begin; iter != _end; ++iter)\n\t\t\t{\n\t\t\t\tdetail::event_v2_impl* evt = (*iter)._event.get();\n\t\t\t\tevt->add_wait_list(_state.get());\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\treturn await_suspend2(handler, nullptr);\n\t\t}\n\n\t\tintptr_t await_resume() noexcept\n\t\t{\n\t\t\tif (_begin == _end)\n\t\t\t\treturn 0;\n\t\t\tif (_event == nullptr)\n\t\t\t\treturn -1;\n\n\t\t\tintptr_t idx = 0;\n\t\t\tfor (auto iter = _begin; iter != _end; ++iter, ++idx)\n\t\t\t{\n\t\t\t\tdetail::event_v2_impl* evt = (*iter)._event.get();\n\t\t\t\tif (evt == _event)\n\t\t\t\t\treturn idx;\n\t\t\t}\n\n\t\t\treturn -1;\n\t\t}\n\tprotected:\n\t\tdetail::event_v2_impl* _event = nullptr;\n\t\tcounted_ptr<detail::state_event_t> _state;\n\t\t_Iter _begin;\n\t\t_Iter _end;\n\t};\n\n\ttemplate<class _Iter>\n\trequires(_IteratorOfT<_Iter, event_t>)\n\tauto event_t::wait_any(_Iter begin_, _Iter end_) ->event_t::any_awaiter<_Iter>\n\t{\n\t\tassert(false && \"Function is flawed!\");\n\t\treturn { begin_, end_ };\n\t}\n\n\ttemplate<class _Cont>\n\trequires(_ContainerOfT<_Cont, event_t>)\n\tauto event_t::wait_any(const _Cont& cnt_) ->event_t::any_awaiter<decltype(std::begin(cnt_))>\n\t{\n\t\tassert(false && \"Function is flawed!\");\n\t\treturn { std::begin(cnt_), std::end(cnt_) };\n\t}\n\n\n\n\ttemplate<class _Iter>\n\tstruct [[nodiscard]] event_t::timeout_any_awaiter : timeout_awaitor_impl<any_awaiter<_Iter>>\n\t{\n\t\tusing timeout_awaitor_impl<any_awaiter<_Iter>>::timeout_awaitor_impl;\n\t};\n\n\ttemplate<class _Rep, class _Period, class _Iter>\n\trequires(_IteratorOfT<_Iter, event_t>)\n\tauto event_t::wait_any_for(const std::chrono::duration<_Rep, _Period>& dt, _Iter begin_, _Iter end_)\n\t\t->event_t::timeout_any_awaiter<_Iter>\n\t{\n\t\tassert(false && \"Function is flawed!\");\n\t\tclock_type::time_point tp = clock_type::now() + std::chrono::duration_cast<clock_type::duration>(dt);\n\t\treturn { tp, begin_, end_ };\n\t}\n\n\ttemplate<class _Rep, class _Period, class _Cont>\n\trequires(_ContainerOfT<_Cont, event_t>)\n\tauto event_t::wait_any_for(const std::chrono::duration<_Rep, _Period>& dt, const _Cont& cnt_)\n\t\t->event_t::timeout_any_awaiter<decltype(std::begin(cnt_))>\n\t{\n\t\tassert(false && \"Function is flawed!\");\n\t\tclock_type::time_point tp = clock_type::now() + std::chrono::duration_cast<clock_type::duration>(dt);\n\t\treturn { tp, std::begin(cnt_), std::end(cnt_) };\n\t}\n\n\n\n\ttemplate<class _Iter>\n\tstruct [[nodiscard]] event_t::all_awaiter\n\t{\n\t\tall_awaiter(_Iter begin, _Iter end) noexcept\n\t\t\t: _begin(begin)\n\t\t\t, _end(end)\n\t\t{\n\t\t}\n\n\t\tbool await_ready() noexcept\n\t\t{\n\t\t\t_value = _begin == _end;\n\t\t\treturn _value;\n\t\t}\n\n\t\ttemplate<class _PromiseT, class _Timeout> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend2(coroutine_handle<_PromiseT> handler, const _Timeout& cb)\n\t\t{\n\t\t\t(void)cb;\n\t\t\tconst intptr_t count = std::distance(_begin, _end);\n\n\t\t\tusing ref_lock_type = std::reference_wrapper<detail::event_v2_impl::lock_type>;\n\t\t\tstd::vector<ref_lock_type> lockes;\n\t\t\tlockes.reserve(count + 1);\n\n\t\t\tfor (auto iter = _begin; iter != _end; ++iter)\n\t\t\t{\n\t\t\t\tdetail::event_v2_impl* evt = (*iter)._event.get();\n\t\t\t\tlockes.push_back(evt->_lock);\n\t\t\t}\n\n\t\t\t_state = new detail::state_event_all_t(count, _value);\n\t\t\tlockes.push_back(_state->_lock);\n\t\t\t(void)_state->on_await_suspend(handler);\n\n\t\t\tif constexpr (!std::is_same_v<std::remove_reference_t<_Timeout>, std::nullptr_t>)\n\t\t\t{\n\t\t\t\tcb();\n\t\t\t}\n\n\t\t\tbatch_lock_t<ref_lock_type> lock_(lockes);\n\n\t\t\tintptr_t idx = 0;\n\t\t\tfor (auto iter = _begin; iter != _end; ++iter, ++idx)\n\t\t\t{\n\t\t\t\tdetail::event_v2_impl* evt = (*iter)._event.get();\n\t\t\t\tif (evt->try_wait_one())\n\t\t\t\t{\n\t\t\t\t\t--_state->_counter;\n\t\t\t\t\t_state->_values[idx].second = evt;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tauto* proxy = new detail::state_event_proxy_t<detail::state_event_all_t>(_state.get(), idx);\n\t\t\t\t\t_state->_values[idx] = detail::state_event_all_t::sub_state_t{ proxy, evt };\n\n\t\t\t\t\tevt->add_wait_list(proxy);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (_state->_counter == 0)\n\t\t\t{\n\t\t\t\t_state = nullptr;\n\t\t\t\t_value = true;\n\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\treturn await_suspend2(handler, nullptr);\n\t\t}\n\n\t\tbool await_resume() noexcept\n\t\t{\n\t\t\treturn _value;\n\t\t}\n\tprotected:\n\t\t_Iter _begin;\n\t\t_Iter _end;\n\t\tcounted_ptr<detail::state_event_all_t> _state;\n\t\tbool _value = false;\n\t};\n\n\ttemplate<class _Iter>\n\trequires(_IteratorOfT<_Iter, event_t>)\n\tauto event_t::wait_all(_Iter begin_, _Iter end_) ->all_awaiter<_Iter>\n\t{\n\t\treturn { begin_, end_ };\n\t}\n\n\ttemplate<class _Cont>\n\trequires(_ContainerOfT<_Cont, event_t>)\n\tauto event_t::wait_all(const _Cont& cnt_) ->all_awaiter<decltype(std::begin(cnt_))>\n\t{\n\t\treturn { std::begin(cnt_), std::end(cnt_) };\n\t}\n\n\n\ttemplate<class _Iter>\n\tstruct [[nodiscard]] event_t::timeout_all_awaiter : timeout_awaitor_impl<all_awaiter<_Iter>>\n\t{\n\t\tusing timeout_awaitor_impl<all_awaiter<_Iter>>::timeout_awaitor_impl;\n\t};\n\n\ttemplate<class _Rep, class _Period, class _Iter>\n\trequires(_IteratorOfT<_Iter, event_t>)\n\tauto event_t::wait_all_for(const std::chrono::duration<_Rep, _Period>& dt, _Iter begin_, _Iter end_)\n\t\t->event_t::timeout_all_awaiter<_Iter>\n\t{\n\t\tclock_type::time_point tp = clock_type::now() + std::chrono::duration_cast<clock_type::duration>(dt);\n\t\treturn { tp, begin_, end_ };\n\t}\n\n\ttemplate<class _Rep, class _Period, class _Cont>\n\trequires(_ContainerOfT<_Cont, event_t>)\n\tauto event_t::wait_all_for(const std::chrono::duration<_Rep, _Period>& dt, const _Cont& cnt_)\n\t\t->event_t::timeout_all_awaiter<decltype(std::begin(cnt_))>\n\t{\n\t\tclock_type::time_point tp = clock_type::now() + std::chrono::duration_cast<clock_type::duration>(dt);\n\t\treturn { tp, std::begin(cnt_), std::end(cnt_) };\n\t}\n}\n"
  },
  {
    "path": "include/librf/src/exception.inl",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\n\t/**\n\t * @brief 错误码。\n\t */\n\tenum struct error_code\n\t{\n\t\tnone,\n\t\tnot_ready,\t\t\t///< get_value called when value not available\n\t\talready_acquired,\t///< attempt to get another future\n\t\tunlock_more,\t\t///< unlock 次数多于 lock 次数\n\t\tread_before_write,\t///< 0容量的 channel，先读后写\n\t\ttimer_canceled,\t\t///< 定时器被意外取消\n\t\tnot_await_lock,\t\t///< 没有在协程中使用 co_await 等待 lock 结果\n\t\tstop_requested,\t\t///< stop_source 触发了\n\n\t\tmax__\n\t};\n\n\t/**\n\t * @brief 通过错误码获得错误描述字符串。\n\t */\n\tLIBRF_API const char* get_error_string(error_code fe, const char* classname);\n\n\t/**\n\t * @brief 在操作future_t<>时产生的异常。\n\t */\n\tstruct future_exception : std::logic_error\n\t{\n\t\terror_code _error;\n\t\tfuture_exception(error_code fe)\n\t\t\t: logic_error(get_error_string(fe, \"future_exception\"))\n\t\t\t, _error(fe)\n\t\t{\n\t\t}\n\t};\n\n\t/**\n\t * @brief 错误使用mutex_t时产生的异常。\n\t */\n\tstruct mutex_exception : std::logic_error\n\t{\n\t\terror_code _error;\n\t\tmutex_exception(error_code fe)\n\t\t\t: logic_error(get_error_string(fe, \"mutex_exception\"))\n\t\t\t, _error(fe)\n\t\t{\n\t\t}\n\t};\n\n\t/**\n\t * @brief 错误使用channel_t时产生的异常(v2.0版本以后已经不再抛此异常了）。\n\t */\n\tstruct channel_exception : std::logic_error\n\t{\n\t\terror_code _error;\n\t\tchannel_exception(error_code fe)\n\t\t\t: logic_error(get_error_string(fe, \"channel_exception\"))\n\t\t\t, _error(fe)\n\t\t{\n\t\t}\n\t};\n\n\t/**\n\t * @brief 定时器提前取消导致的异常。\n\t */\n\tstruct canceled_exception : public std::logic_error\n\t{\n\t\terror_code _error;\n\t\tcanceled_exception(error_code fe)\n\t\t\t: logic_error(get_error_string(fe, \"canceled_exception\"))\n\t\t\t, _error(fe)\n\t\t{\n\t\t}\n\t};\n}"
  },
  {
    "path": "include/librf/src/future.h",
    "content": "﻿\n#pragma once\n\nnamespace librf\n{\n\n\t/**\n\t * @brief 用于librf协程的返回值。\n\t * @details 由于coroutines的限制，协程的返回值必须明确申明，而不能通过auto推导。\\n\n\t * 用在恢复函数(resumeable function)里，支持co_await和co_yield。\\n\n\t * 用在可等待函数(awaitable function)里，与awaitable_t<>配套使用。\n\t */\n\ttemplate<class _Ty>\n\tstruct [[nodiscard]] future_t\n\t{\n\t\tusing promise_type = promise_t<_Ty>;\n\n\t\tusing value_type = _Ty;\n\t\tusing state_type = state_t<value_type>;\n\t\tusing future_type = future_t<value_type>;\n\t\tusing lock_type = typename state_type::lock_type;\n\n\t\tcounted_ptr<state_type> _state;\n\n\t\tfuture_t(counted_ptr<state_type> _st) noexcept\n\t\t\t:_state(std::move(_st)) {}\n\t\tfuture_t(const future_t&) noexcept = default;\n\t\tfuture_t(future_t&&) noexcept = default;\n\n\t\tfuture_t& operator = (const future_t&) noexcept = default;\n\t\tfuture_t& operator = (future_t&&) = default;\n\n\t\tbool await_ready() const noexcept\n\t\t{\n\t\t\treturn _state->future_await_ready();\n\t\t}\n\n\t\ttemplate<class _PromiseT/*, typename = std::enable_if_t<traits::is_promise_v<_PromiseT>>*/>\n\t\tvoid await_suspend(coroutine_handle<_PromiseT> handler) const\n\t\t{\n\t\t\t_state->future_await_suspend(handler);\n\t\t}\n\n\t\t_Ty await_resume() const\n\t\t{\n\t\t\treturn _state->future_await_resume();\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "include/librf/src/generator.h",
    "content": "﻿/*\n* Modify from <experimental/generator_t.h>\n* Purpose: Library support of coroutines. generator_t class\n* http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0057r0.pdf\n*/\n#pragma once\n\n#pragma pack(push,_CRT_PACKING)\n#pragma push_macro(\"new\")\n#undef new\n\nnamespace librf\n{\n\ttemplate <typename _Ty, typename promise_type>\n\tstruct generator_iterator;\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\n\ttemplate<typename promise_type>\n\tstruct generator_iterator<void, promise_type>\n\t{\n\t\ttypedef std::input_iterator_tag iterator_category;\n\t\ttypedef ptrdiff_t difference_type;\n\n\t\tcoroutine_handle<promise_type> _Coro;\n\n\t\tgenerator_iterator(std::nullptr_t) : _Coro(nullptr)\n\t\t{\n\t\t}\n\n\t\tgenerator_iterator(coroutine_handle<promise_type> _CoroArg) : _Coro(_CoroArg)\n\t\t{\n\t\t}\n\n\t\tgenerator_iterator& operator++()\n\t\t{\n\t\t\tif (_Coro.done())\n\t\t\t\t_Coro = nullptr;\n\t\t\telse\n\t\t\t\t_Coro.resume();\n\t\t\treturn *this;\n\t\t}\n\n\t\tvoid operator++(int)\n\t\t{\n\t\t\t// This postincrement operator meets the requirements of the Ranges TS\n\t\t\t// InputIterator concept, but not those of Standard C++ InputIterator.\n\t\t\t++* this;\n\t\t}\n\n\t\tbool operator==(generator_iterator const& right_) const\n\t\t{\n\t\t\treturn _Coro == right_._Coro;\n\t\t}\n\n\t\tbool operator!=(generator_iterator const& right_) const\n\t\t{\n\t\t\treturn !(*this == right_);\n\t\t}\n\t};\n\n\ttemplate <typename promise_type>\n\tstruct generator_iterator<std::nullptr_t, promise_type> : public generator_iterator<void, promise_type>\n\t{\n\t\tgenerator_iterator(std::nullptr_t) : generator_iterator<void, promise_type>(nullptr)\n\t\t{\n\t\t}\n\t\tgenerator_iterator(coroutine_handle<promise_type> _CoroArg) : generator_iterator<void, promise_type>(_CoroArg)\n\t\t{\n\t\t}\n\t};\n\n\ttemplate <typename _Ty, typename promise_type>\n\tstruct generator_iterator : public generator_iterator<void, promise_type>\n\t{\n\t\tusing value_type = _Ty;\n\t\tusing reference = _Ty const&;\n\t\tusing pointer = _Ty const*;\n\n\t\tgenerator_iterator(std::nullptr_t) : generator_iterator<void, promise_type>(nullptr)\n\t\t{\n\t\t}\n\t\tgenerator_iterator(coroutine_handle<promise_type> _CoroArg) : generator_iterator<void, promise_type>(_CoroArg)\n\t\t{\n\t\t}\n\n\t\treference operator*() const\n\t\t{\n\t\t\treturn *this->_Coro.promise()._CurrentValue;\n\t\t}\n\n\t\tpointer operator->() const\n\t\t{\n\t\t\treturn this->_Coro.promise()._CurrentValue;\n\t\t}\n\t};\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\n\t/**\n\t * @brief 专用于co_yield函数。\n\t */\n\ttemplate <typename _Ty, typename _Alloc>\n\tstruct generator_t\n\t{\n\t\tusing value_type = _Ty;\n\t\tusing state_type = state_generator_t;\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\t\tstruct promise_type\n\t\t{\n\t\t\tusing value_type = _Ty;\n\t\t\tusing state_type = state_generator_t;\n\t\t\tusing future_type = generator_t<value_type>;\n\n\t\t\t_Ty const* _CurrentValue;\n\n\t\t\tpromise_type()\n\t\t\t{\n\t\t\t\tget_state()->set_initial_suspend(coroutine_handle<promise_type>::from_promise(*this));\n\t\t\t}\n\t\t\tpromise_type(promise_type&& _Right) noexcept = default;\n\t\t\tpromise_type& operator = (promise_type&& _Right) noexcept = default;\n\t\t\tpromise_type(const promise_type&) = default;\n\t\t\tpromise_type& operator = (const promise_type&) = default;\n\n\t\t\tgenerator_t get_return_object()\n\t\t\t{\n\t\t\t\treturn generator_t{ *this };\n\t\t\t}\n\n\t\t\tsuspend_always initial_suspend() noexcept\n\t\t\t{\n\t\t\t\treturn {};\n\t\t\t}\n\n\t\t\tsuspend_always final_suspend() noexcept\n\t\t\t{\n\t\t\t\treturn {};\n\t\t\t}\n\n\t\t\tsuspend_always yield_value(_Ty const& _Value) noexcept\n\t\t\t{\n\t\t\t\t_CurrentValue = std::addressof(_Value);\n\t\t\t\treturn {};\n\t\t\t}\n\n\t\t\t//template<class = std::enable_if_t<!std::is_same_v<_Ty, void>, _Ty>>\n\t\t\tvoid return_value(_Ty const& _Value) noexcept\n\t\t\t{\n\t\t\t\t_CurrentValue = std::addressof(_Value);\n\t\t\t}\n\t\t\t//template<class = std::enable_if_t<std::is_same_v<_Ty, void>, _Ty>>\n\t\t\tvoid return_value() noexcept\n\t\t\t{\n\t\t\t\t_CurrentValue = nullptr;\n\t\t\t}\n\n\t\t\tvoid set_exception(std::exception_ptr e)\n\t\t\t{\n\t\t\t\t(void)e;\n\t\t\t\t//ref_state()->set_exception(std::move(e));\n\t\t\t\tstd::terminate();\n\t\t\t}\n#if defined(__cpp_impl_coroutine) || defined(__clang__) || defined(__GNUC__)\n\t\t\tvoid unhandled_exception()\n\t\t\t{\n\t\t\t\t//this->ref_state()->set_exception(std::current_exception());\n\t\t\t\tstd::terminate();\n\t\t\t}\n#endif\n\n\t\t\ttemplate <typename _Uty>\n\t\t\t_Uty&& await_transform(_Uty&& _Whatever) noexcept\n\t\t\t{\n\t\t\t\tstatic_assert(std::is_same_v<_Uty, void>,\n\t\t\t\t\t\"co_await is not supported in coroutines of type std::experiemental::generator_t\");\n\t\t\t\treturn std::forward<_Uty>(_Whatever);\n\t\t\t}\n\n\t\t\tstate_type* get_state() noexcept\n\t\t\t{\n\t\t\t\treturn _state.get();\n\t\t\t}\n\t\t\t//counted_ptr<state_type> ref_state() noexcept\n\t\t\t//{\n\t\t\t//\treturn { get_state() };\n\t\t\t//}\n\t\t\tstate_type* ref_state() noexcept\n\t\t\t{\n\t\t\t\treturn get_state();\n\t\t\t}\n\n\t\t\tusing _Alloc_char = typename std::allocator_traits<_Alloc>::template rebind_alloc<char>;\n\t\t\tstatic_assert(std::is_same_v<char*, typename std::allocator_traits<_Alloc_char>::pointer>,\n\t\t\t\t\"generator_t does not support allocators with fancy pointer types\");\n\t\t\tstatic_assert(std::allocator_traits<_Alloc_char>::is_always_equal::value,\n\t\t\t\t\"generator_t only supports stateless allocators\");\n\n\t\t\tvoid* operator new(size_t _Size)\n\t\t\t{\n\t\t\t\t_Alloc_char _Al;\n\t\t\t\tchar* ptr = _Al.allocate(_Size);\n#if RESUMEF_DEBUG_COUNTER\n\t\t\t\tstd::cout << \"  generator_promise::new, alloc size=\" << _Size << std::endl;\n\t\t\t\tstd::cout << \"  generator_promise::new, alloc ptr=\" << (void*)ptr << std::endl;\n\t\t\t\tstd::cout << \"  generator_promise::new, return ptr=\" << (void*)ptr << std::endl;\n#endif\n\n\t\t\t\treturn ptr;\n\t\t\t}\n\n\t\t\tvoid operator delete(void* _Ptr, size_t _Size)\n\t\t\t{\n\t\t\t\t_Alloc_char _Al;\n\t\t\t\treturn _Al.deallocate(reinterpret_cast<char *>(_Ptr), _Size);\n\t\t\t}\n\t\tprivate:\n\t\t\tcounted_ptr<state_type> _state = state_generator_t::_Alloc_state();\n\t\t};\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\n\t\tusing iterator = generator_iterator<_Ty, promise_type>;\n\n\t\titerator begin()\n\t\t{\n\t\t\tif (_Coro)\n\t\t\t{\n\t\t\t\t_Coro.resume();\n\t\t\t\tif (_Coro.done())\n\t\t\t\t\treturn{ nullptr };\n\t\t\t}\n\t\t\treturn { _Coro };\n\t\t}\n\n\t\titerator end()\n\t\t{\n\t\t\treturn{ nullptr };\n\t\t}\n\n\t\texplicit generator_t(promise_type& _Prom)\n\t\t\t: _Coro(coroutine_handle<promise_type>::from_promise(_Prom))\n\t\t{\n\t\t}\n\n\t\tgenerator_t() = default;\n\n\t\tgenerator_t(generator_t const&) = delete;\n\t\tgenerator_t& operator=(generator_t const&) = delete;\n\n\t\tgenerator_t(generator_t&& right_) noexcept\n\t\t\t: _Coro(right_._Coro)\n\t\t{\n\t\t\tright_._Coro = nullptr;\n\t\t}\n\n\t\tgenerator_t& operator=(generator_t&& right_) noexcept\n\t\t{\n\t\t\tif (this != std::addressof(right_)) {\n\t\t\t\t_Coro = right_._Coro;\n\t\t\t\tright_._Coro = nullptr;\n\t\t\t}\n\t\t\treturn *this;\n\t\t}\n\n\t\t~generator_t()\n\t\t{\n\t\t\tif (_Coro) {\n\t\t\t\t_Coro.destroy();\n\t\t\t}\n\t\t}\n\n\t\tstate_type* detach_state()\n\t\t{\n\t\t\tauto t = _Coro;\n\t\t\t_Coro = nullptr;\n\t\t\treturn t.promise().get_state();\n\t\t}\n\n\tprivate:\n\t\tcoroutine_handle<promise_type> _Coro = nullptr;\n\t};\n\n} // namespace librf\n\n#pragma pop_macro(\"new\")\n#pragma pack(pop)\n"
  },
  {
    "path": "include/librf/src/intrusive_link_queue.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\ttemplate<class _Node, class _Nextptr = _Node*>\n\tstruct intrusive_link_node\n\t{\n\tprivate:\n\t\t_Node* _prev;\n\t\t_Nextptr _next;\n\n\t\ttemplate<class _Node2, class _Nodeptr2>\n\t\tfriend struct intrusive_link_queue;\n\t};\n\n#define _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE 1\n\n\ttemplate<class _Node, class _Nodeptr = _Node*>\n\tstruct intrusive_link_queue\n\t{\n\t\tusing node_type = _Node;\n\t\tusing node_ptr_type = _Nodeptr;\n\tpublic:\n\t\tintrusive_link_queue();\n\n\t\tintrusive_link_queue(const intrusive_link_queue&) = delete;\n\t\tintrusive_link_queue(intrusive_link_queue&&) = default;\n\t\tintrusive_link_queue& operator =(const intrusive_link_queue&) = delete;\n\t\tintrusive_link_queue& operator =(intrusive_link_queue&&) = default;\n\n\t\tstd::size_t size() const noexcept;\n\t\tbool empty() const noexcept;\n\t\tvoid push_back(node_ptr_type node) noexcept;\n\t\tvoid push_front(node_ptr_type node) noexcept;\n\t\tvoid erase(node_ptr_type node) noexcept;\n\t\tauto try_pop() noexcept->node_ptr_type;\n\tprivate:\n#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\tstd::atomic<std::size_t> _count;\n#endif\n\t\tnode_ptr_type _header;\n\t\tnode_ptr_type _tailer;\n\t};\n\n\ttemplate<class _Node, class _Nodeptr>\n\tintrusive_link_queue<_Node, _Nodeptr>::intrusive_link_queue()\n#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\t: _count(0)\n#endif\n\t\t, _header(nullptr)\n\t\t, _tailer(nullptr)\n\t{\n\t}\n\n\ttemplate<class _Node, class _Nodeptr>\n\tstd::size_t intrusive_link_queue<_Node, _Nodeptr>::size() const noexcept\n\t{\n#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\treturn _count.load(std::memory_order_acquire);\n#else\n\t\tstd::size_t count = 0;\n\t\tfor (node_type* node = _header; node != nullptr; node = node->next)\n\t\t\t++count;\n\t\treturn count;\n#endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t}\n\n\ttemplate<class _Node, class _Nodeptr>\n\tbool intrusive_link_queue<_Node, _Nodeptr>::empty() const noexcept\n\t{\n\t\treturn _header == nullptr;\n\t}\n\n\ttemplate<class _Node, class _Nodeptr>\n\tvoid intrusive_link_queue<_Node, _Nodeptr>::push_back(node_ptr_type e) noexcept\n\t{\n\t\tassert(e != nullptr);\n\n\t\te->_prev = _tailer;\n\t\te->_next = nullptr;\n\n\t\tif (!_header)\n\t\t\t_header = e;\n\n\t\tif (_tailer)\n\t\t\t_tailer->_next = e;\n\t\t_tailer = e;\n\n#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\t_count.fetch_add(1, std::memory_order_acq_rel);\n#endif\n\t}\n\n\ttemplate<class _Node, class _Nodeptr>\n\tvoid intrusive_link_queue<_Node, _Nodeptr>::push_front(node_ptr_type e) noexcept\n\t{\n\t\tassert(e != nullptr);\n\n\t\te->_prev = nullptr;\n\t\te->_next = _header;\n\n\t\tif (_header)\n\t\t\t_header->_prev = e;\n\t\t_header = e;\n\n\t\tif (!_tailer)\n\t\t\t_tailer = e;\n\n#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\t_count.fetch_add(1, std::memory_order_acq_rel);\n#endif\n\t}\n\n\ttemplate<class _Node, class _Nodeptr>\n\tvoid intrusive_link_queue<_Node, _Nodeptr>::erase(node_ptr_type e) noexcept\n\t{\n\t\tassert(e != nullptr);\n\n\t\tif (_header == e)\n\t\t\t_header = e->_next;\n\t\tif (_tailer == e)\n\t\t\t_tailer = e->_prev;\n\n\t\tif (e->_next)\n\t\t\te->_next->_prev = e->_prev;\n\t\tif (e->_prev)\n\t\t\te->_prev->_next = e->_next;\n\n\t\te->_prev = nullptr;\n\t\te->_next = nullptr;\n\n#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\t_count.fetch_sub(1, std::memory_order_acq_rel);\n#endif\n\t}\n\n\ttemplate<class _Node, class _Nodeptr>\n\tauto intrusive_link_queue<_Node, _Nodeptr>::try_pop() noexcept->node_ptr_type\n\t{\n\t\tif (_header == nullptr)\n\t\t\treturn nullptr;\n\n\t\tnode_ptr_type node = _header;\n\t\t_header = node->_next;\n\t\tif (_header != nullptr)\n\t\t\t_header->_prev = nullptr;\n\n\t\tif (_tailer == node)\n\t\t{\n\t\t\tassert(node->_next == nullptr);\n\t\t\t_tailer = nullptr;\n\t\t}\n\n\t\tnode->_prev = nullptr;\n\t\tnode->_next = nullptr;\n\n#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\t_count.fetch_sub(1, std::memory_order_acq_rel);\n#endif\n\n\t\treturn node;\n\t}\n}"
  },
  {
    "path": "include/librf/src/macro_def.inl",
    "content": "﻿#pragma once\n\n#ifndef _offset_of\n#define _offset_of(c, m) reinterpret_cast<size_t>(&static_cast<c *>(0)->m)\n#endif\n\n#define co_yield_void co_yield nullptr\n#define co_return_void co_return nullptr\n\n#if !defined(_DISABLE_RESUMEF_GO_MACRO)\n#define go (*::librf::this_scheduler()) + \n#define GO (*::librf::this_scheduler()) + [=]()mutable->librf::future_t<>\n#endif\n\n#define librf_current_scheduler() (co_await ::librf::get_current_scheduler())\n#define librf_root_state() (co_await ::librf::get_root_state())\n#define librf_current_task() (co_await ::librf::get_current_task())\n\n#if defined(__clang__) || defined(__GNUC__)\n#ifndef likely\n#define likely(x) __builtin_expect(!!(x), 1)\n#endif // likely\n#ifndef unlikely\n#define unlikely(x) __builtin_expect(!!(x), 0)\n#endif // unlikely\n#else  // defined(__clang__) || defined(__GNUC__)\n#ifndef likely\n#define likely(x) x\n#endif // likely\n#ifndef unlikely\n#define unlikely(x) x\n#endif // unlikely\n#endif // defined(__clang__) || defined(__GNUC__)\n\n#ifndef LIBRF_USE_STATIC_LIBRARY\n#  if _WIN32\n#    ifdef LIBRF_DYNAMIC_EXPORTS\n#      define LIBRF_API __declspec(dllexport)\n#    else //RESUMEF_DYNAMIC_EXPORTS\n#      define LIBRF_API __declspec(dllimport)\n#    endif //RESUMEF_DYNAMIC_EXPORTS\n#  else //_WIN32\n#    define LIBRF_API __attribute__((visibility(\"default\")))\n#  endif //_WIN32\n#else //RESUMEF_USE_SHARD_LIBRARY\n#  define LIBRF_API\n#endif //RESUMEF_USE_SHARD_LIBRARY\n"
  },
  {
    "path": "include/librf/src/mutex.h",
    "content": "﻿#pragma once\n\n#include \"mutex_v2.h\"\n#include \"mutex_v2.inl\"\n"
  },
  {
    "path": "include/librf/src/mutex_v2.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n#ifndef DOXYGEN_SKIP_PROPERTY\n\tnamespace detail\n\t{\n\t\tstruct mutex_v2_impl;\n\t}\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\n\t/**\n\t\t* @brief 提示手工解锁，故相关的lock()函数不再返回batch_unlock_t。\n\t\t*/\n\tstruct adopt_manual_unlock_t{};\n\n\t/**\n\t\t* @brief 提示手工解锁，故相关的lock()函数不再返回batch_unlock_t。\n\t\t*/\n\tconstexpr adopt_manual_unlock_t adopt_manual_unlock;\n\n\t/**\n\t\t* @brief 在析构的时候自动解锁mutex_t的辅助类。\n\t\t*/\n\ttemplate<class... _Mtxs>\n\tstruct batch_unlock_t;\n\n\t/**\n\t\t* @brief 支持递归的锁。\n\t\t* @details 锁被本协程所在的跟协程所拥有。支持在跟协程下的所有协程里递归加锁。\n\t\t*/\n\tstruct mutex_t\n\t{\n\t\tbool is_locked() const;\n\n\t\tstruct lock_awaiter;\n\t\tstruct [[nodiscard]] awaiter;\n\t\tstruct [[nodiscard]] manual_awaiter;\n\n\t\t/**\n\t\t\t* @brief 在协程中加锁，如果不能立即获得锁，则阻塞当前协程。但不会阻塞当前线程。\n\t\t\t* @return [co_await] batch_unlock_t\n\t\t\t*/\n\t\tawaiter/*batch_unlock_t*/ lock() const noexcept;\n\t\t\t\n\t\t/**\n\t\t\t* @brief 在协程中加锁。\n\t\t\t* @see 等同调用 co_await lock()。\n\t\t\t* @return [co_await] batch_unlock_t\n\t\t\t*/\n\t\tawaiter/*batch_unlock_t*/ operator co_await() const noexcept;\n\n\t\t/**\n\t\t\t* @brief 在协程中加锁，如果不能立即获得锁，则阻塞当前协程。但不会阻塞当前线程。\n\t\t\t* @details 需要随后调用unlock()函数解锁。lock()/unlock()调用必须在同一个跟协程下配对调用。\n\t\t\t* @param manual_unlock_tag 提示手工解锁\n\t\t\t* @return [co_await] void\n\t\t\t*/\n\t\tmanual_awaiter/*void*/ lock(adopt_manual_unlock_t manual_unlock_tag) const noexcept;\n\n\n\t\tstruct [[nodiscard]] try_awaiter;\n\n\t\t/**\n\t\t\t* @brief 尝试在协程中加锁。此操作无论成功与否都会立即返回，不会有协程切换。\n\t\t\t* @details 如果加锁成功，则需要调用co_await unlock()解锁。或者使用unlock(librf_root_state())解锁。\\n\n\t\t\t* 如果加锁失败，且要循环尝试加锁，则最好调用co_await yield()让出一次调度。否则，可能造成本调度器死循环。\n\t\t\t* @return [co_await] bool\n\t\t\t*/\n\t\ttry_awaiter/*bool*/ try_lock() const noexcept;\n\n\t\tstruct [[nodiscard]] unlock_awaiter;\n\n\t\t/**\n\t\t\t* @brief 在协程中解锁。此操作立即返回，不会有协程切换。\n\t\t\t* @return [co_await] void\n\t\t\t*/\n\t\tunlock_awaiter/*void*/ unlock() const noexcept;\n\n\n\t\tstruct [[nodiscard]] timeout_awaiter;\n\n\t\t/**\n\t\t\t* @brief 在协程中尝试加锁，直到超时。如果不能立即获得锁，则阻塞当前协程。但不会阻塞当前线程。\n\t\t\t* @param dt 超时时长\n\t\t\t* @return [co_await] bool\n\t\t\t*/\n\t\ttemplate <class _Rep, class _Period>\n\t\ttimeout_awaiter/*bool*/ try_lock_for(const std::chrono::duration<_Rep, _Period>& dt) const noexcept;\n\n\t\t/**\n\t\t\t* @brief 在协程中尝试加锁，直到超时。如果不能立即获得锁，则阻塞当前协程。但不会阻塞当前线程。\n\t\t\t* @param tp 超时时刻\n\t\t\t* @return [co_await] bool\n\t\t\t*/\n\t\ttemplate <class _Rep, class _Period>\n\t\ttimeout_awaiter/*bool*/ try_lock_until(const std::chrono::time_point<_Rep, _Period>& tp) const noexcept;\n\n\n\t\t/**\n\t\t\t* @brief 在非协程中加锁。如果不能立即获得锁，则反复尝试，直到获得锁。故会阻塞当前协程\n\t\t\t* @param unique_address 代表获得锁的拥有者。此地址应当与随后的unlock()的地址一致。\\n\n\t\t\t* 一般做法，是申明一个跟当前线程关联的局部变量，以此局部变量的地址为参数。\n\t\t\t*/\n\t\tvoid lock(void* unique_address) const;\n\n\t\t/**\n\t\t\t* @brief 尝试在非协程中加锁。此操作无论成功与否都会立即返回。\n\t\t\t* @param unique_address 代表获得锁的拥有者。\n\t\t\t*/\n\t\tbool try_lock(void* unique_address) const;\n\n\t\t/**\n\t\t\t* @brief 尝试在非协程中加锁，直到超时。如果不能立即获得锁，则反复尝试，直到获得锁或超时。故会阻塞当前协程\n\t\t\t* @param dt 超时时长\n\t\t\t* @param unique_address 代表获得锁的拥有者。\n\t\t\t*/\n\t\ttemplate <class _Rep, class _Period>\n\t\tbool try_lock_for(const std::chrono::duration<_Rep, _Period>& dt, void* unique_address);\n\n\t\t/**\n\t\t\t* @brief 尝试在非协程中加锁，直到超时。如果不能立即获得锁，则反复尝试，直到获得锁或超时。故会阻塞当前协程\n\t\t\t* @param tp 超时时刻\n\t\t\t* @param unique_address 代表获得锁的拥有者。\n\t\t\t*/\n\t\ttemplate <class _Rep, class _Period>\n\t\tbool try_lock_until(const std::chrono::time_point<_Rep, _Period>& tp, void* unique_address);\n\n\t\t/**\n\t\t\t* @brief 在非协程中解锁。立即返回。由于立即返回，也可在协程中如此使用：mtx.unlock(librf_root_state())\n\t\t\t* @param unique_address 代表获得锁的拥有者。\n\t\t\t*/\n\t\tvoid unlock(void* unique_address) const;\n\n\n\t\t/**\n\t\t\t* @brief 在协程中，无死锁的批量加锁。不会阻塞当前线程。直到获得所有锁之前，会阻塞当前协程。\n\t\t\t* @param mtxs... 需要获得的锁列表。\n\t\t\t* @return [co_await] batch_unlock_t\n\t\t\t*/\n\t\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\t\tstatic future_t<batch_unlock_t<_Mtxs...>> lock(_Mtxs&... mtxs);\n\n\t\t/**\n\t\t\t* @brief 在协程中，无死锁的批量加锁。不会阻塞当前线程。直到获得所有锁之前，会阻塞当前协程。\n\t\t\t* @param manual_unlock_tag 提示手工解锁\n\t\t\t* @param mtxs... 需要获得的锁列表。\n\t\t\t* @return [co_await] void\n\t\t\t*/\n\t\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\t\tstatic future_t<> lock(adopt_manual_unlock_t manual_unlock_tag, _Mtxs&... mtxs);\n\n\t\t/**\n\t\t\t* @brief 在协程中批量解锁。如果可能，使用unlock(librf_root_state(), mtxs...)来替代。\n\t\t\t* @param mtxs... 需要解锁的锁列表。\n\t\t\t* @return [co_await] void\n\t\t\t*/\n\t\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\t\tstatic future_t<> unlock(_Mtxs&... mtxs);\n\n\n\t\t/**\n\t\t\t* @brief 在非协程中，无死锁的批量加锁。会阻塞当前线程，直到获得所有锁为止。\n\t\t\t* @param unique_address 代表获得锁的拥有者。\n\t\t\t* @param mtxs... 需要获得的锁列表。\n\t\t\t* @return batch_unlock_t\n\t\t\t*/\n\t\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\t\tstatic batch_unlock_t<_Mtxs...> lock(void* unique_address, _Mtxs&... mtxs);\n\n\t\t/**\n\t\t\t* @brief 在非协程中，无死锁的批量加锁。会阻塞当前线程，直到获得所有锁为止。\n\t\t\t* @param manual_unlock_tag 提示手工解锁\n\t\t\t* @param unique_address 代表获得锁的拥有者。\n\t\t\t* @param mtxs... 需要获得的锁列表。\n\t\t\t*/\n\t\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\t\tstatic void lock(adopt_manual_unlock_t manual_unlock_tag, void* unique_address, _Mtxs&... mtxs);\n\n\t\t/**\n\t\t\t* @brief 在非协程中批量解锁。立即返回。由于立即返回，也可在协程中如此使用：unlock(librf_root_state(), mtxs...)\n\t\t\t* @param unique_address 代表获得锁的拥有者。\n\t\t\t* @param mtxs... 需要解锁的锁列表。\n\t\t\t*/\n\t\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\t\tstatic void unlock(void* unique_address, _Mtxs&... mtxs);\n\n\t\tLIBRF_API mutex_t();\n\n\t\t/**\n\t\t\t* @brief 构造一个无效的mutex_t。\n\t\t\t*/\n\t\tLIBRF_API mutex_t(std::adopt_lock_t) noexcept;\n\t\tLIBRF_API ~mutex_t() noexcept;\n\n\t\tmutex_t(const mutex_t&) = default;\n\t\tmutex_t(mutex_t&&) = default;\n\t\tmutex_t& operator = (const mutex_t&) = default;\n\t\tmutex_t& operator = (mutex_t&&) = default;\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\t\ttypedef std::shared_ptr<detail::mutex_v2_impl> mutex_impl_ptr;\n\t\ttypedef std::chrono::system_clock clock_type;\n\tprivate:\n\t\tstruct _MutexAwaitAssembleT;\n\n\t\ttemplate<class... _Mtxs> friend struct batch_unlock_t;\n\n\t\tmutex_impl_ptr _mutex;\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\t};\n}\n"
  },
  {
    "path": "include/librf/src/mutex_v2.inl",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\tnamespace detail\n\t{\n\t\tstruct state_mutex_t : public state_base_t\n\t\t{\n\t\t\tstate_mutex_t(mutex_v2_impl*& val)\n\t\t\t\t: _value(&val)\n\t\t\t{}\n\n\t\t\tLIBRF_API virtual void resume() override;\n\t\t\tLIBRF_API virtual bool has_handler() const  noexcept override;\n\t\t\tLIBRF_API virtual state_base_t* get_parent() const noexcept override;\n\n\t\t\tLIBRF_API void on_cancel() noexcept;\n\t\t\tLIBRF_API bool on_notify(mutex_v2_impl* eptr);\n\t\t\tLIBRF_API bool on_timeout();\n\n\t\t\tLIBRF_API void add_timeout_timer(std::chrono::system_clock::time_point tp);\n\n\t\t\tinline void on_await_suspend(coroutine_handle<> handler, scheduler_t* sch, state_base_t* root) noexcept\n\t\t\t{\n\t\t\t\tthis->_scheduler = sch;\n\t\t\t\tthis->_coro = handler;\n\t\t\t\tthis->_root = root;\n\t\t\t}\n\t\tprotected:\n\t\t\ttimer_handler _thandler;\n\t\t\tstate_base_t* _root;\n\t\t\tstd::atomic<mutex_v2_impl**> _value;\n\t\t};\n\n\t\tstruct mutex_v2_impl : public std::enable_shared_from_this<mutex_v2_impl>\n\t\t{\n\t\t\tusing clock_type = std::chrono::system_clock;\n\n\t\t\tmutex_v2_impl() {}\n\n\t\t\tinline void* owner() noexcept\n\t\t\t{\n\t\t\t\tscoped_lock<lock_type> lock_(_lock);\n\t\t\t\treturn _owner.load(std::memory_order_relaxed);\n\t\t\t}\n\n\t\t\tLIBRF_API bool try_lock(void* sch);\t\t\t\t\t\t//内部加锁\n\t\t\tLIBRF_API bool try_lock_until(clock_type::time_point tp, void* sch);\t//内部加锁\n\t\t\tLIBRF_API bool unlock(void* sch);\t\t\t\t\t\t//内部加锁\n\t\t\tLIBRF_API void lock_until_succeed(void* sch);\t\t\t//内部加锁\n\t\tpublic:\n\t\t\tstatic constexpr bool USE_SPINLOCK = true;\n\n\t\t\tusing lock_type = std::conditional_t<USE_SPINLOCK, spinlock, std::recursive_mutex>;\n\t\t\tusing state_mutex_ptr = counted_ptr<state_mutex_t>;\n\t\t\tusing wait_queue_type = std::list<state_mutex_ptr>;\n\n\t\t\tLIBRF_API bool try_lock_lockless(void* sch) noexcept;\t\t\t//内部不加锁，加锁由外部来进行\n\t\t\tLIBRF_API void add_wait_list_lockless(state_mutex_t* state);\t//内部不加锁，加锁由外部来进行\n\n\t\t\tlock_type _lock;\t\t\t\t\t\t\t\t\t//保证访问本对象是线程安全的\n\t\tprivate:\n\t\t\tstd::atomic<void*> _owner = nullptr;\t\t\t\t//锁标记\n\t\t\tstd::atomic<intptr_t> _counter = 0;\t\t\t\t\t//递归锁的次数\n\t\t\twait_queue_type _wait_awakes;\t\t\t\t\t\t//等待队列\n\n\t\t\t// No copying/moving\n\t\t\tmutex_v2_impl(const mutex_v2_impl&) = delete;\n\t\t\tmutex_v2_impl(mutex_v2_impl&&) = delete;\n\t\t\tmutex_v2_impl& operator=(const mutex_v2_impl&) = delete;\n\t\t\tmutex_v2_impl& operator=(mutex_v2_impl&&) = delete;\n\t\t};\n\n\t\tstruct _MutexAddressAssembleT\n\t\t{\n\t\tprivate:\n\t\t\tvoid* _Address;\n\t\tpublic:\n\t\t\tstd::vector<mutex_t> _mutex;\n\n\t\t\ttemplate<class... _Mtxs>\n\t\t\t_MutexAddressAssembleT(void* unique_address, _Mtxs&... mtxs)\n\t\t\t\t: _Address(unique_address)\n\t\t\t\t, _mutex({ mtxs... })\n\t\t\t{}\n\t\t\tsize_t size() const\n\t\t\t{\n\t\t\t\treturn _mutex.size();\n\t\t\t}\n\t\t\tmutex_t& operator[](int _Idx)\n\t\t\t{\n\t\t\t\treturn _mutex[_Idx];\n\t\t\t}\n\t\t\tvoid _Lock_ref(mutex_t& _LkN) const\n\t\t\t{\n\t\t\t\treturn _LkN.lock(_Address);\n\t\t\t}\n\t\t\tbool _Try_lock_ref(mutex_t& _LkN) const\n\t\t\t{\n\t\t\t\treturn _LkN.try_lock(_Address);\n\t\t\t}\n\t\t\tvoid _Unlock_ref(mutex_t& _LkN) const\n\t\t\t{\n\t\t\t\t_LkN.unlock(_Address);\n\t\t\t}\n\t\t\tvoid _Yield() const\n\t\t\t{\n\t\t\t\tstd::this_thread::yield();\n\t\t\t}\n\t\t\tvoid _ReturnValue() const noexcept {}\n\t\t\ttemplate<class U>\n\t\t\tU _ReturnValue(U v) const noexcept\n\t\t\t{\n\t\t\t\treturn v;\n\t\t\t}\n\t\t};\n\n#define LOCK_ASSEMBLE_NAME(fnName) mutex_lock_await_##fnName\n#define LOCK_ASSEMBLE_AWAIT(a) co_await (a)\n#define LOCK_ASSEMBLE_RETURN(a) co_return (a)\n#include \"without_deadlock_assemble.inl\"\n#undef LOCK_ASSEMBLE_NAME\n#undef LOCK_ASSEMBLE_AWAIT\n#undef LOCK_ASSEMBLE_RETURN\n\t}\n\n\ttemplate<>\n\tstruct [[nodiscard]] batch_unlock_t<mutex_t>\n\t{\n\t\ttypedef std::shared_ptr<detail::mutex_v2_impl> mutex_impl_ptr;\n\n\t\tbatch_unlock_t()\n\t\t\t: _owner(nullptr)\n\t\t{}\n\n\t\t//此函数，应该在try_lock()获得锁后使用\n\t\t//或者在协程里，由awaiter使用\n\t\tbatch_unlock_t(std::adopt_lock_t, void* sch, mutex_impl_ptr mtx)\n\t\t\t: _mutex(std::move(mtx))\n\t\t\t, _owner(sch)\n\t\t{}\n\n\t\tbatch_unlock_t(std::adopt_lock_t, void* sch, const mutex_t& mtx)\n\t\t\t: batch_unlock_t(std::adopt_lock, sch, mtx._mutex)\n\t\t{}\n\n/*\n\t\t//此函数，适合在非协程里使用\n\t\tbatch_unlock_t(void* sch, mutex_impl_ptr mtx)\n\t\t\t: _mutex(std::move(mtx))\n\t\t\t, _owner(sch)\n\t\t{\n\t\t\tif (_mutex != nullptr)\n\t\t\t\t_mutex->lock_until_succeed(sch);\n\t\t}\n\n\t\tbatch_unlock_t(void* sch, const mutex_t& mtx)\n\t\t\t: batch_unlock_t(sch, mtx._mutex)\n\t\t{}\n*/\n\n\t\t~batch_unlock_t()\n\t\t{\n\t\t\tif (_mutex != nullptr)\n\t\t\t\t_mutex->unlock(_owner);\n\t\t}\n\n\t\tinline void unlock() noexcept\n\t\t{\n\t\t\tif (_mutex != nullptr)\n\t\t\t{\n\t\t\t\t_mutex->unlock(_owner);\n\t\t\t\t_mutex = nullptr;\n\t\t\t}\n\t\t}\n\n\t\tbatch_unlock_t(const batch_unlock_t&) = delete;\n\t\tbatch_unlock_t& operator = (const batch_unlock_t&) = delete;\n\t\tbatch_unlock_t(batch_unlock_t&& _Right) = default;\n\t\tbatch_unlock_t& operator = (batch_unlock_t&& _Right) = default;\n\tprivate:\n\t\tmutex_impl_ptr _mutex;\n\t\tvoid* _owner;\n\t};\n\n\tstruct mutex_t::lock_awaiter\n\t{\n\t\tlock_awaiter(detail::mutex_v2_impl* mtx) noexcept\n\t\t\t: _mutex(mtx)\n\t\t{\n\t\t\tassert(_mutex != nullptr);\n\t\t}\n\n\t\t~lock_awaiter() noexcept(false)\n\t\t{\n\t\t\tassert(_mutex == nullptr);\n\t\t\tif (_mutex != nullptr)\n\t\t\t{\n\t\t\t\tthrow mutex_exception(error_code::not_await_lock);\n\t\t\t}\n\t\t}\n\n\t\tbool await_ready() noexcept\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\ttemplate<class _PromiseT, class _Timeout> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend2(coroutine_handle<_PromiseT> handler, const _Timeout& cb)\n\t\t{\n\t\t\t(void)cb;\n\t\t\t_PromiseT& promise = handler.promise();\n\t\t\tauto* parent = promise.get_state();\n\t\t\t_root = parent->get_root();\n\t\t\tassert(_root != nullptr);\n\t\t\tassert(_root->get_parent() == nullptr);\n\n\t\t\tscoped_lock<detail::mutex_v2_impl::lock_type> lock_(_mutex->_lock);\n\t\t\tif (_mutex->try_lock_lockless(_root))\n\t\t\t\treturn false;\n\n\t\t\t_state = new detail::state_mutex_t(_mutex);\n\t\t\t_state->on_await_suspend(handler, parent->get_scheduler(), _root);\n\n\t\t\tif constexpr (!std::is_same_v<std::remove_reference_t<_Timeout>, std::nullptr_t>)\n\t\t\t\tcb();\n\n\t\t\t_mutex->add_wait_list_lockless(_state.get());\n\n\t\t\treturn true;\n\t\t}\n\tprotected:\n\t\tdetail::mutex_v2_impl* _mutex;\n\t\tcounted_ptr<detail::state_mutex_t> _state;\n\t\tstate_base_t* _root = nullptr;\n\t};\n\n\tstruct [[nodiscard]] mutex_t::awaiter : public lock_awaiter\n\t{\n\t\tusing lock_awaiter::lock_awaiter;\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\treturn await_suspend2(handler, nullptr);\n\t\t}\n\t\tbatch_unlock_t<mutex_t> await_resume() noexcept\n\t\t{\n\t\t\tmutex_impl_ptr mtx = _mutex ? _mutex->shared_from_this() : nullptr;\n\t\t\t_mutex = nullptr;\n\n\t\t\treturn { std::adopt_lock, _root, mtx };\n\t\t}\n\t};\n\n\tinline mutex_t::awaiter mutex_t::operator co_await() const noexcept\n\t{\n\t\treturn { _mutex.get() };\n\t}\n\n\tinline mutex_t::awaiter mutex_t::lock() const noexcept\n\t{\n\t\treturn { _mutex.get() };\n\t}\n\n\tstruct mutex_t::manual_awaiter : public lock_awaiter\n\t{\n\t\tusing lock_awaiter::lock_awaiter;\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\treturn await_suspend2(handler, nullptr);\n\t\t}\n\t\tvoid await_resume() noexcept\n\t\t{\n\t\t\t_mutex = nullptr;\n\t\t}\n\t};\n\n\tinline mutex_t::manual_awaiter mutex_t::lock(adopt_manual_unlock_t) const noexcept\n\t{\n\t\treturn { _mutex.get() };\n\t}\n\n\tinline bool mutex_t::is_locked() const\n\t{\n\t\treturn _mutex->owner() != nullptr;\n\t}\n\n\tstruct [[nodiscard]] mutex_t::try_awaiter\n\t{\n\t\ttry_awaiter(detail::mutex_v2_impl* mtx) noexcept\n\t\t\t: _mutex(mtx)\n\t\t{\n\t\t\tassert(_mutex != nullptr);\n\t\t}\n\t\t~try_awaiter() noexcept(false)\n\t\t{\n\t\t\tassert(_mutex == nullptr);\n\t\t\tif (_mutex != nullptr)\n\t\t\t{\n\t\t\t\tthrow mutex_exception(error_code::not_await_lock);\n\t\t\t}\n\t\t}\n\n\t\tbool await_ready() noexcept\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\t_PromiseT& promise = handler.promise();\n\t\t\tauto* parent = promise.get_state();\n\t\t\tif (!_mutex->try_lock(parent->get_root()))\n\t\t\t\t_mutex = nullptr;\n\n\t\t\treturn false;\n\t\t}\n\n\t\tbool await_resume() noexcept\n\t\t{\n\t\t\tdetail::mutex_v2_impl* mtx = _mutex;\n\t\t\t_mutex = nullptr;\n\t\t\treturn mtx != nullptr;\n\t\t}\n\tprotected:\n\t\tdetail::mutex_v2_impl* _mutex;\n\t};\n\n\tinline mutex_t::try_awaiter mutex_t::try_lock() const noexcept\n\t{\n\t\treturn { _mutex.get() };\n\t}\n\n\tstruct [[nodiscard]] mutex_t::unlock_awaiter\n\t{\n\t\tunlock_awaiter(detail::mutex_v2_impl* mtx) noexcept\n\t\t\t: _mutex(mtx)\n\t\t{\n\t\t\tassert(_mutex != nullptr);\n\t\t}\n\t\t~unlock_awaiter() noexcept(false)\n\t\t{\n\t\t\tassert(_mutex == nullptr);\n\t\t\tif (_mutex != nullptr)\n\t\t\t{\n\t\t\t\tthrow mutex_exception(error_code::not_await_lock);\n\t\t\t}\n\t\t}\n\n\t\tbool await_ready() noexcept\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\t_PromiseT& promise = handler.promise();\n\t\t\tauto* parent = promise.get_state();\n\t\t\t_mutex->unlock(parent->get_root());\n\n\t\t\treturn false;\n\t\t}\n\n\t\tvoid await_resume() noexcept\n\t\t{\n\t\t\t_mutex = nullptr;\n\t\t}\n\tprotected:\n\t\tdetail::mutex_v2_impl* _mutex;\n\t};\n\n\tinline mutex_t::unlock_awaiter mutex_t::unlock() const noexcept\n\t{\n\t\treturn { _mutex.get() };\n\t}\n\n\n\n\n\tstruct [[nodiscard]] mutex_t::timeout_awaiter : public event_t::timeout_awaitor_impl<lock_awaiter>\n\t{\n\t\tusing event_t::timeout_awaitor_impl<lock_awaiter>::timeout_awaitor_impl;\n\t\tbool await_resume() noexcept\n\t\t{\n\t\t\tdetail::mutex_v2_impl* mtx = this->_mutex;\n\t\t\tthis->_mutex = nullptr;\n\t\t\treturn mtx != nullptr;\n\t\t}\n\t};\n\n\ttemplate <class _Rep, class _Period>\n\tinline mutex_t::timeout_awaiter mutex_t::try_lock_until(const std::chrono::time_point<_Rep, _Period>& tp) const noexcept\n\t{\n\t\treturn { tp, _mutex.get() };\n\t}\n\n\ttemplate <class _Rep, class _Period>\n\tinline mutex_t::timeout_awaiter mutex_t::try_lock_for(const std::chrono::duration<_Rep, _Period>& dt) const noexcept\n\t{\n\t\tauto tp = clock_type::now() + std::chrono::duration_cast<clock_type::duration>(dt);\n\t\treturn { tp, _mutex.get() };\n\t}\n\n\n\tinline void mutex_t::lock(void* unique_address) const\n\t{\n\t\tassert(unique_address != nullptr);\n\t\t_mutex->lock_until_succeed(unique_address);\n\t}\n\n\tinline bool mutex_t::try_lock(void* unique_address) const\n\t{\n\t\tassert(unique_address != nullptr);\n\t\treturn _mutex->try_lock(unique_address);\n\t}\n\n\ttemplate <class _Rep, class _Period>\n\tinline bool mutex_t::try_lock_for(const std::chrono::duration<_Rep, _Period>& dt, void* unique_address)\n\t{\n\t\tassert(unique_address != nullptr);\n\t\treturn _mutex->try_lock_until(clock_type::now() + std::chrono::duration_cast<clock_type::duration>(dt), unique_address);\n\t}\n\n\ttemplate <class _Rep, class _Period>\n\tinline bool mutex_t::try_lock_until(const std::chrono::time_point<_Rep, _Period>& tp, void* unique_address)\n\t{\n\t\tassert(unique_address != nullptr);\n\t\treturn _mutex->try_lock_until(std::chrono::time_point_cast<clock_type::time_point>(tp), unique_address);\n\t}\n\n\tinline void mutex_t::unlock(void* unique_address) const\n\t{\n\t\tassert(unique_address != nullptr);\n\t\t_mutex->unlock(unique_address);\n\t}\n\n\n\n\tstruct mutex_t::_MutexAwaitAssembleT\n\t{\n\tpublic:\n\t\tstd::vector<mutex_t> _mutex;\n\t\tvoid* _owner;\n\n\t\ttemplate<class... _Mtxs>\n\t\t_MutexAwaitAssembleT(void* unique_address, _Mtxs&... mtxs)\n\t\t\t: _mutex({ mtxs... })\n\t\t\t, _owner(unique_address)\n\t\t{}\n\t\tsize_t size() const\n\t\t{\n\t\t\treturn _mutex.size();\n\t\t}\n\t\tmutex_t& operator[](int _Idx)\n\t\t{\n\t\t\treturn _mutex[_Idx];\n\t\t}\n\t\tauto _Lock_ref(mutex_t& _LkN) const\n\t\t{\n\t\t\treturn _LkN.lock(adopt_manual_unlock);\n\t\t}\n\t\tauto _Try_lock_ref(mutex_t& _LkN) const\n\t\t{\n\t\t\treturn _LkN.try_lock();\n\t\t}\n\t\tvoid _Unlock_ref(mutex_t& _LkN) const\n\t\t{\n\t\t\t_LkN.unlock(_owner);\n\t\t}\n\t\tfuture_t<> _Yield() const\n\t\t{\n\t\t\tfor (int cnt = rand() % (1 + _mutex.size()); cnt >= 0; --cnt)\n\t\t\t{\n\t\t\t\tstd::this_thread::yield();\t//还要考虑多线程里运行的情况\n\t\t\t\tco_await yield();\n\t\t\t}\n\t\t}\n\n\t\tfuture_t<> _ReturnValue() const;\n\t\ttemplate<class U>\n\t\tfuture_t<U> _ReturnValue(U v) const;\n\n\t\t_MutexAwaitAssembleT(const _MutexAwaitAssembleT&) = default;\n\t\t_MutexAwaitAssembleT& operator = (const _MutexAwaitAssembleT&) = default;\n\t\t_MutexAwaitAssembleT(_MutexAwaitAssembleT&& _Right) = default;\n\t\t_MutexAwaitAssembleT& operator = (_MutexAwaitAssembleT&& _Right) = default;\n\t};\n\n\ttemplate<class... _Mtxs>\n\tstruct batch_unlock_t\n\t{\n\t\tmutex_t::_MutexAwaitAssembleT _MAA;\n\n\t\ttemplate<class... U>\n\t\tbatch_unlock_t(std::adopt_lock_t, void* sch, U&&... mtxs)\n\t\t\t: _MAA(sch, std::forward<U>(mtxs)...)\n\t\t{}\n\n\t\t~batch_unlock_t()\n\t\t{\n\t\t\tif (_MAA._owner != nullptr)\n\t\t\t{\n\t\t\t\tfor (mutex_t& mtx : _MAA._mutex)\n\t\t\t\t\tmtx.unlock(_MAA._owner);\n\t\t\t}\n\t\t}\n\n\t\tinline void unlock() noexcept\n\t\t{\n\t\t\tif (_MAA._owner != nullptr)\n\t\t\t{\n\t\t\t\tfor (mutex_t& mtx : _MAA._mutex)\n\t\t\t\t\tmtx.unlock(_MAA._owner);\n\t\t\t\t_MAA._owner = nullptr;\n\t\t\t}\n\t\t}\n\n\t\tbatch_unlock_t(const batch_unlock_t&) = delete;\n\t\tbatch_unlock_t& operator = (const batch_unlock_t&) = delete;\n\t\tbatch_unlock_t(batch_unlock_t&& _Right) = default;\n\t\tbatch_unlock_t& operator = (batch_unlock_t&& _Right) = default;\n\t};\n\n\ttemplate<class... _Mtxs>\n\tbatch_unlock_t()->batch_unlock_t<_Mtxs...>;\n\n\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\tinline future_t<batch_unlock_t<_Mtxs...>> mutex_t::lock(_Mtxs&... mtxs)\n\t{\n\t\tbatch_unlock_t<_Mtxs...> unlock_guard{ std::adopt_lock, librf_root_state(), mtxs... };\n\t\tco_await detail::mutex_lock_await_lock_impl::_Lock_range(unlock_guard._MAA);\n\t\tco_return std::move(unlock_guard);\n\t}\n\n\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\tinline future_t<> mutex_t::lock(adopt_manual_unlock_t _noused, _Mtxs&... mtxs)\n\t{\n\t\t(void)_noused;\t//GCC: 这个参数不起一个名字，会导致GCC编译器内部错误。\n\t\tmutex_t::_MutexAwaitAssembleT _MAA{ librf_root_state(), mtxs... };\n\t\tco_await detail::mutex_lock_await_lock_impl::_Lock_range(_MAA);\n\t}\n\n\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\tinline future_t<> mutex_t::unlock(_Mtxs&... mtxs)\n\t{\n\t\tvoid* unique_address = librf_root_state();\n\n\t\t(mtxs.unlock(unique_address), ...);\n\t}\n\n\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\tinline batch_unlock_t<_Mtxs...> mutex_t::lock(void* unique_address, _Mtxs&... mtxs)\n\t{\n\t\tassert(unique_address != nullptr);\n\n\t\tdetail::_MutexAddressAssembleT _MAA{ unique_address, mtxs... };\n\t\tdetail::scoped_lock_range_lock_impl::_Lock_range(_MAA);\n\n\t\tbatch_unlock_t<_Mtxs...> su{ std::adopt_lock, unique_address };\n\t\tsu._MAA._mutex = std::move(_MAA._mutex);\n\t\treturn su;\n\t}\n\n\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\tinline void mutex_t::lock(adopt_manual_unlock_t, void* unique_address, _Mtxs&... mtxs)\n\t{\n\t\tassert(unique_address != nullptr);\n\n\t\tdetail::_MutexAddressAssembleT _MAA{ unique_address, mtxs... };\n\t\tdetail::scoped_lock_range_lock_impl::_Lock_range(_MAA);\n\t}\n\n\ttemplate<class... _Mtxs> requires(std::same_as<_Mtxs, mutex_t> && ...)\n\tinline void mutex_t::unlock(void* unique_address, _Mtxs&... mtxs)\n\t{\n\t\tassert(unique_address != nullptr);\n\n\t\t(mtxs.unlock(unique_address), ...);\n\t}\n}\n"
  },
  {
    "path": "include/librf/src/promise.h",
    "content": "﻿#pragma once\n\n#pragma push_macro(\"new\")\n#undef new\n\n#ifndef DOXYGEN_SKIP_PROPERTY\nnamespace librf\n{\n\t/*\n\tNote: the awaiter object is part of coroutine state (as a temporary whose lifetime crosses a suspension point)\n\tand is destroyed before the co_await expression finishes.\n\tIt can be used to maintain per-operation state as required by some async I/O APIs without resorting to additional heap allocations.\n\t*/\n\n\tstruct suspend_on_initial\n\t{\n\t\tinline bool await_ready() noexcept\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tinline void await_suspend(coroutine_handle<_PromiseT> handler) noexcept\n\t\t{\n\t\t\t_PromiseT& promise = handler.promise();\n\t\t\tauto* _state = promise.get_state();\n\t\t\t_state->promise_initial_suspend(handler);\n\t\t}\n\t\tinline void await_resume() noexcept\n\t\t{\n\t\t}\n\t};\n\n\tstruct suspend_on_final\n\t{\n\t\tinline bool await_ready() noexcept\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tinline void await_suspend(coroutine_handle<_PromiseT> handler) noexcept\n\t\t{\n\t\t\t_PromiseT& promise = handler.promise();\n\t\t\tauto _state = promise.ref_state();\n\t\t\t_state->promise_final_suspend(handler);\n\t\t}\n\t\tinline void await_resume() noexcept\n\t\t{\n\t\t}\n\t};\n\n\ttemplate <typename _Ty>\n\tstruct promise_impl_t\n\t{\n\t\tusing value_type = _Ty;\n\t\tusing state_type = state_t<value_type>;\n\t\tusing promise_type = promise_t<value_type>;\n\t\tusing future_type = future_t<value_type>;\n\n\t\tpromise_impl_t() noexcept = default;\n\t\tpromise_impl_t(promise_impl_t&& _Right) noexcept = default;\n\t\tpromise_impl_t& operator = (promise_impl_t&& _Right) noexcept = default;\n\t\tpromise_impl_t(const promise_impl_t&) = delete;\n\t\tpromise_impl_t& operator = (const promise_impl_t&) = delete;\n\n\t\tauto get_state() noexcept->state_type*;\n\t\t// 如果去掉了调度器，则ref_state()实现为返回counted_ptr<>，以便于处理一些意外情况\n\t\t// auto ref_state() noexcept->counted_ptr<state_type>;\n\t\tauto ref_state() noexcept->state_type*;\n\n\n\t\tfuture_type get_return_object() noexcept\n\t\t{\n\t\t\treturn { this->get_state() };\n\t\t}\n\n\t\tsuspend_on_initial initial_suspend() noexcept\n\t\t{\n\t\t\treturn {};\n\t\t}\n\n\t\tsuspend_on_final final_suspend() noexcept\n\t\t{\n\t\t\treturn {};\n\t\t}\n\n\t\ttemplate <typename _Uty>\n\t\t_Uty&& await_transform(_Uty&& _Whatever) noexcept\n\t\t{\n\t\t\tif constexpr (traits::has_state_v<_Uty>)\n\t\t\t{\n\t\t\t\t_Whatever._state->set_scheduler(get_state()->get_scheduler());\n\t\t\t}\n\n\t\t\treturn std::forward<_Uty>(_Whatever);\n\t\t}\n\n\t\tvoid unhandled_exception()\t\t//If the coroutine ends with an uncaught exception, it performs the following: \n\t\t{\n\t\t\tthis->ref_state()->set_exception(std::current_exception());\n\t\t}\n\n\t\tvoid cancellation_requested() noexcept\n\t\t{\n\n\t\t}\n\n\t\tusing _Alloc_char = std::allocator<char>;\n\t\tvoid* operator new(size_t _Size);\n\t\tvoid operator delete(void* _Ptr, size_t _Size);\n\tprivate:\n\t\tcounted_ptr<state_type> _state = state_future_t::_Alloc_state<state_type>(false);\n\t};\n\n\ttemplate<class _Ty>\n\tstruct promise_t final : public promise_impl_t<_Ty>\n\t{\n\t\tusing typename promise_impl_t<_Ty>::value_type;\n\t\tusing promise_impl_t<_Ty>::get_return_object;\n\n\t\ttemplate<class U>\n\t\tvoid return_value(U&& val)\t//co_return val\n\t\t{\n\t\t\tthis->ref_state()->set_value(std::forward<U>(val));\n\t\t}\n\n\t\ttemplate<class U>\n\t\tsuspend_always yield_value(U&& val)\n\t\t{\n\t\t\tthis->ref_state()->promise_yield_value(this, std::forward<U>(val));\n\t\t\treturn {};\n\t\t}\n\t};\n\n\ttemplate<class _Ty>\n\tstruct promise_t<_Ty&> final : public promise_impl_t<_Ty&>\n\t{\n\t\tusing typename promise_impl_t<_Ty&>::value_type;\n\t\tusing promise_impl_t<_Ty&>::get_return_object;\n\n\t\tvoid return_value(_Ty& val)\t//co_return val\n\t\t{\n\t\t\tthis->ref_state()->set_value(val);\n\t\t}\n\n\t\tsuspend_always yield_value(_Ty& val)\n\t\t{\n\t\t\tthis->ref_state()->promise_yield_value(this, val);\n\t\t\treturn {};\n\t\t}\n\t};\n\n\ttemplate<>\n\tstruct promise_t<void> final : public promise_impl_t<void>\n\t{\n\t\tusing promise_impl_t<void>::get_return_object;\n\n\t\tvoid return_void()\t\t\t//co_return;\n\t\t{\n\t\t\tthis->ref_state()->set_value();\n\t\t}\n\n\t\tsuspend_always yield_value()\n\t\t{\n\t\t\tthis->ref_state()->promise_yield_value(this);\n\t\t\treturn {};\n\t\t}\n\t};\n#endif\t//DOXYGEN_SKIP_PROPERTY\n}\n\n#pragma pop_macro(\"new\")\n"
  },
  {
    "path": "include/librf/src/promise.inl",
    "content": "﻿\nnamespace librf\n{\n\n/*\n\ttemplate<typename _Ty>\n\ttemplate<typename _Uty>\n\t_Uty&& promise_impl_t<_Ty>::await_transform(_Uty&& _Whatever) noexcept\n\t{\n\t\tif constexpr (traits::has_state_v<_Uty>)\n\t\t{\n\t\t\t_Whatever._state->set_scheduler(get_state()->get_scheduler());\n\t\t}\n\n\t\treturn std::forward<_Uty>(_Whatever);\n\t}\n*/\n\n\ttemplate <typename _Ty>\n\tauto promise_impl_t<_Ty>::get_state() noexcept -> state_type*\n\t{\n\t\treturn _state.get();\n\t}\n\n\t// 如果去掉了调度器，则ref_state()实现为返回counted_ptr<>，以便于处理一些意外情况\n\t//template <typename _Ty>\n\t//auto promise_impl_t<_Ty>::ref_state() noexcept -> counted_ptr<state_type>\n\t//{\n\t//\treturn { get_state() };\n\t//}\n\ttemplate <typename _Ty>\n\tauto promise_impl_t<_Ty>::ref_state() noexcept -> state_type*\n\t{\n\t\treturn get_state();\n\t}\n\n\ttemplate <typename _Ty>\n\tvoid* promise_impl_t<_Ty>::operator new(size_t _Size)\n\t{\n\t\t_Alloc_char _Al;\n\t\tchar* ptr = _Al.allocate(_Size);\n#if RESUMEF_DEBUG_COUNTER\n\t\tstd::cout << \"  future_promise::new, alloc size=\" << (_Size) << std::endl;\n\t\tstd::cout << \"  future_promise::new, alloc ptr=\" << (void*)ptr << std::endl;\n\t\tstd::cout << \"  future_promise::new, return ptr=\" << (void*)ptr << std::endl;\n#endif\n\t\treturn ptr;\n\t}\n\n\ttemplate <typename _Ty>\n\tvoid promise_impl_t<_Ty>::operator delete(void* _Ptr, size_t _Size)\n\t{\n\t\t_Alloc_char _Al;\n\t\treturn _Al.deallocate(reinterpret_cast<char*>(_Ptr), _Size);\n\t}\n}\n\n"
  },
  {
    "path": "include/librf/src/rf_task.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n#ifndef DOXYGEN_SKIP_PROPERTY\n\t/**\n\t * @brief 协程任务类。\n\t * @details 每启动一个新的协程，则对应一个协程任务类。\\n\n\t * 一方面，task_t用于标记协程是否执行完毕；\\n\n\t * 另一方面，对于通过函数对象(functor/lambda)启动的协程，有很大概率，此协程的内部变量，依赖此函数对象的生存期。\\n\n\t * tast_t的针对函数对象的特化版本，会持有此函数对象的拷贝，从而保证协程内部变量的生存期。这便于减少外部使用协程函数对象的工作量。\\n\n\t * 如果不希望task_t持有此函数对象，则通过调用此函数对象来启动协程，即:\\n\n\t * go functor; \\n\n\t * 替换为\\n\n\t * go functor();\n\t */\n\tstruct task_t\n\t{\n\t\tLIBRF_API task_t() noexcept;\n\t\tLIBRF_API virtual ~task_t();\n\n\t\t/**\n\t\t * @brief 获取stop_source，第一次获取时，会生成一个有效的stop_source。\n\t\t * @return stop_source\n\t\t */\n\t\tLIBRF_API const stop_source & get_stop_source();\n\n\t\t/**\n\t\t * @brief 获取一个跟stop_source绑定的，新的stop_token。\n\t\t * @return stop_token\n\t\t */\n\t\tstop_token get_stop_token()\n\t\t{\n\t\t\treturn get_stop_source().get_token();\n\t\t}\n\n\t\t/**\n\t\t * @brief 要求停止协程。\n\t\t * @return bool 返回操作成功与否。\n\t\t */\n\t\tbool request_stop()\n\t\t{\n\t\t\treturn get_stop_source().request_stop();\n\t\t}\n\n\t\t/**\n\t\t * @brief 要求停止协程。\n\t\t * @return bool 返回操作成功与否。\n\t\t */\n\t\tbool request_stop_if_possible() const\n\t\t{\n\t\t\tif (_stop.stop_possible())\n\t\t\t\treturn _stop.request_stop();\n\t\t\treturn false;\n\t\t}\n\tprotected:\n\t\tfriend scheduler_t;\n\t\tcounted_ptr<state_base_t> _state;\n\t\tstop_source _stop;\n\t};\n#endif\n\n\t//----------------------------------------------------------------------------------------------\n\n\ttemplate<class _Ty, class = std::void_t<>>\n\tstruct task_impl_t;\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\ttemplate<class _Ty>\n\tstruct task_impl_t<_Ty, std::void_t<traits::is_future<std::remove_reference_t<_Ty>>>> : public task_t\n\t{\n\t\tusing future_type = std::remove_reference_t<_Ty>;\n\t\tusing value_type = typename future_type::value_type;\n\t\tusing state_type = state_t<value_type>;\n\n\t\ttask_impl_t() noexcept = default;\n\t\ttask_impl_t(future_type& f)\n\t\t{\n\t\t\tinitialize(f);\n\t\t}\n\tprotected:\n\t\tvoid initialize(future_type& f)\n\t\t{\n\t\t\t_state = f._state.get();\n\t\t}\n\t};\n\n\ttemplate<class _Ty>\n\tstruct task_impl_t<generator_t<_Ty>> : public task_t\n\t{\n\t\tusing value_type = _Ty;\n\t\tusing future_type = generator_t<value_type>;\n\t\tusing state_type = state_generator_t;\n\n\t\ttask_impl_t() noexcept = default;\n\t\ttask_impl_t(future_type& f)\n\t\t{\n\t\t\tinitialize(f);\n\t\t}\n\tprotected:\n\t\tvoid initialize(future_type& f)\n\t\t{\n\t\t\t_state = f.detach_state();\n\t\t}\n\t};\n\n\t//----------------------------------------------------------------------------------------------\n\n\t//ctx_task_t接受的是一个'函数对象'\n\t//这个'函数对象'被调用后，返回generator<_Ty>/future_t<_Ty>类型\n\t//然后'函数对象'作为异步执行的上下文状态保存起来\n\ttemplate<class _Ctx>\n\tstruct task_ctx_impl_t : public task_impl_t<remove_cvref_t<decltype(std::declval<_Ctx>()())>>\n\t{\n\t\tusing context_type = _Ctx;\n\n\t\tcontext_type\t_context;\n\n\t\ttask_ctx_impl_t(context_type ctx)\n\t\t\t: _context(std::move(ctx))\n\t\t{\n\t\t\tdecltype(auto) f = _context();\n\t\t\tthis->initialize(f);\n\t\t}\n\t};\n#endif\t//DOXYGEN_SKIP_PROPERTY\n}\n"
  },
  {
    "path": "include/librf/src/ring_queue.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\t//使用自旋锁完成的线程安全的环形队列。\n\t//支持多个线程同时push和pop。\n\t//_Option : 如果队列保存的数据不支持拷贝只支持移动，则需要设置为true；或者数据希望pop后销毁，都需要设置为true。\n\t//_Sty : 内存保持数量和索引的整数类型。用于外部控制队列的结构体大小。\n\ttemplate<class _Ty, bool _Option = false, class _Sty = uint32_t>\n\tstruct ring_queue\n\t{\n\t\tusing value_type = _Ty;\n\t\tusing size_type = _Sty;\n\n\t\tstatic constexpr bool use_option = _Option;\n\t\tusing optional_type = std::conditional_t<use_option, std::optional<value_type>, value_type>;\n\tpublic:\n\t\tring_queue(size_t sz);\n\n\t\tring_queue(const ring_queue&) = delete;\n\t\tring_queue(ring_queue&&) = default;\n\t\tring_queue& operator =(const ring_queue&) = delete;\n\t\tring_queue& operator =(ring_queue&&) = default;\n\n\t\tauto size() const noexcept->size_type;\n\t\tauto capacity() const noexcept->size_type;\n\t\tbool empty() const noexcept;\n\t\tbool full() const noexcept;\n\t\ttemplate<class U>\n\t\tbool try_push(U&& value) noexcept(std::is_nothrow_move_assignable_v<U>);\n\t\ttemplate<class U>\n\t\tbool try_pop(U& value) noexcept(std::is_nothrow_move_assignable_v<value_type>);\n\tprivate:\n\t\tusing container_type = std::unique_ptr<optional_type[]>;\n\t\tcontainer_type m_bufferPtr;\n\t\tsize_type m_bufferSize;\n\n\t\tsize_type m_writeIndex;\n\t\tsize_type m_readIndex;\n\t#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\tstd::atomic<size_type> m_count;\n\t#endif\n\n\t\tauto nextIndex(size_type a_count) const noexcept->size_type;\n\t};\n\n\ttemplate<class _Ty, bool _Option, class _Sty>\n\tring_queue<_Ty, _Option, _Sty>::ring_queue(size_t sz)\n\t\t: m_bufferPtr(new optional_type[sz + 1])\n\t\t, m_bufferSize(static_cast<size_type>(sz + 1))\n\t\t, m_writeIndex(0)\n\t\t, m_readIndex(0)\n\t#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\t, m_count(0)\n\t#endif\n\t{\n\t\tassert(sz < (std::numeric_limits<size_type>::max)());\n\t}\n\n\ttemplate<class _Ty, bool _Option, class _Sty>\n\tauto ring_queue<_Ty, _Option, _Sty>::nextIndex(size_type a_count) const noexcept->size_type\n\t{\n\t\treturn static_cast<size_type>((a_count + 1) % m_bufferSize);\n\t}\n\n\ttemplate<class _Ty, bool _Option, class _Sty>\n\tauto ring_queue<_Ty, _Option, _Sty>::size() const noexcept->size_type\n\t{\n\t#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\treturn m_count.load(std::memory_order_acquire);\n\t#else\n\t\tif (m_writeIndex >= m_readIndex)\n\t\t\treturn (m_writeIndex - m_readIndex);\n\t\telse\n\t\t\treturn (m_bufferSize + m_writeIndex - m_readIndex);\n\t#endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t}\n\n\ttemplate<class _Ty, bool _Option, class _Sty>\n\tauto ring_queue<_Ty, _Option, _Sty>::capacity() const noexcept->size_type\n\t{\n\t\treturn m_bufferSize - 1;\n\t}\n\n\ttemplate<class _Ty, bool _Option, class _Sty>\n\tbool ring_queue<_Ty, _Option, _Sty>::empty() const noexcept\n\t{\n\t#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\treturn m_count.load(std::memory_order_acquire) == 0;\n\t#else\n\t\treturn m_writeIndex == m_readIndex;\n\t#endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t}\n\n\ttemplate<class _Ty, bool _Option, class _Sty>\n\tbool ring_queue<_Ty, _Option, _Sty>::full() const noexcept\n\t{\n\t#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\treturn (m_count.load(std::memory_order_acquire) == (m_bufferSize - 1));\n\t#else\n\t\treturn nextIndex(m_writeIndex) == m_readIndex;\n\t#endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t}\n\n\ttemplate<class _Ty, bool _Option, class _Sty>\n\ttemplate<class U>\n\tbool ring_queue<_Ty, _Option, _Sty>::try_push(U&& value) noexcept(std::is_nothrow_move_assignable_v<U>)\n\t{\n\t\tauto nextWriteIndex = nextIndex(m_writeIndex);\n\t\tif (nextWriteIndex == m_readIndex)\n\t\t\treturn false;\n\n\t\tassert(m_writeIndex < m_bufferSize);\n\n\t\tm_bufferPtr[m_writeIndex] = std::move(value);\n\t\tm_writeIndex = nextWriteIndex;\n\n\t#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\tm_count.fetch_add(1, std::memory_order_acq_rel);\n\t#endif\n\t\treturn true;\n\t}\n\n\ttemplate<class _Ty, bool _Option, class _Sty>\n\ttemplate<class U>\n\tbool ring_queue<_Ty, _Option, _Sty>::try_pop(U& value) noexcept(std::is_nothrow_move_assignable_v<value_type>)\n\t{\n\t\tif (m_readIndex == m_writeIndex)\n\t\t\treturn false;\n\n\t\toptional_type& ov = m_bufferPtr[m_readIndex];\n\t\tif constexpr (use_option)\n\t\t{\n\t\t\tvalue = std::move(ov).value();\n\t\t\tov = std::nullopt;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvalue = std::move(ov);\n\t\t}\n\n\t\tm_readIndex = nextIndex(m_readIndex);\n\n\t#ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE\n\t\tm_count.fetch_sub(1, std::memory_order_acq_rel);\n\t#endif\n\t\treturn true;\n\t}\n}"
  },
  {
    "path": "include/librf/src/scheduler.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\t/**\n\t * @brief 获得当前线程下的调度器。\n\t */\n\tLIBRF_API scheduler_t* this_scheduler();\n\n\t/**\n\t * @brief 协程调度器。\n\t * @details librf的设计原则之一，就是要将协程绑定在固定的调度器里执行。\n\t * 通过控制调度器运行的线程和时机，从而控制协程所在的线程和运行时机。\n\t */\n\tstruct scheduler_t : public std::enable_shared_from_this<scheduler_t>\n\t{\n\tprivate:\n\t\tusing state_sptr = counted_ptr<state_base_t>;\n\t\tusing state_vector = std::vector<state_sptr>;\n\t\tusing lock_type = spinlock;\n\t\tusing task_dictionary_type = std::unordered_map<state_base_t*, std::unique_ptr<task_t>>;\n\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\tmutable spinlock _lock_running;\n#endif\n\t\tstate_vector _runing_states;\n\t\tstate_vector _cached_states;\n\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\tmutable spinlock _lock_ready;\n#endif\n\t\ttask_dictionary_type _ready_task;\n\n\t\ttimer_mgr_ptr _timer;\n\n\t\tLIBRF_API task_t* new_task(task_t* task);\n\t\t//void cancel_all_task_();\n\tpublic:\n\t\t/**\n\t\t * @brief 运行一批准备妥当的协程。\n\t\t * @details 这是协程调度器提供的主要接口。同一个调度器非线程安全，不可重入。\\n\n\t\t * 调用者要保证此函数始终在同一个线程里调用。\n\t\t */\n\t\tLIBRF_API bool run_one_batch();\n\n\t\t/**\n\t\t * @brief 循环运行所有的协程，直到所有协程都运行完成。\n\t\t * @details 通常用于测试代码。\n\t\t */\n\t\tLIBRF_API void run_until_notask();\n\n\t\t//void break_all();\n\n\t\t/**\n\t\t * @brief 要求所有已经使用了stop token功能的协程停止\n\t\t */\n\t\tLIBRF_API void request_stop_all_if_possible();\n\n\t\t/**\n\t\t * @brief 将一个协程加入到调度器里开始运行。\n\t\t * @details 推荐使用go或者GO这两个宏来启动协程。\\n\n\t\t * go用于启动future_t<>/generator_t<>；\\n\n\t\t * GO用于启动一个所有变量按值捕获的lambda。\n\t\t * @param coro 协程对象。future_t<>，generator_t<>，或者一个调用后返回future_t<>/generator_t<>的函数对象。\n\t\t * @retval task_t* 返回代表一个新协程的协程任务类。\\n\n\t\t */\n\t\ttemplate<class _Ty>\n\t\trequires(traits::is_callable_v<_Ty> || traits::is_future_v<_Ty> || traits::is_generator_v<_Ty>)\n\t\ttask_t* operator + (_Ty&& coro)\n\t\t{\n\t\t\tif constexpr (traits::is_callable_v<_Ty>)\n\t\t\t\treturn new_task(new task_ctx_impl_t<_Ty>(coro));\n\t\t\telse\n\t\t\t\treturn new_task(new task_impl_t<_Ty>(coro));\n\t\t}\n\n\t\t/**\n\t\t * @brief 判断所有协程是否运行完毕。\n\t\t * @retval bool 以下条件全部满足，返回true：\\n\n\t\t * 1、所有协程运行完毕\\n\n\t\t * 2、没有正在准备执行的state\\n\n\t\t * 3、定时管理器的empty()返回true。\n\t\t */\n\t\tbool empty() const\n\t\t{\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\t\tscoped_lock<spinlock, spinlock> __guard(_lock_ready, _lock_running);\n#endif\n\t\t\treturn _ready_task.empty() && _runing_states.empty() && _timer->empty();\n\t\t}\n\n\t\t/**\n\t\t * @brief 获得定时管理器。\n\t\t */\n\t\ttimer_manager* timer() const noexcept\n\t\t{\n\t\t\treturn _timer.get();\n\t\t}\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\t\tvoid add_generator(state_base_t* sptr);\n\t\tvoid del_final(state_base_t* sptr);\n\t\tLIBRF_API std::unique_ptr<task_t> del_switch(state_base_t* sptr);\n\t\tvoid add_switch(std::unique_ptr<task_t> task);\n\t\ttask_t* find_task(state_base_t* sptr) const noexcept;\n\n\t\tfriend struct local_scheduler_t;\n\tprotected:\n\t\tLIBRF_API scheduler_t();\n\tpublic:\n\t\tLIBRF_API ~scheduler_t();\n\n\t\tscheduler_t(scheduler_t&& right_) = delete;\n\t\tscheduler_t& operator = (scheduler_t&& right_) = delete;\n\t\tscheduler_t(const scheduler_t&) = delete;\n\t\tscheduler_t& operator = (const scheduler_t&) = delete;\n\n\t\tLIBRF_API static scheduler_t g_scheduler;\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\t};\n\n\t/**\n\t * @brief 创建一个线程相关的调度器。\n\t * @details 如果线程之前已经创建了调度器，则第一个调度器会跟线程绑定，此后local_scheduler_t不会创建更多的调度器。\\n\n\t * 否则，local_scheduler_t会创建一个调度器，并绑定到创建local_scheduler_t的线程上。\\n\n\t * 如果local_scheduler_t成功创建了一个调度器，则在local_scheduler_t生命周期结束后，会销毁创建的调度器，并解绑线程。\\n\n\t * 典型用法，是在非主线程里，开始运行协程之前，申明一个local_scheduler_t的局部变量。\n\t */\n\tstruct local_scheduler_t\n\t{\n\t\t/**\n\t\t * @brief 尽可能的创建一个线程相关的调度器。\n\t\t */\n\t\tLIBRF_API local_scheduler_t();\n\n\t\t/**\n\t\t * @brief 将指定的调度器绑定到当前线程上。\n\t\t */\n\t\tLIBRF_API local_scheduler_t(scheduler_t & sch) noexcept;\n\n\t\t/**\n\t\t * @brief 如果当前线程绑定的调度器由local_scheduler_t所创建，则会销毁调度器，并解绑线程。\n\t\t */\n\t\tLIBRF_API ~local_scheduler_t();\n\n\t\tlocal_scheduler_t(local_scheduler_t&& right_) = delete;\n\t\tlocal_scheduler_t& operator = (local_scheduler_t&& right_) = delete;\n\t\tlocal_scheduler_t(const local_scheduler_t&) = delete;\n\t\tlocal_scheduler_t& operator = (const local_scheduler_t&) = delete;\n\tprivate:\n\t\tscheduler_t* _scheduler_ptr;\n\t};\n\n\tinline void scheduler_t::add_generator(state_base_t* sptr)\n\t{\n\t\tassert(sptr != nullptr);\n\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\tscoped_lock<spinlock> __guard(_lock_running);\n#endif\n\t\t_runing_states.emplace_back(sptr);\n\t}\n\n\tinline void scheduler_t::del_final(state_base_t* sptr)\n\t{\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\tscoped_lock<spinlock> __guard(_lock_ready);\n#endif\n\t\tthis->_ready_task.erase(sptr);\n\t}\n\n\tinline void scheduler_t::add_switch(std::unique_ptr<task_t> task)\n\t{\n\t\tstate_base_t* sptr = task->_state.get();\n\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\tscoped_lock<spinlock> __guard(_lock_ready);\n#endif\n\t\tthis->_ready_task.emplace(sptr, std::move(task));\n\t}\n\n\tinline task_t* scheduler_t::find_task(state_base_t* sptr) const noexcept\n\t{\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\tscoped_lock<spinlock> __guard(_lock_ready);\n#endif\n\n\t\tauto iter = this->_ready_task.find(sptr);\n\t\tif (iter != this->_ready_task.end())\n\t\t\treturn iter->second.get();\n\t\treturn nullptr;\n\t}\n}\n"
  },
  {
    "path": "include/librf/src/sleep.h",
    "content": "﻿//协程的定时器\n//如果定时器被取消了，则会抛 timer_canceled_exception 异常\n//不使用co_await而单独sleep_for/sleep_until，是错误的用法，并不能达到等待的目的。而仅仅是添加了一个无效的定时器，造成无必要的内存使用\n//\n#pragma once\n\nnamespace librf\n{\n\t/**\n\t * @brief 协程专用的睡眠功能。\n\t * @details 不能使用操作系统提供的sleep功能，因为会阻塞协程。\\n\n\t * 此函数不会阻塞线程，仅仅将当前协程挂起，直到指定时刻。\\n\n\t * 其精度，取决与调度器循环的精度，以及std::chrono::system_clock的精度。简而言之，可以认为只要循环够快，精度到100ns。\n\t * @return [co_await] void\n\t * @throw timer_canceled_exception 如果定时器被取消，则抛此异常。\n\t */\n\tLIBRF_API future_t<> sleep_until_(std::chrono::system_clock::time_point tp_, scheduler_t& scheduler_);\n\n\t/**\n\t * @brief 协程专用的睡眠功能。\n\t * @see 参考sleep_until_()函数\\n\n\t * @return [co_await] void\n\t * @throw timer_canceled_exception 如果定时器被取消，则抛此异常。\n\t */\n\tinline future_t<> sleep_for_(std::chrono::system_clock::duration dt_, scheduler_t& scheduler_)\n\t{\n\t\treturn sleep_until_(std::chrono::system_clock::now() + dt_, scheduler_);\n\t}\n\n\t/**\n\t * @brief 协程专用的睡眠功能。\n\t * @see 参考sleep_until_()函数\\n\n\t * @return [co_await] void\n\t * @throw timer_canceled_exception 如果定时器被取消，则抛此异常。\n\t */\n\ttemplate<class _Rep, class _Period>\n\tinline future_t<> sleep_for(std::chrono::duration<_Rep, _Period> dt_, scheduler_t& scheduler_)\n\t{\n\t\treturn sleep_for_(std::chrono::duration_cast<std::chrono::system_clock::duration>(dt_), scheduler_);\n\t}\n\n\t/**\n\t * @brief 协程专用的睡眠功能。\n\t * @see 参考sleep_until_()函数\\n\n\t * @return [co_await] void\n\t * @throw timer_canceled_exception 如果定时器被取消，则抛此异常。\n\t */\n\ttemplate<class _Clock, class _Duration = typename _Clock::duration>\n\tinline future_t<> sleep_until(std::chrono::time_point<_Clock, _Duration> tp_, scheduler_t& scheduler_)\n\t{\n\t\treturn sleep_until_(std::chrono::time_point_cast<std::chrono::system_clock::duration>(tp_), scheduler_);\n\t}\n\n\t/**\n\t * @brief 协程专用的睡眠功能。\n\t * @see 参考sleep_until_()函数\\n\n\t * @return [co_await] void\n\t * @throw timer_canceled_exception 如果定时器被取消，则抛此异常。\n\t */\n\ttemplate<class _Rep, class _Period>\n\tinline future_t<> sleep_for(std::chrono::duration<_Rep, _Period> dt_)\n\t{\n\t\tscheduler_t* sch = librf_current_scheduler();\n\t\tco_await sleep_for_(std::chrono::duration_cast<std::chrono::system_clock::duration>(dt_), *sch);\n\t}\n\n\t/**\n\t * @brief 协程专用的睡眠功能。\n\t * @see 参考sleep_until_()函数\\n\n\t * @return [co_await] void\n\t * @throw timer_canceled_exception 如果定时器被取消，则抛此异常。\n\t */\n\ttemplate<class _Clock, class _Duration>\n\tinline future_t<> sleep_until(std::chrono::time_point<_Clock, _Duration> tp_)\n\t{\n\t\tscheduler_t* sch = librf_current_scheduler();\n\t\tco_await sleep_until_(std::chrono::time_point_cast<std::chrono::system_clock::duration>(tp_), *sch);\n\t}\n\n\t/**\n\t * @brief 协程专用的睡眠功能。\n\t * @see 等同调用sleep_for(dt)\\n\n\t * @return [co_await] void\n\t * @throw timer_canceled_exception 如果定时器被取消，则抛此异常。\n\t */\n\ttemplate <class Rep, class Period>\n\tinline future_t<> operator co_await(std::chrono::duration<Rep, Period> dt_)\n\t{\n\t\tscheduler_t* sch = librf_current_scheduler();\n\t\tco_await sleep_for(dt_, *sch);\n\t}\n\n}"
  },
  {
    "path": "include/librf/src/spinlock.h",
    "content": "﻿//用于内部实现的循环锁\n\n#pragma once\n\nnamespace librf\n{\n#if defined(RESUMEF_USE_CUSTOM_SPINLOCK)\n\tusing spinlock = RESUMEF_USE_CUSTOM_SPINLOCK;\n#else\n\n\t/**\n\t * @brief 一个自旋锁实现。\n\t */\n\tstruct spinlock\n\t{\n\t\tstatic const size_t MAX_ACTIVE_SPIN = 4000;\n\t\tstatic const size_t MAX_YIELD_SPIN = 8000;\n\t\tstatic const int FREE_VALUE = 0;\n\t\tstatic const int LOCKED_VALUE = 1;\n\n\t\tstd::atomic<int> lck;\n#if _DEBUG\n\t\tstd::thread::id owner_thread_id;\n#endif\n\n\t\t/**\n\t\t * @brief 初始为未加锁。\n\t\t */\n\t\tspinlock() noexcept\n\t\t{\n\t\t\tlck = FREE_VALUE;\n\t\t}\n\n\t\t/**\n\t\t * @brief 获得锁。会一直阻塞线程直到获得锁。\n\t\t */\n\t\tvoid lock() noexcept\n\t\t{\n\t\t\tint val = FREE_VALUE;\n\t\t\tif (!lck.compare_exchange_weak(val, LOCKED_VALUE, std::memory_order_acq_rel))\n\t\t\t{\n#if _DEBUG\n\t\t\t\t//诊断错误的用法：进行了递归锁调用\n\t\t\t\tassert(owner_thread_id != std::this_thread::get_id());\n#endif\n\n\t\t\t\tsize_t spinCount = 0;\n\t\t\t\tauto dt = std::chrono::milliseconds{ 1 };\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\twhile (lck.load(std::memory_order_relaxed) != FREE_VALUE)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (spinCount < MAX_ACTIVE_SPIN)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t++spinCount;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (spinCount < MAX_YIELD_SPIN)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t++spinCount;\n\t\t\t\t\t\t\tstd::this_thread::yield();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstd::this_thread::sleep_for(dt);\n\t\t\t\t\t\t\tdt = (std::min)(std::chrono::milliseconds{ 128 }, dt * 2);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tval = FREE_VALUE;\n\t\t\t\t} while (!lck.compare_exchange_weak(val, LOCKED_VALUE, std::memory_order_acq_rel));\n\t\t\t}\n\n#if _DEBUG\n\t\t\towner_thread_id = std::this_thread::get_id();\n#endif\n\t\t}\n\n\t\t/**\n\t\t * @brief 尝试获得锁一次。\n\t\t */\n\t\tbool try_lock() noexcept\n\t\t{\n\t\t\tint val = FREE_VALUE;\n\t\t\tbool ret = lck.compare_exchange_strong(val, LOCKED_VALUE, std::memory_order_acq_rel);\n\n#if _DEBUG\n\t\t\tif (ret) owner_thread_id = std::this_thread::get_id();\n#endif\n\n\t\t\treturn ret;\n\t\t}\n\n\t\t/**\n\t\t * @brief 释放锁。\n\t\t */\n\t\tvoid unlock() noexcept\n\t\t{\n#if _DEBUG\n\t\t\towner_thread_id = std::thread::id();\n#endif\n\t\t\tlck.store(FREE_VALUE, std::memory_order_release);\n\t\t}\n\t};\n\n#endif\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\tnamespace detail\n\t{\n\t\ttemplate<class _Ty, class _Cont = std::vector<_Ty>>\n\t\tstruct _LockVectorAssembleT\n\t\t{\n\t\tprivate:\n\t\t\t_Cont& _Lks;\n\t\tpublic:\n\t\t\t_LockVectorAssembleT(_Cont& _LkN)\n\t\t\t\t: _Lks(_LkN)\n\t\t\t{}\n\t\t\tsize_t size() const\n\t\t\t{\n\t\t\t\treturn _Lks.size();\n\t\t\t}\n\t\t\t_Ty& operator[](int _Idx)\n\t\t\t{\n\t\t\t\treturn _Lks[_Idx];\n\t\t\t}\n\t\t\tvoid _Lock_ref(_Ty& _LkN) const\n\t\t\t{\n\t\t\t\t_LkN.lock();\n\t\t\t}\n\t\t\tbool _Try_lock_ref(_Ty& _LkN) const\n\t\t\t{\n\t\t\t\treturn _LkN.try_lock();\n\t\t\t}\n\t\t\tvoid _Unlock_ref(_Ty& _LkN) const\n\t\t\t{\n\t\t\t\t_LkN.unlock();\n\t\t\t}\n\t\t\tvoid _Yield() const\n\t\t\t{\n\t\t\t\tstd::this_thread::yield();\n\t\t\t}\n\t\t\tvoid _ReturnValue() const;\n\t\t\ttemplate<class U>\n\t\t\tU _ReturnValue(U v) const;\n\t\t};\n\n\t\ttemplate<class _Ty, class _Cont>\n\t\tstruct _LockVectorAssembleT<std::reference_wrapper<_Ty>, _Cont>\n\t\t{\n\t\tprivate:\n\t\t\t_Cont& _Lks;\n\t\tpublic:\n\t\t\t_LockVectorAssembleT(_Cont& _LkN)\n\t\t\t\t: _Lks(_LkN)\n\t\t\t{}\n\t\t\tsize_t size() const\n\t\t\t{\n\t\t\t\treturn _Lks.size();\n\t\t\t}\n\t\t\tstd::reference_wrapper<_Ty> operator[](int _Idx)\n\t\t\t{\n\t\t\t\treturn _Lks[_Idx];\n\t\t\t}\n\t\t\tvoid _Lock_ref(std::reference_wrapper<_Ty> _LkN) const\n\t\t\t{\n\t\t\t\t_LkN.get().lock();\n\t\t\t}\n\t\t\tvoid _Unlock_ref(std::reference_wrapper<_Ty> _LkN) const\n\t\t\t{\n\t\t\t\t_LkN.get().unlock();\n\t\t\t}\n\t\t\tbool _Try_lock_ref(std::reference_wrapper<_Ty> _LkN) const\n\t\t\t{\n\t\t\t\treturn _LkN.get().try_lock();\n\t\t\t}\n\t\t\tvoid _Yield() const\n\t\t\t{\n\t\t\t\tstd::this_thread::yield();\n\t\t\t}\n\t\t\tvoid _ReturnValue() const;\n\t\t\ttemplate<class U>\n\t\t\tU _ReturnValue(U v) const;\n\t\t};\n\n#define LOCK_ASSEMBLE_NAME(fnName) scoped_lock_range_##fnName\n#define LOCK_ASSEMBLE_AWAIT(a) (a)\n#define LOCK_ASSEMBLE_RETURN(a) return (a)\n#include \"without_deadlock_assemble.inl\"\n#undef LOCK_ASSEMBLE_NAME\n#undef LOCK_ASSEMBLE_AWAIT\n#undef LOCK_ASSEMBLE_RETURN\n\t}\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\n\t/**\n\t * @brief 无死锁的批量枷锁。\n\t * @param _Ty 锁的类型。例如std::mutex，librf::spinlock，librf::mutex_t(线程用法）均可。\n\t * @param _Cont 容纳一批锁的容器。\n\t * @param _Assemble 与_Cont配套的锁集合，特化了如何操作_Ty。\n\t */\n\ttemplate<class _Ty, class _Cont = std::vector<_Ty>, class _Assemble = detail::_LockVectorAssembleT<_Ty, _Cont>>\n\tclass batch_lock_t\n\t{\n\tpublic:\n\t\t/**\n\t\t * @brief 通过锁容器构造，并立刻应用加锁算法。\n\t\t */\n\t\texplicit batch_lock_t(_Cont& locks_)\n\t\t\t: _LkN(&locks_)\n\t\t\t, _LA(*_LkN)\n\t\t{\n\t\t\tdetail::scoped_lock_range_lock_impl::_Lock_range(_LA);\n\t\t}\n\t\t\n\t\t/**\n\t\t * @brief 通过锁容器和锁集合构造，并立刻应用加锁算法。\n\t\t */\n\t\texplicit batch_lock_t(_Cont& locks_, _Assemble& la_)\n\t\t\t: _LkN(&locks_)\n\t\t\t, _LA(la_)\n\t\t{\n\t\t\tdetail::scoped_lock_range_lock_impl::_Lock_range(_LA);\n\t\t}\n\n\t\t/**\n\t\t * @brief 通过锁容器构造，容器里的锁已经全部获得。\n\t\t */\n\t\texplicit batch_lock_t(std::adopt_lock_t, _Cont& locks_)\n\t\t\t: _LkN(&locks_)\n\t\t\t, _LA(*_LkN)\n\t\t{ // construct but don't lock\n\t\t}\n\n\t\t/**\n\t\t * @brief 通过锁容器和锁集合构造，容器里的锁已经全部获得。\n\t\t */\n\t\texplicit batch_lock_t(std::adopt_lock_t, _Cont& locks_, _Assemble& la_)\n\t\t\t: _LkN(&locks_)\n\t\t\t, _LA(la_)\n\t\t{ // construct but don't lock\n\t\t}\n\n\t\t/**\n\t\t * @brief 析构函数里，释放容器里的锁。\n\t\t */\n\t\t~batch_lock_t() noexcept\n\t\t{\n\t\t\tif (_LkN != nullptr)\n\t\t\t\tdetail::scoped_lock_range_lock_impl::_Unlock_locks(0, (int)_LA.size(), _LA);\n\t\t}\n\n\t\t/**\n\t\t * @brief 手工释放容器里的锁，析构函数里将不再有释放操作。\n\t\t */\n\t\tvoid unlock()\n\t\t{\n\t\t\tif (_LkN != nullptr)\n\t\t\t{\n\t\t\t\t_LkN = nullptr;\n\t\t\t\tdetail::scoped_lock_range_lock_impl::_Unlock_locks(0, (int)_LA.size(), _LA);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * @brief 不支持拷贝构造。\n\t\t */\n\t\tbatch_lock_t(const batch_lock_t&) = delete;\n\n\t\t/**\n\t\t * @brief 不支持拷贝赋值。\n\t\t */\n\t\tbatch_lock_t& operator=(const batch_lock_t&) = delete;\n\n\t\t/**\n\t\t * @brief 支持移动构造。\n\t\t */\n\t\tbatch_lock_t(batch_lock_t&& _Right)\n\t\t\t: _LkN(_Right._LkN)\n\t\t\t, _LA(std::move(_Right._LA))\n\t\t{\n\t\t\t_Right._LkN = nullptr;\n\t\t}\n\n\t\t/**\n\t\t * @brief 支持移动赋值。\n\t\t */\n\t\tbatch_lock_t& operator=(batch_lock_t&& _Right)\n\t\t{\n\t\t\tif (this != &_Right)\n\t\t\t{\n\t\t\t\t_LkN = _Right._LkN;\n\t\t\t\t_Right._LkN = nullptr;\n\n\t\t\t\t_LA = std::move(_Right._LA);\n\t\t\t}\n\t\t}\n\tprivate:\n\t\t_Cont* _LkN;\n\t\t_Assemble _LA;\n\t};\n}\n"
  },
  {
    "path": "include/librf/src/state.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\t/**\n\t * @brief state基类，state用于在协程的promise和future之间共享数据。\n\t */\n\tstruct state_base_t\n\t{\n\t\tusing _Alloc_char = std::allocator<char>;\n\tprivate:\n\t\tstd::atomic<intptr_t> _count{0};\n\tpublic:\n\t\tvoid lock() noexcept\n\t\t{\n\t\t\t_count.fetch_add(1, std::memory_order_acq_rel);\n\t\t}\n\t\tvoid unlock()\n\t\t{\n\t\t\tif (unlikely(_count.fetch_sub(1, std::memory_order_acq_rel) == 1))\n\t\t\t{\n\t\t\t\tdestroy_deallocate();\n\t\t\t}\n\t\t}\n\tprotected:\n\t\tscheduler_t* _scheduler = nullptr;\n\t\t//可能来自协程里的promise产生的，则经过co_await操作后，_coro在初始时不会为nullptr。\n\t\t//也可能来自awaitable_t，如果\n\t\t//\t\t一、经过co_await操作后，_coro在初始时不会为nullptr。\n\t\t//\t\t二、没有co_await操作，直接加入到了调度器里，则_coro在初始时为nullptr。调度器需要特殊处理此种情况。\n\t\tcoroutine_handle<> _coro;\n\n\t\tLIBRF_API virtual ~state_base_t();\n\tprivate:\n\t\tLIBRF_API virtual void destroy_deallocate();\n\tpublic:\n\t\tLIBRF_API virtual void resume();\n\t\tLIBRF_API virtual bool has_handler() const  noexcept;\n\t\tLIBRF_API virtual state_base_t* get_parent() const noexcept;\n\n\t\tvoid set_scheduler(scheduler_t* sch) noexcept\n\t\t{\n\t\t\t_scheduler = sch;\n\t\t}\n\t\tcoroutine_handle<> get_handler() const noexcept\n\t\t{\n\t\t\treturn _coro;\n\t\t}\n\n\t\tstate_base_t* get_root() const\n\t\t{\n\t\t\tstate_base_t* root = const_cast<state_base_t*>(this);\n\t\t\tstate_base_t* next = root->get_parent();\n\t\t\twhile (next != nullptr)\n\t\t\t{\n\t\t\t\troot = next;\n\t\t\t\tnext = next->get_parent();\n\t\t\t}\n\t\t\treturn root;\n\t\t}\n\t\tscheduler_t* get_scheduler() const\n\t\t{\n\t\t\treturn _scheduler ? _scheduler : get_root()->_scheduler;\n\t\t}\n\t};\n\t\n\t/**\n\t * @brief 专用于generator_t<>的state类。\n\t */\n\tstruct state_generator_t : public state_base_t\n\t{\n\tprivate:\n\t\tLIBRF_API virtual void destroy_deallocate() override;\n\t\tstate_generator_t() = default;\n\tpublic:\n\t\tLIBRF_API virtual void resume() override;\n\t\tLIBRF_API virtual bool has_handler() const  noexcept override;\n\n\t\tLIBRF_API bool switch_scheduler_await_suspend(scheduler_t* sch);\n\n\t\tvoid set_initial_suspend(coroutine_handle<> handler) noexcept\n\t\t{\n\t\t\t_coro = handler;\n\t\t}\n\n\t\tLIBRF_API static state_generator_t* _Alloc_state();\n\t};\n\n\t/**\n\t * @brief 专用于future_t<>的state基类，实现了针对于future_t<>的公共方法等。\n\t */\n\tstruct state_future_t : public state_base_t\n\t{\n\t\tenum struct initor_type : uint8_t\n\t\t{\n\t\t\tNone,\n\t\t\tInitial,\n\t\t\tFinal\n\t\t};\n\t\tenum struct result_type : uint8_t\n\t\t{\n\t\t\tNone,\n\t\t\tValue,\n\t\t\tException,\n\t\t};\n\n\t\t//typedef std::recursive_mutex lock_type;\n\t\ttypedef spinlock lock_type;\n\tprotected:\n\t\tmutable lock_type _mtx;\n\t\tcoroutine_handle<> _initor;\n\t\tstate_future_t* _parent = nullptr;\n#if RESUMEF_DEBUG_COUNTER\n\t\tintptr_t _id;\n#endif\n\t\tuint32_t _alloc_size = 0;\n\t\t//注意：_has_value对齐到 4 Byte上，后面必须紧跟 _is_future变量。两者能组合成一个uint16_t数据。\n\t\tstd::atomic<result_type> _has_value{ result_type::None };\n\t\tbool _is_future;\n\t\tinitor_type _is_initor = initor_type::None;\n\t\tstatic_assert(sizeof(std::atomic<result_type>) == 1);\n\t\tstatic_assert(alignof(std::atomic<result_type>) == 1);\n\t\tstatic_assert(sizeof(bool) == 1);\n\t\tstatic_assert(alignof(bool) == 1);\n\t\tstatic_assert(sizeof(std::atomic<initor_type>) == 1);\n\t\tstatic_assert(alignof(std::atomic<initor_type>) == 1);\n\tprotected:\n\t\texplicit state_future_t(bool awaitor) noexcept\n\t\t{\n#if RESUMEF_DEBUG_COUNTER\n\t\t\t_id = ++g_resumef_state_id;\n#endif\n\t\t\t_is_future = !awaitor;\n\t\t}\n\tpublic:\n\t\tLIBRF_API virtual void destroy_deallocate() override;\n\t\tLIBRF_API virtual void resume() override;\n\t\tLIBRF_API virtual bool has_handler() const  noexcept override;\n\t\tLIBRF_API virtual state_base_t* get_parent() const noexcept override;\n\n\t\tinline bool is_ready() const noexcept\n\t\t{\n#pragma warning(disable : 6326)\t//warning C6326: Potential comparison of a constant with another constant.\n\t\t\t//msvc认为是constexpr表达式(不写还给警告)，然而，clang不这么认为。\n\t\t\t//放弃constexpr，反正合格的编译器都会优化掉这个if判断的。\n\t\t\tif \n#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)\n\t\t\t\tconstexpr\n#endif\n\t\t\t\t(_offset_of(state_future_t, _is_future) - _offset_of(state_future_t, _has_value) == 1)\n\t\t\t\treturn 0 != reinterpret_cast<const std::atomic<uint16_t> &>(_has_value).load(std::memory_order_acquire);\n\t\t\telse\n\t\t\t\treturn _has_value.load(std::memory_order_acquire) != result_type::None || _is_future;\n#pragma warning(default : 6326)\n\t\t}\n\t\tinline bool has_handler_skip_lock() const noexcept\n\t\t{\n\t\t\treturn (bool)_coro || _is_initor != initor_type::None;\n\t\t}\n\n\t\tinline uint32_t get_alloc_size() const noexcept\n\t\t{\n\t\t\treturn _alloc_size;\n\t\t}\n\n\t\tinline bool future_await_ready() const noexcept\n\t\t{\n\t\t\t//scoped_lock<lock_type> __guard(this->_mtx);\n\t\t\treturn _has_value.load(std::memory_order_acquire) != result_type::None;\n\t\t}\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tvoid future_await_suspend(coroutine_handle<_PromiseT> handler);\n\n\t\tLIBRF_API bool switch_scheduler_await_suspend(scheduler_t* sch);\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tvoid promise_initial_suspend(coroutine_handle<_PromiseT> handler);\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tvoid promise_final_suspend(coroutine_handle<_PromiseT> handler);\n\n\t\ttemplate<class _Sty>\n\t\tstatic inline _Sty* _Alloc_state(bool awaitor)\n\t\t{\n\t\t\t_Alloc_char _Al;\n\t\t\tsize_t _Size = _Align_size<_Sty>();\n#if RESUMEF_DEBUG_COUNTER\n\t\t\tstd::cout << \"state_future_t::alloc, size=\" << _Size << std::endl;\n#endif\n\t\t\tchar* _Ptr = _Al.allocate(_Size);\n\t\t\t_Sty* st = new(_Ptr) _Sty(awaitor);\n\t\t\tst->_alloc_size = static_cast<uint32_t>(_Size);\n\n\t\t\treturn st;\n\t\t}\n\t};\n\n\t/**\n\t * @brief 专用于future_t<>的state类。\n\t */\n\ttemplate <typename _Ty>\n\tstruct state_t final : public state_future_t\n\t{\n\t\tfriend state_future_t;\n\n\t\tusing state_future_t::lock_type;\n\t\tusing value_type = _Ty;\n\tprivate:\n\t\texplicit state_t(bool awaitor) noexcept :state_future_t(awaitor) {}\n\tpublic:\n\t\t~state_t()\n\t\t{\n\t\t\tswitch (_has_value.load(std::memory_order_acquire))\n\t\t\t{\n\t\t\tcase result_type::Value:\n\t\t\t\t_value.~value_type();\n\t\t\t\tbreak;\n\t\t\tcase result_type::Exception:\n\t\t\t\t_exception.~exception_ptr();\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tauto future_await_resume() -> value_type;\n\n\t\ttemplate<class _PromiseT, typename U> requires(traits::is_promise_v<_PromiseT>)\n\t\tvoid promise_yield_value(_PromiseT* promise, U&& val);\n\n\t\tvoid set_exception(std::exception_ptr e);\n\t\ttemplate<typename U>\n\t\tvoid set_value(U&& val);\n\n\t\ttemplate<class _Exp>\n\t\tinline void throw_exception(_Exp e)\n\t\t{\n\t\t\tset_exception(std::make_exception_ptr(std::move(e)));\n\t\t}\n\tprivate:\n\t\tunion\n\t\t{\n\t\t\tstd::exception_ptr _exception;\n\t\t\tvalue_type _value;\n\t\t};\n\n\t\ttemplate<typename U>\n\t\tvoid set_value_internal(U&& val);\n\t\tvoid set_exception_internal(std::exception_ptr e);\n\t};\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\ttemplate <typename _Ty>\n\tstruct state_t<_Ty&> final : public state_future_t\n\t{\n\t\tfriend state_future_t;\n\n\t\tusing state_future_t::lock_type;\n\t\tusing value_type = _Ty;\n\t\tusing reference_type = _Ty&;\n\tprivate:\n\t\texplicit state_t(bool awaitor) noexcept :state_future_t(awaitor) {}\n\tpublic:\n\t\t~state_t()\n\t\t{\n\t\t\tif (_has_value.load(std::memory_order_acquire) == result_type::Exception)\n\t\t\t\t_exception.~exception_ptr();\n\t\t}\n\n\t\tauto future_await_resume()->reference_type;\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tvoid promise_yield_value(_PromiseT* promise, reference_type val);\n\n\t\tvoid set_exception(std::exception_ptr e);\n\t\tvoid set_value(reference_type val);\n\n\t\ttemplate<class _Exp>\n\t\tinline void throw_exception(_Exp e)\n\t\t{\n\t\t\tset_exception(std::make_exception_ptr(std::move(e)));\n\t\t}\n\tprivate:\n\t\tunion\n\t\t{\n\t\t\tstd::exception_ptr _exception;\n\t\t\tvalue_type* _value;\n\t\t};\n\n\t\tvoid set_value_internal(reference_type val);\n\t\tvoid set_exception_internal(std::exception_ptr e);\n\t};\n\n\ttemplate<>\n\tstruct state_t<void> final : public state_future_t\n\t{\n\t\tfriend state_future_t;\n\t\tusing state_future_t::lock_type;\n\tprivate:\n\t\texplicit state_t(bool awaitor) noexcept :state_future_t(awaitor) {}\n\tpublic:\n\t\tLIBRF_API void future_await_resume();\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tvoid promise_yield_value(_PromiseT* promise);\n\n\t\tLIBRF_API void set_exception(std::exception_ptr e);\n\t\tLIBRF_API void set_value();\n\n\t\ttemplate<class _Exp>\n\t\tinline void throw_exception(_Exp e)\n\t\t{\n\t\t\tset_exception(std::make_exception_ptr(std::move(e)));\n\t\t}\n\tprivate:\n\t\tstd::exception_ptr _exception;\n\t};\n#endif\t//DOXYGEN_SKIP_PROPERTY\n}\n\n"
  },
  {
    "path": "include/librf/src/state.inl",
    "content": "﻿\nnamespace librf\n{\n\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\tvoid state_future_t::promise_initial_suspend(coroutine_handle<_PromiseT> handler)\n\t{\n\t\tassert(this->_scheduler == nullptr);\n\t\tassert(!this->_coro);\n\n\t\tthis->_initor = handler;\n\t\tthis->_is_initor = initor_type::Initial;\n\t}\n\n\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\tvoid state_future_t::promise_final_suspend(coroutine_handle<_PromiseT> handler)\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\n\t\tthis->_initor = handler;\n\t\tthis->_is_initor = initor_type::Final;\n\n\t\tscheduler_t* sch = this->get_scheduler();\n\t\tassert(sch != nullptr);\n\n\t\tif (this->has_handler_skip_lock())\n\t\t\tsch->add_generator(this);\n\t\tsch->del_final(this);\n\t}\n\n\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\tvoid state_future_t::future_await_suspend(coroutine_handle<_PromiseT> handler)\n\t{\n\t\t_PromiseT& promise = handler.promise();\n\t\tauto* parent_state = promise.get_state();\n\t\tscheduler_t* sch = parent_state->get_scheduler();\n\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\n\t\tif (this != parent_state)\n\t\t{\n\t\t\tthis->_parent = parent_state;\n\t\t\tthis->_scheduler = sch;\n\t\t}\n\n\t\tif (!this->_coro)\n\t\t\tthis->_coro = handler;\n\n\t\tif (sch != nullptr && this->is_ready())\n\t\t\tsch->add_generator(this);\n\t}\n\n\t//------------------------------------------------------------------------------------------------\n\n\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\tvoid state_t<void>::promise_yield_value(_PromiseT* promise)\n\t{\n\t\tcoroutine_handle<_PromiseT> handler = coroutine_handle<_PromiseT>::from_promise(*promise);\n\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\n\t\tif (!handler.done())\n\t\t{\n\t\t\tif (!this->_coro)\n\t\t\t\tthis->_coro = handler;\n\t\t}\n\n\t\tthis->_has_value.store(result_type::Value, std::memory_order_release);\n\n\t\tif (!handler.done())\n\t\t{\n\t\t\tscheduler_t* sch = this->get_scheduler();\n\t\t\tif (sch != nullptr)\n\t\t\t\tsch->add_generator(this);\n\t\t}\n\t}\n\n\t//------------------------------------------------------------------------------------------------\n\n\ttemplate<typename _Ty>\n\ttemplate<class _PromiseT, typename U> requires(traits::is_promise_v<_PromiseT>)\n\tvoid state_t<_Ty>::promise_yield_value(_PromiseT* promise, U&& val)\n\t{\n\t\tcoroutine_handle<_PromiseT> handler = coroutine_handle<_PromiseT>::from_promise(*promise);\n\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\n\t\tif (!handler.done())\n\t\t{\n\t\t\tif (this->_coro == nullptr)\n\t\t\t\tthis->_coro = handler;\n\t\t}\n\n\t\tset_value_internal(std::forward<U>(val));\n\n\t\tif (!handler.done())\n\t\t{\n\t\t\tscheduler_t* sch = this->get_scheduler();\n\t\t\tif (sch != nullptr)\n\t\t\t\tsch->add_generator(this);\n\t\t}\n\t}\n\n\ttemplate<typename _Ty>\n\tauto state_t<_Ty>::future_await_resume() -> value_type\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\n\t\tswitch (this->_has_value.load(std::memory_order_acquire))\n\t\t{\n\t\tcase result_type::None:\n\t\t\tstd::rethrow_exception(std::make_exception_ptr(future_exception{ error_code::not_ready }));\n\t\t\tbreak;\n\t\tcase result_type::Exception:\n\t\t\tstd::rethrow_exception(std::exchange(this->_exception, nullptr));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\treturn std::move(this->_value);\n\t}\n\n\ttemplate<typename _Ty>\n\ttemplate<typename U>\n\tvoid state_t<_Ty>::set_value_internal(U&& val)\n\t{\n\t\tswitch (_has_value.load(std::memory_order_acquire))\n\t\t{\n\t\tcase result_type::Value:\n\t\t\t_value = std::forward<U>(val);\n\t\t\tbreak;\n\t\tcase result_type::Exception:\n\t\t\t_exception.~exception_ptr();\n\t\tdefault:\n\t\t\tnew (&this->_value) value_type(std::forward<U>(val));\n\t\t\tthis->_has_value.store(result_type::Value, std::memory_order_release);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\ttemplate<typename _Ty>\n\tvoid state_t<_Ty>::set_exception_internal(std::exception_ptr e)\n\t{\n\t\tswitch (_has_value.load(std::memory_order_acquire))\n\t\t{\n\t\tcase result_type::Exception:\n\t\t\t_exception = std::move(e);\n\t\t\tbreak;\n\t\tcase result_type::Value:\n\t\t\t_value.~value_type();\n\t\tdefault:\n\t\t\tnew (&this->_exception) std::exception_ptr(std::move(e));\n\t\t\tthis->_has_value.store(result_type::Exception, std::memory_order_release);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\ttemplate<typename _Ty>\n\ttemplate<typename U>\n\tvoid state_t<_Ty>::set_value(U&& val)\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\t\tset_value_internal(std::forward<U>(val));\n\n\t\tscheduler_t* sch = this->get_scheduler();\n\t\tif (sch != nullptr)\n\t\t{\n\t\t\tif (this->has_handler_skip_lock())\n\t\t\t\tsch->add_generator(this);\n\t\t\telse\n\t\t\t\tsch->del_final(this);\n\t\t}\n\t}\n\n\ttemplate<typename _Ty>\n\tvoid state_t<_Ty>::set_exception(std::exception_ptr e)\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\t\tset_exception_internal(std::move(e));\n\n\t\tscheduler_t* sch = this->get_scheduler();\n\t\tif (sch != nullptr)\n\t\t{\n\t\t\tif (this->has_handler_skip_lock())\n\t\t\t\tsch->add_generator(this);\n\t\t\telse\n\t\t\t\tsch->del_final(this);\n\t\t}\n\t}\n\n\t//------------------------------------------------------------------------------------------------\n\n\ttemplate<typename _Ty>\n\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\tvoid state_t<_Ty&>::promise_yield_value(_PromiseT* promise, typename state_t<_Ty&>::reference_type val)\n\t{\n\t\tcoroutine_handle<_PromiseT> handler = coroutine_handle<_PromiseT>::from_promise(*promise);\n\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\n\t\tif (!handler.done())\n\t\t{\n\t\t\tif (this->_coro == nullptr)\n\t\t\t\tthis->_coro = handler;\n\t\t}\n\n\t\tset_value_internal(val);\n\n\t\tif (!handler.done())\n\t\t{\n\t\t\tscheduler_t* sch = this->get_scheduler();\n\t\t\tif (sch != nullptr)\n\t\t\t\tsch->add_generator(this);\n\t\t}\n\t}\n\n\ttemplate<typename _Ty>\n\tauto state_t<_Ty&>::future_await_resume() -> reference_type\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\n\t\tswitch (this->_has_value.load(std::memory_order_acquire))\n\t\t{\n\t\tcase result_type::None:\n\t\t\tstd::rethrow_exception(std::make_exception_ptr(future_exception{ error_code::not_ready }));\n\t\t\tbreak;\n\t\tcase result_type::Exception:\n\t\t\tstd::rethrow_exception(std::move(this->_exception));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\treturn static_cast<reference_type>(*this->_value);\n\t}\n\n\ttemplate<typename _Ty>\n\tvoid state_t<_Ty&>::set_value_internal(reference_type val)\n\t{\n\t\tswitch (_has_value.load(std::memory_order_acquire))\n\t\t{\n\t\tcase result_type::Exception:\n\t\t\t_exception.~exception_ptr();\n\t\tdefault:\n\t\t\tthis->_value = std::addressof(val);\n\t\t\tthis->_has_value.store(result_type::Value, std::memory_order_release);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\ttemplate<typename _Ty>\n\tvoid state_t<_Ty&>::set_exception_internal(std::exception_ptr e)\n\t{\n\t\tswitch (_has_value.load(std::memory_order_acquire))\n\t\t{\n\t\tcase result_type::Exception:\n\t\t\t_exception = std::move(e);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tnew (&this->_exception) std::exception_ptr(std::move(e));\n\t\t\tthis->_has_value.store(result_type::Exception, std::memory_order_release);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\ttemplate<typename _Ty>\n\tvoid state_t<_Ty&>::set_value(reference_type val)\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\t\tset_value_internal(val);\n\n\t\tscheduler_t* sch = this->get_scheduler();\n\t\tif (sch != nullptr)\n\t\t{\n\t\t\tif (this->has_handler_skip_lock())\n\t\t\t\tsch->add_generator(this);\n\t\t\telse\n\t\t\t\tsch->del_final(this);\n\t\t}\n\t}\n\n\ttemplate<typename _Ty>\n\tvoid state_t<_Ty&>::set_exception(std::exception_ptr e)\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\t\tset_exception_internal(std::move(e));\n\n\t\tscheduler_t* sch = this->get_scheduler();\n\t\tif (sch != nullptr)\n\t\t{\n\t\t\tif (this->has_handler_skip_lock())\n\t\t\t\tsch->add_generator(this);\n\t\t\telse\n\t\t\t\tsch->del_final(this);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "include/librf/src/stop_token.h",
    "content": "﻿/************************************************\n* @ Bright Dream Robotics-Fundamental Research\n* @ Author BIP:zhangwencong\n* @ V1.0\n*************************************************/\n\n//librf注：暂时使用一个网友提供的stop_token实现。\n//等待C++20的stop_token被各个编译器都实现后，再使用STL里的stop_token来完成对应功能。\n#pragma once\n\nnamespace milk\n{\n\nnamespace concurrency\n{\n\nnamespace details\n{\n\nstruct stop_callback_base\n{\n    void(*callback)(stop_callback_base*) = nullptr;\n    stop_callback_base* next = nullptr;\n    stop_callback_base** prev = nullptr;\n    bool* is_removed = nullptr;\n    std::atomic<bool> finished_executing{false};\n\n    void execute() noexcept\n    {\n        callback(this);\n    }\n\nprotected:\n    stop_callback_base(void(*fnptr)(stop_callback_base*))\n        : callback(fnptr) {}\n\n    ~stop_callback_base() = default;\n\n};\n\nstruct stop_state\n{\n    static constexpr uint64_t kStopRequestedFlag = 1u;\n    static constexpr uint64_t kLockedFlag = 2u;\n    static constexpr uint64_t kTokenRefIncrement = 4u;\n    static constexpr uint64_t kSourceRefIncrement = static_cast<uint64_t>(1u) << 33u;\n\n    static constexpr uint64_t kZeroRef = kTokenRefIncrement + kSourceRefIncrement;\n    static constexpr uint64_t kLockedAndZeroRef = kZeroRef + kZeroRef;\n    static constexpr uint64_t kLockAndStopedFlag = kStopRequestedFlag | kLockedFlag;\n    static constexpr uint64_t kUnlockAndTokenRefIncrement = kLockedFlag - kTokenRefIncrement;\n    static constexpr uint64_t klockAndTokenRefIncrement = kLockedFlag + kTokenRefIncrement;\n\n    // bit 0 - stop-requested\n    // bit 1 - locked43\n    // bits 2-32 - token ref count (31 bits)\n    // bits 33-63 - source ref count (31 bits)\n    std::atomic<uint64_t> state_{kSourceRefIncrement};\n\n    stop_callback_base* head_ = nullptr;\n    std::thread::id signalling_{};\n\n    static bool is_locked(uint64_t state) noexcept\n    {\n        return (state & kLockedFlag) != 0;\n    }\n\n    static bool is_stop_requested(uint64_t state) noexcept\n    {\n        return (state & kStopRequestedFlag) != 0;\n    }\n\n    static bool is_stop_requestable(uint64_t state) noexcept\n    {\n        return is_stop_requested(state) || (state >= kSourceRefIncrement);\n    }\n\n    bool try_lock_and_signal_until_signalled() noexcept\n    {\n        uint64_t old_state = state_.load(std::memory_order_acquire);\n        do\n        {\n            if (is_stop_requested(old_state))\n            {\n                return false;\n            }\n            while (is_locked(old_state))\n            {\n                std::this_thread::yield();\n                old_state = state_.load(std::memory_order_acquire);\n                if (is_stop_requested(old_state))\n                {\n                    return false;\n                }\n            }\n        } while(!state_.compare_exchange_weak(old_state, old_state | kLockAndStopedFlag, std::memory_order_acq_rel, std::memory_order_acquire));\n        return true;\n    }\n\n    void lock() noexcept\n    {\n        uint64_t old_state = state_.load(std::memory_order_relaxed);\n        do\n        {\n            while (is_locked(old_state))\n            {\n                std::this_thread::yield();\n                old_state = state_.load(std::memory_order_relaxed);\n            }\n        }\n        while (!state_.compare_exchange_weak(old_state, old_state | kLockedFlag, std::memory_order_acquire, std::memory_order_relaxed));\n    }\n\n    void unlock() noexcept\n    {\n        state_.fetch_sub(kLockedFlag, std::memory_order_release);\n    }\n\n    void unlock_and_increment_token_ref_count() noexcept\n    {\n        state_.fetch_sub(kUnlockAndTokenRefIncrement, std::memory_order_release);\n    }\n\n    void unlock_and_decrement_token_ref_count() noexcept\n    {\n        if (state_.fetch_sub(klockAndTokenRefIncrement, std::memory_order_acq_rel) < kLockedAndZeroRef)\n        {\n            delete this;\n        }\n    }\n\npublic:\n    void add_token_reference() noexcept\n    {\n        state_.fetch_add(kTokenRefIncrement, std::memory_order_relaxed);\n    }\n\n    void remove_token_reference() noexcept\n    {\n        auto old_state = state_.fetch_sub(kTokenRefIncrement, std::memory_order_acq_rel);\n        if (old_state < kZeroRef)\n        {\n            delete this;\n        }\n    }\n\n    void add_source_reference() noexcept\n    {\n        state_.fetch_add(kSourceRefIncrement, std::memory_order_relaxed);\n    }\n\n    void remove_source_reference() noexcept\n    {\n        auto old_state = state_.fetch_sub(kSourceRefIncrement, std::memory_order_acq_rel);\n        if (old_state < kZeroRef)\n        {\n            delete this;\n        }\n    }\n\n    bool request_stop() noexcept\n    {\n        if (!try_lock_and_signal_until_signalled())\n        {\n            return false;\n        }\n        signalling_ = std::this_thread::get_id();\n\n        while (head_)\n        {\n            auto* cb = head_;\n            head_ = cb->next;\n            const bool any_more = head_ != nullptr;\n            if (any_more)\n            {\n                head_->prev = &head_;\n            }\n            cb->prev = nullptr;\n            unlock();\n            bool removed = false;\n            cb->is_removed = &removed;\n            cb->execute();\n            if (!removed)\n            {\n                cb->is_removed = nullptr;\n                cb->finished_executing.store(true, std::memory_order_release);\n            }\n            if (!any_more)\n            {\n                return true;\n            }\n            lock();\n        }\n        unlock();\n        return true;\n    }\n\n    bool is_stop_requested() noexcept\n    {\n        return is_stop_requested(state_.load(std::memory_order_acquire));\n    }\n\n    bool is_stop_requestable() noexcept\n    {\n        return is_stop_requestable(state_.load(std::memory_order_acquire));\n    }\n\n    bool try_add_callback(stop_callback_base* cb, bool increment_ref_count_if_successful) noexcept\n    {\n        uint64_t old_state;\n        goto __load_state;\n        do\n        {\n            goto __check_state;\n            do\n            {\n                std::this_thread::yield();\n__load_state:\n                old_state = state_.load(std::memory_order_acquire);\n__check_state:\n                if (is_stop_requested(old_state))\n                {\n                    cb->execute();\n                    return false;\n                }\n                else if (!is_stop_requestable(old_state))\n                {\n                    return false;\n                }\n            } while (is_locked(old_state));\n        }while (!state_.compare_exchange_weak(old_state, old_state | kLockedFlag, std::memory_order_acquire));\n\n        // callback入队列\n        cb->next = head_;\n        if (cb->next)\n        {\n            cb->next->prev = &cb->next;\n        }\n        cb->prev = &head_;\n        head_ = cb;\n\n        if (increment_ref_count_if_successful)\n        {\n            unlock_and_increment_token_ref_count();\n        }\n        else\n        {\n            unlock();\n        }\n        return true;\n    }\n\n    void remove_callback(stop_callback_base* cb) noexcept\n    {\n        lock();\n        if (cb->prev)\n        {\n            *cb->prev = cb->next;\n            if (cb->next)\n            {\n                cb->next->prev = cb->prev;\n            }\n            unlock_and_decrement_token_ref_count();\n            return;\n        }\n        unlock();\n        // Callback要么已经执行，要么正在另一个线程上并发执行。\n        if (signalling_ == std::this_thread::get_id())\n        {\n            // 若Callback是当前线程上执行的回调或当前仍在执行并在回调中注销自身的回调。\n            if (cb->is_removed)\n            {\n                // 当前在Callback执行中，告知request_stop()知道对象即将被销毁，不能尝试访问\n                *cb->is_removed = true;\n            }\n        }\n        else\n        {\n            // 等待其他线程执行Callback完成\n            while (!cb->finished_executing.load(std::memory_order_acquire))\n            {\n                std::this_thread::yield();\n            }\n        }\n        remove_token_reference();\n    }\n\n};\n\n}//namespace details\n\nstruct nostopstate_t { explicit nostopstate_t() = default; };\ninline constexpr nostopstate_t nostopstate{};\n\nclass stop_source;\n\ntemplate<typename Callback>\nclass stop_callback;\n\nclass stop_token\n{\nprivate:\n    details::stop_state* state_;\n\n    friend class stop_source;\n    template<typename> friend class stop_callback;\n\n    explicit stop_token(details::stop_state* state) noexcept\n        : state_(state)\n    {\n        if (state_)\n        {\n            state_->add_token_reference();\n        }\n    }\n\npublic:\n    stop_token() noexcept\n        : state_(nullptr)\n    {\n    }\n\n    stop_token(const stop_token& other) noexcept\n        : state_(other.state_)\n    {\n        if (state_)\n        {\n            state_->add_token_reference();\n        }\n    }\n\n    stop_token(stop_token&& other) noexcept\n        : state_(std::exchange(other.state_, nullptr))\n    {\n    }\n\n    ~stop_token()\n    {\n        if (state_)\n        {\n            state_->remove_token_reference();\n        }\n    }\n\n    stop_token& operator=(const stop_token& other) noexcept\n    {\n        if (state_ != other.state_)\n        {\n            stop_token tmp{other};\n            swap(tmp);\n        }\n        return *this;\n    }\n\n    stop_token& operator=(stop_token&& other) noexcept\n    {\n        stop_token tmp{std::move(other)};\n        swap(tmp);\n        return *this;\n    }\n\n    void swap(stop_token& other) noexcept\n    {\n        std::swap(state_, other.state_);\n    }\n\n    [[nodiscard]]\n    bool stop_requested() const noexcept\n    {\n        return state_ && state_->is_stop_requested();\n    }\n\n    [[nodiscard]]\n    bool stop_possible() const noexcept\n    {\n        return state_ && state_->is_stop_requestable();\n    }\n\n    [[nodiscard]]\n    bool operator==(const stop_token& other) noexcept\n    {\n        return state_ == other.state_;\n    }\n\n    [[nodiscard]]\n    bool operator!=(const stop_token& other) noexcept\n    {\n        return state_ != other.state_;\n    }\n\n};\n\nclass stop_source\n{\nprivate:\n    details::stop_state* state_;\n\npublic:\n    stop_source()\n        : state_(new details::stop_state())\n    {\n    }\n\n    explicit stop_source(nostopstate_t) noexcept\n        : state_(nullptr)\n    {\n    }\n\n    ~stop_source()\n    {\n        if (state_)\n        {\n            state_->remove_source_reference();\n        }\n    }\n\n    stop_source(const stop_source& other) noexcept\n        : state_(other.state_)\n    {\n        if (state_)\n        {\n            state_->add_source_reference();\n        }\n    }\n\n    stop_source(stop_source&& other) noexcept\n        : state_(std::exchange(other.state_, nullptr))\n    {\n    }\n\n    stop_source& operator=(stop_source&& other) noexcept\n    {\n        stop_source tmp{std::move(other)};\n        swap(tmp);\n        return *this;\n    }\n\n    stop_source& operator=(const stop_source& other) noexcept\n    {\n        if (state_ != other.state_)\n        {\n            stop_source tmp{other};\n            swap(tmp);\n        }\n        return *this;\n    }\n\n    [[nodiscard]]\n    bool stop_requested() const noexcept\n    {\n        return state_ && state_->is_stop_requested();\n    }\n\n    [[nodiscard]]\n    bool stop_possible() const noexcept\n    {\n        return state_ != nullptr;\n    }\n\n\tvoid make_sure_possible()\n\t{\n        if (state_ == nullptr)\n        {\n            details::stop_state* st = new details::stop_state();\n            details::stop_state* tmp = nullptr;\n            if (!std::atomic_compare_exchange_strong_explicit(\n                reinterpret_cast<std::atomic<details::stop_state*>*>(&state_),\n                &tmp, \n                st, \n                std::memory_order_release, \n                std::memory_order_acquire))\n            {\n                st->remove_source_reference();\n            }\n        }\n\t}\n\n    bool request_stop() const noexcept\n    {\n        if (state_)\n        {\n            return state_->request_stop();\n        }\n        return false;\n    }\n\n    [[nodiscard]]\n    stop_token get_token() const noexcept\n    {\n        return stop_token{state_};\n    }\n\n    void swap(stop_source& other) noexcept\n    {\n        std::swap(state_, other.state_);\n    }\n\n    [[nodiscard]]\n    bool operator==(const stop_source& other) noexcept\n    {\n        return state_ == other.state_;\n    }\n\n    [[nodiscard]]\n    bool operator!=(const stop_source& other) noexcept\n    {\n        return state_ != other.state_;\n    }\n\n};\n\ntemplate <typename Callback>\nclass [[nodiscard]] stop_callback : private details::stop_callback_base\n{\nprivate:\n    using details::stop_callback_base::execute;\n    using callack_type = Callback;\n    details::stop_state* state_;\n    callack_type callack_;\n\n    void execute() noexcept\n    {\n        callack_();\n    }\n\npublic:\n    using callback_type = Callback;\n\n    template<typename Callable, typename = typename std::enable_if<std::is_constructible<Callback, Callable>::value, int>::type>\n    explicit stop_callback(const stop_token& token, Callable&& cb) noexcept(std::is_nothrow_constructible<Callback, Callable>::value)\n        : details::stop_callback_base([](details::stop_callback_base* that) noexcept { static_cast<stop_callback*>(that)->execute(); })\n        , state_(nullptr)\n        , callack_(static_cast<Callable&&>(cb))\n    {\n        if (token.state_ && token.state_->try_add_callback(this, true))\n        {\n            state_ = token.state_;\n        }\n    }\n\n    template<typename Callable, typename = typename std::enable_if<std::is_constructible<Callback, Callable>::value, int>::type>\n    explicit stop_callback(stop_token&& token, Callable&& cb) noexcept(std::is_nothrow_constructible<Callback, Callable>::value)\n        : details::stop_callback_base([](details::stop_callback_base* that) noexcept { static_cast<stop_callback*>(that)->execute(); })\n        , state_(nullptr)\n        , callack_(static_cast<Callable&&>(cb))\n    {\n        if (token.state_ && token.state_->try_add_callback(this, false))\n        {\n            state_ = std::exchange(token.state_, nullptr);\n        }\n    }\n\n    ~stop_callback()\n    {\n        if (state_)\n        {\n            state_->remove_callback(this);\n        }\n    }\n\n    stop_callback& operator=(const stop_callback&) = delete;\n    stop_callback& operator=(stop_callback&&) = delete;\n    stop_callback(const stop_callback&) = delete;\n    stop_callback(stop_callback&&) = delete;\n\n};\n\ntemplate<typename Callback>\nstop_callback(stop_token, Callback)->stop_callback<Callback>;//C++17 deduction guides\n\n}//namespace concurrency\n\n}//namespace milk\n"
  },
  {
    "path": "include/librf/src/switch_scheduler.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\t/**\n\t * @brief 切换协程的可等待对象。\n\t */\n\tstruct switch_scheduler_awaitor\n\t{\n\t\tusing value_type = void;\n\t\tusing state_type = state_t<value_type>;\n\t\tusing promise_type = promise_t<value_type>;\n\t\tusing lock_type = typename state_type::lock_type;\n\n\t\tswitch_scheduler_awaitor(scheduler_t* sch)\n\t\t\t:_scheduler(sch) {}\n\t\tswitch_scheduler_awaitor(const switch_scheduler_awaitor&) = default;\n\t\tswitch_scheduler_awaitor(switch_scheduler_awaitor&&) = default;\n\n\t\tswitch_scheduler_awaitor& operator = (const switch_scheduler_awaitor&) = default;\n\t\tswitch_scheduler_awaitor& operator = (switch_scheduler_awaitor&&) = default;\n\n\t\tbool await_ready() const noexcept\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\t_PromiseT& promise = handler.promise();\n\t\t\tauto* sptr = promise.get_state();\n\t\t\tif (sptr->switch_scheduler_await_suspend(_scheduler))\n\t\t\t{\n\t\t\t\tcounted_ptr<state_t<void>> _state = state_future_t::_Alloc_state<state_type>(true);\n\t\t\t\t_state->set_value();\n\t\t\t\t_state->future_await_suspend(handler);\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tvoid await_resume() const noexcept\n\t\t{\n\t\t}\n\n\tprivate:\n\t\tscheduler_t* _scheduler;\n#ifdef DOXYGEN_SKIP_PROPERTY\n\tpublic:\n\t\t/**\n\t\t * @brief 将本协程切换到指定调度器上运行。\n\t\t * @details 由于调度器必然在某个线程里运行，故达到了切换到特定线程里运行的目的。\\n\n\t\t * 如果指定的协程就是本协程的调度器，则协程不暂停直接运行接下来的代码。\n\t\t * 如果指定的协程不是本协程的调度器，则协程暂停后放入到目的协程的调度队列，等待下一次运行。\n\t\t * @param sch 将要运行后续代码的协程\n\t\t * @return [co_await] void\n\t\t * @note 本函数是librf名字空间下的全局函数。由于doxygen使用上的问题，将之归纳到 switch_scheduler_awaitor 类下。\n\t\t */\n\t\tstatic switch_scheduler_awaitor via(scheduler_t& sch) noexcept;\n\n\t\t/**\n\t\t * @brief 将本协程切换到指定调度器上运行。\n\t\t * @details 由于调度器必然在某个线程里运行，故达到了切换到特定线程里运行的目的。\\n\n\t\t * 如果指定的协程就是本协程的调度器，则协程不暂停直接运行接下来的代码。\n\t\t * 如果指定的协程不是本协程的调度器，则协程暂停后放入到目的协程的调度队列，等待下一次运行。\n\t\t * @param sch 将要运行后续代码的协程\n\t\t * @return [co_await] void\n\t\t * @note 本函数是librf名字空间下的全局函数。由于doxygen使用上的问题，将之归纳到 switch_scheduler_awaitor 类下。\n\t\t */\n\t\tstatic switch_scheduler_awaitor via(scheduler_t* sch) noexcept;\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\t};\n\n\t//由于跟when_all/when_any混用的时候，在clang上编译失败：\n\t//clang把scheduler_t判断成一个is_awaitable，且放弃选择when_all/any(scheduler_t& sch, ...)版本\n\t//故放弃这种用法\n\t//inline switch_scheduler_awaitor operator co_await(scheduler_t& sch) noexcept\n\t//{\n\t//\treturn { &sch };\n\t//}\n\n\t/**\n\t * @brief 将本协程切换到指定调度器上运行。\n\t * @details 由于调度器必然在某个线程里运行，故达到了切换到特定线程里运行的目的。\\n\n\t * 如果指定的协程就是本协程的调度器，则协程不暂停直接运行接下来的代码。\n\t * 如果指定的协程不是本协程的调度器，则协程暂停后放入到目的协程的调度队列，等待下一次运行。\n\t * @param sch 将要运行此后代码的协程\n\t */\n\tinline switch_scheduler_awaitor via(scheduler_t& sch) noexcept\n\t{\n\t\treturn { &sch };\n\t}\n\n\t/**\n\t * @fn 将本协程切换到指定调度器上运行。\n\t * @see 参考 via(scheduler_t&)版本。\n\t */\n\tinline switch_scheduler_awaitor via(scheduler_t* sch) noexcept\n\t{\n\t\treturn { sch };\n\t}\n\n}\n"
  },
  {
    "path": "include/librf/src/timer.h",
    "content": "﻿#pragma once\n#include <memory>\n\nnamespace librf\n{\n\tstruct timer_manager;\n\ttypedef std::shared_ptr<timer_manager> timer_mgr_ptr;\n\ttypedef std::weak_ptr<timer_manager> timer_mgr_wptr;\n\n\tnamespace detail\n\t{\n\t\ttypedef std::chrono::system_clock timer_clock_type;\n\t\ttypedef std::function<void(bool)> timer_callback_type;\n\n\t\t/**\n\t\t * @brief 定时器对象。\n\t\t */\n\t\tstruct timer_target : public std::enable_shared_from_this<timer_target>\n\t\t{\n\t\t\tfriend timer_manager;\n\t\tprivate:\n\t\t\tenum struct State : uint32_t\n\t\t\t{\n\t\t\t\tInvalid,\n\t\t\t\tAdded,\n\t\t\t\tRuning,\n\t\t\t};\n\t\t\ttimer_clock_type::time_point\ttp;\n\t\t\ttimer_callback_type\t\t\t\tcb;\n\t\t\tState\t\t\t\t\t\t\tst = State::Invalid;\n#if _DEBUG\n\t\tprivate:\n\t\t\ttimer_manager *\t\t\t\t\t_manager = nullptr;\n#endif\n\t\tpublic:\n\t\t\ttimer_target(const timer_clock_type::time_point & tp_, const timer_callback_type & cb_)\n\t\t\t\t: tp(tp_)\n\t\t\t\t, cb(cb_)\n\t\t\t{\n\t\t\t}\n\t\t\ttimer_target(const timer_clock_type::time_point & tp_, timer_callback_type && cb_)\n\t\t\t\t: tp(tp_)\n\t\t\t\t, cb(std::forward<timer_callback_type>(cb_))\n\t\t\t{\n\t\t\t}\n\t\tprivate:\n\t\t\ttimer_target() = delete;\n\t\t\ttimer_target(const timer_target &) = delete;\n\t\t\ttimer_target(timer_target && right_) = delete;\n\t\t\ttimer_target & operator = (const timer_target &) = delete;\n\t\t\ttimer_target & operator = (timer_target && right_) = delete;\n\t\t};\n\n\t\ttypedef std::shared_ptr<timer_target> timer_target_ptr;\n\t\ttypedef std::weak_ptr<timer_target> timer_target_wptr;\n\t}\n\n\t/**\n\t * @brief 定时器句柄。\n\t * @details 对定时器对象和定时器管理器都采用弱引用。\n\t */\n\tstruct timer_handler\n\t{\n\tprivate:\n\t\ttimer_mgr_wptr\t\t\t\t_manager;\n\t\tdetail::timer_target_wptr\t_target;\n\tpublic:\n#ifndef DOXYGEN_SKIP_PROPERTY\n\t\ttimer_handler() = default;\n\t\ttimer_handler(const timer_handler &) = default;\n\t\ttimer_handler(timer_handler && right_) noexcept;\n\t\ttimer_handler & operator = (const timer_handler &) = default;\n\t\ttimer_handler & operator = (timer_handler && right_) noexcept;\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\t\ttimer_handler(timer_manager * manager_, const detail::timer_target_ptr & target_);\n\n\t\tvoid reset();\n\t\tbool stop();\n\t\tbool expired() const;\n\t};\n\n\t/**\n\t * @brief 定时器管理器。\n\t */\n\tstruct timer_manager : public std::enable_shared_from_this<timer_manager>\n\t{\n#ifndef DOXYGEN_SKIP_PROPERTY\n\t\ttypedef detail::timer_target timer_target;\n\t\ttypedef detail::timer_target_ptr timer_target_ptr;\n\t\ttypedef detail::timer_clock_type clock_type;\n\t\ttypedef clock_type::duration duration_type;\n\t\ttypedef clock_type::time_point time_point_type;\n\n\t\ttypedef std::vector<timer_target_ptr> timer_vector_type;\n\t\ttypedef std::multimap<clock_type::time_point, timer_target_ptr> timer_map_type;\n#endif\n\tpublic:\n\t\tLIBRF_API timer_manager();\n\t\tLIBRF_API ~timer_manager();\n\n\t\ttemplate<class _Rep, class _Period, class _Cb>\n\t\ttimer_target_ptr add(const std::chrono::duration<_Rep, _Period> & dt_, _Cb && cb_)\n\t\t{\n\t\t\treturn add_(std::chrono::duration_cast<duration_type>(dt_), std::forward<_Cb>(cb_));\n\t\t}\n\t\ttemplate<class _Clock, class _Duration = typename _Clock::duration, class _Cb>\n\t\ttimer_target_ptr add(const std::chrono::time_point<_Clock, _Duration> & tp_, _Cb && cb_)\n\t\t{\n\t\t\treturn add_(std::chrono::time_point_cast<duration_type>(tp_), std::forward<_Cb>(cb_));\n\t\t}\n\t\ttemplate<class _Rep, class _Period, class _Cb>\n\t\ttimer_handler add_handler(const std::chrono::duration<_Rep, _Period> & dt_, _Cb && cb_)\n\t\t{\n\t\t\treturn{ this, add(dt_, std::forward<_Cb>(cb_)) };\n\t\t}\n\t\ttemplate<class _Clock, class _Duration = typename _Clock::duration, class _Cb>\n\t\ttimer_handler add_handler(const std::chrono::time_point<_Clock, _Duration> & tp_, _Cb && cb_)\n\t\t{\n\t\t\treturn{ this, add(tp_, std::forward<_Cb>(cb_)) };\n\t\t}\n\n\t\tLIBRF_API bool stop(const timer_target_ptr & sptr);\n\n\t\tinline bool empty() const\n\t\t{\n\t\t\treturn _runing_timers.empty() && _added_timers.empty();\n\t\t}\n\t\tLIBRF_API void clear();\n\t\tLIBRF_API void update();\n\n#ifndef DOXYGEN_SKIP_PROPERTY\n\t\ttemplate<class _Cb>\n\t\ttimer_target_ptr add_(const duration_type & dt_, _Cb && cb_)\n\t\t{\n\t\t\treturn add_(std::make_shared<timer_target>(clock_type::now() + dt_, std::forward<_Cb>(cb_)));\n\t\t}\n\t\ttemplate<class _Cb>\n\t\ttimer_target_ptr add_(const time_point_type & tp_, _Cb && cb_)\n\t\t{\n\t\t\treturn add_(std::make_shared<timer_target>(tp_, std::forward<_Cb>(cb_)));\n\t\t}\n\tprivate:\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\tspinlock _added_mtx;\n#endif\n\t\ttimer_vector_type\t_added_timers;\n\t\ttimer_map_type\t\t_runing_timers;\n\n\t\tLIBRF_API timer_target_ptr add_(const timer_target_ptr & sptr);\n\t\tLIBRF_API static void call_target_(const timer_target_ptr & sptr, bool canceld);\n#endif\n\t};\n\n\n\tinline timer_handler::timer_handler(timer_manager * manager_, const detail::timer_target_ptr & target_)\n\t\t: _manager(manager_->shared_from_this())\n\t\t, _target(target_)\n\t{\n\t}\n\tinline timer_handler::timer_handler(timer_handler && right_) noexcept\n\t\t: _manager(std::move(right_._manager))\n\t\t, _target(std::move(right_._target))\n\t{\n\t}\n\n\tinline timer_handler & timer_handler::operator = (timer_handler && right_) noexcept\n\t{\n\t\tif (this != &right_)\n\t\t{\n\t\t\t_manager = std::move(right_._manager);\n\t\t\t_target = std::move(right_._target);\n\t\t}\n\t\treturn *this;\n\t}\n\n\tinline void timer_handler::reset()\n\t{\n\t\t_manager.reset();\n\t\t_target.reset();\n\t}\n\n\tinline bool timer_handler::stop()\n\t{\n\t\tbool result = false;\n\t\t\n\t\tif (!_target.expired())\n\t\t{\n\t\t\tauto sptr = _manager.lock();\n\t\t\tif (sptr)\n\t\t\t\tresult = sptr->stop(_target.lock());\n\t\t\t_target.reset();\n\t\t}\n\t\t_manager.reset();\n\n\t\treturn result;\n\t}\n\n\tinline bool timer_handler::expired() const\n\t{\n\t\treturn _target.expired();\n\t}\n}"
  },
  {
    "path": "include/librf/src/type_concept.inl",
    "content": "﻿#pragma once\n\n#include <concepts>\n\nnamespace librf\n{\n\ttemplate<typename T>\n\tconcept _ValidAwaitSuspendReturnT = std::same_as<T, void> || std::same_as<T, bool> || traits::is_coroutine_handle_v<T>;\n\n\ttemplate<typename T>\n\tconcept _AwaitorT = requires(T&& v)\n\t{\n\t\t{ v.await_ready() } -> std::same_as<bool>;\n\t\t{ v.await_suspend(std::declval<std::coroutine_handle<promise_t<>>>()) } -> _ValidAwaitSuspendReturnT;\n\t\t{ v.await_resume() };\n\t};\n\n\ttemplate<typename T> \n\tconcept _HasStateT = requires(T&& v)\n\t{\n\t\t{ v._state };\n\t\trequires traits::is_state_pointer_v<decltype(v._state)>;\n\t};\n\n\ttemplate<typename T>\n\tconcept _FutureT = _AwaitorT<T> && _HasStateT<T> && requires\n\t{\n\t\t{ T::value_type };\n\t\t{ T::state_type };\n\t\t{ T::promise_type };\n\t};\n\n\ttemplate<typename T>\n\tconcept _CallableT = std::invocable<T>;\n\n\t//template<typename T>\n\t//concept _GeneratorT = std::is_same_v<T, generator_t<T>>;\n\n\ttemplate<typename T>\n\tconcept _AwaitableT = requires(T&& v)\n\t{\n\t\t{ traits::get_awaitor(v) } ->_AwaitorT;\n\t};\n\n\ttemplate<typename T>\n\tconcept _WhenTaskT = _AwaitableT<T> || _CallableT<T>;\n\n\ttemplate<typename T>\n\tconcept _IteratorT = requires(T&& u, T&& v)\n\t{\n\t\t{ ++u } ->std::common_with<T>;\n\t\t{ u != v } ->std::same_as<bool>;\n\t\t{ *u };\n\t};\n\n\ttemplate<typename T, typename E>\n\tconcept _IteratorOfT = _IteratorT<T> && requires(T&& u)\n\t{\n\t\t{ *u } ->std::common_with<E&>;\n\t};\n\n\ttemplate<typename T>\n\tconcept _WhenIterT = _IteratorT<T> && requires(T&& u)\n\t{\n\t\t{ *u } ->_WhenTaskT;\n\t};\n\n\ttemplate<typename T>\n\tconcept _ContainerT = requires(T&& v)\n\t{\n\t\t{ std::begin(v) } ->_IteratorT;\n\t\t{ std::end(v) } ->_IteratorT;\n\t\trequires std::same_as<decltype(std::begin(v)), decltype(std::end(v))>;\n\t};\n\n\ttemplate<typename T, typename E>\n\tconcept _ContainerOfT = _ContainerT<T> && requires(T&& v)\n\t{\n\t\t{ *std::begin(v) } ->std::common_with<E&>;\n\t\t//requires std::is_same_v<E, remove_cvref_t<decltype(*std::begin(v))>>;\n\t};\n\n\ttemplate<typename T>\n\tconcept _LockAssembleT = requires(T && v)\n\t{\n\t\t{ v.size() };\n\t\t{ v[0] };\n\t\t{ v._Lock_ref(v[0]) };\n\t\t{ v._Try_lock_ref(v[0]) };\n\t\t{ v._Unlock_ref(v[0]) } ->std::same_as<void>;\n\t\t{ v._Yield() };\n\t\t{ v._ReturnValue() };\n\t\t{ v._ReturnValue(0) };\n\t\trequires std::is_integral_v<decltype(v.size())>;\n\t};\n\n}\n"
  },
  {
    "path": "include/librf/src/type_traits.inl",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\tnamespace traits\n\t{\n\t\t//is_coroutine_handle<T>\n\t\t//is_coroutine_handle_v<T>\n\t\t//判断是不是coroutine_handle<>类型\n\t\t//\n\t\t//is_valid_await_suspend_return_v<T>\n\t\t//判断是不是awaitor的await_suspend()函数的有效返回值\n\t\t//\n\t\t//is_awaitor<T>\n\t\t//is_awaitor_v<T>\n\t\t//判断是不是一个awaitor规范。\n\t\t//一个awaitor可以被co_await操作，要求满足coroutine的awaitor的三个函数接口规范\n\t\t//\n\t\t//is_future<T>\n\t\t//is_future_v<T>\n\t\t//判断是不是一个librf的future规范。\n\t\t//future除了要求是一个awaitor外，还要求定义了value_type/state_type/promise_type三个类型，\n\t\t//并且具备counted_ptr<state_type>类型的_state变量。\n\t\t//\n\t\t//is_promise<T>\n\t\t//is_promise_v<T>\n\t\t//判断是不是一个librf的promise_t类\n\t\t//\n\t\t//is_generator<T>\n\t\t//is_generator_v<T>\n\t\t//判断是不是一个librf的generator_t类\n\t\t//\n\t\t//is_state_pointer<T>\n\t\t//is_state_pointer_v<T>\n\t\t//判断是不是一个librf的state_t类的指针或智能指针\n\t\t//\n\t\t//has_state<T>\n\t\t//has_state_v<T>\n\t\t//判断是否具有_state的成员变量\n\t\t//\n\t\t//get_awaitor<T>(T&&t)\n\t\t//通过T获得其被co_await后的awaitor\n\t\t//\n\t\t//awaitor_traits<T>\n\t\t//获得一个awaitor的特征。\n\t\t//\ttype:awaitor的类型\n\t\t//\tvalue_type:awaitor::await_resume()的返回值类型\n\t\t//\n\t\t//is_awaitable<T>\n\t\t//is_awaitable_v<T>\n\t\t//判断是否可以被co_await操作。可以是一个awaitor，也可以是重载了成员变量的T::operator co_await()，或者被重载了全局的operator co_awaitor(T)\n\t\t//\n\t\t//is_callable<T>\n\t\t//is_callable_v<T>\n\t\t//判断是不是一个可被调用的类型，如函数，仿函数，lambda等\n\t\t//\n\t\t//is_iterator<T>\n\t\t//is_iterator_v<T>\n\t\t//判断是不是一个支持向后迭代的迭代器\n\t\t//\n\t\t//is_iterator_of_v<T, E>\n\t\t//判断是不是一个支持向后迭代的迭代器，并且迭代器通过 operator *()返回的类型是 E&。\n\t\t//\n\t\t//is_container<T>\n\t\t//is_container_v<T>\n\t\t//判断是不是一个封闭区间的容器，或者数组。\n\t\t//\n\t\t//is_container_of<T, E>\n\t\t//is_container_of_v<T, E>\n\t\t//判断是不是一个封闭区间的容器，或者数组。其元素类型是E。\n\n\t\ttemplate<class _Ty>\n\t\tstruct is_coroutine_handle : std::false_type {};\n\t\ttemplate<class _PromiseT>\n\t\tstruct is_coroutine_handle<coroutine_handle<_PromiseT>> : std::true_type {};\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool is_coroutine_handle_v = is_coroutine_handle<remove_cvref_t<_Ty>>::value;\n\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool is_valid_await_suspend_return_v = std::is_void_v<_Ty> || std::is_same_v<_Ty, bool> || is_coroutine_handle_v<_Ty>;\n\n\t\ttemplate<class _Ty, class = std::void_t<>>\n\t\tstruct is_awaitor : std::false_type {};\n\t\ttemplate<class _Ty>\n\t\tstruct is_awaitor\n\t\t\t<_Ty,\n\t\t\t\tstd::void_t<\n\t\t\t\t\tdecltype(std::declval<_Ty>().await_ready())\n\t\t\t\t\t, decltype(std::declval<_Ty>().await_suspend(std::declval<std::coroutine_handle<promise_t<>>>()))\n\t\t\t\t\t, decltype(std::declval<_Ty>().await_resume())\n\t\t\t\t>\n\t\t\t>\n\t\t\t: std::bool_constant<\n\t\t\t\tstd::is_constructible_v<bool, decltype(std::declval<_Ty>().await_ready())>\n\t\t\t\t&& is_valid_await_suspend_return_v<\n\t\t\t\t\tdecltype(std::declval<_Ty>().await_suspend(std::declval<std::coroutine_handle<promise_t<>>>()))\n\t\t\t\t>\n\t\t\t>\n\t\t{};\n\t\ttemplate<class _Ty>\n\t\tstruct is_awaitor<_Ty&> : is_awaitor<_Ty> {};\n\t\ttemplate<class _Ty>\n\t\tstruct is_awaitor<_Ty&&> : is_awaitor<_Ty> {};\n\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool is_awaitor_v = is_awaitor<remove_cvref_t<_Ty>>::value;\n\n\t\ttemplate<class _Ty, class = std::void_t<>>\n\t\tstruct is_future : std::false_type {};\n\t\ttemplate<class _Ty>\n\t\tstruct is_future<_Ty,\n\t\t\t\tstd::void_t<\n\t\t\t\t\tdecltype(std::declval<_Ty>()._state),\n\t\t\t\t\ttypename _Ty::value_type,\n\t\t\t\t\ttypename _Ty::state_type,\n\t\t\t\t\ttypename _Ty::promise_type\n\t\t\t\t>\n\t\t\t> : std::true_type {};\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool is_future_v = is_future<remove_cvref_t<_Ty>>::value;\n\n\t\ttemplate<class _Ty>\n\t\tstruct is_promise : std::false_type {};\n\t\ttemplate<class _Ty>\n\t\tstruct is_promise<promise_t<_Ty>> : std::true_type {};\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool is_promise_v = is_promise<remove_cvref_t<_Ty>>::value;\n\n\t\ttemplate<class _Ty>\n\t\tstruct is_generator : std::false_type {};\n\t\ttemplate <class _Ty, class _Alloc>\n\t\tstruct is_generator<generator_t<_Ty, _Alloc>> : std::true_type {};\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool is_generator_v = is_generator<remove_cvref_t<_Ty>>::value;\n\n\t\ttemplate<class _Ty, class = std::void_t<>>\n\t\tstruct is_state_pointer : std::false_type {};\n\t\ttemplate<class _Ty>\n\t\tstruct is_state_pointer<_Ty, std::void_t<std::enable_if_t<std::is_convertible_v<_Ty, state_base_t*>>>> : std::true_type {};\n\t\ttemplate<class _Ty>\n\t\tstruct is_state_pointer<counted_ptr<_Ty>> : is_state_pointer<_Ty> {};\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool is_state_pointer_v = is_state_pointer<remove_cvref_t<_Ty>>::value;\n\n\n\t\ttemplate<class _Ty, class = std::void_t<>>\n\t\tstruct has_state : std::false_type {};\n\t\ttemplate<class _Ty>\n\t\tstruct has_state<_Ty, std::void_t<decltype(std::declval<_Ty>()._state)>> : std::true_type {};\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool has_state_v = has_state<remove_cvref_t<_Ty>>::value;\n\n\n\t\t//copy from cppcoro\n\t\tnamespace detail\n\t\t{\n\t\t\ttemplate<class T>\n\t\t\tauto get_awaitor_impl(T&& value, int) noexcept(noexcept(static_cast<T&&>(value).operator co_await()))\n\t\t\t\t-> decltype(static_cast<T&&>(value).operator co_await())\n\t\t\t{\n\t\t\t\treturn static_cast<T&&>(value).operator co_await();\n\t\t\t}\n\t\t\ttemplate<class T>\n\t\t\tauto get_awaitor_impl(T&& value, long) noexcept(noexcept(operator co_await(static_cast<T&&>(value))))\n\t\t\t\t-> decltype(operator co_await(static_cast<T&&>(value)))\n\t\t\t{\n\t\t\t\treturn operator co_await(static_cast<T&&>(value));\n\t\t\t}\n\t\t\ttemplate<class T, std::enable_if_t<is_awaitor_v<T>, int> = 0>\n\t\t\tT&& get_awaitor_impl(T&& value, std::any) noexcept\n\t\t\t{\n\t\t\t\treturn static_cast<T&&>(value);\n\t\t\t}\n\t\t}\n\n\t\ttemplate<class T>\n\t\tauto get_awaitor(T&& value) noexcept(noexcept(detail::get_awaitor_impl(static_cast<T&&>(value), 123)))\n\t\t\t-> decltype(detail::get_awaitor_impl(static_cast<T&&>(value), 123))\n\t\t{\n\t\t\treturn detail::get_awaitor_impl(static_cast<T&&>(value), 123);\n\t\t}\n\n\t\ttemplate<class _Ty, class = std::void_t<>>\n\t\tstruct awaitor_traits{};\n\t\ttemplate<class _Ty>\n\t\tstruct awaitor_traits<_Ty, \n\t\t\tstd::void_t<decltype(get_awaitor(std::declval<_Ty>()))>\n\t\t>\n\t\t{\n\t\t\tusing type = decltype(get_awaitor(std::declval<_Ty>()));\n\t\t\tusing value_type = decltype(std::declval<type>().await_resume());\n\t\t};\n\n\t\ttemplate<class _Ty, class = std::void_t<>>\n\t\tstruct is_awaitable : std::false_type {};\n\t\ttemplate<class _Ty>\n\t\tstruct is_awaitable<_Ty, \n\t\t\tstd::void_t<typename awaitor_traits<_Ty>::value_type>\n\t\t> : std::true_type {};\n\t\ttemplate<typename _Ty>\n\t\tconstexpr bool is_awaitable_v = is_awaitable<_Ty>::value;\n\n\n\t\ttemplate<typename _Ty, class = std::void_t<>>\n\t\tstruct is_callable : std::false_type{};\n\t\ttemplate<typename _Ty>\n\t\tstruct is_callable<_Ty, std::void_t<decltype(std::declval<_Ty>()())>> : std::true_type {};\n\t\ttemplate<typename _Ty>\n\t\tconstexpr bool is_callable_v = is_callable<_Ty>::value;\n\n\t\ttemplate<class _Ty, class = std::void_t<>>\n\t\tstruct is_iterator : std::false_type {};\n\t\ttemplate<class _Ty>\n\t\tstruct is_iterator\n\t\t\t<_Ty,\n\t\t\t\tstd::void_t<\n\t\t\t\t\tdecltype(std::declval<_Ty>() + 1)\n\t\t\t\t\t, decltype(std::declval<_Ty>() != std::declval<_Ty>())\n\t\t\t\t\t, decltype(*std::declval<_Ty>())\n\t\t\t\t>\n\t\t\t>\n\t\t\t: std::true_type{};\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool is_iterator_v = is_iterator<_Ty>::value;\n\t\ttemplate<class _Ty, class _Ety>\n\t\tconstexpr bool is_iterator_of_v = std::conjunction<\n\t\t\t\tis_iterator<_Ty>\n\t\t\t\t, std::is_same<_Ety&, decltype(*std::declval<_Ty>())>\n\t\t\t>::value;\n\n\t\ttemplate<class _Ty, class = std::void_t<>>\n\t\tstruct is_container : std::false_type {};\n\t\ttemplate<class _Ty>\n\t\tstruct is_container\n\t\t\t<_Ty,\n\t\t\t\tstd::void_t<\n\t\t\t\t\tdecltype(std::begin(std::declval<_Ty>()))\n\t\t\t\t\t, decltype(std::end(std::declval<_Ty>()))\n\t\t\t\t>\n\t\t\t>\n\t\t\t: is_iterator<decltype(std::begin(std::declval<_Ty>()))> {};\n\t\ttemplate<class _Ty, size_t _Size>\n\t\tstruct is_container<_Ty[_Size]> : std::true_type {};\n\t\ttemplate<class _Ty, size_t _Size>\n\t\tstruct is_container<_Ty(&)[_Size]> : std::true_type {};\n\t\ttemplate<class _Ty, size_t _Size>\n\t\tstruct is_container<_Ty(&&)[_Size]> : std::true_type {};\n\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool is_container_v = is_container<remove_cvref_t<_Ty>>::value;\n\n\t\ttemplate<class _Ty, class _Ety, class = std::void_t<>>\n\t\tstruct is_container_of : std::false_type {};\n\t\ttemplate<class _Ty, class _Ety>\n\t\tstruct is_container_of\n\t\t\t<_Ty, _Ety,\n\t\t\t\tstd::void_t<\n\t\t\t\t\tdecltype(std::begin(std::declval<_Ty>()))\n\t\t\t\t\t, decltype(std::end(std::declval<_Ty>()))\n\t\t\t\t>\n\t\t\t>\n\t\t\t: std::conjunction<\n\t\t\t\tis_iterator<decltype(std::begin(std::declval<_Ty>()))>\n\t\t\t\t, std::is_same<_Ety, remove_cvref_t<decltype(*std::begin(std::declval<_Ty>()))>>\n\t\t\t> {};\n\t\ttemplate<class _Ty, size_t _Size>\n\t\tstruct is_container_of<_Ty[_Size], _Ty> : std::true_type {};\n\t\ttemplate<class _Ty, size_t _Size>\n\t\tstruct is_container_of<_Ty(&)[_Size], _Ty> : std::true_type {};\n\t\ttemplate<class _Ty, size_t _Size>\n\t\tstruct is_container_of<_Ty(&&)[_Size], _Ty> : std::true_type {};\n\n\t\ttemplate<class _Ty, class _Ety>\n\t\tconstexpr bool is_container_of_v = is_container_of<remove_cvref_t<_Ty>, _Ety>::value;\n\t}\n}\n"
  },
  {
    "path": "include/librf/src/unix/clang_builtin.h",
    "content": "#pragma once\n\n//BUILTIN(__builtin_coro_resume, \"vv*\", \"\")\n//BUILTIN(__builtin_coro_destroy, \"vv*\", \"\")\n//BUILTIN(__builtin_coro_done, \"bv*\", \"n\")\n//BUILTIN(__builtin_coro_promise, \"v*v*IiIb\", \"n\")\n//\n//BUILTIN(__builtin_coro_size, \"z\", \"n\")\n//BUILTIN(__builtin_coro_frame, \"v*\", \"n\")\n//BUILTIN(__builtin_coro_noop, \"v*\", \"n\")\n//BUILTIN(__builtin_coro_free, \"v*v*\", \"n\")\n//\n//BUILTIN(__builtin_coro_id, \"v*Iiv*v*v*\", \"n\")\n//BUILTIN(__builtin_coro_alloc, \"b\", \"n\")\n//BUILTIN(__builtin_coro_begin, \"v*v*\", \"n\")\n//BUILTIN(__builtin_coro_end, \"bv*Ib\", \"n\")\n//BUILTIN(__builtin_coro_suspend, \"cIb\", \"n\")\n//BUILTIN(__builtin_coro_param, \"bv*v*\", \"n\")\n\nextern \"C\" void  __builtin_coro_resume(void* addr);\nextern \"C\" void  __builtin_coro_destroy(void* addr);\nextern \"C\" bool  __builtin_coro_done(void* addr);\nextern \"C\" void* __builtin_coro_promise(void* addr, int alignment, bool from_promise);\n#pragma intrinsic(__builtin_coro_resume)\n#pragma intrinsic(__builtin_coro_destroy)\n#pragma intrinsic(__builtin_coro_done)\n#pragma intrinsic(__builtin_coro_promise)\n\nextern \"C\" size_t __builtin_coro_size();\nextern \"C\" void* __builtin_coro_frame();\nextern \"C\" void* __builtin_coro_noop();\nextern \"C\" void* __builtin_coro_free(void* coro_frame);\n#pragma intrinsic(__builtin_coro_size)\n#pragma intrinsic(__builtin_coro_frame)\n#pragma intrinsic(__builtin_coro_noop)\n#pragma intrinsic(__builtin_coro_free)\n\nextern \"C\" void* __builtin_coro_id(int align, void* promise, void* fnaddr, void* parts);\nextern \"C\" bool  __builtin_coro_alloc();\n//extern \"C\" void* __builtin_coro_begin(void* memory);\n//extern \"C\" void* __builtin_coro_end(void* coro_frame, bool unwind);\nextern \"C\" char  __builtin_coro_suspend(bool final);\n//extern \"C\" bool  __builtin_coro_param(void* original, void* copy);\n#pragma intrinsic(__builtin_coro_id)\n#pragma intrinsic(__builtin_coro_alloc)\n//#pragma intrinsic(__builtin_coro_begin)\n//#pragma intrinsic(__builtin_coro_end)\n#pragma intrinsic(__builtin_coro_suspend)\n//#pragma intrinsic(__builtin_coro_param)\n\n#ifdef __clang__\n#define _coro_frame_size() __builtin_coro_size()\n#define _coro_frame_ptr() __builtin_coro_frame()\n#endif"
  },
  {
    "path": "include/librf/src/unix/coroutine.h",
    "content": "//===----------------------------- coroutine -----------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef _LIBCPP_EXPERIMENTAL_COROUTINE\n#define _LIBCPP_EXPERIMENTAL_COROUTINE\n\n#define _EXPERIMENTAL_COROUTINE_\n\n#include <new>\n#include <type_traits>\n#include <functional>\n#include <memory> // for hash<T*>\n#include <cstddef>\n#include <cassert>\n#include <compare>\n\n#if defined(__clang__)\n#include \"clang_builtin.h\"\n#elif defined(__GNUC__)\n#include \"gcc_builtin.h\"\n#endif\n\n#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)\n#pragma GCC system_header\n#endif\n\n#ifndef _LIBCPP_HAS_NO_COROUTINES\n\nnamespace std {\n#if defined(__GNUC__)\n\tinline\n#endif\n\tnamespace experimental {\n\n\t\ttemplate <class _Tp, class = void>\n\t\tstruct __coroutine_traits_sfinae {};\n\n\t\ttemplate <class _Tp>\n\t\tstruct __coroutine_traits_sfinae<_Tp, void_t<typename _Tp::promise_type>>\n\t\t{\n\t\t\tusing promise_type = typename _Tp::promise_type;\n\t\t};\n\n\t\t// 17.12.2, coroutine traits\n\t\ttemplate <typename _Ret, typename... _Args>\n\t\tstruct coroutine_traits : public __coroutine_traits_sfinae<_Ret>\n\t\t{\n\t\t};\n\n\t\t// 17.12.3, coroutine handle\n\t\ttemplate <typename _Promise = void>\n\t\tclass coroutine_handle;\n\n\t\ttemplate <>\n\t\tclass coroutine_handle<void>\n\t\t{\n\t\tpublic:\n\t\t\t// 17.12.3.1, construct/reset\n\t\t\tconstexpr coroutine_handle() noexcept : __handle_(nullptr) {}\n\t\t\tconstexpr coroutine_handle(nullptr_t) noexcept : __handle_(nullptr) {}\n\t\t\tcoroutine_handle& operator=(nullptr_t) noexcept {\n\t\t\t\t__handle_ = nullptr;\n\t\t\t\treturn *this;\n\t\t\t}\n\n\t\t\t// 17.12.3.2, export/import\n\t\t\tconstexpr void* address() const noexcept { return __handle_; }\n\t\t\tstatic constexpr coroutine_handle from_address(void* __addr) noexcept {\n\t\t\t\tcoroutine_handle __tmp;\n\t\t\t\t__tmp.__handle_ = __addr;\n\t\t\t\treturn __tmp;\n\t\t\t}\n\t\t\t// FIXME: Should from_address(nullptr) be allowed?\n\t\t\tstatic constexpr coroutine_handle from_address(nullptr_t) noexcept {\n\t\t\t\treturn coroutine_handle(nullptr);\n\t\t\t}\n\n\t\t\ttemplate <class _Tp, bool _CallIsValid = false>\n\t\t\tstatic coroutine_handle from_address(_Tp*) {\n\t\t\t\tstatic_assert(_CallIsValid,\n\t\t\t\t\t\"coroutine_handle<void>::from_address cannot be called with \"\n\t\t\t\t\t\"non-void pointers\");\n\t\t\t}\n\n\t\t\t// 17.12.3.3, observers\n\t\t\tconstexpr explicit operator bool() const noexcept {\n\t\t\t\treturn __handle_ != nullptr; \n\t\t\t}\n\t\t\tbool done() const {\n\t\t\t\treturn __builtin_coro_done(__handle_);\n\t\t\t}\n\n\t\t\t// 17.12.3.4, resumption \n\t\t\tvoid operator()() const { resume(); }\n\t\t\tvoid resume() const {\n\t\t\t\t__builtin_coro_resume(__handle_);\n\t\t\t}\n\t\t\tvoid destroy() const{\n\t\t\t\t__builtin_coro_destroy(__handle_);\n\t\t\t}\n\t\tprivate:\n\t\t\tbool __is_suspended() const noexcept {\n\t\t\t\t// FIXME actually implement a check for if the coro is suspended.\n\t\t\t\treturn __handle_;\n\t\t\t}\n\n\t\t\ttemplate <class _PromiseT>\n\t\t\tfriend class coroutine_handle;\n\n\t\t\tvoid* __handle_;\n\t\t};\n\n\t\t// 17.12.3.6, comparison operators\n\t\tinline bool operator==(coroutine_handle<> __x, coroutine_handle<> __y) noexcept {\n\t\t\treturn __x.address() == __y.address();\n\t\t}\n\t\tinline bool operator!=(coroutine_handle<> __x, coroutine_handle<> __y) noexcept {\n\t\t\treturn __x.address() != __y.address();\n\t\t}\n\t\tinline bool operator<(coroutine_handle<> __x, coroutine_handle<> __y) noexcept {\n\t\t\treturn __x.address() < __y.address();\n\t\t}\n\t\tinline bool operator>(coroutine_handle<> __x, coroutine_handle<> __y) noexcept {\n\t\t\treturn __x.address() > __y.address();\n\t\t}\n\t\tinline bool operator<=(coroutine_handle<> __x, coroutine_handle<> __y) noexcept {\n\t\t\treturn __x.address() <= __y.address();\n\t\t}\n\t\tinline bool operator>=(coroutine_handle<> __x, coroutine_handle<> __y) noexcept {\n\t\t\treturn __x.address() >= __y.address();\n\t\t}\n\n\t\ttemplate <typename _Promise>\n\t\tclass coroutine_handle : public coroutine_handle<> \n\t\t{\n\t\t\tusing _Base = coroutine_handle<>;\n\t\tpublic:\n\t\t\t// 17.12.3.1, construct/reset \n\t\t\tusing coroutine_handle<>::coroutine_handle;\n\t\t\tcoroutine_handle& operator=(nullptr_t) noexcept {\n\t\t\t\t_Base::operator=(nullptr);\n\t\t\t\treturn *this;\n\t\t\t}\n\n\t\t\t// 17.12.3.5, promise access\n\t\t\t_Promise& promise() const {\n\t\t\t\treturn *static_cast<_Promise*>(\n\t\t\t\t\t__builtin_coro_promise(this->__handle_, alignof(_Promise), false));\n\t\t\t}\n\n\t\tpublic:\n\t\t\t// 17.12.3.2, export/import\n\t\t\tstatic constexpr coroutine_handle from_address(void* __addr) noexcept {\n\t\t\t\tcoroutine_handle __tmp;\n\t\t\t\t__tmp.__handle_ = __addr;\n\t\t\t\treturn __tmp;\n\t\t\t}\n\n\t\t\t// NOTE: this overload isn't required by the standard but is needed so\n\t\t\t// the deleted _Promise* overload doesn't make from_address(nullptr)\n\t\t\t// ambiguous.\n\t\t\t// FIXME: should from_address work with nullptr?\n\t\t\tstatic constexpr coroutine_handle from_address(nullptr_t) noexcept {\n\t\t\t\treturn coroutine_handle(nullptr);\n\t\t\t}\n\n\t\t\ttemplate <class _Tp, bool _CallIsValid = false>\n\t\t\tstatic coroutine_handle from_address(_Tp*) {\n\t\t\t\tstatic_assert(_CallIsValid,\n\t\t\t\t\t\"coroutine_handle<promise_type>::from_address cannot be called with \"\n\t\t\t\t\t\"non-void pointers\");\n\t\t\t}\n\n\t\t\ttemplate <bool _CallIsValid = false>\n\t\t\tstatic coroutine_handle from_address(_Promise*) {\n\t\t\t\tstatic_assert(_CallIsValid,\n\t\t\t\t\t\"coroutine_handle<promise_type>::from_address cannot be used with \"\n\t\t\t\t\t\"pointers to the coroutine's promise type; use 'from_promise' instead\");\n\t\t\t}\n\n\t\t\tstatic coroutine_handle from_promise(_Promise& __promise) noexcept {\n\t\t\t\ttypedef typename remove_cv<_Promise>::type _RawPromise;\n\t\t\t\tcoroutine_handle __tmp;\n\t\t\t\t__tmp.__handle_ = __builtin_coro_promise(\n\t\t\t\t\tstd::addressof(const_cast<_RawPromise&>(__promise)),\n\t\t\t\t\talignof(_Promise), true);\n\t\t\t\treturn __tmp;\n\t\t\t}\n\t\t};\n\n\t\t// 17.12.4, no-op coroutines\n#if __has_builtin(__builtin_coro_noop)\n\t\tstruct noop_coroutine_promise {};\n\n\t\ttemplate <>\n\t\tclass coroutine_handle<noop_coroutine_promise> : public coroutine_handle<>\n\t\t{\n\t\t\tusing _Base = coroutine_handle<>;\n\t\t\tusing _Promise = noop_coroutine_promise;\n\t\tpublic:\n\t\t\t// 17.12.4.2.3, promise access \n\t\t\t_Promise& promise() const noexcept {\n\t\t\t\treturn *static_cast<_Promise*>(\n\t\t\t\t\t__builtin_coro_promise(this->__handle_, alignof(_Promise), false));\n\t\t\t}\n\t\t\t\n\t\t\t// 17.12.4.2.4, address \n\t\t\tconstexpr void* address() const noexcept {\n\t\t\t\treturn this->__handle_;\n\t\t\t}\n\n\t\t\t// 17.12.4.2.1, observers \n\t\t\tconstexpr explicit operator bool() const noexcept { return true; }\n\t\t\tconstexpr bool done() const noexcept { return false; }\n\n\t\t\t// 17.12.4.2.2, resumption\n\t\t\tconstexpr void operator()() const noexcept {}\n\t\t\tconstexpr void resume() const noexcept {}\n\t\t\tconstexpr void destroy() const noexcept {}\n\t\tprivate:\n\t\t\tfriend coroutine_handle<noop_coroutine_promise> noop_coroutine() noexcept;\n\t\t\tcoroutine_handle() noexcept {\n\t\t\t\tthis->__handle_ = __builtin_coro_noop();\n\t\t\t}\n\t\t};\n\n\t\tusing noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;\n\t\t\n\t\tinline noop_coroutine_handle noop_coroutine() noexcept {\n\t\t\treturn noop_coroutine_handle();\n\t\t}\n#endif // __has_builtin(__builtin_coro_noop)\n\n\t\t// 17.12.5, trivial awaitables\n\t\tstruct suspend_never {\n\t\t\tconstexpr bool await_ready() const noexcept { return true; }\n\t\t\tconstexpr void await_suspend(coroutine_handle<>) const noexcept {}\n\t\t\tconstexpr void await_resume() const noexcept {}\n\t\t};\n\t\tstruct suspend_always {\n\t\t\tconstexpr bool await_ready() const noexcept { return false; }\n\t\t\tconstexpr void await_suspend(coroutine_handle<>) const noexcept {}\n\t\t\tconstexpr void await_resume() const noexcept {}\n\t\t};\n\t}\n\n\t// 17.12.3.7, hash support\n\ttemplate <class _Tp>\n\tstruct hash<experimental::coroutine_handle<_Tp>> {\n\t\tusing argument_type = experimental::coroutine_handle<_Tp>;\n\t\tusing result_type = size_t;\n\t\tusing __arg_type = experimental::coroutine_handle<_Tp>;\n\t\tsize_t operator()(__arg_type const& __v) const noexcept\n\t\t{\n\t\t\treturn hash<void*>()(__v.address());\n\t\t}\n\t};\n}\n\n#endif // !defined(_LIBCPP_HAS_NO_COROUTINES)\n\n#endif /* _LIBCPP_EXPERIMENTAL_COROUTINE */"
  },
  {
    "path": "include/librf/src/unix/gcc_builtin.h",
    "content": "#pragma once\n\n#if 0\n\nextern \"C\" void  __builtin_coro_resume(void* addr);\nextern \"C\" void  __builtin_coro_destroy(void* addr);\nextern \"C\" bool  __builtin_coro_done(void* addr);\nextern \"C\" void* __builtin_coro_promise(void* addr, const long unsigned int alignment, bool from_promise);\n#pragma intrinsic(__builtin_coro_resume)\n#pragma intrinsic(__builtin_coro_destroy)\n#pragma intrinsic(__builtin_coro_done)\n#pragma intrinsic(__builtin_coro_promise)\n\n#endif\n"
  },
  {
    "path": "include/librf/src/when.h",
    "content": "﻿#pragma once\n\n//#include \"when_v1.h\"\n#include \"when_v2.h\"\n"
  },
  {
    "path": "include/librf/src/when_v2.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\tusing any_t = std::any;\n\tusing std::any_cast;\n}\n\n//纠结过when_any的返回值，是选用index + std::any，还是选用std::variant<>。最终选择了std::any。\n//std::variant<>存在第一个元素不能默认构造的问题，需要使用std::monostate来占位，导致下标不是从0开始。\n//而且，std::variant<>里面存在类型重复的问题，好几个操作都是病态的\n//最最重要的，要统一ranged when_any的返回值，还得做一个运行时通过下标设置std::variant<>的东西\n//std::any除了内存布局不太理想，其他方面几乎没缺点（在此应用下）\n\nnamespace librf\n{\n#ifndef DOXYGEN_SKIP_PROPERTY\n\tusing when_any_pair = std::pair<intptr_t, any_t>;\n\tusing when_any_pair_ptr = std::shared_ptr<when_any_pair>;\n\n\tnamespace detail\n\t{\n\t\tstruct state_when_t : public state_base_t\n\t\t{\n\t\t\tLIBRF_API state_when_t(intptr_t counter_);\n\n\t\t\tLIBRF_API virtual void resume() override;\n\t\t\tLIBRF_API virtual bool has_handler() const  noexcept override;\n\n\t\t\tLIBRF_API void on_cancel() noexcept;\n\t\t\tLIBRF_API bool on_notify_one();\n\t\t\tLIBRF_API bool on_timeout();\n\n\t\t\t//将自己加入到通知链表里\n\t\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\t\tscheduler_t* on_await_suspend(coroutine_handle<_PromiseT> handler) noexcept\n\t\t\t{\n\t\t\t\t_PromiseT& promise = handler.promise();\n\t\t\t\tauto* parent_state = promise.get_state();\n\t\t\t\tscheduler_t* sch = parent_state->get_scheduler();\n\n\t\t\t\tthis->_scheduler = sch;\n\t\t\t\tthis->_coro = handler;\n\n\t\t\t\treturn sch;\n\t\t\t}\n\n\t\t\ttypedef spinlock lock_type;\n\t\t\tlock_type _lock;\n\t\t\tstd::atomic<intptr_t> _counter;\n\t\t};\n\n\t\ttemplate<class _Ty>\n\t\tstruct [[nodiscard]] when_future_t\n\t\t{\n\t\t\tusing value_type = _Ty;\n\t\t\tusing state_type = detail::state_when_t;\n\t\t\tusing promise_type = promise_t<value_type>;\n\t\t\tusing future_type = when_future_t<value_type>;\n\t\t\tusing lock_type = typename state_type::lock_type;\n\n\t\t\tcounted_ptr<state_type> _state;\n\t\t\tstd::shared_ptr<value_type> _values;\n\n\t\t\twhen_future_t(intptr_t count_) noexcept\n\t\t\t\t: _state(new state_type(count_))\n\t\t\t\t, _values(std::make_shared<value_type>())\n\t\t\t{\n\t\t\t}\n\n\t\t\tbool await_ready() noexcept\n\t\t\t{\n\t\t\t\treturn _state->_counter.load(std::memory_order_relaxed) == 0;\n\t\t\t}\n\n\t\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\t\tvoid await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t\t{\n\t\t\t\t_state->on_await_suspend(handler);\n\t\t\t}\n\n\t\t\tvalue_type await_resume() noexcept(std::is_nothrow_move_constructible_v<value_type>)\n\t\t\t{\n\t\t\t\treturn std::move(*_values);\n\t\t\t}\n\t\t};\n\n\n\t\tusing ignore_type = std::remove_const_t<decltype(std::ignore)>;\n\n\t\ttemplate<class _Ty>\n\t\tstruct convert_void_2_ignore\n\t\t{\n\t\t\tusing type = std::remove_reference_t<_Ty>;\n\t\t\tusing value_type = type;\n\t\t};\n\t\ttemplate<>\n\t\tstruct convert_void_2_ignore<void>\n\t\t{\n\t\t\tusing type = void;\n\t\t\tusing value_type = ignore_type;\n\t\t};\n\n\t\ttemplate<class _Ty, bool = traits::is_callable_v<_Ty>>\n\t\tstruct awaitor_result_impl2\n\t\t{\n\t\t\tusing value_type = typename convert_void_2_ignore<\n\t\t\t\ttypename traits::awaitor_traits<_Ty>::value_type\n\t\t\t>::value_type;\n\t\t};\n\t\ttemplate<class _Ty>\n\t\tstruct awaitor_result_impl2<_Ty, true> : awaitor_result_impl2<decltype(std::declval<_Ty>()()), false> {};\n\n\t\ttemplate<class... _Ty>\n\t\tstruct awaitor_result_impl{};\n\n\t\ttemplate<class _Ty>\n\t\tstruct awaitor_result_impl<_Ty> : public awaitor_result_impl2<_Ty> {};\n\t\ttemplate<_WhenTaskT _Ty>\n\t\tusing awaitor_result_t = typename awaitor_result_impl<std::remove_reference_t<_Ty>>::value_type;\n\n\n\t\ttemplate<class _Ty>\n\t\tstruct is_when_task : std::bool_constant<traits::is_awaitable_v<_Ty> || traits::is_callable_v<_Ty>> {};\n\t\ttemplate<class _Ty>\n\t\tconstexpr bool is_when_task_v = is_when_task<_Ty>::value;\n\n\t\ttemplate<class _Ty, class _Task = decltype(*std::declval<_Ty>())>\n\t\tconstexpr bool is_when_task_iter_v = traits::is_iterator_v<_Ty> && is_when_task_v<_Task>;\n\n\t\ttemplate<_WhenTaskT _Awaitable>\n\t\tdecltype(auto) when_real_awaitor(_Awaitable&& awaitor)\n\t\t{\n\t\t\tif constexpr (traits::is_callable_v<_Awaitable>)\n\t\t\t\treturn awaitor();\n\t\t\telse\n\t\t\t\treturn std::forward<_Awaitable>(awaitor);\n\t\t}\n\n\t\ttemplate<_WhenTaskT _Awaitable, class _Ty>\n\t\tfuture_t<> when_all_connector_1(state_when_t* state, _Awaitable task, _Ty& value)\n\t\t{\n\t\t\tdecltype(auto) awaitor = when_real_awaitor(task);\n\n\t\t\tif constexpr (std::is_same_v<_Ty, ignore_type>)\n\t\t\t\tco_await awaitor;\n\t\t\telse\n\t\t\t\tvalue = co_await awaitor;\n\t\t\tstate->on_notify_one();\n\t\t};\n\n\t\ttemplate<class _FuckBoolean>\n\t\tusing _FuckBoolVectorReference = typename std::vector<_FuckBoolean>::reference;\n\n\t\ttemplate<_WhenTaskT _Awaitable, class _Ty>\n\t\tfuture_t<> when_all_connector_2(state_when_t* state,_Awaitable task, _FuckBoolVectorReference<_Ty> value)\n\t\t{\n\t\t\tauto&& awaitor = when_real_awaitor(task);\n\n\t\t\tif constexpr(std::is_same_v<_Ty, ignore_type>)\n\t\t\t\tco_await awaitor;\n\t\t\telse\n\t\t\t\tvalue = co_await awaitor;\n\t\t\tstate->on_notify_one();\n\t\t};\n\n\t\ttemplate<class _Tup, size_t _Idx>\n\t\tinline void when_all_one__(scheduler_t& , state_when_t*, _Tup& )\n\t\t{\n\t\t}\n\n\t\ttemplate<class _Tup, size_t _Idx, _WhenTaskT _Awaitable, _WhenTaskT... _Rest>\n\t\tinline void when_all_one__(scheduler_t& sch, state_when_t* state, _Tup& values, _Awaitable&& awaitable, _Rest&&... rest)\n\t\t{\n\t\t\tsch + when_all_connector_1(state, std::forward<_Awaitable>(awaitable), std::get<_Idx>(values));\n\n\t\t\twhen_all_one__<_Tup, _Idx + 1, _Rest...>(sch, state, values, std::forward<_Rest>(rest)...);\n\t\t}\n\n\t\ttemplate<class _Val, _WhenIterT _Iter>\n\t\tinline void when_all_range__(scheduler_t& sch, state_when_t* state, std::vector<_Val> & values, _Iter begin, _Iter end)\n\t\t{\n\t\t\tusing _Awaitable = std::remove_reference_t<decltype(*begin)>;\n\n\t\t\tintptr_t _Idx = 0;\n\t\t\tfor (; begin != end; ++begin, ++_Idx)\n\t\t\t{\n\t\t\t\tsch + when_all_connector_2<_Awaitable, _Val>(state, *begin, values[_Idx]);\n\t\t\t}\n\t\t}\n\n//-----------------------------------------------------------------------------------------------------------------------------------------\n\n\t\ttemplate<_WhenTaskT _Awaitable>\n\t\tfuture_t<> when_any_connector(counted_ptr<state_when_t> state, _Awaitable task, when_any_pair_ptr value, intptr_t idx)\n\t\t{\n\t\t\tassert(idx >= 0);\n\n\t\t\tauto awaitor = when_real_awaitor(task);\n\n\t\t\tusing value_type = awaitor_result_t<decltype(awaitor)>;\n\n\t\t\tif constexpr (std::is_same_v<value_type, ignore_type>)\n\t\t\t{\n\t\t\t\tco_await awaitor;\n\n\t\t\t\tintptr_t oldValue = -1;\n\t\t\t\tif (reinterpret_cast<std::atomic<intptr_t>&>(value->first).compare_exchange_strong(oldValue, idx))\n\t\t\t\t{\n\t\t\t\t\tstate->on_notify_one();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdecltype(auto) result = co_await awaitor;\n\n\t\t\t\tintptr_t oldValue = -1;\n\t\t\t\tif (reinterpret_cast<std::atomic<intptr_t>&>(value->first).compare_exchange_strong(oldValue, idx))\n\t\t\t\t{\n\t\t\t\t\tvalue->second = std::move(result);\n\n\t\t\t\t\tstate->on_notify_one();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tinline void when_any_one__(scheduler_t&, state_when_t*, when_any_pair_ptr, intptr_t)\n\t\t{\n\t\t}\n\n\t\ttemplate<_WhenTaskT _Awaitable, _WhenTaskT... _Rest>\n\t\tinline void when_any_one__(scheduler_t& sch, state_when_t* state, when_any_pair_ptr value, intptr_t _Idx, _Awaitable&& awaitable, _Rest&&... rest)\n\t\t{\n\t\t\tsch + when_any_connector(state, std::forward<_Awaitable>(awaitable), value, _Idx);\n\n\t\t\twhen_any_one__(sch, state, value, _Idx + 1, std::forward<_Rest>(rest)...);\n\t\t}\n\n\t\ttemplate<_WhenIterT _Iter>\n\t\tinline void when_any_range__(scheduler_t& sch, state_when_t* state, when_any_pair_ptr value, _Iter begin, _Iter end)\n\t\t{\n\t\t\tusing _Awaitable = std::remove_reference_t<decltype(*begin)>;\n\n\t\t\tintptr_t _Idx = 0;\n\t\t\tfor (; begin != end; ++begin, ++_Idx)\n\t\t\t{\n\t\t\t\tsch + when_any_connector<_Awaitable>(state, *begin, value, static_cast<intptr_t>(_Idx));\n\t\t\t}\n\t\t}\n\t}\n\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\n#ifndef DOXYGEN_SKIP_PROPERTY\ninline namespace when_v2\n{\n#else\n\t/**\n\t * @brief 目前不知道怎么在doxygen里面能搜集到全局函数的文档。故用一个结构体来欺骗doxygen。\n\t * @details 其下的所有成员函数，均是全局函数。\n\t */\n\tstruct when_{\n#endif\n\n\t/**\n\t * @brief 等待所有的可等待对象完成，不定参数版。\n\t * @param sch 当前协程的调度器。\n\t * @param args... 所有的可等待对象。要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @retval [co_await] std::tuple<...>。每个可等待对象的返回值，逐个存入到std::tuple<...>里面。void 返回值，存的是std::ignore。\n\t */\n\ttemplate<_WhenTaskT... _Awaitable>\n\tauto when_all(scheduler_t& sch, _Awaitable&&... args)\n\t\t-> detail::when_future_t<std::tuple<detail::awaitor_result_t<_Awaitable>...> >\n\t{\n\t\tusing tuple_type = std::tuple<detail::awaitor_result_t<_Awaitable>...>;\n\n\t\tdetail::when_future_t<tuple_type> awaitor{ sizeof...(_Awaitable) };\n\t\tdetail::when_all_one__<tuple_type, 0u, _Awaitable...>(sch, awaitor._state.get(), *awaitor._values, std::forward<_Awaitable>(args)...);\n\n\t\treturn awaitor;\n\t}\n\n\t/**\n\t * @brief 等待所有的可等待对象完成，迭代器版。\n\t * @param sch 当前协程的调度器。\n\t * @param begin 可等待对象容器的起始迭代器。迭代器指向的，要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @param end 可等待对象容器的结束迭代器。\n\t * @retval [co_await] std::vector<>。每个可等待对象的返回值，逐个存入到std::vector<>里面。void 返回值，存的是std::ignore。\n\t */\n\ttemplate<_WhenIterT _Iter>\n\tauto when_all(scheduler_t& sch, _Iter begin, _Iter end)\n\t\t-> detail::when_future_t<std::vector<detail::awaitor_result_t<decltype(*std::declval<_Iter>())> > >\n\t{\n\t\tusing value_type = detail::awaitor_result_t<decltype(*std::declval<_Iter>())>;\n\t\tusing vector_type = std::vector<value_type>;\n\n\t\tdetail::when_future_t<vector_type> awaitor{ std::distance(begin, end) };\n\t\tawaitor._values->resize(end - begin);\n\t\twhen_all_range__(sch, awaitor._state.get(), *awaitor._values, begin, end);\n\n\t\treturn awaitor;\n\t}\n\n\t/**\n\t * @brief 等待所有的可等待对象完成，容器版。\n\t * @param sch 当前协程的调度器。\n\t * @param cont 存访可等待对象的容器。容器内存放的，要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @retval [co_await] std::vector<>。每个可等待对象的返回值，逐个存入到std::vector<>里面。void 返回值，存的是std::ignore。\n\t */\n\ttemplate<_ContainerT _Cont>\n\tdecltype(auto) when_all(scheduler_t& sch, _Cont& cont)\n\t{\n\t\treturn when_all(sch, std::begin(cont), std::end(cont));\n\t}\n\n\t/**\n\t * @brief 等待所有的可等待对象完成，不定参数版。\n\t * @details 当前协程的调度器通过 librf_current_scheduler() 宏获得，与带调度器参数的版本相比，多一次resumeable function构造，效率可能低一点。\n\t * @param args... 所有的可等待对象。要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @retval [co_await] std::tuple<...>。每个可等待对象的返回值，逐个存入到std::tuple<...>里面。void 返回值，存的是std::ignore。\n\t */\n\ttemplate<_WhenTaskT... _Awaitable>\n\tauto when_all(_Awaitable&&... args)\n\t\t-> future_t<std::tuple<detail::awaitor_result_t<_Awaitable>...>>\n\t{\n\t\tscheduler_t* sch = librf_current_scheduler();\n\t\tco_return co_await when_all(*sch, std::forward<_Awaitable>(args)...);\n\t}\n\n\t/**\n\t * @brief 等待所有的可等待对象完成，迭代器版。\n\t * @details 当前协程的调度器通过 librf_current_scheduler() 宏获得，与带调度器参数的版本相比，多一次resumeable function构造，效率可能低一点。\n\t * @param begin 可等待对象容器的起始迭代器。迭代器指向的，要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @param end 可等待对象容器的结束迭代器。\n\t * @retval [co_await] std::vector<>。每个可等待对象的返回值，逐个存入到std::vector<>里面。void 返回值，存的是std::ignore。\n\t */\n\ttemplate<_WhenIterT _Iter>\n\tauto when_all(_Iter begin, _Iter end)\n\t\t-> future_t<std::vector<detail::awaitor_result_t<decltype(*begin)>>>\n\t{\n\t\tscheduler_t* sch = librf_current_scheduler();\n\t\tco_return co_await when_all(*sch, begin, end);\n\t}\n\n\t/**\n\t * @brief 等待所有的可等待对象完成，容器版。\n\t * @details 当前协程的调度器通过 librf_current_scheduler() 宏获得，与带调度器参数的版本相比，多一次resumeable function构造，效率可能低一点。\n\t * @param cont 存访可等待对象的容器。容器内存放的，要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @retval [co_await] std::vector<>。每个可等待对象的返回值，逐个存入到std::vector<>里面。void 返回值，存的是std::ignore。\n\t */\n\ttemplate<_ContainerT _Cont>\n\tauto when_all(_Cont&& cont)\n\t\t-> future_t<std::vector<detail::awaitor_result_t<decltype(*std::begin(cont))>>>\n\t{\n\t\treturn when_all(std::begin(cont), std::end(cont));\n\t}\n\n\n\n\t/**\n\t * @brief 等待任一的可等待对象完成，不定参数版。\n\t * @param sch 当前协程的调度器。\n\t * @param args... 所有的可等待对象。要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @retval [co_await] std::pair<intptr_t, std::any>。第一个值指示哪个对象完成了，第二个值存访的对应的返回数据。\n\t */\n\ttemplate<_WhenTaskT... _Awaitable>\n\tauto when_any(scheduler_t& sch, _Awaitable&&... args)\n\t\t-> detail::when_future_t<when_any_pair>\n\t{\n#pragma warning(disable : 6326)\t//warning C6326: Potential comparison of a constant with another constant.\n\t\tdetail::when_future_t<when_any_pair> awaitor{ sizeof...(_Awaitable) > 0 ? 1 : 0 };\n#pragma warning(default : 6326)\n\t\tawaitor._values->first = -1;\n\t\tdetail::when_any_one__(sch, awaitor._state.get(), awaitor._values, 0, std::forward<_Awaitable>(args)...);\n\n\t\treturn awaitor;\n\t}\n\n\t/**\n\t * @brief 等待任一的可等待对象完成，迭代器版。\n\t * @param sch 当前协程的调度器。\n\t * @param begin 可等待对象容器的起始迭代器。迭代器指向的，要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @param end 可等待对象容器的结束迭代器。\n\t * @retval [co_await] std::pair<intptr_t, std::any>。第一个值指示哪个对象完成了，第二个值存访的对应的返回数据。\n\t */\n\ttemplate<_WhenIterT _Iter>\n\tauto when_any(scheduler_t& sch, _Iter begin, _Iter end)\n\t\t-> detail::when_future_t<when_any_pair>\n\t{\n\t\tdetail::when_future_t<when_any_pair> awaitor{ begin == end ? 0 : 1 };\n\t\tawaitor._values->first = -1;\n\t\tdetail::when_any_range__<_Iter>(sch, awaitor._state.get(), awaitor._values, begin, end);\n\n\t\treturn awaitor;\n\t}\n\n\t/**\n\t * @brief 等待任一的可等待对象完成，容器版。\n\t * @param sch 当前协程的调度器。\n\t * @param cont 存访可等待对象的容器。容器内存放的，要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @retval [co_await] std::pair<intptr_t, std::any>。第一个值指示哪个对象完成了，第二个值存访的对应的返回数据。\n\t */\n\ttemplate<_ContainerT _Cont>\n\tauto when_any(scheduler_t& sch, _Cont& cont)\n\t\t-> detail::when_future_t<when_any_pair>\n\t{\n\t\treturn when_any(sch, std::begin(cont), std::end(cont));\n\t}\n\n\t/**\n\t * @brief 等待任一的可等待对象完成，不定参数版。\n\t * @details 当前协程的调度器通过 librf_current_scheduler() 宏获得，与带调度器参数的版本相比，多一次resumeable function构造，效率可能低一点。\n\t * @param args... 所有的可等待对象。要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @retval [co_await] std::pair<intptr_t, std::any>。第一个值指示哪个对象完成了，第二个值存访的对应的返回数据。\n\t */\n\ttemplate<_WhenTaskT... _Awaitable>\n\tauto when_any(_Awaitable&&... args)\n\t\t-> future_t<when_any_pair>\n\t{\n\t\tscheduler_t* sch = librf_current_scheduler();\n\t\tco_return co_await when_any(*sch, std::forward<_Awaitable>(args)...);\n\t}\n\n\t/**\n\t * @brief 等待任一的可等待对象完成，迭代器版。\n\t * @details 当前协程的调度器通过 librf_current_scheduler() 宏获得，与带调度器参数的版本相比，多一次resumeable function构造，效率可能低一点。\n\t * @param begin 可等待对象容器的起始迭代器。迭代器指向的，要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @param end 可等待对象容器的结束迭代器。\n\t * @retval [co_await] std::pair<intptr_t, std::any>。第一个值指示哪个对象完成了，第二个值存访的对应的返回数据。\n\t */\n\ttemplate<_WhenIterT _Iter>\n\tauto when_any(_Iter begin, _Iter end) \n\t\t-> future_t<when_any_pair>\n\t{\n\t\tscheduler_t* sch = librf_current_scheduler();\n\t\tco_return co_await when_any(*sch, begin, end);\n\t}\n\n\t/**\n\t * @brief 等待任一的可等待对象完成，容器版。\n\t * @details 当前协程的调度器通过 librf_current_scheduler() 宏获得，与带调度器参数的版本相比，多一次resumeable function构造，效率可能低一点。\n\t * @param cont 存访可等待对象的容器。容器内存放的，要么是_AwaitableT<>类型，要么是返回_AwaitableT<>类型的函数(对象)。\n\t * @retval [co_await] std::pair<intptr_t, std::any>。第一个值指示哪个对象完成了，第二个值存访的对应的返回数据。\n\t */\n\ttemplate<_ContainerT _Cont>\n\tauto when_any(_Cont&& cont)\n\t\t-> future_t<when_any_pair>\n\t{\n\t\treturn when_any(std::begin(cont), std::end(cont));\n\t}\n\n}\n}\n"
  },
  {
    "path": "include/librf/src/without_deadlock_assemble.inl",
    "content": "﻿\nstruct LOCK_ASSEMBLE_NAME(lock_impl)\n{\n\t// FUNCTION TEMPLATE _Unlock_locks\n\ttemplate<_LockAssembleT _LA>\n\tstatic void _Unlock_locks(int _First, int _Last, _LA& _LkN) noexcept /* terminates */\n\t{\n\t\tfor (; _First != _Last; ++_First) {\n\t\t\t_LkN._Unlock_ref(_LkN[_First]);\n\t\t}\n\t}\n\n\t// FUNCTION TEMPLATE try_lock\n\ttemplate<_LockAssembleT _LA>\n\tstatic auto _Try_lock_range(const int _First, const int _Last, _LA& _LkN)\n\t\t->decltype(_LkN._ReturnValue(123))\n\t{\n\t\tint _Next = _First;\n\t\ttry {\n\t\t\tfor (; _Next != _Last; ++_Next)\n\t\t\t{\n\t\t\t\tauto _Result__ = LOCK_ASSEMBLE_AWAIT(_LkN._Try_lock_ref(_LkN[_Next]));\n\t\t\t\tif (!_Result__)\n\t\t\t\t{ // try_lock failed, backout\n\t\t\t\t\t_Unlock_locks(_First, _Next, _LkN);\n\t\t\t\t\tLOCK_ASSEMBLE_RETURN(_Next);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (...) {\n\t\t\t_Unlock_locks(_First, _Next, _LkN);\n\t\t\tthrow;\n\t\t}\n\n\t\tLOCK_ASSEMBLE_RETURN(-1);\n\t}\n\n\t// FUNCTION TEMPLATE lock\n\ttemplate<_LockAssembleT _LA>\n\tstatic auto _Lock_attempt(const int _Hard_lock, _LA& _LkN)\n\t\t->decltype(_LkN._ReturnValue(123))\n\t{\n\t\t// attempt to lock 3 or more locks, starting by locking _LkN[_Hard_lock] and trying to lock the rest\n\t\tLOCK_ASSEMBLE_AWAIT(_LkN._Lock_ref(_LkN[_Hard_lock]));\n\t\tint _Failed = -1;\n\t\tint _Backout_start = _Hard_lock; // that is, unlock _Hard_lock\n\n\t\ttry {\n\t\t\t_Failed = LOCK_ASSEMBLE_AWAIT(_Try_lock_range(0, _Hard_lock, _LkN));\n\t\t\tif (_Failed == -1)\n\t\t\t{\n\t\t\t\t_Backout_start = 0; // that is, unlock [0, _Hard_lock] if the next throws\n\t\t\t\t_Failed = LOCK_ASSEMBLE_AWAIT(_Try_lock_range(_Hard_lock + 1, (int)_LkN.size(), _LkN));\n\t\t\t\tif (_Failed == -1) { // we got all the locks\n\t\t\t\t\tLOCK_ASSEMBLE_RETURN(-1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (...) {\n\t\t\t_Unlock_locks(_Backout_start, _Hard_lock + 1, _LkN);\n\t\t\tthrow;\n\t\t}\n\n\t\t// we didn't get all the locks, backout\n\t\t_Unlock_locks(_Backout_start, _Hard_lock + 1, _LkN);\n\t\tLOCK_ASSEMBLE_AWAIT(_LkN._Yield());\n\n\t\tLOCK_ASSEMBLE_RETURN(_Failed);\n\t}\n\n\ttemplate<_LockAssembleT _LA>\n\tstatic auto _Lock_nonmember3(_LA& _LkN) ->decltype(_LkN._ReturnValue())\n\t{\n\t\t// lock 3 or more locks, without deadlock\n\t\tint _Hard_lock = 0;\n\t\twhile (_Hard_lock != -1) {\n\t\t\t_Hard_lock = LOCK_ASSEMBLE_AWAIT(_Lock_attempt(_Hard_lock, _LkN));\n\t\t}\n\t}\n\t\t\t\t\t\n\ttemplate<_LockAssembleT _LA>\n\tstatic auto _Lock_attempt_small2(_LA& _LkN, const int _Idx0, const int _Idx1)\n\t\t->decltype(_LkN._ReturnValue(false))\n\t{\n\t\t// attempt to lock 2 locks, by first locking _Lk0, and then trying to lock _Lk1 returns whether to try again\n\t\tLOCK_ASSEMBLE_AWAIT(_LkN._Lock_ref(_LkN[_Idx0]));\n\t\ttry {\n\t\t\tauto _Result__ = LOCK_ASSEMBLE_AWAIT(_LkN._Try_lock_ref(_LkN[_Idx1]));\n\t\t\tif (_Result__)\n\t\t\t\tLOCK_ASSEMBLE_RETURN(false);\n\t\t}\n\t\tcatch (...) {\n\t\t\t_LkN._Unlock_ref(_LkN[_Idx0]);\n\t\t\tthrow;\n\t\t}\n\n\t\t_LkN._Unlock_ref(_LkN[_Idx0]);\n\t\tLOCK_ASSEMBLE_AWAIT(_LkN._Yield());\n\n\t\tLOCK_ASSEMBLE_RETURN(true);\n\t}\n\n\ttemplate<_LockAssembleT _LA>\n\tstatic auto _Lock_nonmember2(_LA& _LkN) ->decltype(_LkN._ReturnValue())\n\t{\n\t\t// lock 2 locks, without deadlock, special case for better codegen and reduced metaprogramming for common case\n\t\tfor (;;)\n\t\t{\n\t\t\tauto _Result__ = LOCK_ASSEMBLE_AWAIT(_Lock_attempt_small2(_LkN, 0, 1));\n\t\t\tif (!_Result__) break;\n\t\t\t_Result__ = LOCK_ASSEMBLE_AWAIT(_Lock_attempt_small2(_LkN, 1, 0));\n\t\t\tif (!_Result__) break;\n\t\t}\n\t}\n\n\ttemplate<_LockAssembleT _LA>\n\tstatic auto _Lock_range(_LA& lockes) ->decltype(lockes._ReturnValue())\n\t{\n\t\tif (lockes.size() == 0)\n\t\t{\n\t\t}\n\t\telse if (lockes.size() == 1)\n\t\t{\n\t\t\tLOCK_ASSEMBLE_AWAIT(lockes._Lock_ref(lockes[0]));\n\t\t}\n\t\telse if (lockes.size() == 2)\n\t\t{\n\t\t\t(void)LOCK_ASSEMBLE_AWAIT(_Lock_nonmember2(lockes));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t(void)LOCK_ASSEMBLE_AWAIT(_Lock_nonmember3(lockes));\n\t\t}\n\t}\n};\n\n"
  },
  {
    "path": "include/librf/src/yield.h",
    "content": "﻿#pragma once\n\nnamespace librf\n{\n\t/**\n\t * @brief 将本协程让渡出一次调用的可等待对象。\n\t */\n\tstruct yield_awaitor\n\t{\n\t\tusing value_type = void;\n\t\tusing state_type = state_t<value_type>;\n\t\tusing promise_type = promise_t<value_type>;\n\t\tusing lock_type = typename state_type::lock_type;\n\n\t\tbool await_ready() const noexcept\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\ttemplate<class _PromiseT> requires(traits::is_promise_v<_PromiseT>)\n\t\tbool await_suspend(coroutine_handle<_PromiseT> handler)\n\t\t{\n\t\t\tcounted_ptr<state_t<void>> _state = state_future_t::_Alloc_state<state_type>(true);\n\t\t\t_state->set_value();\n\t\t\t_state->future_await_suspend(handler);\n\n\t\t\treturn true;\n\t\t}\n\t\tvoid await_resume() const noexcept\n\t\t{\n\t\t}\n\n#ifdef DOXYGEN_SKIP_PROPERTY\n\t\t/**\n\t\t * @brief 将本协程让渡出一次调用的可等待对象。\n\t\t * @return [co_await] void\n\t\t * @note 本函数是librf名字空间下的全局函数。由于doxygen使用上的问题，将之归纳到 yield_awaitor 类下。\n\t\t */\n\t\tstatic yield_awaitor yield() noexcept;\n#endif\t//DOXYGEN_SKIP_PROPERTY\n\t};\n\n\t/**\n\t * @brief 将本协程让渡出一次调用。\n\t * @return [co_await] void\n\t */\n\tinline yield_awaitor yield() noexcept\n\t{\n\t\treturn {};\n\t}\n\n}\n"
  },
  {
    "path": "source/event_v2.cpp",
    "content": "﻿#include \"librf/librf.h\"\n\nnamespace librf\n{\n\tnamespace detail\n\t{\n\n\t\tLIBRF_API void state_event_t::on_cancel() noexcept\n\t\t{\n\t\t\tevent_v2_impl** oldValue = _value.load(std::memory_order_acquire);\n\t\t\tif (oldValue != nullptr && _value.compare_exchange_strong(oldValue, nullptr, std::memory_order_acq_rel))\n\t\t\t{\n\t\t\t\t*oldValue = nullptr;\n\t\t\t\t_thandler.stop();\n\n\t\t\t\tthis->_coro = nullptr;\n\t\t\t}\n\t\t}\n\n\t\tLIBRF_API bool state_event_t::on_notify(event_v2_impl* eptr)\n\t\t{\n\t\t\tevent_v2_impl** oldValue = _value.load(std::memory_order_acquire);\n\t\t\tif (oldValue != nullptr && _value.compare_exchange_strong(oldValue, nullptr, std::memory_order_acq_rel))\n\t\t\t{\n\t\t\t\t*oldValue = eptr;\n\t\t\t\t_thandler.stop();\n\n\t\t\t\tassert(this->_scheduler != nullptr);\n\t\t\t\tif (this->_coro)\n\t\t\t\t\tthis->_scheduler->add_generator(this);\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tLIBRF_API bool state_event_t::on_timeout()\n\t\t{\n\t\t\tevent_v2_impl** oldValue = _value.load(std::memory_order_acquire);\n\t\t\tif (oldValue != nullptr && _value.compare_exchange_strong(oldValue, nullptr, std::memory_order_acq_rel))\n\t\t\t{\n\t\t\t\tevent_v2_impl* evt = *oldValue;\n\t\t\t\tif (evt != nullptr)\n\t\t\t\t\tevt->remove_wait_list(this);\n\t\t\t\t*oldValue = nullptr;\n\t\t\t\t_thandler.reset();\n\n\t\t\t\tassert(this->_scheduler != nullptr);\n\t\t\t\tif (this->_coro)\n\t\t\t\t\tthis->_scheduler->add_generator(this);\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\n\n\n\t\tLIBRF_API void state_event_all_t::on_cancel(intptr_t idx)\n\t\t{\n\t\t\tscoped_lock<event_v2_impl::lock_type> lock_(_lock);\n\t\t\t\n\t\t\tif (_counter <= 0) return ;\n\t\t\tassert(idx < static_cast<intptr_t>(_values.size()));\n\n\t\t\t_values[idx] = sub_state_t{ nullptr, nullptr };\n\t\t\tif (--_counter == 0)\n\t\t\t{\n\t\t\t\t*_result = false;\n\t\t\t\t_thandler.stop();\n\n\t\t\t\tassert(this->_scheduler != nullptr);\n\t\t\t\tif (this->_coro)\n\t\t\t\t\tthis->_scheduler->add_generator(this);\n\t\t\t}\n\t\t}\n\n\t\tLIBRF_API bool state_event_all_t::on_notify(event_v2_impl*, intptr_t idx)\n\t\t{\n\t\t\tscoped_lock<event_v2_impl::lock_type> lock_(_lock);\n\n\t\t\tif (_counter <= 0) return false;\n\t\t\tassert(idx < static_cast<intptr_t>(_values.size()));\n\n\t\t\t_values[idx].first = nullptr;\n\n\t\t\tif (--_counter == 0)\n\t\t\t{\n\t\t\t\tbool result = true;\n\t\t\t\tfor (sub_state_t& sub : _values)\n\t\t\t\t{\n\t\t\t\t\tif (sub.second == nullptr)\n\t\t\t\t\t{\n\t\t\t\t\t\tresult = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t*_result = result;\n\t\t\t\t_thandler.stop();\n\n\t\t\t\tassert(this->_scheduler != nullptr);\n\t\t\t\tif (this->_coro)\n\t\t\t\t\tthis->_scheduler->add_generator(this);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tLIBRF_API bool state_event_all_t::on_timeout()\n\t\t{\n\t\t\tscoped_lock<event_v2_impl::lock_type> lock_(_lock);\n\n\t\t\tif (_counter <= 0) return false;\n\n\t\t\t_counter = 0;\n\t\t\t*_result = false;\n\t\t\t_thandler.reset();\n\n\t\t\tfor (sub_state_t& sub : _values)\n\t\t\t{\n\t\t\t\tif (sub.first != nullptr)\n\t\t\t\t{\n\t\t\t\t\tevent_v2_impl* evt = sub.second;\n\t\t\t\t\tsub.second = nullptr;\n\n\t\t\t\t\tif (evt != nullptr)\n\t\t\t\t\t\tevt->remove_wait_list(sub.first);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tassert(this->_scheduler != nullptr);\n\t\t\tif (this->_coro)\n\t\t\t\tthis->_scheduler->add_generator(this);\n\n\t\t\treturn true;\n\t\t}\n\n\n\n\t\tLIBRF_API event_v2_impl::event_v2_impl(bool initially) noexcept\n\t\t\t: _counter(initially ? 1 : 0)\n\t\t{\n\t\t}\n\n\t\ttemplate<class _Ty, class _Ptr>\n\t\tstatic auto try_pop_list(intrusive_link_queue<_Ty, _Ptr>& list)\n\t\t{\n\t\t\treturn list.try_pop();\n\t\t}\n\t\ttemplate<class _Ptr>\n\t\tstatic _Ptr try_pop_list(std::list<_Ptr>& list)\n\t\t{\n\t\t\tif (!list.empty())\n\t\t\t{\n\t\t\t\t_Ptr ptr = list.front();\n\t\t\t\tlist.pop_front();\n\t\t\t\treturn ptr;\n\t\t\t}\n\t\t\treturn nullptr;\n\t\t}\n\t\ttemplate<class _Ty, class _Ptr>\n\t\tstatic void clear_list(intrusive_link_queue<_Ty, _Ptr>& list)\n\t\t{\n\t\t\tfor (; list.try_pop() != nullptr;);\n\t\t}\n\t\ttemplate<class _Ptr>\n\t\tstatic void clear_list(std::list<_Ptr>& list)\n\t\t{\n\t\t\tlist.clear();\n\t\t}\n\n\t\tLIBRF_API event_v2_impl::~event_v2_impl()\n\t\t{\n\t\t\tclear_list(_wait_awakes);\n\t\t}\n\n\t\tLIBRF_API void event_v2_impl::signal_all() noexcept\n\t\t{\n\t\t\tscoped_lock<lock_type> lock_(_lock);\n\n\t\t\tstate_event_ptr state;\n\t\t\tfor (; (state = try_pop_list(_wait_awakes)) != nullptr;)\n\t\t\t{\n\t\t\t\t(void)state->on_notify(this);\n\t\t\t}\n\t\t}\n\n\t\tLIBRF_API void event_v2_impl::signal() noexcept\n\t\t{\n\t\t\tscoped_lock<lock_type> lock_(_lock);\n\n\t\t\tstate_event_ptr state;\n\t\t\tfor (; (state = try_pop_list(_wait_awakes)) != nullptr;)\n\t\t\t{\n\t\t\t\tif (state->on_notify(this))\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t_counter.fetch_add(1, std::memory_order_acq_rel);\n\t\t}\n\n\t\tLIBRF_API void event_v2_impl::reset() noexcept\n\t\t{\n\t\t\t_counter.store(0, std::memory_order_release);\n\t\t}\n\n\t\tLIBRF_API void event_v2_impl::add_wait_list(state_event_base_t* state)\n\t\t{\n\t\t\tassert(state != nullptr);\n\t\t\t_wait_awakes.push_back(state);\n\t\t}\n\n\t\tLIBRF_API void event_v2_impl::remove_wait_list(state_event_base_t* state)\n\t\t{\n\t\t\tassert(state != nullptr);\n\n\t\t\tscoped_lock<lock_type> lock_(_lock);\n\t\t\t_wait_awakes.erase(state);\n\t\t}\n\t}\n\n\tLIBRF_API event_t::event_t(bool initially)\n\t\t:_event(std::make_shared<detail::event_v2_impl>(initially))\n\t{\n\t}\n\n\tLIBRF_API event_t::event_t(std::adopt_lock_t)\n\t{\n\t}\n\n\tLIBRF_API event_t::~event_t()\n\t{\n\t}\n}\n"
  },
  {
    "path": "source/mutex_v2.cpp",
    "content": "﻿#include \"librf/librf.h\"\n\nnamespace librf\n{\n\tnamespace detail\n\t{\n\t\tLIBRF_API void state_mutex_t::resume()\n\t\t{\n\t\t\tcoroutine_handle<> handler = _coro;\n\t\t\tif (handler)\n\t\t\t{\n\t\t\t\t_coro = nullptr;\n\t\t\t\t_scheduler->del_final(this);\n\t\t\t\thandler.resume();\n\t\t\t}\n\t\t}\n\n\t\tLIBRF_API bool state_mutex_t::has_handler() const  noexcept\n\t\t{\n\t\t\treturn (bool)_coro;\n\t\t}\n\t\t\n\t\tLIBRF_API state_base_t* state_mutex_t::get_parent() const noexcept\n\t\t{\n\t\t\treturn _root;\n\t\t}\n\n\n\t\tLIBRF_API void state_mutex_t::on_cancel() noexcept\n\t\t{\n\t\t\tmutex_v2_impl** oldValue = _value.load(std::memory_order_acquire);\n\t\t\tif (oldValue != nullptr && _value.compare_exchange_strong(oldValue, nullptr, std::memory_order_acq_rel))\n\t\t\t{\n\t\t\t\t*oldValue = nullptr;\n\t\t\t\t_thandler.stop();\n\n\t\t\t\tthis->_coro = nullptr;\n\t\t\t}\n\t\t}\n\n\t\tLIBRF_API bool state_mutex_t::on_notify(mutex_v2_impl* eptr)\n\t\t{\n\t\t\tassert(eptr != nullptr);\n\n\t\t\tmutex_v2_impl** oldValue = _value.load(std::memory_order_acquire);\n\t\t\tif (oldValue != nullptr && _value.compare_exchange_strong(oldValue, nullptr, std::memory_order_acq_rel))\n\t\t\t{\n\t\t\t\t*oldValue = eptr;\n\t\t\t\t_thandler.stop();\n\n\t\t\t\tassert(this->_scheduler != nullptr);\n\t\t\t\tif (this->_coro)\n\t\t\t\t\tthis->_scheduler->add_generator(this);\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tLIBRF_API bool state_mutex_t::on_timeout()\n\t\t{\n\t\t\tmutex_v2_impl** oldValue = _value.load(std::memory_order_acquire);\n\t\t\tif (oldValue != nullptr && _value.compare_exchange_strong(oldValue, nullptr, std::memory_order_acq_rel))\n\t\t\t{\n\t\t\t\t*oldValue = nullptr;\n\t\t\t\t_thandler.reset();\n\n\t\t\t\tassert(this->_scheduler != nullptr);\n\t\t\t\tif (this->_coro)\n\t\t\t\t\tthis->_scheduler->add_generator(this);\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tLIBRF_API void state_mutex_t::add_timeout_timer(std::chrono::system_clock::time_point tp)\n\t\t{\n\t\t\tthis->_thandler = this->_scheduler->timer()->add_handler(tp,\n\t\t\t\t[st = counted_ptr<state_mutex_t>{ this }](bool canceld)\n\t\t\t\t{\n\t\t\t\t\tif (!canceld)\n\t\t\t\t\t\tst->on_timeout();\n\t\t\t\t});\n\t\t}\n\n\n\n\t\tLIBRF_API void mutex_v2_impl::lock_until_succeed(void* sch)\n\t\t{\n\t\t\tassert(sch != nullptr);\n\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tif (try_lock(sch))\n\t\t\t\t\tbreak;\n\t\t\t\tstd::this_thread::yield();\n\t\t\t}\n\t\t}\n\n\t\tLIBRF_API bool mutex_v2_impl::try_lock(void* sch)\n\t\t{\n\t\t\tassert(sch != nullptr);\n\n\t\t\tscoped_lock<detail::mutex_v2_impl::lock_type> lock_(_lock);\n\t\t\treturn try_lock_lockless(sch);\n\t\t}\n\n\t\tLIBRF_API bool mutex_v2_impl::try_lock_until(clock_type::time_point tp, void* sch)\n\t\t{\n\t\t\tassert(sch != nullptr);\n\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (try_lock(sch))\n\t\t\t\t\treturn true;\n\t\t\t\tstd::this_thread::yield();\n\t\t\t} while (clock_type::now() <= tp);\n\t\t\treturn false;\n\t\t}\n\n\t\tLIBRF_API bool mutex_v2_impl::try_lock_lockless(void* sch) noexcept\n\t\t{\n\t\t\tassert(sch != nullptr);\n\n\t\t\tvoid* oldValue = _owner.load(std::memory_order_relaxed);\n\t\t\tif (oldValue == nullptr)\n\t\t\t{\n\t\t\t\t_owner.store(sch, std::memory_order_relaxed);\n\t\t\t\t_counter.fetch_add(1, std::memory_order_relaxed);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (oldValue == sch)\n\t\t\t{\n\t\t\t\t_counter.fetch_add(1, std::memory_order_relaxed);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tLIBRF_API bool mutex_v2_impl::unlock(void* sch)\n\t\t{\n\t\t\tassert(sch != nullptr);\n\n\t\t\tscoped_lock<lock_type> lock_(_lock);\n\n\t\t\tvoid* oldValue = _owner.load(std::memory_order_relaxed);\n\t\t\tif (oldValue == sch)\n\t\t\t{\n\t\t\t\tif (_counter.fetch_sub(1, std::memory_order_relaxed) == 1)\n\t\t\t\t{\n\t\t\t\t\t_owner.store(nullptr, std::memory_order_relaxed);\n\n\t\t\t\t\twhile (!_wait_awakes.empty())\n\t\t\t\t\t{\n\t\t\t\t\t\tstate_mutex_ptr state = _wait_awakes.front();\n\t\t\t\t\t\t_wait_awakes.pop_front();\n\n\t\t\t\t\t\t//先将锁定状态转移到新的state上\n\t\t\t\t\t\t_owner.store(state->get_root(), std::memory_order_release);\n\t\t\t\t\t\t_counter.fetch_add(1, std::memory_order_acq_rel);\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (state->on_notify(this))\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t//转移状态失败，恢复成空\n\t\t\t\t\t\t_owner.store(nullptr, std::memory_order_relaxed);\n\t\t\t\t\t\t_counter.fetch_sub(1, std::memory_order_relaxed);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tLIBRF_API void mutex_v2_impl::add_wait_list_lockless(state_mutex_t* state)\n\t\t{\n\t\t\tassert(state != nullptr);\n\n\t\t\t_wait_awakes.push_back(state);\n\t\t}\n\t}\n\n\tLIBRF_API mutex_t::mutex_t()\n\t\t: _mutex(std::make_shared<detail::mutex_v2_impl>())\n\t{\n\t}\n\n\tLIBRF_API mutex_t::mutex_t(std::adopt_lock_t) noexcept\n\t{\n\t}\n\n\tLIBRF_API mutex_t::~mutex_t() noexcept\n\t{\n\t}\n}\n"
  },
  {
    "path": "source/rf_task.cpp",
    "content": "﻿#include \"librf/librf.h\"\n\nnamespace librf\n{\n\tLIBRF_API task_t::task_t() noexcept\n\t\t: _stop(nostopstate)\n\t{\n\t}\n\n\tLIBRF_API task_t::~task_t()\n\t{\n\t}\n\n\tLIBRF_API const stop_source & task_t::get_stop_source()\n\t{\n\t\t_stop.make_sure_possible();\n\t\treturn _stop;\n\t}\n}\n"
  },
  {
    "path": "source/scheduler.cpp",
    "content": "﻿#include \"librf/librf.h\"\n\n#if RESUMEF_DEBUG_COUNTER\nstd::mutex g_resumef_cout_mutex;\nstd::atomic<intptr_t> g_resumef_state_count = 0;\nstd::atomic<intptr_t> g_resumef_task_count = 0;\nstd::atomic<intptr_t> g_resumef_evtctx_count = 0;\nstd::atomic<intptr_t> g_resumef_state_id = 0;\n#endif\n\nnamespace librf\n{\n\tconst char * future_error_string[(size_t)error_code::max__]\n\t{\n\t\t\"none\",\n\t\t\"not_ready\",\n\t\t\"already_acquired\",\n\t\t\"unlock_more\",\n\t\t\"read_before_write\",\n\t\t\"timer_canceled\",\n\t\t\"not_await_lock\",\n\t\t\"stop_requested\",\n\t};\n\n\tthread_local char sz_future_error_buffer[256];\n\n\tLIBRF_API const char * get_error_string(error_code fe, const char * classname)\n\t{\n\t\tif (classname)\n\t\t{\n#if defined(__clang__) || defined(__GNUC__)\n#define sprintf_s sprintf\n#endif\n\t\t\tsprintf_s(sz_future_error_buffer, \"%s, code=%s\", classname, future_error_string[(size_t)(fe)]);\n\t\t\treturn sz_future_error_buffer;\n\t\t}\n\t\treturn future_error_string[(size_t)(fe)];\n\t}\n\n\tthread_local scheduler_t * th_scheduler_ptr = nullptr;\n\n\t//获得当前线程下的调度器\n\tLIBRF_API scheduler_t * this_scheduler()\n\t{\n\t\treturn th_scheduler_ptr ? th_scheduler_ptr : &scheduler_t::g_scheduler;\n\t}\n\n\tLIBRF_API local_scheduler_t::local_scheduler_t()\n\t{\n\t\tif (th_scheduler_ptr == nullptr)\n\t\t{\n\t\t\t_scheduler_ptr = new scheduler_t;\n\t\t\tth_scheduler_ptr = _scheduler_ptr;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t_scheduler_ptr = nullptr;\n\t\t}\n\t}\n\n\tLIBRF_API local_scheduler_t::local_scheduler_t(scheduler_t& sch) noexcept\n\t{\n\t\tif (th_scheduler_ptr == nullptr)\n\t\t{\n\t\t\tth_scheduler_ptr = &sch;\n\t\t}\n\n\t\t_scheduler_ptr = nullptr;\n\t}\n\n\tLIBRF_API local_scheduler_t::~local_scheduler_t()\n\t{\n\t\tif (th_scheduler_ptr == _scheduler_ptr)\n\t\t\tth_scheduler_ptr = nullptr;\n\t\tdelete _scheduler_ptr;\n\t}\n\n\tLIBRF_API scheduler_t::scheduler_t()\n\t\t: _timer(std::make_shared<timer_manager>())\n\t{\n\t\t_runing_states.reserve(1024);\n\t\t_cached_states.reserve(1024);\n\n\t\tif (th_scheduler_ptr == nullptr)\n\t\t\tth_scheduler_ptr = this;\n\t}\n\n\tLIBRF_API scheduler_t::~scheduler_t()\n\t{\n\t\t//cancel_all_task_();\n\t\tif (th_scheduler_ptr == this)\n\t\t\tth_scheduler_ptr = nullptr;\n\t}\n\n\tLIBRF_API task_t* scheduler_t::new_task(task_t * task)\n\t{\n\t\tstate_base_t* sptr = task->_state.get();\n\t\tsptr->set_scheduler(this);\n\n\t\t{\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\t\tscoped_lock<spinlock> __guard(_lock_ready);\n#endif\n\t\t\t_ready_task.emplace(sptr, task);\n\t\t}\n\n\t\t//如果是单独的future，没有被co_await过，则handler是nullptr。\n\t\tif (sptr->has_handler())\n\t\t{\n\t\t\tadd_generator(sptr);\n\t\t}\n\n\t\treturn task;\n\t}\n\n\tLIBRF_API std::unique_ptr<task_t> scheduler_t::del_switch(state_base_t* sptr)\n\t{\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\tscoped_lock<spinlock> __guard(_lock_ready);\n#endif\n\t\n\t\tstd::unique_ptr<task_t> task_ptr;\n\n\t\tauto iter = this->_ready_task.find(sptr);\n\t\tif (iter != this->_ready_task.end())\n\t\t{\n\t\t\ttask_ptr = std::exchange(iter->second, nullptr);\n\t\t\tthis->_ready_task.erase(iter);\n\t\t}\n\n\t\treturn task_ptr;\n\t}\n\n\tLIBRF_API void scheduler_t::request_stop_all_if_possible()\n\t{\n\t\tscoped_lock<spinlock> __guard(_lock_ready);\n\n\t\tfor (auto& kv : this->_ready_task)\n\t\t\tkv.second->request_stop_if_possible();\n\t\t//this->_ready_task.clear();\n\t\tthis->_timer->clear();\n\t}\n\n/*\n\tvoid scheduler_t::cancel_all_task_()\n\t{\n\t\tscoped_lock<spinlock, spinlock> __guard(_lock_ready, _lock_running);\n\t\t\n\t\tthis->_ready_task.clear();\n\t\tthis->_runing_states.clear();\n\t}\n\n\tvoid scheduler_t::break_all()\n\t{\n\t\tcancel_all_task_();\n\t\tthis->_timer->clear();\n\t}\n*/\n\n\tLIBRF_API bool scheduler_t::run_one_batch()\n\t{\n\t\tthis->_timer->update();\n\n\t\t{\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\t\tscoped_lock<spinlock> __guard(_lock_running);\n#endif\n\t\t\tif (likely(_runing_states.empty()))\n\t\t\t\treturn false;\n\n\t\t\tstd::swap(_cached_states, _runing_states);\n\t\t}\n\n\t\tfor (state_sptr& sptr : _cached_states)\n\t\t\tsptr->resume();\n\n\t\t_cached_states.clear();\n\t\treturn true;\n\t}\n\n\tLIBRF_API void scheduler_t::run_until_notask()\n\t{\n\t\tfor(;;)\n\t\t{\n\t\t\t//介于网上有人做评测，导致单协程切换数据很难看，那就注释掉吧。\n\t\t\t//std::this_thread::yield();\n\n\t\t\tif (likely(this->run_one_batch())) continue;\t//当前运行了一个state，则认为还可能有任务未完成\n\n\t\t\t{\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\t\t\tscoped_lock<spinlock> __guard(_lock_ready);\n#endif\n\t\t\t\tif (likely(!_ready_task.empty())) continue;\t//当前还存在task，则必然还有任务未完成\n\t\t\t}\n\t\t\tif (unlikely(!_timer->empty())) continue;\t\t\t//定时器不为空，也需要等待定时器触发\n\n\t\t\tbreak;\n\t\t};\n\t}\n\n\tLIBRF_API scheduler_t scheduler_t::g_scheduler;\n}\n"
  },
  {
    "path": "source/sleep.cpp",
    "content": "﻿#include \"librf/librf.h\"\n\nnamespace librf\n{\n\tLIBRF_API future_t<> sleep_until_(std::chrono::system_clock::time_point tp_, scheduler_t& scheduler_)\n\t{\n\t\tawaitable_t<> awaitable;\n\n\t\t(void)scheduler_.timer()->add(tp_,\n\t\t\t[awaitable](bool cancellation_requested)\n\t\t\t{\n\t\t\t\tif (cancellation_requested)\n\t\t\t\t\tawaitable.throw_exception(canceled_exception{ error_code::timer_canceled });\n\t\t\t\telse\n\t\t\t\t\tawaitable.set_value();\n\t\t\t});\n\n\t\treturn awaitable.get_future();\n\t}\n}\n"
  },
  {
    "path": "source/state.cpp",
    "content": "﻿#include \"librf/librf.h\"\n\nnamespace librf\n{\n\tLIBRF_API state_base_t::~state_base_t()\n\t{\n\t}\n\t\n\tLIBRF_API void state_base_t::destroy_deallocate()\n\t{\n\t\tdelete this;\n\t}\n\n\tLIBRF_API void state_base_t::resume()\n\t{\n\t\tif (likely(_coro))\n\t\t{\n\t\t\tcoroutine_handle<> handler = _coro;\n\t\t\t_coro = nullptr;\n\t\t\t_scheduler->del_final(this);\n\t\t\thandler.resume();\n\t\t}\n\t}\n\n\tLIBRF_API bool state_base_t::has_handler() const  noexcept\n\t{\n\t\treturn (bool)_coro;\n\t}\n\n\tLIBRF_API state_base_t* state_base_t::get_parent() const noexcept\n\t{\n\t\treturn nullptr;\n\t}\n\n\tLIBRF_API void state_future_t::destroy_deallocate()\n\t{\n\t\tsize_t _Size = this->_alloc_size;\n#if RESUMEF_DEBUG_COUNTER\n\t\tstd::cout << \"destroy_deallocate, size=\" << _Size << std::endl;\n#endif\n\t\tthis->~state_future_t();\n\n\t\t_Alloc_char _Al;\n\t\treturn _Al.deallocate(reinterpret_cast<char*>(this), _Size);\n\t}\n\n\tLIBRF_API state_generator_t* state_generator_t::_Alloc_state()\n\t{\n\t\t_Alloc_char _Al;\n\t\tsize_t _Size = _Align_size<state_generator_t>();\n#if RESUMEF_DEBUG_COUNTER\n\t\tstd::cout << \"state_generator_t::alloc, size=\" << sizeof(state_generator_t) << std::endl;\n#endif\n\t\tchar* _Ptr = _Al.allocate(_Size);\n\t\treturn new(_Ptr) state_generator_t();\n\t}\n\n\tLIBRF_API void state_generator_t::destroy_deallocate()\n\t{\n\t\tsize_t _Size = _Align_size<state_generator_t>();\n#if RESUMEF_DEBUG_COUNTER\n\t\tstd::cout << \"destroy_deallocate, size=\" << _Size << std::endl;\n#endif\n\t\tthis->~state_generator_t();\n\n\t\t_Alloc_char _Al;\n\t\treturn _Al.deallocate(reinterpret_cast<char*>(this), _Size);\n\t}\n\n\tLIBRF_API void state_generator_t::resume()\n\t{\n\t\tif (likely(_coro))\n\t\t{\n\t\t\t_coro.resume();\n\t\t\tif (likely(!_coro.done()))\n\t\t\t{\n\t\t\t\t_scheduler->add_generator(this);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcoroutine_handle<> handler = _coro;\n\t\t\t\t_coro = nullptr;\n\t\t\t\t_scheduler->del_final(this);\n\n\t\t\t\thandler.destroy();\n\t\t\t}\n\t\t}\n\t}\n\n\tLIBRF_API bool state_generator_t::has_handler() const noexcept\n\t{\n\t\treturn (bool)_coro;\n\t}\n\t\n\tLIBRF_API bool state_generator_t::switch_scheduler_await_suspend(scheduler_t* sch)\n\t{\n\t\tassert(sch != nullptr);\n\n\t\tif (_scheduler != nullptr)\n\t\t{\n\t\t\tif (_scheduler == sch) return false;\n\n\t\t\tauto task_ptr = _scheduler->del_switch(this);\n\t\t\t\n\t\t\t_scheduler = sch;\n\t\t\tif (task_ptr != nullptr)\n\t\t\t\tsch->add_switch(std::move(task_ptr));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t_scheduler = sch;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tLIBRF_API state_base_t* state_future_t::get_parent() const noexcept\n\t{\n\t\treturn _parent;\n\t}\n\n\tLIBRF_API void state_future_t::resume()\n\t{\n\t\tstd::unique_lock<lock_type> __guard(_mtx);\n\n\t\tif (_is_initor == initor_type::Initial)\n\t\t{\n\t\t\tassert((bool)_initor);\n\n\t\t\tcoroutine_handle<> handler = _initor;\n\t\t\t_is_initor = initor_type::None;\n\t\t\t__guard.unlock();\n\n\t\t\thandler.resume();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (_coro)\n\t\t{\n\t\t\tcoroutine_handle<> handler = _coro;\n\t\t\t_coro = nullptr;\n\t\t\t__guard.unlock();\n\n\t\t\thandler.resume();\n\t\t\treturn;\n\t\t}\n\n\t\tif (_is_initor == initor_type::Final)\n\t\t{\n\t\t\tassert((bool)_initor);\n\n\t\t\tcoroutine_handle<> handler = _initor;\n\t\t\t_is_initor = initor_type::None;\n\t\t\t__guard.unlock();\n\n\t\t\thandler.destroy();\n\t\t\treturn;\n\t\t}\n\t}\n\n\tLIBRF_API bool state_future_t::has_handler() const noexcept\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\t\treturn has_handler_skip_lock();\n\t}\n\n\tLIBRF_API bool state_future_t::switch_scheduler_await_suspend(scheduler_t* sch)\n\t{\n\t\tassert(sch != nullptr);\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\n\t\tif (_scheduler != nullptr)\n\t\t{\n\t\t\tif (_scheduler == sch) return false;\n\n\t\t\tauto task_ptr = _scheduler->del_switch(this);\n\n\t\t\t_scheduler = sch;\n\t\t\tif (task_ptr != nullptr)\n\t\t\t\tsch->add_switch(std::move(task_ptr));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t_scheduler = sch;\n\t\t}\n\n\t\tif (_parent != nullptr)\n\t\t\t_parent->switch_scheduler_await_suspend(sch);\n\n\t\treturn true;\n\t}\n\n\tLIBRF_API void state_t<void>::future_await_resume()\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\n\t\tif (this->_exception)\n\t\t\tstd::rethrow_exception(std::move(this->_exception));\n\t\tif (this->_has_value.load(std::memory_order_acquire) == result_type::None)\n\t\t\tstd::rethrow_exception(std::make_exception_ptr(future_exception{error_code::not_ready}));\n\t}\n\n\tLIBRF_API void state_t<void>::set_value()\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\t\tthis->_has_value.store(result_type::Value, std::memory_order_release);\n\n\t\tscheduler_t* sch = this->get_scheduler();\n\t\tif (sch != nullptr)\n\t\t{\n\t\t\tif (this->has_handler_skip_lock())\n\t\t\t\tsch->add_generator(this);\n\t\t\telse\n\t\t\t\tsch->del_final(this);\n\t\t}\n\t}\n\n\tLIBRF_API void state_t<void>::set_exception(std::exception_ptr e)\n\t{\n\t\tscoped_lock<lock_type> __guard(this->_mtx);\n\t\tthis->_exception = std::move(e);\n\n\t\tscheduler_t* sch = this->get_scheduler();\n\t\tif (sch != nullptr)\n\t\t{\n\t\t\tif (this->has_handler_skip_lock())\n\t\t\t\tsch->add_generator(this);\n\t\t\telse\n\t\t\t\tsch->del_final(this);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "source/timer.cpp",
    "content": "﻿#include \"librf/librf.h\"\n\nnamespace librf\n{\n\tLIBRF_API timer_manager::timer_manager()\n\t{\n\t\t_added_timers.reserve(128);\n\t}\n\n\tLIBRF_API timer_manager::~timer_manager()\n\t{\n\t\tclear();\n\t}\n\t\n\tLIBRF_API void timer_manager::call_target_(const timer_target_ptr & sptr, bool canceld)\n\t{\n\t\tauto cb = std::move(sptr->cb);\n\t\tsptr->st = timer_target::State::Invalid;\n#if _DEBUG\n\t\tsptr->_manager = nullptr;\n#endif\n\n\t\tif(cb) cb(canceld);\n\t}\n\n\tLIBRF_API void timer_manager::clear()\n\t{\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\tstd::unique_lock<spinlock> __lock(_added_mtx);\n#endif\n\t\tauto _atimer = std::move(_added_timers);\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\t__lock.unlock();\n#endif\n\n\t\tfor (auto& sptr : _atimer)\n\t\t\tcall_target_(sptr, true);\n\n\t\tauto _rtimer = std::move(_runing_timers);\n\t\tfor (auto & kv : _rtimer)\n\t\t\tcall_target_(kv.second, true);\n\t}\n\n\tLIBRF_API detail::timer_target_ptr timer_manager::add_(const timer_target_ptr & sptr)\n\t{\n\t\tassert(sptr);\n\t\tassert(sptr->st == timer_target::State::Invalid);\n\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\tscoped_lock<spinlock> __lock(_added_mtx);\n#endif\n#if _DEBUG\n\t\tassert(sptr->_manager == nullptr);\n\t\tsptr->_manager = this;\n#endif\n\n\t\tsptr->st = timer_target::State::Added;\n\t\t_added_timers.push_back(sptr);\n\n\t\treturn sptr;\n\t}\n\n\tLIBRF_API bool timer_manager::stop(const timer_target_ptr & sptr)\n\t{\n\t\tif (!sptr || sptr->st == timer_target::State::Invalid) \n\t\t\treturn false;\n#if _DEBUG\n\t\tassert(sptr->_manager == this);\n#endif\n\t\tsptr->st = timer_target::State::Invalid;\n\n\t\treturn true;\n\t}\n\n\tLIBRF_API void timer_manager::update()\n\t{\n\t\t{\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\t\tstd::unique_lock<spinlock> __lock(_added_mtx);\n#endif\n\n\t\t\tif (unlikely(_added_timers.size() > 0))\n\t\t\t{\n\t\t\t\tauto _atimer = std::move(_added_timers);\n\t\t\t\t_added_timers.reserve(128);\n#if !RESUMEF_DISABLE_MULT_THREAD\n\t\t\t\t__lock.unlock();\n#endif\n\n\t\t\t\tfor (auto& sptr : _atimer)\n\t\t\t\t{\n\t\t\t\t\tif (sptr->st == timer_target::State::Added)\n\t\t\t\t\t{\n\t\t\t\t\t\tsptr->st = timer_target::State::Runing;\n\t\t\t\t\t\t_runing_timers.insert({ sptr->tp, sptr });\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tassert(sptr->st == timer_target::State::Invalid);\n\t\t\t\t\t\tcall_target_(sptr, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (unlikely(_runing_timers.size() > 0))\n\t\t{\n\t\t\tauto now_ = clock_type::now();\n\n\t\t\tauto iter = _runing_timers.begin();\n\t\t\tfor (; iter != _runing_timers.end(); ++iter)\n\t\t\t{\n\t\t\t\tauto & kv = *iter;\n\t\t\t\tif (kv.first > now_)\n\t\t\t\t\tbreak;\n\n\t\t\t\tcall_target_(kv.second, kv.second->st == timer_target::State::Invalid);\n\t\t\t}\n\n\t\t\t_runing_timers.erase(_runing_timers.begin(), iter);\n\t\t}\n\t}\n}"
  },
  {
    "path": "source/when_v2.cpp",
    "content": "﻿#include \"librf/librf.h\"\n\nnamespace librf\n{\n\tnamespace detail\n\t{\n\t\tLIBRF_API state_when_t::state_when_t(intptr_t counter_)\n\t\t\t:_counter(counter_)\n\t\t{\n\t\t}\n\n\t\tLIBRF_API void state_when_t::resume()\n\t\t{\n\t\t\tcoroutine_handle<> handler = _coro;\n\t\t\tif (handler)\n\t\t\t{\n\t\t\t\t_coro = nullptr;\n\t\t\t\t_scheduler->del_final(this);\n\t\t\t\thandler.resume();\n\t\t\t}\n\t\t}\n\n\t\tLIBRF_API bool state_when_t::has_handler() const  noexcept\n\t\t{\n\t\t\treturn (bool)_coro;\n\t\t}\n\n\t\tLIBRF_API void state_when_t::on_cancel() noexcept\n\t\t{\n\t\t\tscoped_lock<lock_type> lock_(_lock);\n\n\t\t\t_counter.store(0);\n\t\t\tthis->_coro = nullptr;\n\t\t}\n\n\t\tLIBRF_API bool state_when_t::on_notify_one()\n\t\t{\n\t\t\tscoped_lock<lock_type> lock_(_lock);\n\n\t\t\tif (_counter.fetch_sub(1, std::memory_order_acq_rel) == 1)\n\t\t\t{\n\t\t\t\tassert(this->_scheduler != nullptr);\n\t\t\t\tif (this->_coro)\n\t\t\t\t\tthis->_scheduler->add_generator(this);\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tLIBRF_API bool state_when_t::on_timeout()\n\t\t{\n\t\t\tscoped_lock<lock_type> lock_(_lock);\n\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "test_librf.cpp",
    "content": "\n#include \"librf/librf.h\"\n#include <iostream>\n\nextern void resumable_main_yield_return();\nextern void resumable_main_timer();\nextern void resumable_main_suspend_always();\nextern void resumable_main_sleep();\nextern void resumable_main_routine();\nextern void resumable_main_resumable();\nextern void resumable_main_mutex();\nextern void resumable_main_exception(bool bomb);\nextern void resumable_main_event();\nextern void resumable_main_event_v2();\nextern void resumable_main_event_timeout();\nextern void resumable_main_dynamic_go();\nextern void resumable_main_channel();\nextern void resumable_main_cb();\nextern void resumable_main_modern_cb();\nextern void resumable_main_multi_thread();\nextern void resumable_main_channel_mult_thread();\nextern void resumable_main_when_all();\nextern void resumable_main_layout();\nextern void resumable_main_switch_scheduler();\nextern void resumable_main_stop_token();\n\nextern void resumable_main_benchmark_mem(bool wait_key);\nextern void benchmark_main_channel_passing_next();\nextern void resumable_main_benchmark_asio_server();\nextern void resumable_main_benchmark_asio_client(intptr_t nNum);\n\nextern void test_async_cinatra_client();\n\nint main(int argc, const char* argv[])\n{\n\t(void)argc;\n\t(void)argv;\n\n\t//resumable_main_resumable();\n\t//return 0;\n\n\t//if (argc > 1)\n\t//\tresumable_main_benchmark_asio_client(atoi(argv[1]));\n\t//else\n\t//\tresumable_main_benchmark_asio_server();\n\n\tresumable_main_cb();\n\tresumable_main_layout();\n\tresumable_main_modern_cb();\n\tresumable_main_suspend_always();\n\tresumable_main_yield_return();\n\tresumable_main_resumable();\n\tresumable_main_routine();\n#ifndef __clang__\n\tresumable_main_exception(false);\n#endif\n\tresumable_main_dynamic_go();\n\tresumable_main_multi_thread();\n\tresumable_main_timer();\n\tresumable_main_benchmark_mem(false);\n\tresumable_main_mutex();\n\tresumable_main_event();\n\tresumable_main_event_v2();\n\tresumable_main_event_timeout();\n\tresumable_main_channel();\n\tresumable_main_channel_mult_thread();\n\tresumable_main_sleep();\n\tresumable_main_when_all();\n\tresumable_main_switch_scheduler();\n\tresumable_main_stop_token();\n\tstd::cout << \"ALL OK!\" << std::endl;\n\n\tbenchmark_main_channel_passing_next();\n\n\treturn 0;\n}\n"
  },
  {
    "path": "tutorial/CMakeLists.txt",
    "content": "﻿set(TUTORIAL_FILES \"\")\naux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} TUTORIAL_FILES)\n\nforeach(TUTORIAL_FILE_PATH ${TUTORIAL_FILES})\n    string(REGEX REPLACE \".+[/\\]([^/\\.]+)\\\\.cpp\" \"\\\\1\" TUTORIAL_FILE_NAME ${TUTORIAL_FILE_PATH})\n    message(STATUS \"Generating test target: ${TUTORIAL_FILE_NAME}\")\n\n    add_executable(${TUTORIAL_FILE_NAME} ${TUTORIAL_FILE_PATH})\n    target_link_libraries(${TUTORIAL_FILE_NAME} PUBLIC librf)\n    target_compile_definitions(${TUTORIAL_FILE_NAME}\n        PRIVATE LIBRF_TUTORIAL_STAND_ALONE=1\n    )\n    if(UNIX)\n        set_target_properties(${TUTORIAL_FILE_NAME} PROPERTIES INSTALL_RPATH \"$ORIGIN/\")\n    endif(UNIX)\nendforeach(TUTORIAL_FILE_PATH)\n"
  },
  {
    "path": "tutorial/test_async_cb.cpp",
    "content": "﻿#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\ntemplate<class _Ctype>\r\nstatic void callback_get_long(int64_t val, _Ctype&& cb)\r\n{\r\n\tusing namespace std::chrono;\r\n\tstd::thread([val, cb = std::forward<_Ctype>(cb)]\r\n\t\t{\r\n\t\t\tstd::this_thread::sleep_for(500ms);\r\n\t\t\tcb(val * val);\r\n\t\t}).detach();\r\n}\r\n\r\n//这种情况下，没有生成 frame-context，因此，并没有promise_type被内嵌在frame-context里\r\nstatic future_t<int64_t> async_get_long(int64_t val)\r\n{\r\n\tawaitable_t<int64_t> awaitable;\r\n\tcallback_get_long(val, [awaitable](int64_t val)\r\n\t{\r\n\t\tawaitable.set_value(val);\r\n\t});\r\n\treturn awaitable.get_future();\r\n}\r\n\r\nstatic future_t<int64_t> wait_get_long(int64_t val)\r\n{\r\n\tval = co_await async_get_long(val);\r\n\tco_return val;\r\n}\r\n\r\n//这种情况下，会生成对应的 frame-context，一个promise_type被内嵌在frame-context里\r\nstatic future_t<int64_t> resumable_get_long(int64_t val)\r\n{\r\n\tstd::cout << val << std::endl;\r\n\tval = co_await wait_get_long(val);\r\n\tstd::cout << val << std::endl;\r\n\tval = co_await wait_get_long(val);\r\n\tstd::cout << val << std::endl;\r\n\tval = co_await wait_get_long(val);\r\n\tstd::cout << val << std::endl;\r\n\tco_return val;\r\n}\r\n\r\nstatic future_t<int64_t> loop_get_long(int64_t val)\r\n{\r\n\tstd::cout << val << std::endl;\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tval = co_await async_get_long(val);\r\n\t\tstd::cout << val << std::endl;\r\n\t}\r\n\tco_return val;\r\n}\r\n\r\nstatic future_t<std::string&> async_get_string(std::string & ref_string)\r\n{\r\n\tawaitable_t<std::string&> awaitable;\r\n\tcallback_get_long(std::stoi(ref_string), [awaitable, &ref_string](int64_t val)\r\n\t\t{\r\n\t\t\tref_string = std::to_string(val);\r\n\t\t\tawaitable.set_value(ref_string);\r\n\t\t});\r\n\treturn awaitable.get_future();\r\n}\r\n\r\nstatic future_t<std::string&> resumable_get_string(std::string& val)\r\n{\r\n\tstd::cout << val << std::endl;\r\n\tval = co_await async_get_string(val);\r\n\tstd::cout << val << std::endl;\r\n\tval = co_await async_get_string(val);\r\n\tstd::cout << val << std::endl;\r\n\tval = co_await async_get_string(val);\r\n\tstd::cout << val << std::endl;\r\n\tco_return static_cast<std::string&>(val);\r\n}\r\n\r\nvoid resumable_main_cb()\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\t//由于使用者可能不能明确的区分是resume function返回的awaitor还是awaitable function返回的awaitor\r\n\t//导致均有可能加入到协程里去调度。\r\n\t//所以，协程调度器应该需要能处理这种情况。\r\n\tgo async_get_long(3);\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tstd::string ref_string{\"2\"};\r\n\tgo resumable_get_string(ref_string);\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tGO\r\n\t{\r\n\t\tauto val = co_await resumable_get_long(2);\r\n\t\tstd::cout << \"GO:\" << val << std::endl;\r\n\t};\r\n\r\n\tgo loop_get_long(3);\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_cb();\r\n\treturn 0;\r\n}\r\n#endif"
  },
  {
    "path": "tutorial/test_async_channel.cpp",
    "content": "﻿#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n#include <deque>\r\n#include <mutex>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\nusing namespace std::chrono;\r\n\r\nconst size_t MAX_CHANNEL_QUEUE = 1;\t\t//0, 1, 5, 10, -1\r\n\r\n//如果使用move_only_type来操作channel失败，说明中间过程发生了拷贝操作----这不是设计目标。\r\ntemplate<class _Ty>\r\nstruct move_only_type\r\n{\r\n\t_Ty value;\r\n\r\n\tmove_only_type() = default;\r\n\texplicit move_only_type(const _Ty& val) : value(val) {}\r\n\texplicit move_only_type(_Ty&& val) : value(std::forward<_Ty>(val)) {}\r\n\r\n\tmove_only_type(const move_only_type&) = delete;\r\n\tmove_only_type& operator =(const move_only_type&) = delete;\r\n\r\n\tmove_only_type(move_only_type&&) = default;\r\n\tmove_only_type& operator =(move_only_type&&) = default;\r\n};\r\n\r\n//如果channel缓存的元素不能凭空产生，或者产生代价较大，则推荐第二个模板参数使用true。从而减小不必要的开销。\r\nusing string_channel_t = channel_t<move_only_type<std::string>>;\r\n\r\n//channel其实内部引用了一个channel实现体，故可以支持复制拷贝操作\r\nfuture_t<> test_channel_read(string_channel_t c)\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tfor (size_t i = 0; i < 10; ++i)\r\n\t{\r\n\t\ttry\r\n\t\t{\r\n\t\t\t//auto val = co_await c.read();\r\n\t\t\tauto val = co_await c;\t\t//第二种从channel读出数据的方法。利用重载operator co_await()，而不是c是一个awaitable_t。\r\n\r\n\t\t\tstd::cout << val.value << \":\";\r\n\t\t\tstd::cout << std::endl;\r\n\t\t}\r\n\t\tcatch (librf::channel_exception& e)\r\n\t\t{\r\n\t\t\t//MAX_CHANNEL_QUEUE=0,并且先读后写，会触发read_before_write异常\r\n\t\t\tstd::cout << e.what() << std::endl;\r\n\t\t}\r\n\r\n\t\tco_await sleep_for(50ms);\r\n\t}\r\n}\r\n\r\nfuture_t<> test_channel_write(string_channel_t c)\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tfor (size_t i = 0; i < 10; ++i)\r\n\t{\r\n\t\t//co_await c.write(std::to_string(i));\r\n\t\tco_await(c << std::to_string(i));\t\t\t\t//第二种写入数据到channel的方法。因为优先级关系，需要将'c << i'括起来\r\n\t\tstd::cout << \"<\" << i << \">:\";\r\n\t\t\r\n\t\tstd::cout << std::endl;\r\n\t}\r\n}\r\n\r\nvoid test_channel_read_first()\r\n{\r\n\tstring_channel_t c(MAX_CHANNEL_QUEUE);\r\n\r\n\tgo test_channel_read(c);\r\n\tgo test_channel_write(c);\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\nvoid test_channel_write_first()\r\n{\r\n\tstring_channel_t c(MAX_CHANNEL_QUEUE);\r\n\r\n\tgo test_channel_write(c);\r\n\tgo test_channel_read(c);\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\nstatic const int N = 1000000;\r\n\r\nvoid test_channel_performance_single_thread(size_t buff_size)\r\n{\r\n\t//1的话，效率跟golang比，有点惨不忍睹。\r\n\t//1000的话，由于几乎不需要调度器接入，效率就很高了，随便过千万数量级。\r\n\tchannel_t<int, false, true> c{ buff_size };\r\n\r\n\tgo[&]() -> future_t<>\r\n\t{\r\n\t\tfor (int i = N - 1; i >= 0; --i)\r\n\t\t{\r\n\t\t\tco_await(c << i);\r\n\t\t}\r\n\t};\r\n\tgo[&]() -> future_t<>\r\n\t{\r\n\t\tauto tstart = high_resolution_clock::now();\r\n\r\n\t\tint i;\r\n\t\tdo\r\n\t\t{\r\n\t\t\ti = co_await c;\r\n\t\t} while (i > 0);\r\n\r\n\t\tauto dt = duration_cast<duration<double>>(high_resolution_clock::now() - tstart).count();\r\n\t\tstd::cout << \"channel buff=\" << c.capacity() << \", w/r \" << N << \" times, cost time \" << dt << \"s\" << std::endl;\r\n\t};\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\nvoid test_channel_performance_double_thread(size_t buff_size)\r\n{\r\n\t//1的话，效率跟golang比，有点惨不忍睹。\r\n\t//1000的话，由于几乎不需要调度器接入，效率就很高了，随便过千万数量级。\r\n\tchannel_t<int, false, true> c{ buff_size };\r\n\r\n\tstd::thread wr_th([c]\r\n\t{\r\n\t\tlocal_scheduler_t ls;\r\n\r\n\t\tGO\r\n\t\t{\r\n\t\t\tfor (int i = N - 1; i >= 0; --i)\r\n\t\t\t{\r\n\t\t\t\tco_await(c << i);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tthis_scheduler()->run_until_notask();\r\n\t});\r\n\r\n\tgo[&]() -> future_t<>\r\n\t{\r\n\t\tauto tstart = high_resolution_clock::now();\r\n\r\n\t\tint i;\r\n\t\tdo\r\n\t\t{\r\n\t\t\ti = co_await c;\r\n\t\t} while (i > 0);\r\n\r\n\t\tauto dt = duration_cast<duration<double>>(high_resolution_clock::now() - tstart).count();\r\n\t\tstd::cout << \"channel buff=\" << c.capacity() << \", w/r \" << N << \" times, cost time \" << dt << \"s\" << std::endl;\r\n\t};\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\twr_th.join();\r\n}\r\n\r\nvoid test_channel_performance_four_thread(size_t buff_size)\r\n{\r\n\tauto tstart = high_resolution_clock::now();\r\n\r\n\tchannel_t<bool> q{ 8 };\r\n\tchannel_t<int, false, true> c{ buff_size };\r\n\r\n\tstd::thread wr_th[4];\r\n\tstd::thread rd_th[4];\r\n\tfor (int i = 0; i < 4; ++i) {\r\n\t\twr_th[i] = std::thread([c, q] {\r\n\t\t\tfor (int i = N - 1; i >= 0; --i)\r\n\t\t\t\t(void)(c << i);\r\n\t\t\t(void)(q << true);\r\n\t\t});\r\n\t}\r\n\r\n\tfor (int i = 0; i < 4; ++i) {\r\n\t\trd_th[i] = std::thread([c, q] {\r\n\t\t\tfor (int i = N - 1; i >= 0; --i)\r\n\t\t\t\t(void)c.read();\r\n\t\t\t(void)(q << true);\r\n\t\t});\r\n\t}\r\n\r\n\tGO {\r\n\t\tfor (size_t i = 0; i < 8; ++i)\r\n\t\t\tco_await q;\r\n\t};\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tauto dt = duration_cast<duration<double>>(high_resolution_clock::now() - tstart).count();\r\n\tstd::cout << \"channel buff=\" << c.capacity() << \", w/r \" << N << \" times, cost time \" << dt << \"s\" << std::endl;\r\n\r\n\tfor (int i = 0; i < 4; ++i)\r\n\t{\r\n\t\twr_th[i].join();\r\n\t\trd_th[i].join();\r\n\t}\r\n}\r\n\r\nvoid test_channel_performance_four_coroutine(size_t capacity, size_t nThreads, int n)\r\n{\r\n\tauto tstart = high_resolution_clock::now();\r\n\r\n\tchannel_t<bool> q{ nThreads };\r\n\tchannel_t<bool> c{ capacity };\r\n\r\n\tfor (size_t i = 0; i < nThreads; ++i)\r\n\t{\r\n\t\tGO\r\n\t\t{\r\n\t\t\tfor (int i = 0; i < n; ++i)\r\n\t\t\t\tco_await(c << true);\r\n\t\t\tco_await(q << true);\r\n\t\t};\r\n\r\n\t\tGO\r\n\t\t{\r\n\t\t\tfor (int i = 0; i < n; ++i)\r\n\t\t\t\tco_await c;\r\n\t\t\tco_await(q << true);\r\n\t\t};\r\n\t}\r\n\r\n\tGO\r\n\t{\r\n\t\tfor (size_t i = 0; i < nThreads * 2; ++i)\r\n\t\t{\r\n\t\t\tco_await q;\r\n\t\t}\r\n\t};\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tauto dt = duration_cast<duration<double>>(high_resolution_clock::now() - tstart).count();\r\n\tstd::cout << \"channel buff=\" << c.capacity() << \", w/r \" << n << \" times, cost time \" << dt << \"s\" << std::endl;\r\n}\r\n\r\nvoid resumable_main_channel()\r\n{\r\n\ttest_channel_read_first();\r\n\tstd::cout << std::endl;\r\n\r\n\ttest_channel_write_first();\r\n\tstd::cout << std::endl;\r\n\r\n\tstd::cout << \"single thread\" << std::endl;\r\n\ttest_channel_performance_single_thread(1);\r\n\ttest_channel_performance_single_thread(10);\r\n\ttest_channel_performance_single_thread(100);\r\n\ttest_channel_performance_single_thread(1000);\r\n\r\n\tstd::cout << \"double thread\" << std::endl;\r\n\ttest_channel_performance_double_thread(1);\r\n\ttest_channel_performance_double_thread(10);\r\n\ttest_channel_performance_double_thread(100);\r\n\ttest_channel_performance_double_thread(1000);\r\n\r\n\tstd::cout << \"four thread\" << std::endl;\r\n\ttest_channel_performance_four_thread(1);\r\n\ttest_channel_performance_four_thread(1000);\r\n\r\n\tstd::cout << \"four coroutine\" << std::endl;\r\n\ttest_channel_performance_four_coroutine(1, 4, N);\r\n\ttest_channel_performance_four_coroutine(1000, 4, N);\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_channel();\r\n\treturn 0;\r\n}\r\n#endif"
  },
  {
    "path": "tutorial/test_async_channel_mult_thread.cpp",
    "content": "﻿//验证channel是否线程安全\r\n\r\n#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n#include <deque>\r\n#include <mutex>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\nusing namespace std::chrono;\r\nstatic std::mutex cout_mutex;\r\nstd::atomic<intptr_t> gcounter = 0;\r\n\r\n#define OUTPUT_DEBUG\t0\r\n\r\nfuture_t<> test_channel_consumer(channel_t<std::string> c, size_t cnt)\r\n{\r\n\tfor (size_t i = 0; i < cnt; ++i)\r\n\t{\r\n\t\ttry\r\n\t\t{\r\n\t\t\tauto val = co_await c.read();\r\n\t\t\t++gcounter;\r\n#if OUTPUT_DEBUG\r\n\t\t\t{\r\n\t\t\t\tscoped_lock<std::mutex> __lock(cout_mutex);\r\n\t\t\t\tstd::cout << \"R \" << val << \"@\" << std::this_thread::get_id() << std::endl;\r\n\t\t\t}\r\n#endif\r\n\t\t}\r\n\t\tcatch (channel_exception& e)\r\n\t\t{\r\n\t\t\t//MAX_CHANNEL_QUEUE=0,并且先读后写，会触发read_before_write异常\r\n\t\t\tscoped_lock<std::mutex> __lock(cout_mutex);\r\n\t\t\tstd::cout << e.what() << std::endl;\r\n\t\t}\r\n\r\n#if OUTPUT_DEBUG\r\n\t\tco_await sleep_for(50ms);\r\n#endif\r\n\t}\r\n}\r\n\r\nfuture_t<> test_channel_producer(channel_t<std::string> c, size_t cnt)\r\n{\r\n\tfor (size_t i = 0; i < cnt; ++i)\r\n\t{\r\n\t\tco_await c.write(std::to_string(i));\r\n#if OUTPUT_DEBUG\r\n\t\t{\r\n\t\t\tscoped_lock<std::mutex> __lock(cout_mutex);\r\n\t\t\tstd::cout << \"W \" << i << \"@\" << std::this_thread::get_id() << std::endl;\r\n\t\t}\r\n#endif\r\n\t}\r\n}\r\n\r\nconst size_t WRITE_THREAD = 6;\r\nconst size_t READ_THREAD = 6;\r\nconst size_t READ_BATCH = 1000000;\r\nconst size_t MAX_CHANNEL_QUEUE = 5;\t\t//0, 1, 5, 10, -1\r\n\r\nvoid resumable_main_channel_mult_thread()\r\n{\r\n\tchannel_t<std::string> c(MAX_CHANNEL_QUEUE);\r\n\r\n\tstd::thread write_th[WRITE_THREAD];\r\n\tfor (size_t i = 0; i < WRITE_THREAD; ++i)\r\n\t{\r\n\t\twrite_th[i] = std::thread([&]\r\n\t\t{\r\n\t\t\tlocal_scheduler_t my_scheduler;\r\n\t\t\tgo test_channel_producer(c, READ_BATCH * READ_THREAD / WRITE_THREAD);\r\n\t\t\tthis_scheduler()->run_until_notask();\r\n\r\n\t\t\t{\r\n\t\t\t\tscoped_lock<std::mutex> __lock(cout_mutex);\r\n\t\t\t\tstd::cout << \"Write OK\\r\\n\";\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\tstd::this_thread::sleep_for(100ms);\r\n\r\n\tstd::thread read_th[READ_THREAD];\r\n\tfor (size_t i = 0; i < READ_THREAD; ++i)\r\n\t{\r\n\t\tread_th[i] = std::thread([&]\r\n\t\t{\r\n\t\t\tlocal_scheduler_t my_scheduler;\r\n\t\t\tgo test_channel_consumer(c, READ_BATCH);\r\n\t\t\tthis_scheduler()->run_until_notask();\r\n\r\n\t\t\t{\r\n\t\t\t\tscoped_lock<std::mutex> __lock(cout_mutex);\r\n\t\t\t\tstd::cout << \"Read OK\\r\\n\";\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\t\r\n\tstd::this_thread::sleep_for(100ms);\r\n\tscheduler_t::g_scheduler.run_until_notask();\r\n\r\n\tfor(auto & th : read_th)\r\n\t\tth.join();\r\n\tfor (auto& th : write_th)\r\n\t\tth.join();\r\n\r\n\tstd::cout << \"OK: counter = \" << gcounter.load() << std::endl;\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_channel_mult_thread();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_dynamic_go.cpp",
    "content": "﻿\r\n#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n\r\n#include \"librf/librf.h\"\r\n\r\n\r\nstatic const int M = 10;\r\n\r\nsize_t dynamic_go_count = 0;\r\nstd::array<std::array<std::array<int32_t, M>, M>, 3> dynamic_cells;\r\n\r\nvoid test_dynamic_go()\r\n{\r\n\tauto co_star = [](int j) -> librf::future_t<int>\r\n\t{\r\n\t\tfor (int i = 0; i < M; ++i)\r\n\t\t{\r\n\t\t\tgo[=]() -> librf::generator_t<int>\r\n\t\t\t{\r\n\t\t\t\tfor (int k = 0; k < M; ++k)\r\n\t\t\t\t{\r\n\t\t\t\t\t++dynamic_cells[j][i][k];\r\n\t\t\t\t\t++dynamic_go_count;\r\n\r\n\t\t\t\t\tstd::cout << j << \"  \" << i << \"  \" << k << std::endl;\r\n\t\t\t\t\tco_yield k;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tco_return M;\r\n\t\t\t};\r\n\r\n\t\t\tco_yield i;\r\n\t\t}\r\n\r\n\t\tco_return M;\r\n\t};\r\n\tgo co_star(0);\r\n\tgo co_star(1);\r\n\tgo co_star(2);\r\n\r\n\tlibrf::this_scheduler()->run_until_notask();\r\n\r\n\tstd::cout << \"dynamic_go_count = \" << dynamic_go_count << std::endl;\r\n\tfor (auto & j : dynamic_cells)\r\n\t{\r\n\t\tfor (auto & i : j)\r\n\t\t{\r\n\t\t\tfor (auto k : i)\r\n\t\t\t\tstd::cout << k;\r\n\t\t\tstd::cout << std::endl;\r\n\t\t}\r\n\t\tstd::cout << std::endl;\r\n\t}\r\n}\r\n\r\nvoid resumable_main_dynamic_go()\r\n{\r\n\ttest_dynamic_go();\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_dynamic_go();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_event.cpp",
    "content": "﻿#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\n//非协程的逻辑线程，或异步代码，可以通过event_t通知到协程，并且不会阻塞协程所在的线程。\r\nstatic std::thread async_set_event(const event_t & e, std::chrono::milliseconds dt)\r\n{\r\n\treturn std::thread([=]\r\n\t{\r\n\t\tstd::this_thread::sleep_for(dt);\r\n\t\te.signal();\r\n\t});\r\n}\r\n\r\n\r\nstatic future_t<> resumable_wait_event(const event_t & e)\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tauto result = co_await e.wait();\r\n\tif (result == false)\r\n\t\tstd::cout << \"time out!\" << std::endl;\r\n\telse\r\n\t\tstd::cout << \"event signal!\" << std::endl;\r\n}\r\n\r\nstatic void test_wait_one()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\t{\r\n\t\tevent_t evt;\r\n\t\tgo resumable_wait_event(evt);\r\n\t\tauto tt = async_set_event(evt, 1000ms);\r\n\t\tthis_scheduler()->run_until_notask();\r\n\r\n\t\ttt.join();\r\n\t}\r\n\t{\r\n\t\tevent_t evt2(1);\r\n\t\tgo[&]() -> future_t<>\r\n\t\t{\r\n\t\t\t(void)co_await evt2.wait();\r\n\t\t\tstd::cout << \"event signal on 1!\" << std::endl;\r\n\t\t};\r\n\t\tgo[&]() -> future_t<>\r\n\t\t{\r\n\t\t\t(void)co_await evt2.wait();\r\n\t\t\tstd::cout << \"event signal on 2!\" << std::endl;\r\n\t\t};\r\n\t\tstd::cout << std::this_thread::get_id() << std::endl;\r\n\t\tauto tt = async_set_event(evt2, 1000ms);\r\n\r\n\t\tthis_scheduler()->run_until_notask();\r\n\r\n\t\ttt.join();\r\n\t}\r\n}\r\n\r\nstatic void test_wait_three()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evt1, evt2, evt3;\r\n\r\n\tgo[&]() -> future_t<>\r\n\t{\r\n\t\tauto result = co_await event_t::wait_all(std::initializer_list<event_t>{ evt1, evt2, evt3 });\r\n\t\tif (result)\r\n\t\t\tstd::cout << \"all event signal!\" << std::endl;\r\n\t\telse\r\n\t\t\tstd::cout << \"time out!\" << std::endl;\r\n\t};\r\n\r\n\tstd::vector<std::thread> vtt;\r\n\r\n\tsrand((int)time(nullptr));\r\n\tvtt.emplace_back(async_set_event(evt1, 1ms * (500 + rand() % 1000)));\r\n\tvtt.emplace_back(async_set_event(evt2, 1ms * (500 + rand() % 1000)));\r\n\tvtt.emplace_back(async_set_event(evt3, 1ms * (500 + rand() % 1000)));\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tfor (auto& tt : vtt)\r\n\t\ttt.join();\r\n}\r\n\r\n#if 0\r\nstatic void test_wait_any()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evts[8];\r\n\r\n\tgo[&]() -> future_t<>\r\n\t{\r\n\t\tfor (size_t i = 0; i < std::size(evts); ++i)\r\n\t\t{\r\n\t\t\tintptr_t idx = co_await event_t::wait_any(evts);\r\n\t\t\tstd::cout << \"event \" << idx << \" signal!\" << std::endl;\r\n\t\t}\r\n\t};\r\n\r\n\tstd::vector<std::thread> vtt;\r\n\r\n\tsrand((int)time(nullptr));\r\n\tfor (auto & e : evts)\r\n\t{\r\n\t\tvtt.emplace_back(async_set_event(e, 1ms * (500 + rand() % 1000)));\r\n\t}\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tfor (auto & tt : vtt)\r\n\t\ttt.join();\r\n}\r\n#endif\r\n\r\nstatic void test_wait_all()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evts[8];\r\n\r\n\tgo[&]() -> future_t<>\r\n\t{\r\n\t\tauto result = co_await event_t::wait_all(evts);\r\n\t\tif (result)\r\n\t\t\tstd::cout << \"all event signal!\" << std::endl;\r\n\t\telse\r\n\t\t\tstd::cout << \"time out!\" << std::endl;\r\n\t};\r\n\r\n\tstd::vector<std::thread> vtt;\r\n\r\n\tsrand((int)time(nullptr));\r\n\tfor (auto & e : evts)\r\n\t{\r\n\t\tvtt.emplace_back(async_set_event(e, 1ms * (500 + rand() % 1000)));\r\n\t}\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tfor (auto & tt : vtt)\r\n\t\ttt.join();\r\n}\r\n\r\nstatic void test_wait_all_timeout()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evts[8];\r\n\r\n\tgo[&]() -> future_t<>\r\n\t{\r\n\t\tauto result = co_await event_t::wait_all_for(1000ms, evts);\r\n\t\tif (result)\r\n\t\t\tstd::cout << \"all event signal!\" << std::endl;\r\n\t\telse\r\n\t\t\tstd::cout << \"time out!\" << std::endl;\r\n\t};\r\n\r\n\tstd::vector<std::thread> vtt;\r\n\r\n\tsrand((int)time(nullptr));\r\n\tfor (auto & e : evts)\r\n\t{\r\n\t\tvtt.emplace_back(async_set_event(e, 1ms * (500 + rand() % 1000)));\r\n\t}\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tfor (auto & tt : vtt)\r\n\t\ttt.join();\r\n}\r\n\r\nvoid resumable_main_event()\r\n{\r\n\ttest_wait_one();\r\n\tstd::cout << std::endl;\r\n\r\n\ttest_wait_three();\r\n\tstd::cout << std::endl;\r\n\r\n\t//test_wait_any();\r\n\t//std::cout << std::endl;\r\n\r\n\ttest_wait_all();\r\n\tstd::cout << std::endl;\r\n\r\n\ttest_wait_all_timeout();\r\n\tstd::cout << std::endl;\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_event();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_event_timeout.cpp",
    "content": "﻿#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\nfuture_t<> resumalbe_set_event(const event_t & e, std::chrono::milliseconds dt)\r\n{\r\n\tco_await librf::sleep_for(dt);\r\n\te.signal();\r\n\tstd::cout << \"+\";\r\n}\r\n\r\nvoid async_set_event(const event_t & e, std::chrono::milliseconds dt)\r\n{\r\n\tstd::thread([=]\r\n\t{\r\n\t\tstd::this_thread::sleep_for(dt);\r\n\t\te.signal();\r\n\t}).detach();\r\n}\r\n\r\nvoid test_wait_timeout_one()\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evt;\r\n\r\n\tgo [&evt]() -> future_t<>\r\n\t{\r\n\t\tintptr_t counter = 0;\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\tauto result = co_await evt.wait_for(100ms);\r\n\t\t\tif (result)\r\n\t\t\t\tbreak;\r\n\t\t\t++counter;\r\n\t\t\tstd::cout << \".\";\r\n\t\t}\r\n\t\tstd::cout << counter << std::endl;\r\n\t};\r\n\r\n\tasync_set_event(evt, 2s + 50ms);\r\n\t//go resumalbe_set_event(evt, 2s + 50ms);\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\n#if 0\r\nvoid test_wait_timeout_any_invalid()\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evts[8];\r\n\r\n\t//无效的等待\r\n\tgo[&]()-> future_t<>\r\n\t{\r\n\t\tintptr_t idx = co_await event_t::wait_any_for(500ms, std::begin(evts), std::end(evts));\r\n\t\tassert(idx < 0);\r\n\t\t(void)idx;\r\n\r\n\t\tstd::cout << \"invalid wait!\" << std::endl;\r\n\t};\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\nvoid test_wait_timeout_any()\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evts[8];\r\n\r\n\tgo[&]() -> future_t<>\r\n\t{\r\n\t\tintptr_t counter = 0;\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\tintptr_t idx = co_await event_t::wait_any_for(500ms, evts);\r\n\t\t\tif (idx >= 0)\r\n\t\t\t{\r\n\t\t\t\tstd::cout << counter << std::endl;\r\n\t\t\t\tstd::cout << \"event \" << idx << \" signal!\" << std::endl;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t++counter;\r\n\t\t\tstd::cout << \".\";\r\n\t\t}\r\n\r\n\t\t//取消剩下的定时器，以便于协程调度器退出来\r\n\t\tthis_scheduler()->timer()->clear();\r\n\t};\r\n\r\n\tsrand((int)time(nullptr));\r\n\tfor (auto & e : evts)\r\n\t{\r\n\t\t//go resumalbe_set_event(e, 1ms * (1000 + rand() % 5000));\r\n\t\tasync_set_event(e, 1ms * (1000 + rand() % 5000));\r\n\t}\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n#endif\r\n\r\nvoid test_wait_timeout_all_invalid()\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evts[8];\r\n\r\n\t//无效的等待\r\n\tgo[&]()-> future_t<>\r\n\t{\r\n\t\tbool result = co_await event_t::wait_all_for(500ms, std::begin(evts), std::end(evts));\r\n\t\tassert(!result);\r\n\t\t(void)result;\r\n\r\n\t\tstd::cout << \"invalid wait!\" << std::endl;\r\n\t};\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\nvoid test_wait_timeout_all()\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evts[8];\r\n\r\n\tgo[&]() -> future_t<>\r\n\t{\r\n\t\tintptr_t counter = 0;\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\tauto result = co_await event_t::wait_all_for(1500ms, evts);\r\n\t\t\tif (result)\r\n\t\t\t{\r\n\t\t\t\tstd::cout << counter << std::endl;\r\n\t\t\t\tstd::cout << \"all event signal!\" << std::endl;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t++counter;\r\n\t\t\tstd::cout << \".\";\r\n\t\t\tstd::cout << \"timeout!\" << std::endl;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t};\r\n\r\n\tsrand((int)time(nullptr));\r\n\tfor (auto & e : evts)\r\n\t{\r\n\t\t//go resumalbe_set_event(e, 1ms * (1000 + rand() % 5000));\r\n\t\tasync_set_event(e, 1ms * (1000 + rand() % 1000));\r\n\t}\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\nvoid resumable_main_event_timeout()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\ttest_wait_timeout_one();\r\n\tstd::cout << std::endl;\r\n\r\n#if 0\r\n\ttest_wait_timeout_any_invalid();\r\n\tstd::cout << std::endl << std::endl;\r\n\r\n\ttest_wait_timeout_any();\r\n\tstd::cout << std::endl << std::endl;\r\n#endif\r\n\r\n\ttest_wait_timeout_all_invalid();\r\n\tstd::cout << std::endl;\r\n\r\n\ttest_wait_timeout_all();\r\n\tstd::cout << std::endl;\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_event_timeout();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_event_v2.cpp",
    "content": "﻿#include <chrono>\n#include <iostream>\n#include <string>\n#include <thread>\n\n#include \"librf/librf.h\"\n\nusing namespace librf;\nusing namespace std::chrono;\n\n//非协程的逻辑线程，或异步代码，可以通过event_t通知到协程，并且不会阻塞协程所在的线程。\nstatic std::thread async_set_event_all(const event_t & e, std::chrono::milliseconds dt)\n{\n\treturn std::thread([=]\n\t{\n\t\tstd::this_thread::sleep_for(dt);\n\t\te.signal_all();\n\t});\n}\n\nstatic std::thread async_set_event_one(event_t e, std::chrono::milliseconds dt)\n{\n\treturn std::thread([=]\n\t{\n\t\tstd::this_thread::sleep_for(dt);\n\t\te.signal();\n\t});\n}\n\nstatic future_t<> resumable_wait_event(event_t e, int idx)\n{\n\tauto result = co_await e;\n\tif (result)\n\t\tstd::cout << \"[\" << idx << \"]event signal!\" << std::endl;\n\telse\n\t\tstd::cout << \"[\" << idx << \"]time out!\" << std::endl;\n}\n\nstatic future_t<> resumable_wait_timeout(event_t e, milliseconds dt, int idx)\n{\n\tauto result = co_await e.wait_for(dt);\n\tif (result)\n\t\tstd::cout << \"[\" << idx << \"]event signal!\" << std::endl;\n\telse\n\t\tstd::cout << \"[\" << idx << \"]time out!\" << std::endl;\n}\n\nstatic void test_notify_all()\n{\n\tevent_t evt;\n\tgo resumable_wait_event(evt, 0);\n\tgo resumable_wait_event(evt, 1);\n\tgo resumable_wait_event(evt, 2);\n\n\tauto tt = async_set_event_all(evt, 100ms);\n\tthis_scheduler()->run_until_notask();\n\n\ttt.join();\n}\n\nstatic void test_notify_one()\n{\n\tusing namespace std::chrono;\n\n\t{\n\t\tevent_t evt;\n\t\tgo resumable_wait_event(evt, 10);\n\t\tgo resumable_wait_event(evt, 11);\n\t\tgo resumable_wait_event(evt, 12);\n\n\t\tauto tt1 = async_set_event_one(evt, 100ms);\n\t\tauto tt2 = async_set_event_one(evt, 500ms);\n\t\tauto tt3 = async_set_event_one(evt, 800ms);\n\t\tthis_scheduler()->run_until_notask();\n\n\t\ttt1.join();\n\t\ttt2.join();\n\t\ttt3.join();\n\t}\n}\n\nstatic void test_wait_all_timeout()\n{\n\tusing namespace std::chrono;\n\n\tsrand((int)time(nullptr));\n\n\tevent_t evts[10];\n\n\tstd::vector<std::thread> vtt;\n\tfor(size_t i = 0; i < std::size(evts); ++i)\n\t{\n\t\tgo resumable_wait_timeout(evts[i], 100ms, (int)i);\n\t\tvtt.emplace_back(async_set_event_one(evts[i], 1ms * (50 + i * 10)));\n\t}\n\n\tthis_scheduler()->run_until_notask();\n\n\tfor (auto& tt : vtt)\n\t\ttt.join();\n}\n\nvoid resumable_main_event_v2()\n{\n\ttest_notify_all();\n\tstd::cout << std::endl;\n\n\ttest_notify_one();\n\tstd::cout << std::endl;\n\n\ttest_wait_all_timeout();\n\tstd::cout << std::endl;\n}\n\n#if LIBRF_TUTORIAL_STAND_ALONE\nint main()\n{\n\tresumable_main_event_v2();\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "tutorial/test_async_exception.cpp",
    "content": "﻿#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\n//请打开结构化异常(/EHa)\r\nauto async_signal_exception(const intptr_t dividend)\r\n{\r\n\tawaitable_t<int64_t> awaitable;\r\n\r\n\tstd::thread([dividend, awaitable]\r\n\t{\r\n\t\tstd::this_thread::sleep_for(std::chrono::milliseconds(50));\r\n\t\ttry\r\n\t\t{\r\n\t\t\t//也可以注释掉这个判断，使用结构化异常。但就获得不了具体描述信息了\r\n\t\t\tif (dividend == 0)\r\n\t\t\t\tthrow std::logic_error(\"divided by zero\");\r\n\t\t\tawaitable.set_value(10000 / dividend);\r\n\t\t}\r\n\t\tcatch (...)\r\n\t\t{\r\n\t\t\tawaitable.set_exception(std::current_exception());\r\n\t\t}\r\n\t}).detach();\r\n\r\n\treturn awaitable.get_future();\r\n}\r\n\r\nauto async_signal_exception2(const intptr_t dividend)\r\n{\r\n\tawaitable_t<int64_t> awaitable;\r\n\r\n\tstd::thread([dividend, awaitable]\r\n\t{\r\n\t\tstd::this_thread::sleep_for(std::chrono::milliseconds(50));\r\n\t\tif (dividend == 0)\r\n\t\t\tawaitable.throw_exception(std::logic_error(\"divided by zero\"));\r\n\t\telse\r\n\t\t\tawaitable.set_value(10000 / dividend);\r\n\t}).detach();\r\n\r\n\treturn awaitable.get_future();\r\n}\r\n\r\nfuture_t<> test_signal_exception()\r\n{\r\n\tfor (intptr_t i = 10; i >= 0; --i)\r\n\t{\r\n\t\ttry\r\n\t\t{\r\n\t\t\tauto r = co_await async_signal_exception2(i);\r\n\t\t\tstd::cout << \"result is \" << r << std::endl;\r\n\t\t}\r\n\t\tcatch (const std::exception& ex)\r\n\t\t{\r\n\t\t\tstd::cout << \"exception signal : \" << ex.what() << std::endl;\r\n\t\t}\r\n\t\tcatch (...)\r\n\t\t{\r\n\t\t\tstd::cout << \"exception signal : who knows?\" << std::endl;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nfuture_t<> test_bomb_exception()\r\n{\r\n\tfor (intptr_t i = 10; i >= 0; --i)\r\n\t{\r\n\t\tauto r = co_await async_signal_exception(i);\r\n\t\tstd::cout << \"result is \" << r << std::endl;\r\n\t}\r\n}\r\n\r\nvoid resumable_main_exception(bool bomb)\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\tgo test_signal_exception();\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tstd::cout << std::endl;\r\n\tif (bomb)\r\n\t{\r\n\t\tgo test_bomb_exception();\r\n\t\tthis_scheduler()->run_until_notask();\r\n\t}\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_exception(true);\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_memory_layout.cpp",
    "content": "﻿#include <chrono>\n#include <iostream>\n#include <string>\n#include <thread>\n\n#include \"librf/librf.h\"\n\nusing namespace librf;\n\n#ifndef __GNUC__\t//GCC: 没有提供__builtin_coro_frame这样的内置函数\n\ntemplate<class _Ctype>\nstatic void callback_get_long(int64_t a, int64_t b, _Ctype&& cb)\n{\n\tstd::cout << std::endl << __FUNCTION__ << \" - begin\" << std::endl;\n\n\t//编译失败。因为这个函数不是\"可恢复函数(resumeable function)\"，甚至都不是\"可等待函数(awaitable function)\"\n\t//void* frame_ptr = _coro_frame_ptr();\n\n\tusing namespace std::chrono;\n\tstd::thread([=, cb = std::forward<_Ctype>(cb)]\n\t\t{\n\t\t\tstd::this_thread::sleep_for(500ms);\n\t\t\tcb(a + b);\n\t\t}).detach();\n\n\tstd::cout << __FUNCTION__ << \" - end\" << std::endl;\n}\n\n//这种情况下，没有生成 frame-context，因此，并没有promise_type被内嵌在frame-context里\nfuture_t<int64_t> awaitable_get_long(int64_t a, int64_t b)\n{\n\tstd::cout << std::endl << __FUNCTION__ << \" - begin\" << std::endl;\n\t//编译失败。因为这个函数不是\"可恢复函数(resumeable function)\"，仅仅是\"可等待函数(awaitable function)\"\n\t//void* frame_ptr = _coro_frame_ptr();\n\n\tlibrf::awaitable_t<int64_t> awaitable;\n\tcallback_get_long(a, b, [awaitable](int64_t val)\n\t{\n\t\tawaitable.set_value(val);\n\t});\n\n\tstd::cout << __FUNCTION__ << \" - end\" << std::endl;\n\n\treturn awaitable.get_future();\n}\n\nfuture_t<int64_t> resumeable_get_long(int64_t x, int64_t y)\n{\n\tstd::cout << std::endl << __FUNCTION__ << \" - begin\" << std::endl;\n\n\tusing future_type = future_t<int64_t>;\n\tusing promise_type = typename future_type::promise_type;\n\tusing state_type = typename future_type::state_type;\n\n\tvoid* frame_ptr = _coro_frame_ptr();\n\tauto handler = coroutine_handle<promise_type>::from_address(frame_ptr);\n\tpromise_type* promise = &handler.promise();\n\tstate_type* state = handler.promise().get_state();\n\n\tstd::cout << \"  future size=\" << sizeof(future_type) << \" / \" << _Align_size<future_type>() << std::endl;\n\tstd::cout << \"  promise size=\" << sizeof(promise_type) << \" / \" << _Align_size<promise_type>() << std::endl;\n\tstd::cout << \"  state size=\" << sizeof(state_type) << \" / \"<< _Align_size<state_type>() << std::endl;\n\tstd::cout << \"  frame size=\" << _coro_frame_size() << \", alloc size=\" << state->get_alloc_size() << std::endl;\n\n\tstd::cout << \"  frame ptr=\" << frame_ptr << \",\" << (void*)&frame_ptr << std::endl;\n\tstd::cout << \"  frame end=\" << (void*)((char*)(frame_ptr)+_coro_frame_size()) << std::endl;\n\tstd::cout << \"  promise ptr=\" << promise << \",\" << (void*)&promise << \",offset=\" << ((char*)promise - (char*)frame_ptr) << std::endl;\n\tstd::cout << \"  handle ptr=\" << handler.address() << \",\" << (void*)&handler << \",offset=\" << ((char*)handler.address() - (char*)frame_ptr) << std::endl;\n\tstd::cout << \"  state ptr=\" << state << \",\" << (void*)&state << \",offset=\" << ((char*)state - (char*)frame_ptr) << std::endl;\n\tstd::cout << \"  parent ptr=\" << state->get_parent() << std::endl;\n\n\tstd::cout << \"    x=\" << x << \", &x=\" << std::addressof(x) << std::endl;\n\tstd::cout << \"    y=\" << y << \", &y=\" << std::addressof(y) << std::endl;\n\n\tint64_t val = co_await awaitable_get_long(x, y);\n\tstd::cout << \"    val=\" << val << \", &val=\" << std::addressof(val) << std::endl;\n\n\tstd::cout << __FUNCTION__ << \" - end\" << std::endl;\n\n\tco_return val;\n}\n\n//这种情况下，会生成对应的 frame-context，一个promise_type被内嵌在frame-context里\nfuture_t<> resumable_get_long_2(int64_t a, int64_t b, int64_t c)\n{\n\tint64_t v1, v2, v3;\n\n\tstd::cout << std::endl << __FUNCTION__ << \" - begin\" << std::endl;\n\n\tusing future_type = future_t<>;\n\tusing promise_type = typename future_type::promise_type;\n\tusing state_type = typename future_type::state_type;\n\n\tvoid* frame_ptr = _coro_frame_ptr();\n\tauto handler = coroutine_handle<promise_type>::from_address(frame_ptr);\n\tpromise_type * promise = &handler.promise();\n\tstate_type * state = handler.promise().get_state();\n\n\tstd::cout << \"  future size=\" << sizeof(future_type) << \" / \" << _Align_size<future_type>() << std::endl;\n\tstd::cout << \"  promise size=\" << sizeof(promise_type) << \" / \" << _Align_size<promise_type>() << std::endl;\n\tstd::cout << \"  state size=\" << sizeof(state_type) << \" / \"<< _Align_size<state_type>() << std::endl;\n\tstd::cout << \"  frame size=\" << _coro_frame_size() << \", alloc size=\" << state->get_alloc_size() << std::endl;\n\n\tstd::cout << \"  frame ptr=\" << frame_ptr << \",\"<< (void*)&frame_ptr << std::endl;\n\tstd::cout << \"  frame end=\" << (void *)((char*)(frame_ptr) + _coro_frame_size()) << std::endl;\n\tstd::cout << \"  promise ptr=\" << promise << \",\" << (void *)&promise << std::endl;\n\tstd::cout << \"  handle ptr=\" << handler.address() << \",\" << (void*)&handler << std::endl;\n\tstd::cout << \"  state ptr=\" << state << \",\" << (void*)&state << std::endl;\n\tstd::cout << \"  parent ptr=\" << state->get_parent() << std::endl;\n\n\tstd::cout << \"    a=\" << a << \", &a=\" << std::addressof(a) << std::endl;\n\tstd::cout << \"    b=\" << b << \", &b=\" << std::addressof(b) << std::endl;\n\tstd::cout << \"    c=\" << c << \", &c=\" << std::addressof(c) << std::endl;\n\n\tv1 = co_await resumeable_get_long(a, b);\n\tstd::cout << \"    v1=\" << v1 << \", &v1=\" << std::addressof(v1) << std::endl;\n\n\tv2 = co_await resumeable_get_long(b, c);\n\tstd::cout << \"    v2=\" << v2 << \", &v2=\" << std::addressof(v2) << std::endl;\n\n\tv3 = co_await resumeable_get_long(v1, v2);\n\tstd::cout << \"    v3=\" << v3 << \", &v3=\" << std::addressof(v3) << std::endl;\n\n\tint64_t v4 = v1 * v2 * v3;\n\tstd::cout << \"    v4=\" << v4 << \", &v4=\" << std::addressof(v4) << std::endl;\n\n\tstd::cout << __FUNCTION__ << \" - end\" << std::endl;\n}\n#endif //#ifndef __GNUC__\n\nvoid resumable_main_layout()\n{\n\tstd::cout << std::endl << __FUNCTION__ << \" - begin\" << std::endl;\n\n#ifndef __GNUC__\t//GCC: 没有提供__builtin_coro_frame这样的内置函数\n\tgo resumable_get_long_2(1, 2, 5);\n#endif //#ifndef __GNUC__\n\tlibrf::this_scheduler()->run_until_notask();\n\n\tstd::cout << __FUNCTION__ << \" - end\" << std::endl;\n}\n\n#if LIBRF_TUTORIAL_STAND_ALONE\nint main()\n{\n\tresumable_main_layout();\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "tutorial/test_async_modern_cb.cpp",
    "content": "﻿//依赖 https://github.com/tearshark/modern_cb 项目\n//依赖 https://github.com/tearshark/librf 项目\n\n#include <future>\n#include <string>\n#include <iostream>\n\n#include \"modern_callback.h\"\n\n//原旨主义的异步函数，其回调写法大致如下\ntemplate<typename _Input_t, typename _Callable_t>\nvoid tostring_async_originalism(_Input_t&& value, _Callable_t&& token)\n{\n\tstd::thread([callback = std::move(token), value = std::forward<_Input_t>(value)]\n\t\t{\n\t\t\tcallback(std::to_string(value));\n\t\t}).detach();\n}\nvoid tostring_async_originalism2(int value, std::function<void(std::string)>&& token, double mu)\n{\n\tstd::thread([callback = std::move(token), value, mu]\n\t\t{\n\t\t\tcallback(std::to_string(value * mu));\n\t\t}).detach();\n}\n\n//使用原旨主义的方式扩展异步方法来支持future\ntemplate<typename _Input_t>\nauto tostring_async_originalism_future(_Input_t&& value)\n{\n\tstd::promise<std::string> _promise;\n\tstd::future<std::string> _future = _promise.get_future();\n\n\tstd::thread([_promise = std::move(_promise), value = std::forward<_Input_t>(value)]() mutable\n\t{\n\t\t_promise.set_value(std::to_string(value));\n\t}).detach();\n\n\treturn std::move(_future);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n//下面演示如何扩展tostring_async函数，以支持future模式\n\ntemplate<typename _Input_t, typename _Callable_t>\nauto tostring_async(_Input_t&& value, _Callable_t&& token)\n{\n\tMODERN_CALLBACK_TRAITS(token, void(std::string));\n\n\tstd::thread([callback = MODERN_CALLBACK_CALL(), value = std::forward<_Input_t>(value)]\n\t\t{\n\t\t\tcallback(std::to_string(value));\n\t\t}).detach();\n\n\tMODERN_CALLBACK_RETURN();\n}\n\n//演示异步库有多个异步回调函数，只要按照Modern Callback范式去做回调，就不再需要写额外的代码，就可以适配到future+librf，以及更多的其他库\ntemplate<typename _Ty1, typename _Ty2, typename _Callable_t>\nauto add_async(_Ty1&& val1, _Ty2&& val2, _Callable_t&& token)\n{\n\tMODERN_CALLBACK_TRAITS(token, void(decltype(val1 + val2)));\n\n\tstd::thread([=, callback = MODERN_CALLBACK_CALL()]\n\t\t{\n\t\t\tusing namespace std::literals;\n\t\t\tstd::this_thread::sleep_for(0.1s);\n\t\t\tcallback(val1 + val2);\n\t\t}).detach();\n\n\tMODERN_CALLBACK_RETURN();\n}\n\n//演示异步库有多个异步回调函数，只要按照Modern Callback范式去做回调，就不再需要写额外的代码，就可以适配到future+librf，以及更多的其他库\ntemplate<typename _Ty1, typename _Ty2, typename _Callable_t>\nauto muldiv_async(_Ty1&& val1, _Ty2&& val2, _Callable_t&& token)\n{\n\tMODERN_CALLBACK_TRAITS(token, void(std::exception_ptr, decltype(val1 * val2), decltype(val1 / val2)));\n\n\tstd::thread([=, callback = MODERN_CALLBACK_CALL()]\n\t\t{\n\t\t\tusing namespace std::literals;\n\t\t\tstd::this_thread::sleep_for(0.1s);\n\n\t\t\tauto v1 = val1 * val2;\n\n\t\t\tif (val2 == 0)\n\t\t\t\tcallback(std::make_exception_ptr(std::logic_error(\"divided by zero\")), v1, 0);\n\t\t\telse\n\t\t\t\tcallback(nullptr, v1, val1 / val2);\n\t\t}).detach();\n\n\tMODERN_CALLBACK_RETURN();\n}\n\n#include \"use_future.h\"\n#if TEST_ASYNC_CALLBACK\n#include \"async_call.hpp\"\n#endif\n\nstatic void example_future()\n{\n\tusing namespace std::literals;\n\n\t//使用lambda作为异步回调函数，传统用法\n\ttostring_async_originalism(-1.0, [](std::string&& value)\n\t\t{\n\t\t\tstd::cout << value << std::endl;\n\t\t});\n\tstd::this_thread::sleep_for(0.5s);\n\n\ttostring_async(1.0, [](std::string&& value)\n\t\t{\n\t\t\tstd::cout << value << std::endl;\n\t\t});\n\n\tstd::this_thread::sleep_for(0.5s);\n\tstd::cout << \"......\" << std::endl;\n\n\t//支持future的用法\n\tstd::future<std::string> f1 = tostring_async_originalism_future(5);\n\tstd::cout << f1.get() << std::endl;\n\n\tstd::future<std::string> f2 = tostring_async(6.0f, std_future);\n\tstd::cout << f2.get() << std::endl;\n\n#if TEST_ASYNC_CALLBACK\n\tstd::future<std::string> f3 = async_call(&tostring_async_originalism2, 99, placeholder::_cb(std_future), 2.0);\n\tstd::cout << f3.get() << std::endl;\n#endif\n}\n\n#include \"librf/librf.h\"\n#include \"use_librf.h\"\n\nstatic void example_librf()\n{\n\t//支持librf的用法\n\tGO\n\t{\n\t\ttry\n\t\t{\n\t\t\tint val = co_await add_async(1, 2, use_librf);\n\t\t\tstd::cout << val << std::endl;\n\n\t\t\t//muldiv_async函数可能会抛异常，取决于val是否是0\n\t\t\t//异常将会带回到本协程里的代码，所以需要try-catch\n\t\t\tauto [a, b] = co_await muldiv_async(9, val, use_librf);\n\n\t\t\tstd::string result = co_await tostring_async(a + b, use_librf);\n\t\t\tstd::cout << result << std::endl;\n\n#if TEST_ASYNC_CALLBACK\n\t\t\tresult = co_await async_call(&tostring_async_originalism2, 99, placeholder::_cb(use_librf), 2.0);\n\t\t\tstd::cout << result << std::endl;\n#endif\n\t\t}\n\t\tcatch (const std::exception & e)\n\t\t{\n\t\t\tstd::cout << \"exception signal : \" << e.what() << std::endl;\n\t\t}\n\t\tcatch (...)\n\t\t{\n\t\t\tstd::cout << \"exception signal : who knows?\" << std::endl;\n\t\t}\n\t};\n\n\tlibrf::this_scheduler()->run_until_notask();\n}\n\nvoid resumable_main_modern_cb()\n{\n\tstd::cout << __FUNCTION__ << std::endl;\n\texample_future();\n\texample_librf();\n}\n\n#if LIBRF_TUTORIAL_STAND_ALONE\nint main()\n{\n\tresumable_main_modern_cb();\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "tutorial/test_async_multi_thread.cpp",
    "content": "﻿#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\nstatic std::mutex cout_mutex;\r\n\r\n//这是一个重度计算任务，只能单开线程来避免主线程被阻塞\r\nauto async_heavy_computing_tasks(int64_t val)\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tawaitable_t<int64_t> awaitable;\r\n\r\n\tstd::thread([val, st = awaitable._state]\r\n\t{\r\n\t\tstd::this_thread::sleep_for(500ms);\r\n\t\tst->set_value(val * val);\r\n\t}).detach();\r\n\r\n\treturn awaitable.get_future();\r\n}\r\n\r\nfuture_t<> heavy_computing_sequential(int64_t val)\r\n{\r\n\tfor(size_t i = 0; i < 3; ++i)\r\n\t{\r\n\t\t{\r\n\t\t\tscoped_lock<std::mutex> __lock(cout_mutex);\r\n\t\t\tstd::cout << val << \" @\" << std::this_thread::get_id() << std::endl;\r\n\t\t}\r\n\t\tval = co_await async_heavy_computing_tasks(val);\r\n\t}\r\n\t{\r\n\t\tscoped_lock<std::mutex> __lock(cout_mutex);\r\n\t\tstd::cout << val << \" @\" << std::this_thread::get_id() << std::endl;\r\n\t}\r\n}\r\n\r\nvoid test_use_single_thread(int64_t val)\r\n{\r\n\t//使用local_scheduler_t来申明一个绑定到本线程的调度器 my_scheduler\r\n\t//后续在本线程运行的协程，通过this_scheduler()获得my_scheduler的地址\r\n\t//从而将这些协程的所有操作都绑定到my_scheduler里面去调度\r\n\t//实现一个协程始终绑定到一个线程的目的\r\n\t//在同一个线程里，申明多个local_scheduler_t会怎么样？\r\n\t//----我也不知道\r\n\t//如果不申明my_scheduler，则this_scheduler()获得默认主调度器的地址\r\n\tlocal_scheduler_t my_scheduler;\r\n\t\r\n\t{\r\n\t\tscoped_lock<std::mutex> __lock(cout_mutex);\r\n\t\tstd::cout << \"running in thread @\" << std::this_thread::get_id() << std::endl;\r\n\t}\r\n\tgo heavy_computing_sequential(val);\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\nconst size_t N = 2;\r\nvoid test_use_multi_thread()\r\n{\r\n\tstd::thread th_array[N];\r\n\tfor (size_t i = 0; i < N; ++i)\r\n\t\tth_array[i] = std::thread(&test_use_single_thread, 4 + i);\r\n\r\n\ttest_use_single_thread(3);\r\n\r\n\tfor (auto & th : th_array)\r\n\t\tth.join();\r\n}\r\n\r\nvoid resumable_main_multi_thread()\r\n{\r\n\tstd::cout << \"test_use_single_thread @\" << std::this_thread::get_id() << std::endl << std::endl;\r\n\ttest_use_single_thread(2);\r\n\r\n\tstd::cout << std::endl;\r\n\tstd::cout << \"test_use_multi_thread @\" << std::this_thread::get_id() << std::endl << std::endl;\r\n\ttest_use_multi_thread();\r\n\r\n\t//运行主调度器里面的协程\r\n\t//但本范例不应该有协程存在，仅演示不要忽略了主调度器\r\n\tscheduler_t::g_scheduler.run_until_notask();\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_multi_thread();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_mutex.cpp",
    "content": "﻿#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n#include <deque>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\nusing namespace std::chrono;\r\n\r\nstatic mutex_t g_lock;\r\nstatic intptr_t g_counter = 0;\r\n\r\nstatic const size_t N = 10;\r\n\r\n//🔒-50ms-🔒🗝🗝-150ms-|\r\n//-------------.........\r\nstatic future_t<> test_mutex_pop(size_t idx)\r\n{\r\n\tfor (size_t i = 0; i < N / 2; ++i)\r\n\t{\r\n\t\t{\r\n\t\t\tbatch_unlock_t _locker = co_await g_lock.lock();\t//_locker析构后，会调用对应的unlock()函数。\r\n\r\n\t\t\t--g_counter;\r\n\t\t\tstd::cout << \"pop :\" << g_counter << \" on \" << idx << std::endl;\r\n\r\n\t\t\tco_await 50ms;\r\n\r\n\t\t\tbatch_unlock_t _locker_2 = co_await g_lock;\r\n\r\n\t\t\t--g_counter;\r\n\t\t\tstd::cout << \"pop :\" << g_counter << \" on \" << idx << std::endl;\r\n\t\t}\r\n\t\tco_await 150ms;\r\n\t}\r\n}\r\n\r\n#ifndef __clang__\r\n//🔒-50ms-🗝-50ms-🔒-50ms-🗝-50ms-|\r\n//---------........---------.......\r\n//方法之一\r\nstatic future_t<> test_mutex_push(size_t idx)\r\n{\r\n\tfor (size_t i = 0; i < N; ++i)\r\n\t{\r\n\t\t{\r\n\t\t\tbatch_unlock_t _locker = co_await g_lock.lock();\r\n\r\n\t\t\t++g_counter;\r\n\t\t\tstd::cout << \"push:\" << g_counter << \" on \" << idx << std::endl;\r\n\r\n\t\t\tco_await 50ms;\r\n\t\t}\r\n\t\tco_await 50ms;\r\n\t}\r\n}\r\n\r\nstatic future_t<> test_mutex_try_push(size_t idx)\r\n{\r\n\tfor (size_t i = 0; i < N; ++i)\r\n\t{\r\n\t\t{\r\n\t\t\tfor (;;)\r\n\t\t\t{\r\n\t\t\t\tauto result = co_await g_lock.try_lock();\r\n\t\t\t\tif (result) break;\r\n\t\t\t\tco_await yield();\r\n\t\t\t}\r\n\r\n\t\t\t++g_counter;\r\n\t\t\tstd::cout << \"push:\" << g_counter << \" on \" << idx << std::endl;\r\n\r\n\t\t\tco_await 50ms;\r\n\t\t\tco_await g_lock.unlock();\r\n\t\t}\r\n\t\tco_await 50ms;\r\n\t}\r\n}\r\n#endif\r\n\r\nstatic future_t<> test_mutex_timeout_push(size_t idx)\r\n{\r\n\tfor (size_t i = 0; i < N; ++i)\r\n\t{\r\n\t\t{\r\n\t\t\tfor (;;)\r\n\t\t\t{\r\n\t\t\t\tauto result = co_await g_lock.try_lock_for(10ms);\r\n\t\t\t\tif (result) break;\r\n\t\t\t\tco_await yield();\r\n\t\t\t}\r\n\r\n\t\t\t++g_counter;\r\n\t\t\tstd::cout << \"push:\" << g_counter << \" on \" << idx << std::endl;\r\n\r\n\t\t\tco_await 50ms;\r\n\t\t\tco_await g_lock.unlock();\r\n\t\t}\r\n\t\tco_await 50ms;\r\n\t}\r\n}\r\n\r\n//🔒-50ms-🗝-50ms-🔒-50ms-🗝-50ms-|\r\n//---------........---------.......\r\nstatic std::thread test_mutex_async_push(size_t idx)\r\n{\r\n\treturn std::thread([=]\r\n\t{\r\n\t\tchar provide_unique_address = 0;\r\n\t\tfor (size_t i = 0; i < N; ++i)\r\n\t\t{\r\n\t\t\tif (g_lock.try_lock_for(500ms, &provide_unique_address))\r\n\t\t\t{\r\n\t\t\t\tbatch_unlock_t _locker(std::adopt_lock, &provide_unique_address, g_lock);\r\n\r\n\t\t\t\t++g_counter;\r\n\t\t\t\tstd::cout << \"push:\" << g_counter << \" on \" << idx << std::endl;\r\n\t\t\t\tstd::this_thread::sleep_for(50ms);\r\n\r\n\t\t\t\t//g_lock.unlock(&provide_unique_address);\r\n\t\t\t}\r\n\r\n\t\t\tstd::this_thread::sleep_for(50ms);\r\n\t\t}\r\n\t});\r\n}\r\n\r\nstatic void resumable_mutex_synch()\r\n{\r\n\tgo test_mutex_timeout_push(0);\r\n\tgo test_mutex_pop(1);\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tstd::cout << \"result:\" << g_counter << std::endl;\r\n}\r\n\r\nstatic void resumable_mutex_async()\r\n{\r\n\tauto th = test_mutex_async_push(0);\r\n\tstd::this_thread::sleep_for(25ms);\r\n\tgo test_mutex_pop(1);\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n\tth.join();\r\n\r\n\tstd::cout << \"result:\" << g_counter << std::endl;\r\n}\r\n\r\nstatic future_t<> resumable_mutex_range_push(size_t idx, mutex_t a, mutex_t b, mutex_t c)\r\n{\r\n\tfor (int i = 0; i < 10000; ++i)\r\n\t{\r\n\t\tbatch_unlock_t __lockers = co_await mutex_t::lock(a, b, c);\r\n\t\tassert(a.is_locked());\r\n\t\tassert(b.is_locked());\r\n\t\tassert(c.is_locked());\r\n\r\n\t\t++g_counter;\r\n\t\t//std::cout << \"push:\" << g_counter << \" on \" << idx << std::endl;\r\n\t\t//co_await 5ms;\r\n\t}\r\n}\r\n\r\nstatic future_t<> resumable_mutex_range_pop(size_t idx, mutex_t a, mutex_t b, mutex_t c)\r\n{\r\n\tfor (int i = 0; i < 10000; ++i)\r\n\t{\r\n\t\tbatch_unlock_t __lockers = co_await mutex_t::lock(a, b, c);\r\n\t\tassert(a.is_locked());\r\n\t\tassert(b.is_locked());\r\n\t\tassert(c.is_locked());\r\n\r\n\t\t--g_counter;\r\n\t\t//std::cout << \"pop :\" << g_counter << \" on \" << idx << std::endl;\r\n\t\t//co_await 5ms;\r\n\t}\r\n}\r\n\r\nstatic void resumable_mutex_lock_range()\r\n{\r\n\tmutex_t mtxA, mtxB, mtxC;\r\n\r\n\t//不同的线程里加锁也需要是线程安全的\r\n\tstd::thread push_th([&]\r\n\t{\r\n\t\tlocal_scheduler_t __ls__;\r\n\r\n\t\tgo resumable_mutex_range_push(10, mtxA, mtxB, mtxC);\r\n\t\tgo resumable_mutex_range_push(11, mtxA, mtxC, mtxB);\r\n\r\n\t\tthis_scheduler()->run_until_notask();\r\n\t});\r\n\r\n\tgo resumable_mutex_range_pop(12, mtxC, mtxB, mtxA);\r\n\tgo resumable_mutex_range_pop(13, mtxB, mtxA, mtxC);\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n\tpush_th.join();\r\n\r\n\tstd::cout << \"result:\" << g_counter << std::endl;\r\n}\r\n\r\nvoid resumable_main_mutex()\r\n{\r\n\tstd::cout << \"begin resumable_mutex_synch()\" << std::endl;\r\n\tresumable_mutex_synch();\r\n\tstd::cout << std::endl;\r\n\r\n\tstd::cout << \"begin resumable_mutex_async()\" << std::endl;\r\n\tresumable_mutex_async();\r\n\tstd::cout << std::endl;\r\n\r\n\tstd::cout << \"begin resumable_mutex_lock_range()\" << std::endl;\r\n\tresumable_mutex_lock_range();\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_mutex();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_resumable.cpp",
    "content": "﻿\r\n#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n\r\n#include \"librf/librf.h\"\r\n\r\n\r\nstatic std::mutex lock_console;\r\n\r\ntemplate <typename T>\r\nvoid dump(size_t idx, std::string name, T start, T end, intptr_t count)\r\n{\r\n\tlock_console.lock();\r\n\r\n\tauto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();\r\n\r\n\tstd::cout << idx << \":\" << name << \"    \";\r\n\tstd::cout << count << \"      \" << ns << \" ns      \";\r\n\tstd::cout << (ns / count) << \" ns/op\" << \"    \";\r\n\tstd::cout << (count * 100ll * 1000ll / ns) << \"w/cps\" << std::endl;\r\n\r\n\tlock_console.unlock();\r\n}\r\n\r\nstatic const intptr_t N = 3000000;\r\n//static const int N = 10;\r\n\r\nauto yield_switch(intptr_t coro) -> librf::generator_t<intptr_t>\r\n{\r\n\tfor (intptr_t i = N / coro; i > 0; --i)\r\n\t\tco_yield i;\r\n\tco_return 0;\r\n}\r\n\r\nvoid resumable_switch(intptr_t coro, size_t idx)\r\n{\r\n\tlibrf::local_scheduler_t ls;\r\n\r\n\tauto start = std::chrono::steady_clock::now();\r\n\t\r\n\tfor (intptr_t i = 0; i < coro; ++i)\r\n\t{\r\n\t\tgo yield_switch(coro);\r\n\t}\r\n\tauto middle = std::chrono::steady_clock::now();\r\n\tdump(idx, \"BenchmarkCreate_\" + std::to_string(coro), start, middle, coro);\r\n\r\n\tlibrf::this_scheduler()->run_until_notask();\r\n\r\n\tauto end = std::chrono::steady_clock::now();\r\n\tdump(idx, \"BenchmarkSwitch_\" + std::to_string(coro), middle, end, N);\r\n}\r\n\r\nvoid resumable_main_resumable()\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\tresumable_switch(1, 99);\r\n\r\n\tresumable_switch(1, 0);\r\n\tresumable_switch(10, 0);\r\n\tresumable_switch(100, 0);\r\n\tresumable_switch(1000, 0);\r\n\tresumable_switch(10000, 0);\r\n\tresumable_switch(30000, 0);\r\n\r\n/*\r\n\tstd::thread works[32];\r\n\tfor (size_t w = 1; w <= std::size(works); ++w)\r\n\t{\r\n\t\tfor (size_t idx = 0; idx < w; ++idx)\r\n\t\t\tworks[idx] = std::thread(&resumable_switch, 1000, idx);\r\n\t\tfor (size_t idx = 0; idx < w; ++idx)\r\n\t\t\tworks[idx].join();\r\n\r\n\t\tstd::cout << std::endl << std::endl;\r\n\t}\r\n*/\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_resumable();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_routine.cpp",
    "content": "﻿\r\n#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\n#ifndef __GNUC__\t//GCC: 没有提供__builtin_coro_frame这样的内置函数\r\nfuture_t<> test_routine_use_timer()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tfor (size_t i = 0; i < 3; ++i)\r\n\t{\r\n\t\tco_await librf::sleep_for(100ms);\r\n\t\tstd::cout << \"timer after 100ms\" << std::endl;\r\n\t\tstd::cout << \"1:frame=\" << _coro_frame_ptr() << std::endl;\r\n\t}\r\n}\r\n\r\nfuture_t<> test_routine_use_timer_2()\r\n{\r\n\tstd::cout << \"test_routine_use_timer_2\" << std::endl;\r\n\r\n\tco_await test_routine_use_timer();\r\n\tstd::cout << \"2:frame=\" << _coro_frame_ptr() << std::endl;\r\n\tco_await test_routine_use_timer();\r\n\tstd::cout << \"2:frame=\" << _coro_frame_ptr() << std::endl;\r\n\tco_await test_routine_use_timer();\r\n\tstd::cout << \"2:frame=\" << _coro_frame_ptr() << std::endl;\r\n}\r\n#endif //#ifndef __GNUC__\r\n\r\nvoid resumable_main_routine()\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\t//go test_routine_use_timer_2();\r\n#ifndef __GNUC__\t//GCC: 没有提供__builtin_coro_frame这样的内置函数\r\n\tgo test_routine_use_timer();\r\n#endif //#ifndef __GNUC__\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_routine();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_sleep.cpp",
    "content": "﻿\r\n#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\nfuture_t<> test_sleep_use_timer()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\t(void)sleep_for(100ms);\t\t//incorrect!!!\r\n\r\n\tco_await sleep_for(100ms);\r\n\tstd::cout << \"sleep_for 100ms.\" << std::endl;\r\n\tco_await 100ms;\r\n\tstd::cout << \"co_await 100ms.\" << std::endl;\r\n\r\n\ttry\r\n\t{\r\n\t\tco_await sleep_until(system_clock::now() + 200ms);\r\n\t\tstd::cout << \"timer after 200ms.\" << std::endl;\r\n\t}\r\n\tcatch (canceled_exception)\r\n\t{\r\n\t\tstd::cout << \"timer canceled.\" << std::endl;\r\n\t}\r\n}\r\n\r\nvoid test_wait_all_events_with_signal_by_sleep()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evts[8];\r\n\r\n\tgo[&]() -> future_t<>\r\n\t{\r\n\t\tauto result = co_await event_t::wait_all(evts);\r\n\t\tif (result)\r\n\t\t\tstd::cout << \"all event signal!\" << std::endl;\r\n\t\telse\r\n\t\t\tstd::cout << \"time out!\" << std::endl;\r\n\t};\r\n\r\n\tsrand((int)time(nullptr));\r\n\tfor (size_t i = 0; i < std::size(evts); ++i)\r\n\t{\r\n\t\tgo[&, i]() -> future_t<>\r\n\t\t{\r\n\t\t\tco_await sleep_for(1ms * (500 + rand() % 1000));\r\n\t\t\tevts[i].signal();\r\n\t\t\tstd::cout << \"event[ \" << i << \" ] signal!\" << std::endl;\r\n\t\t};\r\n\t}\r\n\r\n\twhile (!this_scheduler()->empty())\r\n\t{\r\n\t\tthis_scheduler()->run_one_batch();\r\n\t\t//std::cout << \"press any key to continue.\" << std::endl;\r\n\t\t//_getch();\r\n\t}\r\n}\r\n\r\nvoid resumable_main_sleep()\r\n{\r\n\tgo test_sleep_use_timer();\r\n\tthis_scheduler()->run_until_notask();\r\n\tstd::cout << std::endl;\r\n\r\n\ttest_wait_all_events_with_signal_by_sleep();\r\n\tstd::cout << std::endl;\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_sleep();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_stop_token.cpp",
    "content": "﻿#include <chrono>\n#include <iostream>\n#include <string>\n#include <thread>\n\n#include \"librf/librf.h\"\n\nusing namespace librf;\nusing namespace std::chrono;\n\n//_Ctype签名:void(bool, int64_t)\ntemplate<class _Ctype, typename=std::enable_if_t<std::is_invocable_v<_Ctype, bool, int64_t>>>\nstatic void callback_get_long_with_stop(stop_token token, int64_t val, _Ctype&& cb)\n{\n\tstd::thread([val, token = std::move(token), cb = std::forward<_Ctype>(cb)]\n\t\t{\n\t\t\tfor (int i = 0; i < 10; ++i)\n\t\t\t{\n\t\t\t\tif (token.stop_requested())\n\t\t\t\t{\n\t\t\t\t\tcb(false, 0);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tstd::this_thread::sleep_for(10ms);\n\t\t\t}\n\n\t\t\t//有可能未检测到token的停止要求\n\t\t\t//如果使用stop_callback来停止，则务必保证检测到的退出要求是唯一的，且线程安全的\n\t\t\t//否则，多次调用cb，会导致协程在半退出状态下，外部的awaitable_t管理的state获取跟root出现错误。\n\t\t\tcb(true, val * val);\n\t\t}).detach();\n}\n\n//token触发后，设置canceled_exception异常。\nstatic future_t<int64_t> async_get_long_with_stop(stop_token token, int64_t val)\n{\n\tawaitable_t<int64_t> awaitable;\n\n\t//在这里通过stop_callback来处理退出，并将退出转化为error_code::stop_requested异常。\n\t//则必然会存在线程竞争问题，导致协程提前于callback_get_long_with_stop的回调之前而退出。\n\t//同时，callback_get_long_with_stop还未必一定能检测到退出要求----毕竟只是一个要求，而不是强制。\n\n\tcallback_get_long_with_stop(token, val, [awaitable](bool ok, int64_t val)\n\t{\n\t\tif (ok)\n\t\t\tawaitable.set_value(val);\n\t\telse\n\t\t\tawaitable.throw_exception(canceled_exception{error_code::stop_requested});\n\t});\n\treturn awaitable.get_future();\n}\n\n//如果关联的协程被取消了，则触发canceled_exception异常。\nstatic future_t<int64_t> async_get_long_with_stop(int64_t val)\n{\n\ttask_t* task = librf_current_task();\n\tco_return co_await async_get_long_with_stop(task->get_stop_token(), val);\n}\n\n//测试取消协程\nstatic void test_get_long_with_stop(int64_t val)\n{\n\t//异步获取值的协程\n\ttask_t* task = GO\n\t{\n\t\ttry\n\t\t{\n\t\t\tint64_t result = co_await async_get_long_with_stop(val);\n\t\t\tstd::cout << result << std::endl;\n\t\t}\n\t\tcatch (const std::logic_error& e)\n\t\t{\n\t\t\tstd::cout << e.what() << std::endl;\n\t\t}\n\t};\n\t//task的生命周期只在task代表的协程生存期间存在。\n\t//但通过复制与其关联的stop_source，生存期可以超过task的生存期。\n\tstop_source stops = task->get_stop_source();\n\n\t//取消上一个协程的延迟协程\n\tGO\n\t{\n\t\tco_await sleep_for(1ms * (rand() % 300));\n\t\tstops.request_stop();\n\t};\n\n\tthis_scheduler()->run_until_notask();\n}\n\nvoid resumable_main_stop_token()\n{\n\tsrand((int)time(nullptr));\n\tfor (int i = 0; i < 10; ++i)\n\t\ttest_get_long_with_stop(i);\n\n\tstd::cout << \"OK - stop_token!\" << std::endl;\n}\n\n#if LIBRF_TUTORIAL_STAND_ALONE\nint main()\n{\n\tresumable_main_stop_token();\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "tutorial/test_async_suspend_always.cpp",
    "content": "﻿\r\n#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\nfuture_t<> test_loop_sleep(size_t _N, const char * ch)\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tfor (size_t i = 0; i < _N; ++i)\r\n\t{\r\n\t\tco_await librf::sleep_for(100ms);\r\n\t\tstd::cout << ch;\r\n\t}\r\n\tstd::cout << std::endl;\r\n}\r\n\r\nfuture_t<> test_recursive_await()\r\n{\r\n\tstd::cout << \"A:---1\" << std::endl;\r\n\tco_await test_loop_sleep(5, \"=\");\r\n\r\n\tstd::cout << \"A:---2\" << std::endl;\r\n\tco_await test_loop_sleep(6, \"=\");\r\n\r\n\tstd::cout << \"A:---3\" << std::endl;\r\n\tco_await test_loop_sleep(7, \"=\");\r\n\r\n\tstd::cout << \"A:---4\" << std::endl;\r\n}\r\n\r\nfuture_t<> test_recursive_go()\r\n{\r\n\tstd::cout << \"B:---1\" << std::endl;\r\n\tco_await test_loop_sleep(3, \"+\");\r\n\r\n\tstd::cout << \"B:---2\" << std::endl;\r\n\tgo test_loop_sleep(8, \"*\");\r\n\r\n\tstd::cout << \"B:---3\" << std::endl;\r\n\tco_await test_loop_sleep(4, \"+\");\r\n\r\n\tstd::cout << \"B:---4\" << std::endl;\r\n}\r\n\r\nvoid resumable_main_suspend_always()\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\tgo test_recursive_await();\r\n\tgo test_recursive_go();\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\n/*\r\nresume from 0000016B8477CE00 on thread 7752\r\n\r\nresume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.\r\nresume from 0000016B8477CE00 on thread 7752\r\n\r\nresume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.\r\nresume from 0000016B8477CE00 on thread 7752\r\n\r\nresume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.resume from 0000016B847726C0 on thread 7752\r\n.\r\nresume from 0000016B8477CE00 on thread 7752\r\n\r\n说明有四个协程对象（其中三个对象的内存被复用，表现为地址一样）\r\ntest_recursive_await\t->\t\t0000016B8477CE00\r\ntest_loop_sleep<5>\t\t->\t\t0000016B847726C0\r\ntest_loop_sleep<6>\t\t->\t\t0000016B847726C0\r\ntest_loop_sleep<7>\t\t->\t\t0000016B847726C0\r\n*/\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_suspend_always();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_switch_scheduler.cpp",
    "content": "﻿#include <chrono>\n#include <iostream>\n#include <string>\n#include <thread>\n\n#include \"librf/librf.h\"\n\nusing namespace librf;\n\nstatic scheduler_t* sch_in_main = nullptr;\nstatic std::atomic<scheduler_t*> sch_in_thread = nullptr;\n\nvoid run_in_thread(channel_t<bool> c_done)\n{\n\tlocal_scheduler_t my_scheduler;\t\t\t//产生本线程唯一的调度器\n\tsch_in_thread = this_scheduler();\t\t//本线程唯一的调度器赋值给sch_in_thread，以便于后续测试直接访问此线程的调度器\n\n\t(void)c_done.write(true);\t\t\t\t//数据都准备好了，通过channel通知其他协程可以启动后续依赖sch_in_thread变量的协程了\n\n\t//循环直到sch_in_thread为nullptr\n\tfor (;;)\n\t{\n\t\tauto sch = sch_in_thread.load(std::memory_order_acquire);\n\t\tif (sch == nullptr)\n\t\t\tbreak;\n\t\tsch->run_one_batch();\n\t\tstd::this_thread::yield();\n\t}\n}\n\ntemplate<class _Ctype>\nstatic void callback_get_long_switch_scheduler(int64_t val, _Ctype&& cb)\n{\n\tusing namespace std::chrono;\n\tstd::thread([val, cb = std::forward<_Ctype>(cb)]\n\t\t{\n\t\t\tstd::this_thread::sleep_for(500ms);\n\t\t\tcb(val + 1);\n\t\t}).detach();\n}\n\n//这种情况下，没有生成 frame-context，因此，并没有promise_type被内嵌在frame-context里\nstatic future_t<int64_t> async_get_long_switch_scheduler(int64_t val)\n{\n\tawaitable_t<int64_t> awaitable;\n\tcallback_get_long_switch_scheduler(val, [awaitable](int64_t result)\n\t{\n\t\tawaitable.set_value(result);\n\t});\n\treturn awaitable.get_future();\n}\n\n//这种情况下，会生成对应的 frame-context，一个promise_type被内嵌在frame-context里\nstatic future_t<> resumable_get_long_switch_scheduler(int64_t val, channel_t<bool> c_done)\n{\n\tstd::cout << \"thread = \" << std::this_thread::get_id();\n\tstd::cout << \", scheduler = \" << librf_current_scheduler();\n\tstd::cout << \", value = \" << val << std::endl;\n\n\tco_await via(sch_in_thread);\n\tval = co_await async_get_long_switch_scheduler(val);\n\n\tstd::cout << \"thread = \" << std::this_thread::get_id();\n\tstd::cout << \", scheduler = \" << librf_current_scheduler();\n\tstd::cout << \", value = \" << val << std::endl;\n\n\tco_await via(sch_in_main);\n\tval = co_await async_get_long_switch_scheduler(val);\n\n\tstd::cout << \"thread = \" << std::this_thread::get_id();\n\tstd::cout << \", scheduler = \" << librf_current_scheduler();\n\tstd::cout << \", value = \" << val << std::endl;\n\n\tco_await via(sch_in_thread);\n\tval = co_await async_get_long_switch_scheduler(val);\n\n\tstd::cout << \"thread = \" << std::this_thread::get_id();\n\tstd::cout << \", scheduler = \" << librf_current_scheduler();\n\tstd::cout << \", value = \" << val << std::endl;\n\n\tco_await via(sch_in_thread);\t//fake switch\n\tval = co_await async_get_long_switch_scheduler(val);\n\n\tstd::cout << \"thread = \" << std::this_thread::get_id();\n\tstd::cout << \", scheduler = \" << librf_current_scheduler();\n\tstd::cout << \", value = \" << val << std::endl;\n\n\t(void)c_done.write(true);\n}\n\n#if defined(__GNUC__)\nstatic future_t<> resumable_main_switch_scheduler_fix_gcc_bugs(std::thread & other, channel_t<bool> c_done)\n{\n\tco_await c_done;\t\t//第一次等待，等待run_in_thread准备好了\n\n\tstd::cout << \"other thread = \" << other.get_id();\n\tstd::cout << \", sch_in_thread = \" << sch_in_thread << std::endl;\n\n\tgo resumable_get_long_switch_scheduler(1, c_done);\t//开启另外一个协程\n\t//co_await resumable_get_long(3, c_done);\n\tco_await c_done;\t\t//等待新的协程运行完毕，从而保证主线程的协程不会提早退出\n}\n#endif\n\nvoid resumable_main_switch_scheduler()\n{\n\tsch_in_main = this_scheduler();\n\n\tstd::cout << \"main thread = \" << std::this_thread::get_id();\n\tstd::cout << \", scheduler = \" << sch_in_main << std::endl;\n\n\tchannel_t<bool> c_done{ 1 };\n\tstd::thread other(&run_in_thread, std::ref(c_done));\n\n#if defined(__GNUC__)\n\tgo resumable_main_switch_scheduler_fix_gcc_bugs(other, c_done);\n#else\n\tgo[&other, c_done]()->future_t<>\n\t{\n\t\tco_await c_done;\t\t//第一次等待，等待run_in_thread准备好了\n\t\t\n\t\tstd::cout << \"other thread = \" << other.get_id();\n\t\tstd::cout << \", sch_in_thread = \" << sch_in_thread << std::endl;\n\t\n\t\tgo resumable_get_long_switch_scheduler(1, c_done);\t//开启另外一个协程\n\t\t//co_await resumable_get_long(3, c_done);\n\t\tco_await c_done;\t\t//等待新的协程运行完毕，从而保证主线程的协程不会提早退出\n\t}; //GCC: internal compiler error: in captures_temporary, at cp/coroutines.cc:2716\n#endif\n\n\tsch_in_main->run_until_notask();\n\n\t//通知另外一个线程退出\n\tsch_in_thread.store(nullptr, std::memory_order_release);\n\tother.join();\n}\n\n#if LIBRF_TUTORIAL_STAND_ALONE\nint main()\n{\n\tresumable_main_switch_scheduler();\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "tutorial/test_async_timer.cpp",
    "content": "﻿\r\n#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\nvoid resumable_main_timer()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tauto th = this_scheduler()->timer()->add_handler(system_clock::now() + 5s, \r\n\t\t[](bool bValue) \r\n\t\t{\r\n\t\t\tif (bValue)\r\n\t\t\t\tstd::cout << \"timer canceled.\" << std::endl;\r\n\t\t\telse\r\n\t\t\t\tstd::cout << \"timer after 5s.\" << std::endl;\r\n\t\t});\r\n\r\n\tauto th2 = this_scheduler()->timer()->add_handler(1s, \r\n\t\t[&th](bool)\r\n\t\t{\r\n\t\t\tstd::cout << \"timer after 1s.\" << std::endl;\r\n\t\t\tth.stop();\r\n\t\t});\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\r\n\tth2.stop();\t//but th2 is invalid\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_timer();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_when_all.cpp",
    "content": "﻿\r\n#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n#include <inttypes.h>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\n#if !defined(__GNUC__)\r\nvoid test_when_any()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tGO\r\n\t{\r\n\t\tauto vals = co_await when_any();\r\n\r\n\t\tvals = co_await when_any(\r\n\t\t\t[]() ->future_t<int>\r\n\t\t\t{\r\n\t\t\t\tauto dt = rand() % 1000;\r\n\t\t\t\tco_await sleep_for(1ms * dt);\r\n\t\t\t\tstd::cout << dt << \"@a\" << std::endl;\r\n\r\n\t\t\t\tco_return dt;\r\n\t\t\t},\r\n\t\t\t[]() ->future_t<>\r\n\t\t\t{\r\n\t\t\t\tauto dt = rand() % 1000;\r\n\t\t\t\tco_await sleep_for(1ms * dt);\r\n\t\t\t\tstd::cout << dt << \"@b\" << std::endl;\r\n\t\t\t},\r\n\t\t\t[]() ->future_t<>\r\n\t\t\t{\r\n\t\t\t\tauto dt = rand() % 1000;\r\n\t\t\t\tco_await sleep_for(1ms * dt);\r\n\t\t\t\tstd::cout << dt << \"@c\" << std::endl;\r\n\t\t\t});\r\n\r\n\t\tif (vals.first == 0)\r\n\t\t\tstd::cout << \"first done! value is \" << librf::any_cast<int>(vals.second) << std::endl;\r\n\t\telse\r\n\t\t\tstd::cout << \"any done! index is \" << vals.first << std::endl;\r\n\r\n\t\tco_await 1010ms;\r\n\t\tstd::cout << std::endl;\r\n\r\n\t\tauto my_sleep = [](const char * name) -> future_t<int>\r\n\t\t{\r\n\t\t\tauto dt = rand() % 1000;\r\n\t\t\tco_await sleep_for(1ms * dt);\r\n\t\t\tstd::cout << dt << \"@\" << name << std::endl;\r\n\r\n\t\t\tco_return dt;\r\n\t\t};\r\n\r\n\t\tstd::vector<future_t<int> > v{ my_sleep(\"g\"), my_sleep(\"h\"), my_sleep(\"i\") };\r\n\t\t//vals = co_await when_any(*this_scheduler(), std::begin(v), std::end(v));\r\n\t\t//vals = co_await when_any(std::begin(v), std::end(v));\r\n\t\tvals = co_await when_any(v);\r\n\r\n\t\tstd::cout << \"any range done! index is \" << vals.first << \", valus is \" << librf::any_cast<int>(vals.second) << std::endl;\r\n\t};\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\nvoid test_when_all()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tauto my_sleep = [](const char * name) -> future_t<int>\r\n\t{\r\n\t\tauto dt = rand() % 1000;\r\n\t\tco_await sleep_for(1ms * dt);\r\n\t\tstd::cout << dt << \"@\" << name << std::endl;\r\n\r\n\t\tco_return dt;\r\n\t};\r\n\r\n\tauto my_sleep_v = [](const char * name) -> future_t<>\r\n\t{\r\n\t\tauto dt = rand() % 1000;\r\n\t\tco_await sleep_for(1ms * dt);\r\n\t\tstd::cout << dt << \"@\" << name << std::endl;\r\n\t};\r\n\r\n\r\n\tGO\r\n\t{\r\n\t\tco_await when_all();\r\n\t\tstd::cout << \"when all: zero!\" << std::endl << std::endl;\r\n\r\n\t\tauto vals1 = co_await when_all(\r\n\t\t\t[]() ->future_t<int>\r\n\t\t\t{\r\n\t\t\t\tauto dt = rand() % 1000;\r\n\t\t\t\tco_await sleep_for(1ms * dt);\r\n\t\t\t\tstd::cout << dt << \"@i\" << std::endl;\r\n\r\n\t\t\t\tco_return dt;\r\n\t\t\t},\r\n\t\t\t[]() ->future_t<>\r\n\t\t\t{\r\n\t\t\t\tauto dt = rand() % 1000;\r\n\t\t\t\tco_await sleep_for(1ms * dt);\r\n\t\t\t\tstd::cout << dt << \"@j\" << std::endl;\r\n\t\t\t},\r\n\t\t\t[]() ->future_t<>\r\n\t\t\t{\r\n\t\t\t\tauto dt = rand() % 1000;\r\n\t\t\t\tco_await sleep_for(1ms * dt);\r\n\t\t\t\tstd::cout << dt << \"@k\" << std::endl;\r\n\t\t\t});\r\n\r\n\t\tstd::cout << \"when all - 1:\" << std::get<0>(vals1) << std::endl << std::endl;\r\n\r\n\t\tauto ab = co_await when_all(my_sleep(\"a\"), my_sleep_v(\"b\"));\r\n\t\t//ab.1 is std::ignore\r\n\t\tstd::cout << \"when all - 2:\" << std::get<0>(ab) << std::endl << std::endl;\r\n\r\n\t\tauto c = co_await my_sleep(\"c\");\r\n\t\tstd::cout << \"when all - 3:\" << c << std::endl << std::endl;\r\n\r\n\t\tauto def = co_await when_all(my_sleep(\"d\"), my_sleep_v(\"e\"), my_sleep(\"f\"));\r\n\t\t//def.1 is std::ignore\r\n\t\tstd::cout << \"when all - 4:\" << std::get<0>(def) << \",\" << std::get<2>(def) << std::endl << std::endl;\r\n\r\n\t\tstd::vector<future_t<int> > v{ my_sleep(\"g\"), my_sleep(\"h\"), my_sleep(\"i\") };\r\n\t\t//auto vals = co_await when_all(*this_scheduler(), std::begin(v), std::end(v));\r\n\t\t//auto vals = co_await when_all(*this_scheduler(), v);\r\n\t\tauto vals = co_await when_all(v);\r\n\t\tstd::cout << \"when all - 5:\" << vals[0] << \",\" << vals[1] << \",\" << vals[2] << \",\" << std::endl << std::endl;\r\n\r\n\t\tstd::cout << \"all range done!\" << std::endl;\r\n\t};\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\nvoid test_when_any_mix()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tevent_t evt;\r\n\r\n\tGO\r\n\t{\r\n\t\tauto vals = co_await when_any(\r\n\t\t\t[]() ->future_t<int>\r\n\t\t\t{\r\n\t\t\t\tauto dt = rand() % 200;\r\n\t\t\t\tco_await sleep_for(1ms * dt);\r\n\t\t\t\tco_return dt;\r\n\t\t\t},\r\n\t\t\tevt.wait(),\r\n\t\t\tsleep_for(100ms)\r\n\t\t\t);\r\n\r\n\t\tif (vals.first == 0)\r\n\t\t\tstd::cout << \"first done! value is \" << librf::any_cast<int>(vals.second) << std::endl;\r\n\t\telse\r\n\t\t\tstd::cout << \"any done! index is \" << vals.first << std::endl;\r\n\t};\r\n\t\r\n\tGO\r\n\t{\r\n\t\tauto dt = rand() % 120;\r\n\t\tco_await sleep_for(1ms * dt);\r\n\t\tevt.signal();\r\n\t};\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\n//这能模拟golang的select吗?\r\nvoid test_when_select()\r\n{\r\n\tusing namespace std::chrono;\r\n\r\n\tchannel_t<int> ch1, ch2;\r\n\r\n\tGO\r\n\t{\r\n\t\tauto vals = co_await when_any(\r\n\t\t\tsleep_for(60ms),\r\n\t\t\t[=]() ->future_t<int>\r\n\t\t\t{\r\n\t\t\t\tint val = co_await ch1;\r\n\t\t\t\tco_return val;\r\n\t\t\t},\r\n\t\t\t[=]() ->future_t<int>\r\n\t\t\t{\r\n\t\t\t\tint val = co_await ch2;\r\n\t\t\t\tco_return val;\r\n\t\t\t}\r\n\t\t\t);\r\n\r\n\t\tif (vals.first == 0)\r\n\t\t\tstd::cout << \"time out!\" << std::endl;\r\n\t\telse\r\n\t\t\tstd::cout << \"index is \" << vals.first << \", value is \" << librf::any_cast<int>(vals.second) << std::endl;\r\n\t};\r\n\r\n\tGO\r\n\t{\r\n\t\tauto dt = rand() % 120;\r\n\t\tco_await sleep_for(1ms * dt);\r\n\t\tco_await (ch1 << (int)dt);\r\n\t};\r\n\r\n\tGO\r\n\t{\r\n\t\tauto dt = rand() % 120;\r\n\t\tco_await sleep_for(1ms * dt);\r\n\t\tco_await (ch2 << (int)dt);\r\n\t};\r\n\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n#endif\r\n\r\nvoid resumable_main_when_all()\r\n{\r\n#if !defined(__GNUC__)\r\n\tsrand((uint32_t)time(nullptr));\r\n\r\n\ttest_when_any();\r\n\tstd::cout << std::endl;\r\n\t\r\n\ttest_when_all();\r\n\tstd::cout << std::endl;\r\n\r\n\tfor(int i = 0; i < 10; ++i)\r\n\t\ttest_when_any_mix();\r\n\r\n\tfor (int i = 0; i < 10; ++i)\r\n\t\ttest_when_select();\r\n#endif\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_when_all();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_async_yield_return.cpp",
    "content": "﻿\r\n#include <chrono>\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n\r\n#include \"librf/librf.h\"\r\n\r\nusing namespace librf;\r\n\r\ngenerator_t<int> test_yield_int()\r\n{\r\n\tstd::cout << \"1 will yield return\" << std::endl;\r\n\tco_yield 1;\r\n\tstd::cout << \"2 will yield return\" << std::endl;\r\n\tco_yield 2;\r\n\tstd::cout << \"3 will yield return\" << std::endl;\r\n\tco_yield 3;\r\n\tstd::cout << \"4 will return\" << std::endl;\r\n\tco_return 4;\r\n\r\n\tstd::cout << \"5 will never yield return\" << std::endl;\r\n\tco_yield 5;\r\n}\r\n\r\n/*不能编译*/\r\n/*\r\nauto test_yield_void()\r\n{\r\n\tstd::cout << \"1 will yield return\" << std::endl;\r\n\tco_yield ;\r\n\tstd::cout << \"2 will yield return\" << std::endl;\r\n\tco_yield ;\r\n\tstd::cout << \"3 will yield return\" << std::endl;\r\n\tco_yield ;\r\n\tstd::cout << \"4 will return\" << std::endl;\r\n\tco_return ;\r\n\r\n\tstd::cout << \"5 will never yield return\" << std::endl;\r\n\tco_yield ;\r\n}\r\n*/\r\n\r\nauto test_yield_void() -> generator_t<>\r\n{\r\n\tstd::cout << \"block 1 will yield return\" << std::endl;\r\n\tco_yield_void;\r\n\tstd::cout << \"block 2 will yield return\" << std::endl;\r\n\tco_yield_void;\r\n\tstd::cout << \"block 3 will yield return\" << std::endl;\r\n\tco_yield_void;\r\n\tstd::cout << \"block 4 will return\" << std::endl;\r\n\tco_return_void;\r\n\r\n\tstd::cout << \"block 5 will never yield return\" << std::endl;\r\n\tco_yield_void;\r\n}\r\n\r\nauto test_yield_future() -> future_t<int64_t>\r\n{\r\n\tstd::cout << \"future 1 will yield return\" << std::endl;\r\n\tco_yield 1;\r\n\tstd::cout << \"future 2 will yield return\" << std::endl;\r\n\tco_yield 2;\r\n\tstd::cout << \"future 3 will yield return\" << std::endl;\r\n\tco_yield 3;\r\n\tstd::cout << \"future 4 will return\" << std::endl;\r\n\tco_return 4;\r\n\r\n\tstd::cout << \"future 5 will never yield return\" << std::endl;\r\n\tco_yield 5;\r\n}\r\n\r\nvoid resumable_main_yield_return()\r\n{\r\n\tstd::cout << __FUNCTION__ << std::endl;\r\n\tfor (int i : test_yield_int())\r\n\t{\r\n\t\tstd::cout << i << \" had return\" << std::endl;\r\n\t}\r\n\r\n\tgo test_yield_int();\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tgo test_yield_void();\r\n\tthis_scheduler()->run_until_notask();\r\n\r\n\tgo test_yield_future();\r\n\tthis_scheduler()->run_until_notask();\r\n}\r\n\r\n#if LIBRF_TUTORIAL_STAND_ALONE\r\nint main()\r\n{\r\n\tresumable_main_yield_return();\r\n\treturn 0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "tutorial/test_memory_leak.cpp",
    "content": "\n#include <chrono>\n#include <iostream>\n#include <string>\n#include <thread>\n\n#include \"librf/librf.h\"\n\nusing namespace librf;\nusing namespace std::chrono;\n\nvoid test_memory_leak_event_wait_for()\n{\n\tgo[]()->future_t<void>\n\t{\n\t\tevent_t e;\n\t\tfor (;;)\n\t\t{\n\t\t\tbool val = co_await e.wait_for(1ms);\n\t\t\t(void)val;\n\t\t\tassert(val == false);\n\t\t\tco_await yield();\n\t\t}\n\t};\n\n\tfor (;;)\n\t{\n\t\tauto have = this_scheduler()->run_one_batch();\n\t\tif (!have) {\n\t\t\tstd::this_thread::sleep_for(1ms);\n\t\t}\n\t}\n}\n\nvoid test_memory_leak_event_wait_all_for()\n{\n\tgo[]()->future_t<void>\n\t{\n\t\tevent_t e[4];\n\t\tfor (;;)\n\t\t{\n\t\t\tbool val = co_await event_t::wait_all_for(1ms, e);\n\t\t\t(void)val;\n\t\t\tassert(val == false);\n\t\t\tco_await yield();\n\t\t}\n\t};\n\n\tfor (;;)\n\t{\n\t\tauto have = this_scheduler()->run_one_batch();\n\t\tif (!have) {\n\t\t\tstd::this_thread::sleep_for(1ms);\n\t\t}\n\t}\n}\n\n#if LIBRF_TUTORIAL_STAND_ALONE\nint main()\n{\n\t//test_memory_leak_event_wait_for();\n\ttest_memory_leak_event_wait_all_for();\n\n\treturn 0;\n}\n#endif\n"
  }
]