Full Code of sogou/workflow for AI

master fa48b351cc81 cached
307 files
1.7 MB
532.4k tokens
1863 symbols
1 requests
Download .txt
Showing preview only (1,876K chars total). Download the full file or copy to clipboard to get everything.
Repository: sogou/workflow
Branch: master
Commit: fa48b351cc81
Files: 307
Total size: 1.7 MB

Directory structure:
gitextract_sj2a21rt/

├── .editorconfig
├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── xmake.yml
├── .gitignore
├── BUILD
├── CMakeLists.txt
├── CMakeLists_Headers.txt
├── CODE_OF_CONDUCT.md
├── GNUmakefile
├── LICENSE
├── LICENSE_GPLV2
├── README.md
├── README_cn.md
├── WORKSPACE
├── benchmark/
│   ├── CMakeLists.txt
│   ├── GNUmakefile
│   ├── README.md
│   ├── benchmark-01-http_server.cc
│   ├── benchmark-02-http_server_long_req.cc
│   ├── util/
│   │   ├── args.h
│   │   ├── content.h
│   │   └── date.h
│   └── xmake.lua
├── docs/
│   ├── about-conditional.md
│   ├── about-config.md
│   ├── about-connection-context.md
│   ├── about-counter.md
│   ├── about-dns.md
│   ├── about-error.md
│   ├── about-exit.md
│   ├── about-go-task.md
│   ├── about-module.md
│   ├── about-resource-pool.md
│   ├── about-selector.md
│   ├── about-service-governance.md
│   ├── about-timeout.md
│   ├── about-timer.md
│   ├── about-tlv-message.md
│   ├── about-upstream.md
│   ├── benchmark.md
│   ├── bugs.md
│   ├── en/
│   │   ├── CONTRIBUTING.md
│   │   ├── about-config.md
│   │   ├── about-connection-context.md
│   │   ├── about-counter.md
│   │   ├── about-dns.md
│   │   ├── about-error.md
│   │   ├── about-exit.md
│   │   ├── about-go-task.md
│   │   ├── about-module.md
│   │   ├── about-resource-pool.md
│   │   ├── about-service-governance.md
│   │   ├── about-timeout.md
│   │   ├── about-timer.md
│   │   ├── about-tlv-message.md
│   │   ├── about-upstream.md
│   │   ├── tutorial-01-wget.md
│   │   ├── tutorial-02-redis_cli.md
│   │   ├── tutorial-03-wget_to_redis.md
│   │   ├── tutorial-04-http_echo_server.md
│   │   ├── tutorial-05-http_proxy.md
│   │   ├── tutorial-06-parallel_wget.md
│   │   ├── tutorial-07-sort_task.md
│   │   ├── tutorial-08-matrix_multiply.md
│   │   ├── tutorial-09-http_file_server.md
│   │   ├── tutorial-10-user_defined_protocol.md
│   │   ├── tutorial-11-graph_task.md
│   │   ├── tutorial-12-mysql_cli.md
│   │   ├── tutorial-13-kafka_cli.md
│   │   └── xmake.md
│   ├── tutorial-01-wget.md
│   ├── tutorial-02-redis_cli.md
│   ├── tutorial-03-wget_to_redis.md
│   ├── tutorial-04-http_echo_server.md
│   ├── tutorial-05-http_proxy.md
│   ├── tutorial-06-parallel_wget.md
│   ├── tutorial-07-sort_task.md
│   ├── tutorial-08-matrix_multiply.md
│   ├── tutorial-09-http_file_server.md
│   ├── tutorial-10-user_defined_protocol.md
│   ├── tutorial-11-graph_task.md
│   ├── tutorial-12-mysql_cli.md
│   ├── tutorial-13-kafka_cli.md
│   ├── tutorial-15-name_service.md
│   ├── tutorial-17-dns_cli.md
│   ├── tutorial-18-redis_subscriber.md
│   ├── tutorial-19-dns_server.md
│   └── xmake.md
├── src/
│   ├── CMakeLists.txt
│   ├── client/
│   │   ├── CMakeLists.txt
│   │   ├── WFConsulClient.cc
│   │   ├── WFConsulClient.h
│   │   ├── WFDnsClient.cc
│   │   ├── WFDnsClient.h
│   │   ├── WFHttpChunkedClient.cc
│   │   ├── WFHttpChunkedClient.h
│   │   ├── WFKafkaClient.cc
│   │   ├── WFKafkaClient.h
│   │   ├── WFMySQLConnection.cc
│   │   ├── WFMySQLConnection.h
│   │   ├── WFRedisSubscriber.cc
│   │   ├── WFRedisSubscriber.h
│   │   └── xmake.lua
│   ├── factory/
│   │   ├── CMakeLists.txt
│   │   ├── DnsTaskImpl.cc
│   │   ├── FileTaskImpl.cc
│   │   ├── HttpTaskImpl.cc
│   │   ├── HttpTaskImpl.inl
│   │   ├── KafkaTaskImpl.cc
│   │   ├── KafkaTaskImpl.inl
│   │   ├── MySQLTaskImpl.cc
│   │   ├── RedisTaskImpl.cc
│   │   ├── RedisTaskImpl.inl
│   │   ├── WFAlgoTaskFactory.h
│   │   ├── WFAlgoTaskFactory.inl
│   │   ├── WFConnection.h
│   │   ├── WFGraphTask.cc
│   │   ├── WFGraphTask.h
│   │   ├── WFMessageQueue.cc
│   │   ├── WFMessageQueue.h
│   │   ├── WFOperator.h
│   │   ├── WFResourcePool.cc
│   │   ├── WFResourcePool.h
│   │   ├── WFTask.h
│   │   ├── WFTask.inl
│   │   ├── WFTaskError.h
│   │   ├── WFTaskFactory.cc
│   │   ├── WFTaskFactory.h
│   │   ├── WFTaskFactory.inl
│   │   ├── Workflow.cc
│   │   ├── Workflow.h
│   │   └── xmake.lua
│   ├── kernel/
│   │   ├── CMakeLists.txt
│   │   ├── CommRequest.cc
│   │   ├── CommRequest.h
│   │   ├── CommScheduler.cc
│   │   ├── CommScheduler.h
│   │   ├── Communicator.cc
│   │   ├── Communicator.h
│   │   ├── ExecRequest.h
│   │   ├── Executor.cc
│   │   ├── Executor.h
│   │   ├── IORequest.h
│   │   ├── IOService_linux.cc
│   │   ├── IOService_linux.h
│   │   ├── IOService_thread.cc
│   │   ├── IOService_thread.h
│   │   ├── SleepRequest.h
│   │   ├── SubTask.cc
│   │   ├── SubTask.h
│   │   ├── list.h
│   │   ├── mpoller.c
│   │   ├── mpoller.h
│   │   ├── msgqueue.c
│   │   ├── msgqueue.h
│   │   ├── poller.c
│   │   ├── poller.h
│   │   ├── rbtree.c
│   │   ├── rbtree.h
│   │   ├── thrdpool.c
│   │   ├── thrdpool.h
│   │   └── xmake.lua
│   ├── manager/
│   │   ├── CMakeLists.txt
│   │   ├── DnsCache.cc
│   │   ├── DnsCache.h
│   │   ├── EndpointParams.h
│   │   ├── RouteManager.cc
│   │   ├── RouteManager.h
│   │   ├── UpstreamManager.cc
│   │   ├── UpstreamManager.h
│   │   ├── WFFacilities.h
│   │   ├── WFFacilities.inl
│   │   ├── WFFuture.h
│   │   ├── WFGlobal.cc
│   │   ├── WFGlobal.h
│   │   └── xmake.lua
│   ├── nameservice/
│   │   ├── CMakeLists.txt
│   │   ├── UpstreamPolicies.cc
│   │   ├── UpstreamPolicies.h
│   │   ├── WFDnsResolver.cc
│   │   ├── WFDnsResolver.h
│   │   ├── WFNameService.cc
│   │   ├── WFNameService.h
│   │   ├── WFServiceGovernance.cc
│   │   ├── WFServiceGovernance.h
│   │   └── xmake.lua
│   ├── protocol/
│   │   ├── CMakeLists.txt
│   │   ├── ConsulDataTypes.h
│   │   ├── DnsMessage.cc
│   │   ├── DnsMessage.h
│   │   ├── DnsUtil.cc
│   │   ├── DnsUtil.h
│   │   ├── HttpMessage.cc
│   │   ├── HttpMessage.h
│   │   ├── HttpUtil.cc
│   │   ├── HttpUtil.h
│   │   ├── KafkaDataTypes.cc
│   │   ├── KafkaDataTypes.h
│   │   ├── KafkaMessage.cc
│   │   ├── KafkaMessage.h
│   │   ├── KafkaResult.cc
│   │   ├── KafkaResult.h
│   │   ├── MySQLMessage.cc
│   │   ├── MySQLMessage.h
│   │   ├── MySQLMessage.inl
│   │   ├── MySQLResult.cc
│   │   ├── MySQLResult.h
│   │   ├── MySQLResult.inl
│   │   ├── MySQLUtil.cc
│   │   ├── MySQLUtil.h
│   │   ├── PackageWrapper.cc
│   │   ├── PackageWrapper.h
│   │   ├── ProtocolMessage.h
│   │   ├── RedisMessage.cc
│   │   ├── RedisMessage.h
│   │   ├── SSLWrapper.cc
│   │   ├── SSLWrapper.h
│   │   ├── TLVMessage.cc
│   │   ├── TLVMessage.h
│   │   ├── dns_parser.c
│   │   ├── dns_parser.h
│   │   ├── dns_types.h
│   │   ├── http_parser.c
│   │   ├── http_parser.h
│   │   ├── kafka_parser.c
│   │   ├── kafka_parser.h
│   │   ├── mysql_byteorder.c
│   │   ├── mysql_byteorder.h
│   │   ├── mysql_parser.c
│   │   ├── mysql_parser.h
│   │   ├── mysql_stream.c
│   │   ├── mysql_stream.h
│   │   ├── mysql_types.h
│   │   ├── redis_parser.c
│   │   ├── redis_parser.h
│   │   └── xmake.lua
│   ├── server/
│   │   ├── CMakeLists.txt
│   │   ├── WFDnsServer.h
│   │   ├── WFHttpServer.h
│   │   ├── WFMySQLServer.cc
│   │   ├── WFMySQLServer.h
│   │   ├── WFRedisServer.h
│   │   ├── WFServer.cc
│   │   ├── WFServer.h
│   │   └── xmake.lua
│   ├── util/
│   │   ├── CMakeLists.txt
│   │   ├── EncodeStream.cc
│   │   ├── EncodeStream.h
│   │   ├── LRUCache.h
│   │   ├── StringUtil.cc
│   │   ├── StringUtil.h
│   │   ├── URIParser.cc
│   │   ├── URIParser.h
│   │   ├── crc32c.c
│   │   ├── crc32c.h
│   │   ├── json_parser.c
│   │   ├── json_parser.h
│   │   └── xmake.lua
│   └── xmake.lua
├── test/
│   ├── CMakeLists.txt
│   ├── GNUmakefile
│   ├── algo_unittest.cc
│   ├── dns_unittest.cc
│   ├── facilities_unittest.cc
│   ├── graph_unittest.cc
│   ├── http_unittest.cc
│   ├── memory_unittest.cc
│   ├── mysql_unittest.cc
│   ├── redis_unittest.cc
│   ├── resource_unittest.cc
│   ├── task_unittest.cc
│   ├── upstream_unittest.cc
│   ├── uriparser_unittest.cc
│   └── xmake.lua
├── tutorial/
│   ├── CMakeLists.txt
│   ├── GNUmakefile
│   ├── tutorial-00-helloworld.cc
│   ├── tutorial-01-wget.cc
│   ├── tutorial-02-redis_cli.cc
│   ├── tutorial-03-wget_to_redis.cc
│   ├── tutorial-04-http_echo_server.cc
│   ├── tutorial-05-http_proxy.cc
│   ├── tutorial-06-parallel_wget.cc
│   ├── tutorial-07-sort_task.cc
│   ├── tutorial-08-matrix_multiply.cc
│   ├── tutorial-09-http_file_server.cc
│   ├── tutorial-10-user_defined_protocol/
│   │   ├── client-uds.cc
│   │   ├── client.cc
│   │   ├── message.cc
│   │   ├── message.h
│   │   ├── server-uds.cc
│   │   ├── server.cc
│   │   └── xmake.lua
│   ├── tutorial-11-graph_task.cc
│   ├── tutorial-12-mysql_cli.cc
│   ├── tutorial-13-kafka_cli.cc
│   ├── tutorial-14-consul_cli.cc
│   ├── tutorial-15-name_service.cc
│   ├── tutorial-16-graceful_restart/
│   │   ├── bootstrap.c
│   │   ├── server.cc
│   │   └── xmake.lua
│   ├── tutorial-17-dns_cli.cc
│   ├── tutorial-18-redis_subscriber.cc
│   ├── tutorial-19-dns_server.cc
│   └── xmake.lua
├── workflow-config.cmake.in
└── xmake.lua

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# top-most EditorConfig file
root = true
 
# all files
[*]
indent_style = tab
indent_size = 4
[src/kernel/list.h]
indent_style = tab
indent_size = 8
[src/kernel/rbtree.*]
indent_style = tab
indent_size = 8


================================================
FILE: .github/workflows/ci.yml
================================================
name: ci build

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  ubuntu-cmake:
    name: ubuntu
    runs-on: ubuntu-latest

    steps:
    - name: setup
      run: |
       sudo apt-get update
       sudo apt-get install -y cmake g++ libgtest-dev make libssl-dev
       sudo apt-get install -y valgrind
       sudo apt-get install -y libsnappy-dev libzstd-dev liblz4-dev
    - uses: actions/checkout@v2
    - name: make
      run: make KAFKA=y
    - name: make check
      run: make check KAFKA=y
    - name: make tutorial
      run: make tutorial KAFKA=y
      
  fedora-cmake:
    name: fedora
    runs-on: ubuntu-latest
    container:
      image: fedora:latest

    steps:
    - uses: actions/checkout@v3
    - run: cat /etc/os-release
    - name: install dependencies
      run: |
       sudo dnf -y update
       sudo dnf install -y cmake gcc-c++ gtest-devel make
       sudo dnf install -y openssl-devel valgrind
       sudo dnf install -y snappy-devel libzstd-devel lz4-devel zlib-devel
    - name: make
      run: make KAFKA=y
    - name: make check
      run: make check KAFKA=y
    - name: make tutorial
      run: make tutorial KAFKA=y

  freebsd-cmake:
    name: freebsd
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Build and test on FreeBSD
      uses: vmactions/freebsd-vm@v1
      with:
        usesh: true
        mem: 2048
        copyback: false
        prepare: |
          pkg update -f
          pkg install -y cmake gmake gcc pkgconf openssl devel/googletest
          pkg install -y valgrind
          pkg install -y snappy zstd liblz4
        run: |
          freebsd-version
          gmake KAFKA=y
          gmake check KAFKA=y
          gmake tutorial KAFKA=y


================================================
FILE: .github/workflows/xmake.yml
================================================
name: xmake build

on:
  workflow_dispatch:

jobs:
  build:
    name: build-linux
    runs-on: ubuntu-latest

    steps:
    - name: install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y g++ libssl-dev libgtest-dev

    - name: setup xmake
      uses: xmake-io/github-action-setup-xmake@v1
      with:
        xmake-version: latest

    - name: pull code
      uses: actions/checkout@v2

    - name: xmake
      run: |
        xmake -r
        xmake -g test
        xmake -g tutorial
        xmake -g benchmark

    - name : run shared
      run: |
        xmake f -k shared
        xmake -r
        xmake -g test
        xmake -g tutorial
        xmake -g benchmark




================================================
FILE: .gitignore
================================================
# Prerequisites
*.d

# Compiled Object files
*.slo
*.lo
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.so.*
*.dylib
*.dll

# Fortran module files
*.mod
*.smod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app

# bazel env
bazel-*

# vscode configs
.vscode

# idea configs
.idea
cmake-build-debug/
workflow-config.cmake

# xmake configs
.xmake
build.xmake
_include


================================================
FILE: BUILD
================================================
config_setting(
	name = 'linux',
	constraint_values = [
		"@platforms//os:linux",
	],
	visibility = ['//visibility:public'],
)

cc_library(
	name = 'workflow_hdrs',
	hdrs = glob(['src/include/workflow/*']),
	includes = ['src/include'],
	visibility = ["//visibility:public"],
	linkopts = [
		'-lpthread',
		'-lssl',
		'-lcrypto',
	],
)
cc_library(
	name = 'common_c',
	srcs = [
		'src/kernel/mpoller.c',
		'src/kernel/msgqueue.c',
		'src/kernel/poller.c',
		'src/kernel/rbtree.c',
		'src/kernel/thrdpool.c',
		'src/util/crc32c.c',
		'src/util/json_parser.c',
	],
	hdrs = glob(['src/*/*.h']) + glob(['src/*/*.inl']),
	includes = [
		'src/kernel',
		'src/util',
	],
	copts = ['-std=gnu90'],
	visibility = ["//visibility:public"],
)
cc_library(
	name = 'common',
	srcs = [
		'src/client/WFDnsClient.cc',
		'src/factory/DnsTaskImpl.cc',
		'src/factory/FileTaskImpl.cc',
		'src/factory/WFGraphTask.cc',
		'src/factory/WFResourcePool.cc',
		'src/factory/WFMessageQueue.cc',
		'src/factory/WFTaskFactory.cc',
		'src/factory/Workflow.cc',
		'src/manager/DnsCache.cc',
		'src/manager/RouteManager.cc',
		'src/manager/WFGlobal.cc',
		'src/nameservice/WFDnsResolver.cc',
		'src/nameservice/WFNameService.cc',
		'src/protocol/TLVMessage.cc',
		'src/protocol/DnsMessage.cc',
		'src/protocol/DnsUtil.cc',
		'src/protocol/SSLWrapper.cc',
		'src/protocol/PackageWrapper.cc',
		'src/protocol/dns_parser.c',
		'src/server/WFServer.cc',
		'src/kernel/CommRequest.cc',
		'src/kernel/CommScheduler.cc',
		'src/kernel/Communicator.cc',
		'src/kernel/Executor.cc',
		'src/kernel/SubTask.cc',
	] + select({
		':linux': [
			'src/kernel/IOService_linux.cc',
		],
		'//conditions:default': [
			'src/kernel/IOService_thread.cc',
		],
	}) + glob(['src/util/*.cc']),
	hdrs = glob(['src/*/*.h']) + glob(['src/*/*.inl']),
	includes = [
		'src/algorithm',
		'src/client',
		'src/factory',
		'src/kernel',
		'src/manager',
		'src/nameservice',
		'src/protocol',
		'src/server',
		'src/util',
	],
	deps = ['workflow_hdrs', 'common_c'],
	visibility = ["//visibility:public"],
)
cc_library(
	name = 'http',
	hdrs = [
		'src/factory/HttpTaskImpl.inl',
		'src/protocol/HttpMessage.h',
		'src/protocol/HttpUtil.h',
		'src/protocol/http_parser.h',
		'src/server/WFHttpServer.h',
		'src/client/WFHttpChunkedClient.h',
	],
	includes = [
		'src/protocol',
		'src/server',
	],
	srcs = [
		'src/factory/HttpTaskImpl.cc',
		'src/protocol/HttpMessage.cc',
		'src/protocol/HttpUtil.cc',
		'src/protocol/http_parser.c',
		'src/client/WFHttpChunkedClient.cc',
	],
	deps = [
		':common',
	],
	visibility = ["//visibility:public"],
)
cc_library(
	name = 'redis',
	hdrs = [
		'src/factory/RedisTaskImpl.inl',
		'src/protocol/RedisMessage.h',
		'src/protocol/redis_parser.h',
		'src/server/WFRedisServer.h',
		'src/client/WFRedisSubscriber.h',
	],
	includes = [
		'src/protocol',
		'src/server',
	],
	srcs = [
		'src/factory/RedisTaskImpl.cc',
		'src/protocol/RedisMessage.cc',
		'src/protocol/redis_parser.c',
		'src/client/WFRedisSubscriber.cc',
	],
	deps = [
		':common',
	],
	visibility = ["//visibility:public"],
)
cc_library(
	name = 'mysql',
	hdrs = [
		'src/protocol/MySQLMessage.h',
		'src/protocol/MySQLMessage.inl',
		'src/protocol/MySQLResult.h',
		'src/protocol/MySQLResult.inl',
		'src/protocol/MySQLUtil.h',
		'src/protocol/mysql_byteorder.h',
		'src/protocol/mysql_parser.h',
		'src/protocol/mysql_stream.h',
		'src/protocol/mysql_types.h',
		'src/server/WFMySQLServer.h',
		'src/client/WFMySQLConnection.h',
	],
	includes = [
		'src/protocol',
		'src/client',
		'src/server',
	],
	srcs = [
		'src/factory/MySQLTaskImpl.cc',
		'src/protocol/MySQLMessage.cc',
		'src/protocol/MySQLResult.cc',
		'src/protocol/MySQLUtil.cc',
		'src/protocol/mysql_byteorder.c',
		'src/protocol/mysql_parser.c',
		'src/protocol/mysql_stream.c',
		'src/server/WFMySQLServer.cc',
		'src/client/WFMySQLConnection.cc',
	],
	deps = [
		':common',
	],
	visibility = ["//visibility:public"],
)

cc_library(
	name = 'upstream',
	hdrs = [
		'src/manager/UpstreamManager.h',
		'src/nameservice/UpstreamPolicies.h',
		'src/nameservice/WFServiceGovernance.h',
	],
	includes = [
		'src/manager',
		'src/nameservice',
	],
	srcs = [
		'src/manager/UpstreamManager.cc',
		'src/nameservice/UpstreamPolicies.cc',
		'src/nameservice/WFServiceGovernance.cc',
	],
	deps = [
		':common',
	],
	visibility = ["//visibility:public"],
)

cc_library(
	name = 'kafka',
	hdrs = [
		'src/client/WFKafkaClient.h',
		'src/factory/KafkaTaskImpl.inl',
		'src/protocol/KafkaDataTypes.h',
		'src/protocol/KafkaMessage.h',
		'src/protocol/KafkaResult.h',
		'src/protocol/kafka_parser.h',
	],
	includes = [
		'src/client',
		'src/factory',
		'src/protocol',
	],
	srcs = [
		'src/client/WFKafkaClient.cc',
		'src/protocol/KafkaDataTypes.cc',
		'src/protocol/kafka_parser.c',
		'src/factory/KafkaTaskImpl.cc',
		'src/protocol/KafkaMessage.cc',
		'src/protocol/KafkaResult.cc',
	],
	deps = [
		':common',
	],
	visibility = ["//visibility:public"],
	linkopts = [
		'-lsnappy',
		'-llz4',
		'-lz',
		'-lzstd',
	],
)

cc_library(
	name = 'consul',
	hdrs = [
		'src/client/WFConsulClient.h',
		'src/protocol/ConsulDataTypes.h',
	],
	includes = [ 
		'src/client',
		'src/factory',
		'src/protocol',
		'src/util',
	],
	srcs = [ 
		'src/client/WFConsulClient.cc',
	],
	deps = [
		':common',
		':http',
	],
	visibility = ["//visibility:public"],
)

cc_binary(
	name = 'helloworld',
	srcs = ['tutorial/tutorial-00-helloworld.cc'],
	deps = [':http'],
)
cc_binary(
	name = 'wget',
	srcs = ['tutorial/tutorial-01-wget.cc'],
	deps = [':http'],
)
cc_binary(
	name = 'redis_cli',
	srcs = ['tutorial/tutorial-02-redis_cli.cc'],
	deps = [':redis'],
)

cc_binary(
	name = 'wget_to_redis',
	srcs = ['tutorial/tutorial-03-wget_to_redis.cc'],
	deps = [':http', 'redis'],
)

cc_binary(
	name = 'http_echo_server',
	srcs = ['tutorial/tutorial-04-http_echo_server.cc'],
	deps = [':http'],
)

cc_binary(
	name = 'http_proxy',
	srcs = ['tutorial/tutorial-05-http_proxy.cc'],
	deps = [':http'],
)

cc_binary(
	 name = 'parallel_wget',
	 srcs = ['tutorial/tutorial-06-parallel_wget.cc'],
	 deps = [':http'],
)

cc_binary(
	name = 'sort_task',
	srcs = ['tutorial/tutorial-07-sort_task.cc'],
	deps = [':common'],
)

cc_binary(
	name = 'matrix_multiply',
	srcs = ['tutorial/tutorial-08-matrix_multiply.cc'],
	deps = [':common'],
)

cc_binary(
	name = 'http_file_server',
	srcs = ['tutorial/tutorial-09-http_file_server.cc'],
	deps = [':http'],
)

cc_library(
	name = 'user_hdrs',
	hdrs = ['tutorial/tutorial-10-user_defined_protocol/message.h'],
	includes = ['tutorial/tutorial-10-user_defined_protocol'],
)

cc_binary(
	name = 'server',
	srcs = [
		'tutorial/tutorial-10-user_defined_protocol/server.cc',
		'tutorial/tutorial-10-user_defined_protocol/message.cc',
	],
	deps = [':common', ':user_hdrs'],
)

cc_binary(
	name = 'client',
	srcs = [
		'tutorial/tutorial-10-user_defined_protocol/client.cc',
		'tutorial/tutorial-10-user_defined_protocol/message.cc',
	],
	deps = [':common', ':user_hdrs'],
)

cc_binary(
	name = 'graph_task',
	srcs = ['tutorial/tutorial-11-graph_task.cc'],
	deps = [':http'],
)

cc_binary(
	name = 'mysql_cli',
	srcs = ['tutorial/tutorial-12-mysql_cli.cc'],
	deps = [':mysql'],
)

cc_binary(
	name = 'kafka_cli',
	srcs = ['tutorial/tutorial-13-kafka_cli.cc'],
	deps = [':kafka', ':workflow_hdrs'],
)

cc_binary(
	name = 'consul_cli',
	srcs = ['tutorial/tutorial-14-consul_cli.cc'],
	deps = [':consul'],
)

cc_binary(
	name = 'name_service',
	srcs = ['tutorial/tutorial-15-name_service.cc'],
	deps = [':http'],
)

cc_binary(
	name = 'graceful_restart_bootstrap',
	srcs = [
		'tutorial/tutorial-16-graceful_restart/bootstrap.c',
	],
)

cc_binary(
	name = 'graceful_restart_server',
	srcs = [
		'tutorial/tutorial-16-graceful_restart/server.cc',
	],
	deps = [':http'],
)

cc_binary(
	name = 'dns_cli',
	srcs = ['tutorial/tutorial-17-dns_cli.cc'],
	deps = [':common'],
)

cc_binary(
	name = 'redis_subscriber',
	srcs = ['tutorial/tutorial-18-redis_subscriber.cc'],
	deps = [':redis'],
)

cc_binary(
	name = 'dns_server',
	srcs = ['tutorial/tutorial-19-dns_server.cc'],
	deps = [':common'],
)


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.10)

set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "build type")
set(CMAKE_SKIP_RPATH TRUE)

project(
	workflow
	VERSION 1.0.0
	LANGUAGES C CXX
)

if (CYGWIN)
	message(FATAL_ERROR "Sorry, DO NOT support Cygwin")
endif ()

if (MINGW)
	message(FATAL_ERROR "Sorry, DO NOT support MinGW")
endif ()

include(GNUInstallDirs)

set(CMAKE_CONFIG_INSTALL_FILE ${PROJECT_BINARY_DIR}/config.toinstall.cmake)
set(CMAKE_CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
set(INC_DIR ${PROJECT_SOURCE_DIR}/_include CACHE PATH "workflow inc")
set(LIB_DIR ${PROJECT_SOURCE_DIR}/_lib CACHE PATH "workflow lib")

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIB_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIB_DIR})

add_custom_target(
	LINK_HEADERS ALL
	COMMENT "link headers..."
)

INCLUDE(CMakeLists_Headers.txt)

macro(makeLink src dest target)
	add_custom_command(
		TARGET ${target} PRE_BUILD
		COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dest}
	)
endmacro()

add_custom_command(
	TARGET LINK_HEADERS PRE_BUILD
	COMMAND ${CMAKE_COMMAND} -E make_directory ${INC_DIR}/${PROJECT_NAME}
)

foreach(header_file ${INCLUDE_HEADERS} ${INCLUDE_KERNEL_HEADERS})
	string(REPLACE "/" ";" arr ${header_file})
	list(GET arr -1 file_name)
	makeLink(${PROJECT_SOURCE_DIR}/${header_file} ${INC_DIR}/${PROJECT_NAME}/${file_name} LINK_HEADERS)
endforeach()

message("CMAKE_C_FLAGS_DEBUG is ${CMAKE_C_FLAGS_DEBUG}")
message("CMAKE_C_FLAGS_RELEASE is ${CMAKE_C_FLAGS_RELEASE}")
message("CMAKE_C_FLAGS_RELWITHDEBINFO is ${CMAKE_C_FLAGS_RELWITHDEBINFO}")
message("CMAKE_C_FLAGS_MINSIZEREL is ${CMAKE_C_FLAGS_MINSIZEREL}")

message("CMAKE_CXX_FLAGS_DEBUG is ${CMAKE_CXX_FLAGS_DEBUG}")
message("CMAKE_CXX_FLAGS_RELEASE is ${CMAKE_CXX_FLAGS_RELEASE}")
message("CMAKE_CXX_FLAGS_RELWITHDEBINFO is ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
message("CMAKE_CXX_FLAGS_MINSIZEREL is ${CMAKE_CXX_FLAGS_MINSIZEREL}")

set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -Wall -fPIC -pipe -std=gnu90")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -pipe -std=c++11 -fno-exceptions -Wno-invalid-offsetof")
if (APPLE)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
endif()

add_subdirectory(src)

####CONFIG

include(CMakePackageConfigHelpers)
set(CONFIG_INC_DIR ${INC_DIR})
set(CONFIG_LIB_DIR ${LIB_DIR})
configure_package_config_file(
	${PROJECT_NAME}-config.cmake.in
	${PROJECT_SOURCE_DIR}/${PROJECT_NAME}-config.cmake
	INSTALL_DESTINATION ${CMAKE_CONFIG_INSTALL_DIR}
	PATH_VARS CONFIG_INC_DIR CONFIG_LIB_DIR
)

set(CONFIG_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR})
set(CONFIG_LIB_DIR ${CMAKE_INSTALL_LIBDIR})
configure_package_config_file(
	${PROJECT_NAME}-config.cmake.in
	${CMAKE_CONFIG_INSTALL_FILE}
	INSTALL_DESTINATION ${CMAKE_CONFIG_INSTALL_DIR}
	PATH_VARS CONFIG_INC_DIR CONFIG_LIB_DIR
)

write_basic_package_version_file(
	${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
	VERSION ${WORKFLOW_VERSION}
	COMPATIBILITY AnyNewerVersion 
)

install(
	FILES ${CMAKE_CONFIG_INSTALL_FILE}
	DESTINATION ${CMAKE_CONFIG_INSTALL_DIR}
	COMPONENT devel
	RENAME ${PROJECT_NAME}-config.cmake
)

install(
	FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
	DESTINATION ${CMAKE_CONFIG_INSTALL_DIR}
	COMPONENT devel
)

install(
	FILES ${INCLUDE_HEADERS} ${INCLUDE_KERNEL_HEADERS}
	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
	COMPONENT devel
)

install(
	FILES README.md
	DESTINATION "${CMAKE_INSTALL_DOCDIR}-${PROJECT_VERSION}"
	COMPONENT devel
)


================================================
FILE: CMakeLists_Headers.txt
================================================
cmake_minimum_required(VERSION 3.10)

set(COMMON_KERNEL_HEADERS
	src/kernel/CommRequest.h
	src/kernel/CommScheduler.h
	src/kernel/Communicator.h
	src/kernel/SleepRequest.h
	src/kernel/ExecRequest.h
	src/kernel/IORequest.h
	src/kernel/Executor.h
	src/kernel/list.h
	src/kernel/mpoller.h
	src/kernel/poller.h
	src/kernel/msgqueue.h
	src/kernel/rbtree.h
	src/kernel/SubTask.h
	src/kernel/thrdpool.h
)

if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
	set(INCLUDE_KERNEL_HEADERS
		${COMMON_KERNEL_HEADERS}
		src/kernel/IOService_linux.h
	)
elseif (UNIX)
	set(INCLUDE_KERNEL_HEADERS
		${COMMON_KERNEL_HEADERS}
		src/kernel/IOService_thread.h
	)
else ()
	message(FATAL_ERROR "IOService unsupported.")
endif ()

set(INCLUDE_HEADERS
	src/protocol/ProtocolMessage.h
	src/protocol/http_parser.h
	src/protocol/HttpMessage.h
	src/protocol/HttpUtil.h
	src/protocol/redis_parser.h
	src/protocol/RedisMessage.h
	src/protocol/mysql_stream.h
	src/protocol/MySQLMessage.h
	src/protocol/MySQLMessage.inl
	src/protocol/MySQLResult.h
	src/protocol/MySQLResult.inl
	src/protocol/MySQLUtil.h
	src/protocol/mysql_parser.h
	src/protocol/mysql_types.h
	src/protocol/mysql_byteorder.h
	src/protocol/PackageWrapper.h
	src/protocol/SSLWrapper.h
	src/protocol/dns_types.h
	src/protocol/dns_parser.h
	src/protocol/DnsMessage.h
	src/protocol/DnsUtil.h
	src/protocol/TLVMessage.h
	src/protocol/ConsulDataTypes.h
	src/server/WFServer.h
	src/server/WFDnsServer.h
	src/server/WFHttpServer.h
	src/server/WFRedisServer.h
	src/server/WFMySQLServer.h
	src/client/WFHttpChunkedClient.h
	src/client/WFMySQLConnection.h
	src/client/WFRedisSubscriber.h
	src/client/WFConsulClient.h
	src/client/WFDnsClient.h
	src/manager/DnsCache.h
	src/manager/WFGlobal.h
	src/manager/UpstreamManager.h
	src/manager/RouteManager.h
	src/manager/EndpointParams.h
	src/manager/WFFuture.h
	src/manager/WFFacilities.h
	src/manager/WFFacilities.inl
	src/util/json_parser.h
	src/util/EncodeStream.h
	src/util/LRUCache.h
	src/util/StringUtil.h
	src/util/URIParser.h
	src/factory/WFConnection.h
	src/factory/WFTask.h
	src/factory/WFTask.inl
	src/factory/WFGraphTask.h
	src/factory/WFTaskError.h
	src/factory/WFTaskFactory.h
	src/factory/WFTaskFactory.inl
	src/factory/WFAlgoTaskFactory.h
	src/factory/WFAlgoTaskFactory.inl
	src/factory/Workflow.h
	src/factory/WFOperator.h
	src/factory/WFResourcePool.h
	src/factory/WFMessageQueue.h
	src/factory/HttpTaskImpl.inl
	src/factory/RedisTaskImpl.inl
	src/nameservice/WFNameService.h
	src/nameservice/WFDnsResolver.h
	src/nameservice/WFServiceGovernance.h
	src/nameservice/UpstreamPolicies.h
)

if(KAFKA STREQUAL "y")
	set(INCLUDE_HEADERS
		${INCLUDE_HEADERS}
		src/util/crc32c.h
		src/protocol/KafkaMessage.h
		src/protocol/KafkaDataTypes.h
		src/protocol/KafkaResult.h
		src/protocol/kafka_parser.h
		src/client/WFKafkaClient.h
		src/factory/KafkaTaskImpl.inl
	)
endif()



================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others’ private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at xiehan@sogou-inc.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: GNUmakefile
================================================
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
ALL_TARGETS := all base check install preinstall clean tutorial
MAKE_FILE := Makefile

DEFAULT_BUILD_DIR := build.cmake
BUILD_DIR := $(shell if [ -f $(MAKE_FILE) ]; then echo "."; else echo $(DEFAULT_BUILD_DIR); fi)
CMAKE3 := $(shell if which cmake3>/dev/null ; then echo cmake3; else echo cmake; fi;)

.PHONY: $(ALL_TARGETS)

all: base
	$(MAKE) -C $(BUILD_DIR) -f Makefile

base:
	mkdir -p $(BUILD_DIR)

ifeq ($(DEBUG),y)
	cd $(BUILD_DIR) && $(CMAKE3) -D CMAKE_BUILD_TYPE=Debug -D CONSUL=$(CONSUL) -D KAFKA=$(KAFKA) -D MYSQL=$(MYSQL) -D REDIS=$(REDIS) -D UPSTREAM=$(UPSTREAM) $(ROOT_DIR)
else ifneq ("${INSTALL_PREFIX}install_prefix", "install_prefix")
	cd $(BUILD_DIR) && $(CMAKE3) -DCMAKE_INSTALL_PREFIX:STRING=${INSTALL_PREFIX} -D CONSUL=$(CONSUL) -D KAFKA=$(KAFKA) -D MYSQL=$(MYSQL) -D REDIS=$(REDIS) -D UPSTREAM=$(UPSTREAM) $(ROOT_DIR)
else
	cd $(BUILD_DIR) && $(CMAKE3) -D CONSUL=$(CONSUL) -D KAFKA=$(KAFKA) -D MYSQL=$(MYSQL) -D REDIS=$(REDIS) -D UPSTREAM=$(UPSTREAM) $(ROOT_DIR)
endif

tutorial: all
	$(MAKE) -C tutorial

check: all
	$(MAKE) -C test check

install preinstall: base
	mkdir -p $(BUILD_DIR)
	cd $(BUILD_DIR) && $(CMAKE3) $(ROOT_DIR)
	$(MAKE) -C $(BUILD_DIR) -f Makefile $@

clean:
	-$(MAKE) -C test clean
	-$(MAKE) -C tutorial clean
	rm -rf $(DEFAULT_BUILD_DIR)
	rm -rf _include
	rm -rf _lib
	find . -name CMakeCache.txt | xargs rm -f
	find . -name Makefile       | xargs rm -f
	find . -name "*.cmake"      | xargs rm -f
	find . -name CMakeFiles     | xargs rm -rf


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright 2020 Sogou Inc.  All rights reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: LICENSE_GPLV2
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.


================================================
FILE: README.md
================================================
[简体中文版(推荐)](README_cn.md)

## Sogou C++ Workflow

[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://github.com/sogou/workflow/blob/master/LICENSE)
[![Language](https://img.shields.io/badge/language-c++-red.svg)](https://en.cppreference.com/) 
[![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey.svg)](https://img.shields.io/badge/platform-linux%20%7C%20macos20%7C%20windows-lightgrey.svg)
[![Build Status](https://img.shields.io/github/actions/workflow/status/sogou/workflow/ci.yml?branch=master)](https://github.com/sogou/workflow/actions?query=workflow%3A%22ci+build%22++)

As **Sogou\`s C++ server engine**, Sogou C++ Workflow supports almost all **back-end C++ online services** of Sogou, including all search services, cloud input method, online advertisements, etc., handling more than **10 billion** requests every day. This is an **enterprise-level programming engine** in light and elegant design which can satisfy most C++ back-end development requirements.

#### You can use it:

* To quickly build an **HTTP server**:

~~~cpp
#include <stdio.h>
#include "workflow/WFHttpServer.h"

int main()
{
    WFHttpServer server([](WFHttpTask *task) {
        task->get_resp()->append_output_body("<html>Hello World!</html>");
    });

    if (server.start(8888) == 0) { // start server on port 8888
        getchar(); // press "Enter" to end.
        server.stop();
    }

    return 0;
}
~~~

* As a **multifunctional asynchronous client**, it currently supports `HTTP`, `Redis`, `MySQL` and `Kafka` protocols.
  * ``MySQL`` protocol supports ``MariaDB``, ``TiDB`` as well.
* To implement **client/server on user-defined protocol** and build your own **RPC system**.
  * [srpc](https://github.com/sogou/srpc) is based on it and it is an independent open source project, which supports srpc, brpc, trpc and thrift protocols.
* To build **asynchronous workflow**; support common **series** and **parallel** structures, and also support any **DAG** structures.
* As a **parallel computing tool**. In addition to **networking tasks**, Sogou C++ Workflow also includes **the scheduling of computing tasks**. All types of tasks can be put into **the same** flow.
* As an **asynchronous file IO tool** in `Linux` system, with high performance exceeding any system call. Disk file IO is also a task.
* To realize any **high-performance** and **high-concurrency** back-end service with a very complex relationship between computing and networking.
* To build a **micro service** system.
  * This project has built-in **service governance** and **load balancing** features.
* Wiki link : [PaaS Architecture](https://github.com/sogou/workflow/wiki)

#### Compiling and Running Environment

* This project supports `Linux`, `macOS`, `Windows`, `Android` and other operating systems.
  * `Windows` version is currently released as an independent [branch](https://github.com/sogou/workflow/tree/windows), using `iocp` to implement asynchronous networking. All user interfaces are consistent with the `Linux` version.
* Supports all CPU platforms, including 32 or 64-bit `x86` processors, big-endian or little-endian `arm` processors, `loongson` processors.
* Master branch requires `OpenSSL 1.1` or above, and BoringSSL is fully compatible. If you don't like SSL, you may checkout the [nossl](https://github.com/sogou/workflow/tree/nossl) branch.
* Uses the `C++11` standard and therefore, it should be compiled with a compiler which supports `C++11`. Does not rely on `boost` or `asio`.
* No other dependencies. However, if you need `Kafka` protocol, some compression libraries should be installed, including `lz4`, `zstd` and `snappy`.

### Get Started (Linux, macOS):
~~~sh
git clone https://github.com/sogou/workflow
cd workflow
make
cd tutorial
make
~~~~

#### With SRPC Tool (NEW!):
https://github.com/sogou/srpc/blob/master/tools/README.md

#### With [apt-get](https://launchpad.net/ubuntu/+source/workflow) on Debian Linux, ubuntu:
Sogou C++ Workflow has been packaged for Debian Linux and ubuntu 22.04.  
To install the Workflow library for development purposes:
~~~~sh
sudo apt-get install libworkflow-dev
~~~~

To install the Workflow library for deployment:
~~~~sh
sudo apt-get install libworkflow1
~~~~

#### With [dnf](https://packages.fedoraproject.org/pkgs/workflow) on Fedora Linux:
Sogou C++ Workflow has been packaged for Fedora Linux.  
To install the Workflow library for development purposes:
~~~~sh
sudo dnf install workflow-devel
~~~~

To install the Workflow library for deployment:
~~~~sh
sudo dnf install workflow
~~~~

#### With xmake

If you want to use xmake to build workflow, you can see [xmake build document](docs/en/xmake.md)

# Tutorials

* Client
  * [Creating your first task:wget](docs/en/tutorial-01-wget.md)
  * [Implementing Redis set and get:redis\_cli](docs/en/tutorial-02-redis_cli.md)
  * [More features about series:wget\_to\_redis](docs/en/tutorial-03-wget_to_redis.md)
* Server
  * [First server:http\_echo\_server](docs/en/tutorial-04-http_echo_server.md)
  * [Asynchronous server:http\_proxy](docs/en/tutorial-05-http_proxy.md)
* Parallel tasks and Series 
  * [A simple parallel wget:parallel\_wget](docs/en/tutorial-06-parallel_wget.md)
* Important topics
  * [About error](docs/en/about-error.md)
  * [About timeout](docs/en/about-timeout.md)
  * [About global configuration](docs/en/about-config.md)
  * [About DNS](docs/en/about-dns.md)
  * [About exit](docs/en/about-exit.md)
* Computing tasks
  * [Using the build-in algorithm factory:sort\_task](docs/en/tutorial-07-sort_task.md)
  * [User-defined computing task:matrix\_multiply](docs/en/tutorial-08-matrix_multiply.md)
  * [Use computing task in a simple way: go task](docs/en/about-go-task.md)
* Asynchronous File IO tasks
  * [Http server with file IO:http\_file\_server](docs/en/tutorial-09-http_file_server.md)
* User-defined protocol
  * [A simple user-defined protocol: client/server](docs/en/tutorial-10-user_defined_protocol.md)
  * [Use TLV message](docs/en/about-tlv-message.md)
* Other important tasks/components
  * [About timer](docs/en/about-timer.md)
  * [About counter](docs/en/about-counter.md)
  * [About resource pool](docs/en/about-resource-pool.md)
  * [About module](docs/en/about-module.md)
  * [About DAG](docs/en/tutorial-11-graph_task.md)
* Service governance
  * [About service governance](docs/en/about-service-governance.md)
  * [More documents about upstream](docs/en/about-upstream.md)
* Connection context
  * [About connection context](docs/en/about-connection-context.md)
* Built-in clients
  * [Asynchronous MySQL client:mysql\_cli](docs/en/tutorial-12-mysql_cli.md)
  * [Asynchronous Kafka client: kafka\_cli](docs/en/tutorial-13-kafka_cli.md)

#### Programming Paradigm

Program = Protocol + Algorithm + Workflow

* Protocol
  * In most cases, users use built-in common network protocols, such as HTTP, Redis or various rpc.
  * Users can also easily customize user-defined network protocol. In the customization, they only need to provide serialization and deserialization functions to define their own client/server.
* Algorithm
  * In our design, the algorithm is a concept symmetrical to the protocol.
    * If protocol call is rpc, then algorithm call is an apc (Async Procedure Call).
  * We have provided some general algorithms, such as sort, merge, psort, reduce, which can be used directly.
  * Compared with a user-defined protocol, a user-defined algorithm is much more common. Any complicated computation with clear boundaries should be packaged into an algorithm.
* Workflow
  * Workflow is the actual business logic, which is to put the protocols and algorithms into the flow graph for use.
  * The typical workflow is a closed series-parallel graph. Complex business logic may be a non-closed DAG.
  * The workflow graph can be constructed directly or dynamically generated based on the results of each step. All tasks are executed asynchronously.

Structured Concurrency and Task Abstraction

* Our system contains five basic tasks: communication, computation, file IO, timer, and counter.
* All tasks are generated by the task factory, and users organize the concurrency structure by calling interfaces, such as series, parallel, DAG, etc.
* In most cases, the tasks generated by the user through the task factory is a complex task which encapsulates multiple asynchronous processes, but it is transparent to the user.
  * For example, an HTTP request may include many asynchronous processes (DNS, redirection), but for user, it is just a networking task.
  * File sorting seems to be an algorithm, but it actually includes many complex interaction processes between file IO and CPU computation.
  * If you think of business logic as building circuits with well-designed electronic components, then each electronic component may be a complex circuit.
  * The task abstraction mechanism greatly reduces the number of tasks users need to create and the depth of callbacks.
* Any task runs in a **SeriesWork** and the tasks in the same SeriesWork shares the series context, which simplifies data transfer between asynchronous tasks.

Callback and Memory Reclamation Mechanism

* All calls are executed asynchronously, and there is almost no operation that occupies a thread.
* Explicit callback mechanism. Users are aware that they are writing asynchronous programs.
* **A set of object lifecycle mechanisms greatly simplifies memory management for asynchronous programs.**
  * The lifecycle of any task created by the framework is from creation until the callback function finishes running. There is no risk of leakage.
    * If a task is created but the user does not want to run it, the user needs to release it through the `dismiss()` interface.
  * Any data in the task, such as the response of the network request, will also be recycled with the task. At this time, the user can use `std::move()` to move the required data.
  * The project doesn’t use `std::shared_ptr` to manage memory.

* We try to avoid user's derivations, and encapsulate user behavior with `std::function` instead, including:
  * The callback of any task.
  * Any server's process. This conforms to the `FaaS` (Function as a Service) idea.
  * The realization of an algorithm is simply a `std::function`. But the algorithm can also be implemented by derivation.
  * If used deeply, one will find that everything can be derived.

#### Any Other Questions?

You may check the [FAQ](https://github.com/sogou/workflow/issues/406) and [issues](https://github.com/sogou/workflow/issues) list first to see if you can find the answer.

You are very welcome to send the problems you encounter in use to [issues](https://github.com/sogou/workflow/issues), and we will answer them as soon as possible. At the same time, more issues will also help new users.



================================================
FILE: README_cn.md
================================================
[English version](README.md)

## Sogou C++ Workflow
[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://github.com/sogou/workflow/blob/master/LICENSE)
[![Language](https://img.shields.io/badge/language-c++-red.svg)](https://en.cppreference.com/)
[![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey.svg)](https://img.shields.io/badge/platform-linux%20%7C%20macos20%7C%20windows-lightgrey.svg)
[![Build Status](https://img.shields.io/github/actions/workflow/status/sogou/workflow/ci.yml?branch=master)](https://github.com/sogou/workflow/actions?query=workflow%3A%22ci+build%22++)

搜狗公司C++服务器引擎,编程范式。支撑搜狗几乎所有后端C++在线服务,包括所有搜索服务,云输入法,在线广告等,每日处理数百亿请求。这是一个设计轻盈优雅的企业级程序引擎,可以满足大多数后端与嵌入式开发需求。  
#### 你可以用来:
* 快速搭建http服务器:
~~~cpp
#include <stdio.h>
#include "workflow/WFHttpServer.h"

int main()
{
    WFHttpServer server([](WFHttpTask *task) {
        task->get_resp()->append_output_body("<html>Hello World!</html>");
    });

    if (server.start(8888) == 0) {  // start server on port 8888
        getchar(); // press "Enter" to end.
        server.stop();
    }

    return 0;
}
~~~
* 作为万能异步客户端。目前支持``http``,``redis``,``mysql``和``kafka``协议。
  * 轻松构建效率极高的spider。
  * ``mysql``协议同时也支持``MariaDB``和``TiDB``等数据库。
* 实现自定义协议client/server,构建自己的RPC系统。
  * [srpc](https://github.com/sogou/srpc)就是以它为基础,作为独立项目开源。支持``srpc``,``brpc``,``trpc``和``thrift``等协议。
* 构建异步任务流,支持常用的串并联,也支持更加复杂的DAG结构。
* 作为并行计算工具使用。除了网络任务,我们也包含计算任务的调度。所有类型的任务都可以放入同一个流中。
* 在``Linux``系统下作为文件异步IO工具使用,性能超过任何标准调用。磁盘IO也是一种任务。
* 实现任何计算与通讯关系非常复杂的高性能高并发的后端服务。
* 构建微服务系统。
  * 项目内置服务治理与负载均衡等功能。
* Wiki链接 : [PaaS 架构图](https://github.com/sogou/workflow/wiki)

#### 编译和运行环境
* 项目支持``Linux``,``macOS``,``Windows``,``Android``等操作系统。
  *  ``Windows``版以[windows](https://github.com/sogou/workflow/tree/windows)分支发布,使用``iocp``实现异步网络。用户接口与``Linux``版一致。
* 支持所有CPU平台,包括32或64位``x86``处理器,大端或小端``arm``处理器,国产``loongson``龙芯处理器实测支持。
* 需要依赖于``OpenSSL 1.1``或以上版本,也兼容BoringSSL。
  * 不喜欢SSL的用户可以使用[nossl](https://github.com/sogou/workflow/tree/nossl)分支,代码更简洁。
* 项目使用了``C++11``标准,需要用支持``C++11``的编译器编译。但不依赖``boost``或``asio``。
* 项目无其它依赖。如需使用``kafka``协议,需自行安装``lz4``,``zstd``和``snappy``几个压缩库。

#### 快速开始(Linux, macOS):
~~~sh
git clone https://github.com/sogou/workflow # From gitee: git clone https://gitee.com/sogou/workflow
cd workflow
make
cd tutorial
make
~~~
#### 使用SRPC工具(NEW!)
SRPC工具可以生成完整的workflow工程,根据用户命令生成对应的server,client或proxy框架,以及CMake工程文件和JSON格式的配置文件。  
并且,工具会下载最小的必要的依赖。例如在用户指定产生RPC项目时,自动下载并配置好protobuf等依赖。  
SRPC工具的使用方法可以参考:https://github.com/sogou/srpc/blob/master/tools/README_cn.md

#### Debian Linux或ubuntu上使用[apt-get](https://launchpad.net/ubuntu/+source/workflow)安装:
作为是Debian Linux与Ubuntu Linux 22.04版自带软件,可以通过``apt-get``命令直接安装开发包:
~~~sh
sudo apt-get install libworkflow-dev
~~~
或部署运行环境:
~~~sh
sudo apt-get install workflow1
~~~
注意ubuntu只有最新22.04版或以上自带workflow。更推荐用git直接下载最新源代码编译。
#### Fedora Linux上使用[dnf](https://packages.fedoraproject.org/pkgs/workflow)安装:
Workflow也是Fedora Linux的自带软件,可以使用最新的rpm包管理工具``dnf``直接安装开发包:
~~~~sh
sudo dnf install workflow-devel
~~~~
或部署运行环境:
~~~~sh
sudo dnf install workflow
~~~~
#### 使用xmake
如果你想用xmake去构建 workflow, 你可以看 [xmake build document](docs/xmake.md)

# 示例教程
  * Client基础
    * [创建第一个任务:wget](docs/tutorial-01-wget.md)
    * [实现一次redis写入与读出:redis_cli](docs/tutorial-02-redis_cli.md)
    * [任务序列的更多功能:wget_to_redis](docs/tutorial-03-wget_to_redis.md)
  * Server基础
    * [第一个server:http_echo_server](docs/tutorial-04-http_echo_server.md)
    * [异步server的示例:http_proxy](docs/tutorial-05-http_proxy.md)
  * 并行任务与工作流 
    * [一个简单的并行抓取:parallel_wget](docs/tutorial-06-parallel_wget.md)
  * 几个重要的话题
    * [关于错误处理](docs/about-error.md)
    * [关于超时](docs/about-timeout.md)
	* [关于全局配置](docs/about-config.md)
    * [关于DNS](docs/about-dns.md)
    * [关于程序退出](docs/about-exit.md)
  * 计算任务
    * [使用内置算法工厂:sort_task](docs/tutorial-07-sort_task.md)
    * [自定义计算任务:matrix_multiply](docs/tutorial-08-matrix_multiply.md)
    * [更加简单的使用计算任务:go_task](docs/about-go-task.md)【推荐】
  * 文件异步IO任务
    * [异步IO的http server:http_file_server](docs/tutorial-09-http_file_server.md)
  * 用户定义协议基础
    * [简单的用户自定义协议client/server](docs/tutorial-10-user_defined_protocol.md)
    * [使用TLV格式消息](docs/about-tlv-message.md)
  * 其它一些重要任务与组件
    * [关于定时器](docs/about-timer.md)
    * [关于计数器](docs/about-counter.md)
    * [模块任务](docs/about-module.md)
    * [DAG图任务](docs/tutorial-11-graph_task.md)
    * [Selector任务](docs/about-selector.md)
  * 任务间通信
    * [条件任务与观察者模式](docs/about-conditional.md)
    * [资源池与消息队列](docs/about-resource-pool.md)
  * 服务治理
    * [关于服务治理](docs/about-service-governance.md)
    * [Upstream更多文档](docs/about-upstream.md)
    * [自定义名称服务策略](docs/tutorial-15-name_service.md)
  * 连接上下文的使用
    * [关于连接上下文](docs/about-connection-context.md)
  * 内置客户端
    * [异步MySQL客户端:mysql_cli](docs/tutorial-12-mysql_cli.md)
    * [异步kafka客户端:kafka_cli](docs/tutorial-13-kafka_cli.md)
    * [异步DNS客户端:dns_cli](docs/tutorial-17-dns_cli.md)
    * [Redis订阅客户端:redis_subscriber](docs/tutorial-18-redis_subscriber.md)

#### 编程范式

程序 = 协议 + 算法 + 任务流
* 协议
  * 大多数情况下,用户使用的是内置的通用网络协议,例如http,redis或各种rpc。
  * 用户可以方便的自定义网络协议,只需提供序列化和反序列化函数,就可以定义出自己的client/server。
* 算法
  * 在我们的设计里,算法是与协议对称的概念。
    * 如果说协议的调用是rpc,算法的调用就是一次apc(Async Procedure Call)。
  * 我们提供了一些通用算法,例如sort,merge,psort,reduce,可以直接使用。
  * 与自定义协议相比,自定义算法的使用要常见得多。任何一次边界清晰的复杂计算,都应该包装成算法。
* 任务流
  * 任务流就是实际的业务逻辑,就是把开发好的协议与算法放在流程图里使用起来。
  * 典型的任务流是一个闭合的串并联图。复杂的业务逻辑,可能是一个非闭合的DAG。
  * 任务流图可以直接构建,也可以根据每一步的结果动态生成。所有任务都是异步执行的。

结构化并发与任务隐藏
* 我们系统中包含五种基础任务:通讯,计算,文件IO,定时器,计数器。
* 一切任务都由任务工厂产生,用户通过调用接口组织并发结构。例如串联并联,DAG等。
* 大多数情况下,用户通过任务工厂产生的任务,都隐藏了多个异步过程,但用户并不感知。
  * 例如,一次http请求,可能包含许多次异步过程(DNS,重定向),但对用户来讲,就是一次通信任务。
  * 文件排序,看起来就是一个算法,但其实包括复杂的文件IO与CPU计算的交互过程。
  * 如果把业务逻辑想象成用设计好的电子元件搭建电路,那么每个电子元件内部可能又是一个复杂电路。
  * 任务隐藏机制大幅减少了用户需要创建的任务数量和回调深度。
* 任何任务都运行在某个串行流(series)里,共享series上下文,让异步任务之间数据传递变得简单。

回调与内存回收机制
* 一切调用都是异步执行,几乎不存在占着线程等待的操作。
* 显式的回调机制。用户清楚自己在写异步程序。
* **通过一套对象生命周期机制,大幅简化异步程序的内存管理**
  * 任何框架创建的任务,生命周期都是从创建到callback函数运行结束为止。没有泄漏风险。
    * 如果创建了任务之后不想运行,则需要通过dismiss()接口删除。
  * 任务中的数据,例如网络请求的resp,也会随着任务被回收。此时用户可通过``std::move()``把需要的数据移走。
  * 项目中不使用任何智能指针来管理内存。代码观感清新。
* 尽量避免用户级别派生,以``std::function``封装用户行为,包括:
  * 任何任务的callback。
  * 任何server的process。符合``FaaS``(Function as a Service)思想。
  * 一个算法的实现,简单来讲也是一个``std::function``。
  * 如果深入使用,又会发现一切皆可派生。

# 使用中有疑问?
可以先查看[FAQ](https://github.com/sogou/workflow/issues/170)和[issues](https://github.com/sogou/workflow/issues)列表,看看是否能找到答案。  
非常欢迎将您使用中遇到的问题发送到[issues](https://github.com/sogou/workflow/issues),我们将第一时间进行解答。同时更多的issue对新用户也会带来帮助。  
也可以通过QQ群:**618773193** 联系我们。

<img src="https://user-images.githubusercontent.com/1880011/92300953-e9cc5400-ef91-11ea-82f5-4cf3174cd851.jpeg" align=center width = "200" alt="qq_qrcode" />

#### Gitee仓库
用户可以在访问GitHub遇到困难时,使用我们的Gitee官方仓库:https://gitee.com/sogou/workflow  
**另外也麻烦在Gitee上star了项目的用户,尽量同步star一下[GitHub仓库](https://github.com/sogou/workflow)。谢谢!**


================================================
FILE: WORKSPACE
================================================


================================================
FILE: benchmark/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.10)

set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "build type")

project(benchmark
		LANGUAGES C CXX
)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR})

find_library(LIBRT rt)
find_package(OpenSSL REQUIRED)
find_package(workflow REQUIRED CONFIG HINTS ..)
include_directories(${OPENSSL_INCLUDE_DIR} ${WORKFLOW_INCLUDE_DIR})
link_directories(${WORKFLOW_LIB_DIR})
find_library(WORKFLOW_LIB NAMES libworkflow.a workflow HINTS ${WORKFLOW_LIB_DIR})

set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -Wall -fPIC -pipe -std=gnu90")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -pipe -std=c++11 -fno-exceptions -Wno-invalid-offsetof")
if (APPLE)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
endif()

set(BENCHMARK_LIST
	benchmark-01-http_server
	benchmark-02-http_server_long_req
)

if (APPLE)
	set(LIB ${WORKFLOW_LIB} pthread OpenSSL::SSL OpenSSL::Crypto)
else ()
	set(LIB ${WORKFLOW_LIB} pthread OpenSSL::SSL OpenSSL::Crypto ${LIBRT})
endif ()

foreach(src ${BENCHMARK_LIST})
	string(REPLACE "-" ";" arr ${src})
	list(GET arr -1 bin_name)
	add_executable(${bin_name} ${src}.cc)
	target_link_libraries(${bin_name} ${LIB})
endforeach()


================================================
FILE: benchmark/GNUmakefile
================================================
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
ALL_TARGETS := all clean
MAKE_FILE := Makefile

DEFAULT_BUILD_DIR := build
BUILD_DIR := $(shell if [ -f $(MAKE_FILE) ]; then echo "."; else echo $(DEFAULT_BUILD_DIR); fi)
CMAKE3 := $(shell if which cmake3>/dev/null ; then echo cmake3; else echo cmake; fi;)

.PHONY: $(ALL_TARGETS)

all:
	mkdir -p $(BUILD_DIR)
ifeq ($(DEBUG),y)
	cd $(BUILD_DIR) && $(CMAKE3) -D CMAKE_BUILD_TYPE=Debug $(ROOT_DIR)
else
	cd $(BUILD_DIR) && $(CMAKE3) $(ROOT_DIR)
endif
	$(MAKE) -C $(BUILD_DIR) -f Makefile

clean:
ifeq ($(MAKE_FILE), $(wildcard $(MAKE_FILE)))
	-$(MAKE) -f Makefile clean
else ifeq (build, $(wildcard build))
	-$(MAKE) -C build clean
endif
	rm -rf build



================================================
FILE: benchmark/README.md
================================================
# 性能测试

Sogou C++ Workflow是一款性能优异的网络框架,本文介绍我们进行的性能测试,
包括方案、代码、结果,以及与其他同类产品的对比。

更多场景下的实验正在进行中,本文将持续更新。

## HTTP Server

HTTP Client/Server是Sogou C++ Workflow常见的应用场景,
我们首先对Server端进行实验。

### 环境

我们部署了两台相同机器作为Server和Client,软硬件配置如下:

| 软硬件 | 配置 |
|:---:|:---|
| CPU | 40 Cores, x86_64, Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz |
| Memory | 192GB |
| NIC | 25000Mbps |
| OS | CentOS 7.8.2003 |
| Kernel | Linux version 3.10.0-1127.el7.x86_64 |
| GCC | 4.8.5 |

两者间`ping`测得的RTT为0.1ms左右。

### 对照组

我们选择nginx和brpc作为对照组。
选择前者是因为它在生产中部署十分广泛,性能不俗;
对于后者,我们在本次实验中只关注HTTP Server方面的能力,
其他的特性已有[单独的实验][Sogou RPC Benchmark]进行更为详尽的测试。

事实上,我们也对此二者之外的其他某些框架同时进行了实验,
但结果其性能表现相差较远,因此未在本文中体现。

后续我们将选取更多合适的框架加入对比测试中。

### Client工具

本次实验我们使用的压测工具为[wrk][wrk]和[wrk2][wrk2]。
前者适合测试特定并发下的QPS极限和延时,
后者适合在特定QPS下测试延时分布。

我们也尝试过使用其他测试工具,例如[ab][ab]等,但无法打出足够的压力。
有鉴于此,我们也在着手开发基于Sogou C++ Workflow的benchmark工具。

### 变量和指标

一般而言,对网络框架的性能测试,切入的角度可谓纷繁多样。
通过控制不同的变量、观测不同的指标,可以探究程序在不同场景下的适应能力。

本次实验,我们选择其中最普遍常见的变量和指标:
通过控制Client并发度和承载数据的大小,来测试QPS和延时的变化情况。
另外,我们还测试了在掺杂慢请求的正常请求的延时分布。

下面依次介绍两个测试场景。

### 测试方法

#### 启动http server
1. 编译benchmark
2. 进入到benchmark目录,执行 

```
./http_server 12 9000 11
```

说明: 启动参数分别为线程数、端口和响应的随机字符串长度。

### wrk测试

```
wrk --latency -d10 -c200 --timeout 8 -t 6 http://127.0.0.1:9000
```
**命令行解释**

-c200: 启动200个连接

-t6: 开启6个线程做压力测试

-d10: 压测持续10s

--timeout 8: 连接超时时间8s

### 不同并发度和数据长度下的QPS和延时

#### 代码和配置

我们搭建了一个极其简约的HTTP服务器,
忽略掉所有的业务逻辑,
将测试点聚焦在纯粹的网络框架性能上。

代码片段如下,
完整代码移步[这里][benchmark-01 Code]。

```cpp
// ...

auto * resp = task->get_resp();
resp->add_header_pair("Date", timestamp);
resp->add_header_pair("Content-Type", "text/plain; charset=UTF-8");
resp->append_output_body_nocopy(content.data(), content.size());

// ...
```

可以从上述代码中看到,
对于到来的任何HTTP请求,
我们都会返回一段固定的内容作为Body,
并设置必要的Header,
包括代码中指明的`content-type`、`date`,
以及自动填充的`connection`和`content-length`。

HTTP Body的固定内容是在Server启动时随机生成的ASCII字符串,
其长度可以通过启动参数配置。
同时可以配置的还有使用的poller线程数和监听的端口号。
前者我们在本次测试中固定为16,
因此Sogou C++ Workflow将使用16个poller线程和20个handler线程(默认配置)。

对于nginx和brpc,
我们也构建了相同的返回内容,
并为nginx配置了40个进程、
brpc配置了40个线程。


#### 变量

我们控制并发度在`[1, 2K]`之间翻倍增长,
数据长度在`[16B, 64KB]`之间翻倍增长,
两者正交。

#### 指标

鉴于并发度和数据长度组合之后数量较多,
我们选择其中部分数据绘制为曲线。

##### 固定数据长度下QPS与并发度关系

![Concurrency and QPS][Con-QPS]

上图可以看出,当数据长度保持不变,
QPS随着并发度提高而增大,后趋于平稳。
此过程中Sogou C++ Workflow一直有明显优势,
高于brpc和nginx。
特别是数据长度为64和512的两条曲线,
并发度足够的时候,可以保持500K的QPS。

注意上图中nginx-64与nginx-512的曲线重叠度很高,
不易辨识。

##### 固定并发度下QPS与数据长度关系

![Body Length and QPS][Len-QPS]

上图可以看出,当并发度保持不变,
随着数据长度的增长,
QPS保持平稳至4K时下降。
此过程中,Sogou C++ Workflow也一直保持优势。

##### 固定数据长度下延时与并发度关系

![Concurrency and Latency][Con-Lat]

上图可以看出,保持数据长度不变,
延时随并发度提高而有所上升。
此过程中,Sogou C++ Workflow略好于brpc,
大好于nginx。

##### 固定并发度下延时与数据长度关系

![Body Length and Latency][Len-Lat]

上图可以看出,并发度保持不变时,
增大数据长度,造成延时上升。
此过程中,Sogou C++ Workflow好于nginx,
好于brpc。

### 掺杂慢请求的延时分布

#### 代码

我们在上一个测试的基础上,简单添加了一个慢请求的逻辑,
模拟业务场景中可能出现的特殊情况。

代码片段如下,
完整代码请移步[这里][benchmark-02 Code]。

```cpp
// ...

if (std::strcmp(uri, "/long_req/") == 0)
{
    auto timer_task = WFTaskFactory::create_timer_task(microseconds, nullptr);
    series_of(task)->push_back(timer_task);
}
// ...
```

我们在Server的process里进行判断,
如果访问的是特定的路径,
则添加一个`WFTimerTask`到Series的末尾,
能够模拟一个异步耗时处理过程。
类似地,对brpc使用`bthread_usleep()`函数进行异步睡眠。

#### 配置

在本次实验中,我们固定并发度为1024,数据长度为1024字节,
分别以QPS为20K、100K和200K进行正常请求测试,
测绘延时;
与此同时,有另一路压力,进行慢请求,
QPS是上述QPS的1%,
数据不计入统计。
慢请求的时长固定为5ms。

#### 延时CDF图

![Latency CDF][Lat CDF]

从上图可以看出,当QPS为20K时,
Sogou C++ Workflow略次于brpc;
当QPS为100K时,两者几乎相当;
当QPS为200K时,Sogou C++ Workflow略好于brpc。
总之,可以认为两者在这方面旗鼓相当。


[Sogou RPC Benchmark]: https://github.com/holmes1412/sogou-rpc-benchmark
[wrk]: https://github.com/wg/wrk
[wrk2]: https://github.com/giltene/wrk2
[ab]: https://httpd.apache.org/docs/2.4/programs/ab.html
[benchmark-01 Code]: benchmark-01-http_server.cc
[benchmark-02 Code]: benchmark-02-http_server_long_req.cc
[Con-QPS]: https://raw.githubusercontent.com/wiki/sogou/workflow/img/benchmark-01.png
[Len-QPS]: https://raw.githubusercontent.com/wiki/sogou/workflow/img/benchmark-02.png
[Con-Lat]: https://raw.githubusercontent.com/wiki/sogou/workflow/img/benchmark-03.png
[Len-Lat]: https://raw.githubusercontent.com/wiki/sogou/workflow/img/benchmark-04.png
[Lat CDF]: https://raw.githubusercontent.com/wiki/sogou/workflow/img/benchmark-05.png


================================================
FILE: benchmark/benchmark-01-http_server.cc
================================================
#include <csignal>

#include <workflow/WFHttpServer.h>
#include <workflow/WFGlobal.h>
#include <workflow/WFFacilities.h>

#include "util/args.h"
#include "util/content.h"
#include "util/date.h"

static WFFacilities::WaitGroup wait_group{1};

void signal_handler(int)
{
	wait_group.done();
}

int main(int argc, char ** argv)
{
	size_t pollers;
	unsigned short port;
	size_t length;

	if (parse_args(argc, argv, pollers, port, length) != 3)
	{
		return -1;
	}

	std::signal(SIGINT, signal_handler);
	std::signal(SIGTERM, signal_handler);

	WFGlobalSettings settings = GLOBAL_SETTINGS_DEFAULT;
	settings.poller_threads = pollers;
	WORKFLOW_library_init(&settings);

	const std::string content = make_content(length);
	WFHttpServer server([&content](WFHttpTask * task)
	{
		auto * resp = task->get_resp();

		char timestamp[32];
		date(timestamp, sizeof(timestamp));
		resp->add_header_pair("Date", timestamp);

		resp->add_header_pair("Content-Type", "text/plain; charset=UTF-8");

		resp->append_output_body_nocopy(content.data(), content.size());
	});

	if (server.start(port) == 0)
	{
		wait_group.wait();
		server.stop();
	}

	return 0;
}



================================================
FILE: benchmark/benchmark-02-http_server_long_req.cc
================================================
#include <csignal>
#include <cstring>

#include <workflow/WFHttpServer.h>
#include <workflow/WFGlobal.h>
#include <workflow/WFFacilities.h>

#include "util/args.h"
#include "util/content.h"
#include "util/date.h"

static WFFacilities::WaitGroup wait_group{1};

void signal_handler(int)
{
	wait_group.done();
}

int main(int argc, char ** argv)
{
	size_t pollers;
	unsigned short port;
	size_t length;
	size_t microseconds;

	if (parse_args(argc, argv, pollers, port, length, microseconds) != 4)
	{
		return -1;
	}

	std::signal(SIGINT, signal_handler);
	std::signal(SIGTERM, signal_handler);

	WFGlobalSettings settings = GLOBAL_SETTINGS_DEFAULT;
	settings.poller_threads = pollers;
	WORKFLOW_library_init(&settings);

	const std::string content = make_content(length);
	WFHttpServer server([&content, &microseconds](WFHttpTask * task)
	{
		auto resp = task->get_resp();

		char timestamp[32];
		date(timestamp, sizeof(timestamp));
		resp->add_header_pair("Date", timestamp);

		resp->add_header_pair("Content-Type", "text/plain; charset=UTF-8");

		resp->append_output_body_nocopy(content.data(), content.size());

		auto req = task->get_req();
		auto uri = req->get_request_uri();
		if (std::strcmp(uri, "/long_req/") == 0)
		{
			auto timer_task = WFTaskFactory::create_timer_task(microseconds, nullptr);
			series_of(task)->push_back(timer_task);
		}
	});

	if (server.start(port) == 0)
	{
		wait_group.wait();
		server.stop();
	}

	return 0;
}



================================================
FILE: benchmark/util/args.h
================================================
#ifndef _BENCHMARK_ARGS_H_
#define _BENCHMARK_ARGS_H_

#include <algorithm>
#include <numeric>
#include <string>

namespace details
{
	inline bool extract(const char * p, size_t & t)
	{
		char * e;
		long long ll = std::strtoll(p, &e, 0);
		if (*e || ll < 0)
		{
			return false;
		}
		t = static_cast<size_t>(ll);
		return true;
	}

	inline bool extract(const char * p, unsigned short & t)
	{
		char * e;
		long long ll = std::strtoll(p, &e, 0);
		if (*e
		    || ll < static_cast<long long>(std::numeric_limits<unsigned short>::min())
		    || ll > static_cast<long long>(std::numeric_limits<unsigned short>::max())
			)
		{
			return false;
		}
		t = static_cast<unsigned short>(ll);
		return true;
	}

	inline bool extract(const char * p, std::string & t)
	{
		t = p;
		return true;
	}

	inline bool extract(const char * p, const char *& t)
	{
		t = p;
		return true;
	}

	template <typename ARG>
	inline int parse_one(bool & flag, char **& p, char ** end, ARG & arg)
	{
		if (flag && (flag = p < end) && (flag = extract(*p, arg)))
		{
			p++;
		}
		return 0;
	}

	template <typename ... ARGS>
	inline size_t parse_all(char ** begin, char ** end, ARGS & ... args)
	{
		bool flag = true;
		char ** p = begin;
		static_cast<void>(std::initializer_list<int>{parse_one(flag, p, end, args) ...});
		return p - begin;
	}

	template <typename ... ARGS>
	inline size_t parse_args(int & argc, char ** argv, ARGS & ... args)
	{
		if (argc <= 1)
		{
			return 0;
		}

		size_t length = argc - 1;
		char ** begin = argv + 1;
		char ** end = begin + length;

		size_t done = parse_all(begin, end, args ...);
		std::rotate(begin, begin + done, end);
		std::reverse(end - done, end);

		argc -= done;
		return done;
	}
}

template <typename ... ARGS>
inline static size_t parse_args(int & argc, char ** argv, ARGS & ... args)
{
	return details::parse_args(argc, argv, args ...);
}

#endif //_BENCHMARK_ARGS_H_


================================================
FILE: benchmark/util/content.h
================================================
#ifndef _BENCHMARK_CONTENT_H_
#define _BENCHMARK_CONTENT_H_

#include <random>
#include <string>

static inline std::string make_content(size_t length)
{
	std::mt19937_64 gen{42};
	std::uniform_int_distribution<int> dis{32, 126};

	std::string s;
	s.reserve(length);
	for (size_t i = 0; i < length; i++)
	{
		s.push_back(static_cast<char>(dis(gen)));
	}
	return s;
}

#endif //_BENCHMARK_CONTENT_H_


================================================
FILE: benchmark/util/date.h
================================================
#ifndef _BENCHMARK_DATE_H_
#define _BENCHMARK_DATE_H_

#include <ctime>

static inline void date(char * buf, size_t n)
{
	auto tt = std::time(nullptr);
	std::tm cur{};
	// gmtime_r(&tt, &cur);
	localtime_r(&tt, &cur);
	strftime(buf, n, "%a, %d %b %Y %H:%M:%S %Z", &cur);
}

#endif //_BENCHMARK_DATE_H_


================================================
FILE: benchmark/xmake.lua
================================================
set_group("benchmark")
set_default(false)

add_deps("workflow")

if not is_plat("macosx") then
    add_ldflags("-lrt")
end

function all_benchs()
    local res = {}
    for _, x in ipairs(os.files("**.cc")) do
        local item = {}
        local s = path.filename(x)
        table.insert(item, s:sub(1, #s - 3))       -- target
        table.insert(item, path.relative(x, "."))  -- source
        table.insert(res, item)
    end
    return res
end

for _, bench in ipairs(all_benchs()) do
target(bench[1])
    set_kind("binary")
    add_files(bench[2])
end


================================================
FILE: docs/about-conditional.md
================================================
# 条件任务与观察者模式

有的时候,我们需要让任务在某个条件下才被执行。条件任务(WFConditional)就是用于解决这种问题。  
条件任务是一种任务包装器,可以包装任何的任务并取代原任务。通过对条件任务发送信号来触发被包装任务的执行。  

# 条件任务的创建
在[WFTaskFactory.h](/src/factory/WFTaskFactory.h)里,可以看到条件任务的创建接口。
~~~cpp
class WFTaskFactory
{
public:
    static WFConditional *create_conditional(SubTask *task);
    static WFConditional *create_conditional(SubTask *task, void **msgbuf);
};
~~~
可以看到,我们通过工厂的create_conditional接口创建条件任务。  
其中,task为被包装的任务。msgbuf是用于接收消息的缓冲区,如果无需关注消息的具体内容,msgbuf可以缺省。  
WFConditional的主要接口:
~~~cpp
class WFConditional : public WFGenericTask
{
public:
    virtual void signal(void *msg);
    ...
};
~~~
WFConditional是一种任务,所以,它满足普通workflow任务的一切属性。特别的接口只有signal,用于发送信号。  

# 示例

以下示例,通过timer和conditional,实现一个延迟1秒执行的计算任务。
~~~cpp
int main()
{
    WFGoTask *task = WFTaskFactory::create_go_task("test", [](){ printf("Done\n"); });
    WFConditional *cond = WFTaskFactory::create_conditional(task);
    WFTimerTask *timer = WFTaskFactory::create_timer_task(1, 0, [cond](void *){
        cond->signal(NULL);
    });
    timer->start();
    cond->start();
    getchar();
}
~~~
这个示例里,在定时器的回调里向cond发送信号,让被包装的go task可以被执行。  
注意,无论cond->signal()与cond->start()哪一个先被调用,程序都完全正确。  

# 观察者模式

我们看到,如果直接对cond发送信息,需要发送者直接持有cond的指针,这在一些情况下并不是很方便。  
于是,我们引入了观察者模式,也就是命名的条件任务。通过向某个名称发送信号,同时唤醒所有在这个名称下的条件任务。  
命名条件任务的创建与唤醒:
~~~cpp
class WFTaskFactory
{
public:
    static WFConditional *create_conditional(const std::string& cond_name, SubTask *task);
    static WFConditional *create_conditional(const std::string& cond_name, SubTask *task, void **msgbuf);
    static int signal_by_name(const std::string& cond_name, void *msg);
    static int signal_by_name(const std::string& cond_name, void *msg, size_t max);
    template<typename T>
    static int signal_by_name(const std::string& cond_name, T *const msg[], size_t max);
};
~~~
我们看到,与普通条件任务唯一区别是,命名条件任务创建时,需要传入一个cond_name。  
而signal_by_name()接口,默认将msg发送到所有在这个名称上等待的条件任务,将它们全部唤醒。  
也可以通过max参数指定唤醒的最大任务数。此时,msg还可以是一个指针数组,可给不同的条件任务发送不同的消息。  
任何一个signal_by_name的重载函数,其返回值都是表示实际唤醒的条件任务个数。  
这就相当于实现了观察者模式。  

# 示例
还是上面的延迟计算示例,我们增加到两个计算任务并用观察者模式来实现。用"slot1"作为条件任务名。
~~~cpp
int main()
{
    WFGoTask *task1 = WFTaskFactory::create_go_task("test", [](){ printf("test1 done\n"); });
    WFGoTask *task2 = WFTaskFactory::create_go_task("test", [](){ printf("test2 done\n"); });
    WFConditional *cond1 = WFTaskFactory::create_conditional("slot1", task1);
    WFConditional *cond2 = WFTaskFactory::create_conditional("slot1", task2);
    WFTimerTask *timer = WFTaskFactory::create_timer_task(1, 0, [](void *){
        WFTaskFactory::signal_by_name("slot1", NULL);
    });
    timer->start();
    cond1->start();
    cond2->start();
    getchar();
}
~~~
我们看到,在这个示例里,timer在回调中通过signal_by_name方法,同时唤醒了slot1下两个计算任务。  

# 使用条件任务注意事项

Workflow里的任何任务,如果创建之后不想运行,都可以通过dismiss接口直接释放。  
对于条件任务,如果要被dismiss(或者在某个被cancel的series里),必须保证这个条件任务没有被signal过。
以下代码的行为无定义:
~~~cpp
int main()
{
    WFEmptyTask *task = WFTaskFactory::create_empty_task();
    WFConditional *cond = WFTaskFactory::create_conditional("slot1", task);
    WFTimerTask *timer = WFTaskFactory::create_timer_task(0, 0, [](void *) {
        WFTaskFactory::signal_by_name("slot1");
    });
    timer->start();
    cond->dismiss();  // 取消任务
    getchar();
}
~~~
显然,如果timer的callback里已经执行或正在执行了signal_by_name,cond被signal,再dismiss()是一种错误行为。  
这种情况一般也只会出现在命名条件任务里。所以,dismiss一个命名条件任务,需要特别的小心。  


================================================
FILE: docs/about-config.md
================================================
# 关于全局配置

全局配置用于配置全局默认参数,以适应的实际业务需求,提升程序性能。
全局配置的修改必须在使用框架任何调用之前,否则修改可能无法生效。
另外,一些全局配置选项,可以被upstream配置覆盖。这部分请参考upstream相关文档。

# 修改默认配置

在[WFGlobal.h](../src/manager/WFGlobal.h)里,包含了全局配置的结构体与默认值:
~~~cpp
struct WFGlobalSettings
{
    struct EndpointParams endpoint_params;
    struct EndpointParams dns_server_params;
    unsigned int dns_ttl_default;   ///< in seconds, DNS TTL when network request success
    unsigned int dns_ttl_min;       ///< in seconds, DNS TTL when network request fail
    int dns_threads;
    int poller_threads;
    int handler_threads;
    int compute_threads;            ///< auto-set by system CPU number if value<=0
    int fio_max_events;
    const char *resolv_conf_path;
    const char *hosts_path;
};


static constexpr struct WFGlobalSettings GLOBAL_SETTINGS_DEFAULT =
{
    .endpoint_params    =   ENDPOINT_PARAMS_DEFAULT,
    .dns_server_params  =   ENDPOINT_PARAMS_DEFAULT,
    .dns_ttl_default    =   12 * 3600,
    .dns_ttl_min        =   180,
    .dns_threads        =   4,
    .poller_threads     =   4,
    .handler_threads    =   20,
    .compute_threads    =   -1,
    .fio_max_events     =   4096,
    .resolv_conf_path   =   "/etc/resolv.conf",
    .hosts_path         =   "/etc/hosts",
};
~~~

其中EndpointParams结构体和默认值在[EndpointParams.h](../src/manager/EndpointParams.h)文件里:

~~~cpp

struct EndpointParams
{
    size_t max_connections;
    int connect_timeout;
    int response_timeout;
    int ssl_connect_timeout;
    bool use_tls_sni;
};

static constexpr struct EndpointParams ENDPOINT_PARAMS_DEFAULT =
{
    .max_connections        = 200,
    .connect_timeout        = 10 * 1000,
    .response_timeout       = 10 * 1000,
    .ssl_connect_timeout    = 10 * 1000,
    .use_tls_sni            = false,
};
~~~

举个例子,把默认的连接超时改为5秒,dns默认ttl改为1小时,用于消息反序列化的poller线程增加到10个:

~~~cpp
#include "workflow/WFGlobal.h"

int main()
{
    struct WFGlobalSettings settings = GLOBAL_SETTINGS_DEFAULT;

    settings.endpoint_params.connect_timeout = 5 * 1000;
    settings.dns_ttl_default = 3600;
    settings.poller_threads = 10;
    WORKFLOW_library_init(&settings);

    ...
}
~~~

大多数参数的意义都比较清晰。注意dns ttl相关参数,单位是**秒**。endpoint相关超时参数单位是**毫秒**,并且可以用-1表示无限。  
dns_threads表示并行访问dns的线程数。但目前我们默认使用我们自己的异步DNS解析,所以并不会创建DNS线程(Window平台除外)。  
dns_server_params表示是我们访问DNS server的参数,包括最大并发连接,以及连接与响应超时。  
compute_threads表示用于计算的线程数,默认-1代表与当前节点CPU核数相同。  
fio_max_events是异步文件IO的最大并发事件数。
resolv_conf_path是dns配置文件的路径,unix平台下默认为"/etc/resolv.conf"。Windows下默认为NULL,将使用多线程dns解析。  
hosts_path是hosts文件路径。unix平台下默认为"/etc/hosts“。只有配置了resolv_conf_path,这个配置才起作用。  

与网络性能相关的两个参数为poller_threads和handler_threads:
* poller线程主要负责epoll(kqueue)和消息反序列化。
* handler线程是网络任务callback和process所在线程。

所有框架需要的资源,都是在第一次被使用时才申请的。例如用户没有用到dns解析,那么异步dns解析器或dns线程不会被启动。  


================================================
FILE: docs/about-connection-context.md
================================================
# 关于连接上下文

连接上下文是使用本框架编程的一个高级课题。使用上会有一些复杂性。  
从之前的示例里可以看出,无论是client还是server任务,我们并没有手段指定使用的具体连接。  
但是有一些业务场景,特别是server端,可能是需要维护连接状态的。也就是说我们需要把一段上下文和连接绑定。  
我们的框架里,是提供了连接上下文机制给用户使用的。  

# 连接上下文的应用场景

http协议可以说是一种完全无连接状态的协议,http会话,是通过cookie来实现的。这种协议对于我们的框架最友好。类似的还有kafka。  
而redis和mysql的连接则是明显带状态,redis通过SELECT命令,指定当前连接上的数据库ID。mysql则是一个彻彻底底的有状态连接。  
使用框架的redis或非事务mysql client任务时,由于URL里已经包含了所有和连接选择有关的信息,包括:
* 用户名密码
* 数据库名或数据库号
* mysql的字符集

框架会根据这些信息自动登录和选择可复用的连接,用户无需关心连接上下文的问题。  
这也是为什么,框架里redis的SELECT命令和mysql的USE命令是禁止用户使用的,切换数据库需要用新的URL创建任务。  
事务型mysql,可以固定连接,这部分内容请参考mysql相关文档。  
但是,如果我们实现一个redis协议的server,那我们需要知道当前连接上的状态了。  

此外,我们还可以通过连接上下文件被释放的事件来感知连接被远端关闭。

# 使用连接上下文的方法

我们需要强调的是,一般情况下只有server任务需要使用连接上下文,并且只需要在process函数内部使用,这也是最安全最简单的用法。  
但是,任务在callback里也可以使用或修改连接上下文,只是使用的时候需要考虑并发的问题。我们会详细地讨论相关问题。    
任何网络任务都可以调用接口获得连接对象,进而获得或修改连接上下文。在[WFTask.h](../src/factory/WFTask.h)里,调用如下:
~~~cpp
template<class REQ, class, RESP>
class WFNetworkTask : public CommRequest
{
public:
    virtual WFConnection *get_connection() const = 0;
    ...
};
~~~
文件[WFConneciton.h](../src/factory/WFConnection.h)里,包含了对连接对象的操作接口:
~~~cpp
class WFConnection : public CommConnection
{
public:
    void *get_context() const;
    void set_context(void *context, std::function<void (void *)> deleter);
    void set_context(void *context);
    void *test_set_context(void *test_context, void *new_context,
                           std::function<void (void *)> deleter);
    void *test_set_context(void *test_context, void *new_context);
};
~~~
get_connection()只可在process或callback里调用,而且如果callback里调用,需要检查返回值是否为NULL。  
如果成功取得WFConnection对象,就可以操作连接上下文了。连接上下文是一个void *指针。  
设置连接上下文可以同时传入deleter函数,在连接被关闭时,deleter被自动调用。    
如果调用无deleter参数的接口,可以只设置新的上下文,保持原有的deleter不变。  

# 访问连接上下文的时机和并发问题

client task被创建的时候,连接对象没有确定,因此所有client task对连接上下文的使用只有在callback里。  
server task可能在两个地方使用连接上下文,process和callback。  
在callback里使用连接上下文时,需要考虑并发问题,因为同一个连接,会被多个task复用,并且同时运行到callback。  
所以,我们推荐只process函数里访问或修改连接上下文,process过程中连接不会被复用或释放,是最简单安全的方法。  
注意,我们指的process只包括process函数内部,在process函数结束后,callback之前,get_connection调用一律返回NULL。  
WFConnection的test_set_context(),就是为了解决callback里使用连接上下文是的并发问题,但我们不推荐使用。  
总之,如果你不是对系统实现非常了解,请只在server task的process函数里使用连接上下文。  

# 示例:减少Http/1.1的请求header传输

http协议可以说是一个连接无状态的协议,同一个连接上,每一次请求都必须发送完整的header。  
假设请求里的cookie非常大,那么这显然就增加了很大的数据传输量。我们可以通过server端连接上下文来解决这个问题。  
我们约定http request里的cookie,对本连接上所有后续请求有效,后续请求header里可以不再发送cookie。  
以下是server端代码:
~~~cpp
void process(WFHttpTask *server_task)
{
    protocol::HttpRequest *req = server_task->get_req();
    protocol::HttpHeaderCursor cursor(req);
    WFConnection *conn = server_task->get_connection();
    void *context = conn->get_context();
    std::string cookie;

    if (cursor.find("Cookie", cookie))
    {
        if (context)
            delete (std::string *)context;
        context = new std::string(cookie);
        conn->set_context(context, [](void *p) { delete (std::string *)p; });
    }
    else if (context)
        cookie = *(std::string *)context;

    ...
}
~~~
通过这种方式,与client端约定好每次只在连接的第一个请求传输cookie,就可以实现流量的节省。  
client端的实现需要用到一个新的回调函数,用法如下:  
~~~cpp

using namespace protocol;

void prepare_func(WFHttpTask *task)
{
    if (task->get_task_seq() == 0)
        task->get_req()->add_header_pair("Cookie", my_cookie);
}

int some_function()
{
    WFHttpTask *task = WFTaskFactory::create_http_task(...);
    task->set_prepare(prepare_func);
    ...
}
~~~
在这个示例中,当http task是连接上的首个请求时,我们设置了cookie。如果不是首个请求,根据约定,不再设置cookie。  
另外,prepare函数里,可以安全的使用连接上下文。同一个连接上,prepare不会并发。


================================================
FILE: docs/about-counter.md
================================================
# 关于计数器

计数器是我们框架中一种非常重要的基础任务,计数器本质上是一个不占线程的信号量。  
计数器主要用于工作流的控制,包括匿名计数器和命名计数器两种,可以实现非常复杂的业务逻辑。  

# 计数器的创建

由于计数器也是一种任务,它的创建同样通过WFTaskFactory来完成,包括两种创建方法:
~~~cpp
using counter_callback_t = std::function<void (WFCounterTask *)>;

class WFTaskFactory
{
    ...
    static WFCounterTask *create_counter_task(unsigned int target_value,
                                              counter_callback_t callback);
                                              
    static WFCounterTask *create_counter_task(const std::string& counter_name,
                                              unsigned int target_value,
                                              counter_callback_t callback);

    ...
};
~~~
每个计数器都包含一个target_value,当计数器的计数到达target_value,callback被调用。  
以上两个接口分别产生匿名计数器和命名计数器,匿名计数器直接通过WFCounterTask的count方法来增加计数:  
~~~cpp
class WFCounterTask
{
public:
    virtual void count()
    {
        ...
    }
    ...
}
~~~
如果创建计数器时,传入一个counter_name,则产生一个命名计数器,可以通过count_by_name函数来增加计数。  

# 用匿名计数器实现任务并行

在[并行抓取](./tutorial-06-parallel_wget.md)的示例中,我们通过创建一个ParallelWork来实现多个series并行。  
通过ParallelWork和SeriesWork的组合,可以构建任意的串并连图,已经可以满足大多数应用场景需求。  
而计数器的存在,可以让我们构建更复杂的任务依赖关系,比如实现一个全连接的神经网络。  
以下简单的代码,可代替ParallelWork,实现一个并行的http抓取。  
~~~cpp
void http_callback(WFHttpTask *task)
{
    /* Save http page. */
    ...

    WFCounterTask *counter = (WFCounterTask *)task->user_data;
    counter->count();
}

std::mutex mutex;
std::condition_variable cond;
bool finished = false;

void counter_callback(WFCounterTask *counter)
{
    mutex.lock();
    finished = true;
    cond.notify_one();
    mutex.unlock();
}

int main(int argc, char *argv[])
{
    WFCounterTask *counter = create_counter_task(url_count, counter_callback);
    WFHttpTask *task;
    std::string url[url_count];

    /* init urls */
    ...

    for (int i = 0; i < url_count; i++)
    {
        task = create_http_task(url[i], http_callback);
        task->user_data = counter;
        task->start();
    }

    counter->start();
    std::unique_lock<std:mutex> lock(mutex);
    while (!finished)   
        cond.wait(lock);
    lock.unlock();
    return 0;
}
~~~
以上创建一个目标值为url_count的计数器,每个http任务完成之后,调用一次count。  
注意,匿名计数器的count次数不可以超过目标值,否则counter可能已经callback销毁了,程序行为无定义。  
counter->start()调用可以放在for循环之前。counter只要被创建,就可以调用其count接口,无论counter是否已经启动。  
匿名计数器的count接口调用,也可以写成counter->WFCounterTask::count(); 在非常注重性能的应用下可以这么用。  

# Server与其它异步引擎结合使用

某些情况下,我们的server可能需要调用非本框架的异步客户端等待结果。简单的方法我们可以在process里同步等待,通过条件变量来唤醒。  
这么做的缺点是我们占用了一个处理线程,把其它框架的异步客户端变为同步客户端。但通过counter,我们可以不占线程地等待。  
方法很简单:
~~~cpp

void some_callback(void *context)
{
    protocol::HttpResponse *resp = get_resp_from_context(context);
    WFCounterTask *counter = get_counter_from_context(context);
    /* write data to resp. */
    ...
    counter->count();
}

void process(WFHttpTask *task)
{
    WFCounterTask *counter = WFTaskFactory::create_counter_task(1, nullptr);

    SomeOtherAsyncClient client(some_callback, context);

    *series_of(task) << counter;
}
~~~
在这里,我们可以把server任务所在的series理解为一个协程,而目标值为1的counter,可以理解为一个条件变量。  
Counter的缺点是count操作不传递数据。如果业务有数据传达的需求,可以使用[Mailbox任务](https://github.com/sogou/workflow/blob/master/src/factory/WFTaskFactory.h#L268)。  

# 命名计数器

对匿名计数器进行count操作时,直接访问了counter对象指针。这就必然要求在操作时,调用count的次数不超过目标值。  
但想象这样一个应用场景,我们同时启动4个任务,只要其中有任意3个任务完成,工作流就可以继续进行。  
我们可以用一个目标值为3的计数器,每个任务完成之后,count一次,这样只要任务3个任务完成,计数器就被callback。  
但这样的问题是,当第4个任务完成,再调用counter->count()的时候,计数器已经是一个野指针了,程序崩溃。  
这时候我们可以用命名计数器来解决这个问题。通过给计数器命名,并通过名字来计数,例如以下实现:
~~~cpp
void counter_callback(WFCounterTask *counter)
{
    WFRedisTask *next = WFTaskFactory::create_redis_task(...);
    series_of(counter)->push_back(next);
}

int main(void)
{
    WFHttpTask *tasks[4];
    WFCounterTask *counter;

    counter = WFTaskFactory::create_counter_task("c1", 3, counter_callback);
    counter->start();

    for (int i = 0; i < 4; i++)
    {
        tasks[i] = WFTaskFactory::create_http_task(..., [](WFHttpTask *task){
                                            WFTaskFactory::count_by_name("c1"); });
        tasks[i]->start();
    }

    ...

}
~~~
这个示例中,调起4个并发的http任务,其中3个完成了,立刻启动一个redis任务。实际应用中,可能还需要加入数据传递的代码。  
示例中创建命名为"c1"的计数器,在http回调里,使用WFTaskFactory::count_by_name()调用来进行计数。
~~~cpp
class WFTaskFactory
{
    ...
    static int count_by_name(const std::string& counter_name);

    static int count_by_name(const std::string& counter_name, unsigned int n);
    ...
};
~~~
WFTaskFactory::count_by_name方法还可以传入一个整数n,表示这一次操作要增加的计数值,显然:  
count_by_name("c1")等价于count_by_name("c1", 1)。  
如果"c1"计数器不存在(未创建或已经完成),那么对"c1"的操作不产生任何效果,因此不会有匿名计数器野指针的问题。  
函数的返回值表示被唤醒的计数器个数。当n大于1时,count_by_name操作可能让多个计数器达到目标值。  

# 命名计数器详细行为定义

调用WFTaskFactory::count_by_name(name, n)的时候:
* 如果name不存在(未创建或已经完成),无任何行为。
* 如果只有一个名字为name的计数器:
  * 如果该计数器剩余的值小于或等于n,计数完成,callback被调用,该计数器被销毁。结束。
  * 如果计数器剩余值大于n,则计数值加n。结束。
* 如果存在多个同名为name的计数器:
  * 按照创建顺序,取第一个计数器,假设其剩余值为m:
      * 如果m值大于n,则计数加n。结束(剩余值为m-n)。
      * 如果m小于或等于n,计数完成,callback被调用,第一个计数器被销毁。置n=n-m。
          * 如果n为0,结束。
          * 如果n大于0,再取出下一个同名计数器,重复整个的操作。

虽然描述很复杂,但总结起来就一句话,按照创建顺序,依次访问所有名字为name的计数器,直到n为0。  
也就是说,一次count_by_name(name, n)可以唤醒多个计数器。  
用好计数器,可以实现非常复杂的业务逻辑。计数器在我们框架里,往往用于实现异步锁,或者用于任务之间的通道。形态上更像一种控制任务。  


================================================
FILE: docs/about-dns.md
================================================
# 关于DNS
当使用域名请求网络时,首先需要通过域名解析获取服务器地址,再使用网络地址进行后续的请求。Workflow已经实现了完备的域名解析和缓存系统,通常来说用户无需知晓内部机制即可流畅地发起网络任务。

## DNS相关配置
Workflow中的全局配置包括

~~~cpp
struct WFGlobalSettings
{
    struct EndpointParams endpoint_params;
    struct EndpointParams dns_server_params;
    unsigned int dns_ttl_default;
    unsigned int dns_ttl_min;
    int dns_threads;
    int poller_threads;
    int handler_threads;
    int compute_threads;
    int fio_max_events;
    const char *resolv_conf_path;
    const char *hosts_path;
};
~~~

其中与域名解析相关的配置项有

* dns_server_params
  * address_family: 该项会在后续展开说明
  * max_connections: 向DNS服务器发送请求的最大并发数,默认为200
  * connect_timeout/response_timeout/ssl_connect_timeout: 参考[超时](about-timeout.md)相关说明
* dns_threads: 当使用同步方式实现域名解析时,解析操作会在独立的线程池中执行,该项指定线程池的线程数,默认为4
* dns_ttl_default: 域名解析成功的结果会被放到域名缓存中,该项指定其存活时间,单位为秒,默认值1小时,当解析结果过期后会重新解析以获取最新内容
* dns_ttl_min: 当通信失败时,有可能出现缓存的结果已经失效的情况,该项指定一个较短的存活时间,当通信失败时以更频繁的速率更新缓存,单位为秒,默认值1分钟
* resolv_conf_path: 该文件保存了访问DNS相关的配置,在常见的Linux发行版上通常位于`/etc/resolv.conf`,若该项配置为`NULL`则表示使用多线程同步解析的模式
* hosts_path: 该文件是一个本地的域名查找表,若被解析的域名命中该表则不会向DNS发起请求,在常见的Linux发行版上通常位于`/etc/hosts`,若该项配置为`NULL`则表示不使用查找表

### resolv.conf扩展功能
Workflow对`resolv.conf`配置文件进行了扩展,用户可以通过修改配置以支持`DNS over TLS(DoT)`功能,**注意**直接修改`/etc/resolv.conf`会影响其他进程,可以将该文件复制一份用于修改,并将Workflow的`resolv_conf_path`配置修改为新文件的路径。例如使用`dnss`协议的`nameserver`会通过SSL进行连接

~~~bash
nameserver dnss://8.8.8.8/
nameserver dnss://[2001:4860:4860::8888]/
~~~

### Address Family
在某些网络环境下,虽然本机支持IPv6,但因未被分配公网IPv6地址而无法与外部通信(例如本地IPv6地址以`fe80`开始)。此时可以将`endpoint_params.address_family`设置为`AF_INET`来强制域名解析时仅解析IPv4地址。同样的,`resolv.conf`文件中可能同时指定了`nameserver`的IPv4地址和IPv6地址,此时可以将`dns_server_params.address_family`设置为`AF_INET`或`AF_INET6`来强制仅使用IPv4或IPv6地址来访问DNS。

### 使用Upstream配置
全局配置默认对每个域名生效,若需要对某些域名单独指定不同的配置,则可使用[Upstream](./about-upstream.md#Address属性)功能。使用Upstream可以单独指定`dns_ttl_default`、`dns_ttl_min`配置项,以及通过`endpoint_params.address_family`单独指定该域名使用的IP地址类别。


## 域名解析与缓存策略
网络任务通常需要通过域名解析获取到需要访问的IP地址,Workflow中域名解析相关策略如下

1. 检查域名缓存是否有该域名对应的IP地址,若有缓存且未过期,则使用该组IP地址
2. 检查域名是否为IPv4、IPv6地址或`Unix Domain Socket`,若是则直接使用该地址,无需发起域名解析
3. 检查`hosts_path`文件中是否包含该域名对应的IP地址,若有则直接使用该地址
4. 获取异步锁,保证同一域名的解析请求在同一时刻仅发起一次,并向DNS发起解析请求
5. 解析成功后会将解析结果保存到当前进程的域名缓存中,以供下次使用,并释放异步锁
6. 解析失败后会释放异步锁且将失败原因通知给等在同一个异步锁上的所有任务,通知结束后再发起的新的任务则会再次请求DNS

许多需要大量发起网络请求的场景都会配备域名缓存组件,如果每次发起网络任务时都向DNS发起解析请求,则DNS必然会不堪重负。Workflow设置了缓存存活时长(dns_ttl_default和dns_ttl_min)来保证缓存会在合理的时间后过期,以及时更新域名的解析结果。当某个域名的缓存项过期后,首先发现过期的任务会将其存活时间延长5秒并向DNS发起解析请求,5秒内同一域名上的请求会直接使用缓存的DNS解析结果,而无需等待本次解析结束。

异步锁机制可以保证**同一域名**的解析请求在同一时刻仅发起一次,在没有锁保护的情况下,若短时间内对同一域名发起大量网络任务,每个任务都会因无法从缓存中获取结果而向DNS发起解析请求,这会对DNS带来很大且不必要的负担。这里的同一域名表示的是`(host, port, family)`三元组,若通过Upstream的方式对某域名分别要求只使用IPv4和IPv6,则他们会被不同的异步锁保护,也就有可能同时发起DNS请求。


### 异步域名解析
Workflow实现了完备的DNS任务(参考[dns_cli](./tutorial-17-dns_cli.md)),若指定了`resolv_conf_path`配置项,则向DNS发起域名解析时会使用异步请求的方式进行,在类Unix系统下,Workflow默认使用`/etc/resolv.conf`作为该配置的值。异步域名解析不会阻塞任何线程,也不会独占线程池,可以更高效地完成域名解析的任务。

### 同步域名解析
若指定`resolv_conf_path`为`NULL`,则会通过调用`getaddrinfo`函数来实现同步域名解析,该方式会使用独立的线程池,其线程数通过`dns_threads`参数配置。若短时间内需要发起较多的域名解析请求,则同步的方式会带来较大的延迟。


================================================
FILE: docs/about-error.md
================================================
# 关于错误处理

任何软件系统里,错误处理都是一个重要而复杂的问题。在我们框架内部,错误处理可以说是无处不在并且极其繁琐的。  
而在我们暴露给用户的接口里,我们尽可能地让事情变简单,但用户还是不可避免地需要了解一些错误信息。

### 禁用C++异常

我们框架内不使用C++异常,用户编译自己代码的时候,最好也加上-fno-exceptions标志,以减少代码大小。  
参考业界通用做法,我们会忽略new操作失败的可能,并且内部也避免用new去分配大块内存。而C语言风格的内存分配则是有查错的。  

### 关于工厂函数

从之前的实例中我们看到,所有的task,series都是从WFTaskFactory或Workflow这两个工厂类产生的。  
这些工厂类,以及我们以后可能遇到的更多的工厂类接口,都是确保成功的。也就是说,一定不会返回NULL。用户无需对返回值做检查。  
为了达到这个目的,当URL不合法时,工厂也能正常产生task。并且在任务的callback里再得到错误。

### 任务的状态和错误码

在之前的示例里,我们经常在callback里看到这样的代码:
~~~cpp
void callback(WFXxxTask *task)
{
    int state = task->get_state();
    int error = task->get_error();
    ...
}
~~~
其中,state代表任务的结束状态,在[WFTask.h](../src/factory/WFTask.h)文件中,可以看到所有可能的状态值:
~~~cpp
enum
{
    WFT_STATE_UNDEFINED = -1,
    WFT_STATE_SUCCESS = CS_STATE_SUCCESS,
    WFT_STATE_TOREPLY = CS_STATE_TOREPLY,        /* for server task only */
    WFT_STATE_NOREPLY = CS_STATE_TOREPLY + 1,    /* for server task only */
    WFT_STATE_SYS_ERROR = CS_STATE_ERROR,
    WFT_STATE_SSL_ERROR = 65,
    WFT_STATE_DNS_ERROR = 66,                    /* for client task only */
    WFT_STATE_TASK_ERROR = 67,
    WFT_STATE_ABORTED = CS_STATE_STOPPED         /* main process terminated */
};
~~~
##### 需要关注的几个状态:
  * SUCCESS:任务成功。client接收到完整的回复,或server把回复完全写进入发送缓冲(但不能确保对方一定能收到)。
  * SYS_ERROR: 系统错误。这种情况,task->get_error()得到的是系统错误码errno。
    * 当get_error()得到ETIMEDOUT,可以调用task->get_timeout_reason()进一步得到超时原因。
  * DNS_ERROR: DNS解析错误。get_error()得到的是getaddrinfo()调用的返回码。关于DNS,有一篇文档专门说明[about-dns.md](./about-dns.md)。
    * server任务永远不会有DNS_ERROR。
  * SSL_ERROR: SSL错误。get_error()得到的是SSL_get_error()的返回值。
    * 目前SSL错误信息没有做得很全,得不到ERR_get_error()的值。所以,基本上get_error()返回值也就三个可能:
      * SSL_ERROR_ZERO_RETURN, SSL_ERROR_X509_LOOKUP, SSL_ERROR_SSL。
    * 更加详细的SSL错误信息,我们在后续版本会考虑加入。
  * TASK_ERROR: 任务错误。常见的例如URL不合法,登录失败等。get_error()的返回值可以在[WFTaskError.h](../src/factory/WFTaskError.h)中查看。

##### 用户一般无需关注的几个状态:
  * UNDEFINED: 刚创建完,还没有运行的client任务,状态是UNDEFINED。
  * TOREPLY: server任务回复之前,没有被调用过task->noreply(),都是TOREPLY状态。
  * NOREPLY: server任务被调用了task->noreply()之后,一直是NOREPLY状态。callback里也是这个状态。连接会被关闭。

### 其它错误处理需求
除了任务本身的错误处理,各种具体协议的消息接口上,也会有判断错误的需要。一般这些接口都通过返回false来表示错误,并且通过errno传递错误原因。  
此外,一些更复杂的用法,可能需要接触到更复杂一点的错误信息。我们在具体的文档里再做介绍。


================================================
FILE: docs/about-exit.md
================================================
# 关于程序退出

由于我们的大多数调用都是非阻塞的,所以在之前的示例里我们都需要用一些机制来防止main函数提前退出。  
例如wget示例中等待用户的Ctrl-C,或者像parallel_wget在所有抓取结束之后唤醒主线程。  
而在几个server的示例中,stop()操作是阻塞的,可以确保所有server task的正常结束,主线程可安全退出。

# 程序安全退出的原则

一般情况下,用户只要正常写程序,模仿示例中的方法,不太会有什么关于退出的疑惑。但这里还是需要把程序正常退出的条件定义好。  
* 用户不可以在callback或process等任何回调函数里调用系统的exit()函数,否则行为无定义。
* 主线程可以安全结束(main函数调用exit()或return)的条件是所有任务已经运行到callback,并且没有新的任务被调起。
  * 我们所有的示例都符合这个假设,在callback里唤醒main函数。这是安全的,不用担心main返回的时候,callback还没结束的情况。
  * ParallelWork是一种task,也需要运行到callback。
  * 这一条规则某下情况下可以违反,我们将在下一节解释。
* 所有server必须stop完成,否则行为无定义。因为stop操作用户都会调,所以一般server程序不会有什么退出方面的问题。
  * server的stop会等待所有server任务所在series结束。但如果用户在process直接start一个新任务,则需要考虑任务结束的问题。

# 为什么需要等待运行中的任务callback?能不能提前结束程序?

首先解释一下需要等待任务callback再结束程序的原因。在大多数情况下,我们通过任务工厂产生的任务,都是一个复合任务。  
http抓取任务为例,一个http任务可能需要先解析dns,再发起http抓取。如遇到302重定向,可能需要再次dns。任务失败可能还会重试。  
也就是说,我们一个异步任务可能包含多个异步过程,但对用户完全无感。而内部每个异步过程之间,并不会检查程序是否已经退出。  
如果用户明确知道一个任务是原子任务,例如以IP地址(或肯定能dns cache命中)创建http任务,并且无重定向或重试。  
那么,这个任务可以被程序退出打断并提前来到callback,callback里任务的状态是WFT_STATE_ABORTED。  
例如以下程序是绝对安全的:
~~~cpp
void callback(WFHttpTask *task)
{
    // 这里打印的结果大概率是2,WFT_STATE_ABORTED。
    printf("state = %d\n", task->get_state());
}

int main()
{
    WFHttpTask *task = WFTaskFactory::create_http_task("https://127.0.0.1/", 0, 0, callback);
    task->start();
    // 这里直接结束程序
    return 1;
}
~~~
如果dns cache命中,也是安全的。因为内部无需再发起一个dns异步任务了。例如:
~~~cpp
WFFacilities::WaitGroup wg(1);

void callback_normal(WFHttpTask *task)
{
    wg.done();
}

void callback_abort(WFHttpTask *task)
{
    // 这里打印的结果大概率是2,WFT_STATE_ABORTED。
    printf("state = %d\n", task->get_state());
}

int main()
{
    WFHttpTask *task = WFTaskFactory::create_http_task("https://www.sogou.com/", 3, 2, callback_normal);
    task->start();
    // 等待第一个访问www.sogou.com的任务结束。
    wg.wait();
    // 第二次访问www.sogou.com, dns信息已经被cache。
    WFHttpTask *task = WFTaskFactory::create_http_task("https://www.sogou.com/", 0, 0, callback_abort);
    task->start();
    // 这里直接结束程序
    return 1;
}
~~~
所以,对于网络任务而言,只要能确定是一个原子任务,都可以被程序结束打断。这个原则可以扩展到任何类型的任务。  
例如,定时器任务是一个就原子任务,以下程序也是绝对安全的:
~~~cpp
void callback(WFTimerTask *task)
{
    // 这里打印的结果肯定是2,WFT_STATE_ABORTED。
    printf("state = %d\n", task->get_state());
}
int main()
{
    WFTimerTask *task = WFTaskFactory::create_timer_task(1000000, callback);
    task->start();
    // 这里直接结束程序
    return 1;
}
~~~
在[关于定时器](https://github.com/sogou/workflow/blob/master/docs/about-timer.md)的文档里,我们将会详细展开描述。  
此外,单线程的计算任务,文件IO任务,也可以在callback之前直接结束程序。  
其中,已经在执行计算的计算任务,程序会等待计算结束,最终以SUCCESS状态callback。还未被调起的,则以ABORTED状态退出。  
文件IO任务,只要已经start,肯定会等待IO完成。因此直接退出程序完全安全。

# 关于OpenSSL 1.1版本在退出时的内存泄露

我们发现某些openssl1.1版本,存在退出时内存释放不完全的问题,通过valgrind内存检查工具可以看出内存泄露。  
这个问题只有在用户使用了SSL,例如抓取了https网页时才会发生,而且一般情况下用户可以忽略这个泄露。
如果一定要解决,方法如下:
~~~cpp
#include <openssl/ssl.h>

int main()
{
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
    OPENSSL_init_ssl(0, NULL);
#endif
    ...
}
~~~
也就是说在使用我们的库之前,先初始化openssl。如果你有需要也可以同时配置openssl的参数。  
注意这个函数只在openssl1.1以上版本才有提供,所以调用之前需要先判断openssl版本。  
这个内存泄露与openssl1.1的内存释放原理有关。我们提供的这个方案可以解决这个问题(但我们还是建议用户忽略)。


================================================
FILE: docs/about-go-task.md
================================================
# 关于go task

我们提供了另一种更简单的使用计算任务的方法,模仿go语言实现的go task。  
使用go task来实计算任务无需定义输入与输出,所有数据通过函数参数传递。

# 创建go task
~~~cpp
class WFTaskFactory
{
    ...
public:
    template<class FUNC, class... ARGS>
    static WFGoTask *create_go_task(const std::string& queue_name,
                                    FUNC&& func, ARGS&&... args);
};
~~~
函数参数的queue_name为计算队列名,其作用在之前示例文档中有过介绍。  
func可以是函数指针,函数对象,仿函数,lambda函数,类的成员函数等任意可调用对象。  
args为func的参数列表。注意当func是一个类的非静态成员函数时,args的第一个成员必须是对象地址。

# 示例
我们想异步的运行一个加法函数:void add(int a, int b, int& res);  
并且我们还想在函数运行结束的时候打印出结果。于是可以这样实现:
~~~cpp
#include <stdio.h>
#include <utility>
#include "workflow/WFTaskFactory.h"
#include "workflow/WFFacilities.h"

void add(int a, int b, int& res)
{
    res = a + b;
}

int main(void)
{
    WFFacilities::WaitGroup wait_group(1);
    int a = 1;
    int b = 1;
    int res;

    WFGoTask *task = WFTaskFactory::create_go_task("test", add, a, b, std::ref(res));
    task->set_callback([&](WFGoTask *task) {
        printf("%d + %d = %d\n", a, b, res);
        wait_group.done();
    });
 
    task->start();
    wait_group.wait();
    return 0;
}
~~~
以上的示例异步运行一个加法,打印结果并退出程序。go task的使用与其它的任务没有多少区别,也有user_data域可以使用。  
唯一一点不同,是go task创建时不传callback,但和其它任务一样可以set_callback。  
如果go task函数的某个参数是引用,需要使用std::ref,否则会变成值传递,这是c++11的特征。

# 把workflow当成线程池

用户可以只使用go task,这样可以将workflow退化成一个线程池,而且线程数量默认等于机器cpu数。  
但是这个线程池比一般的线程池又有更多的功能,比如每个任务有queue name,任务之间还可以组成各种串并联或更复杂的依赖关系。

# 带执行时间限制的go task
通过create_timedgo_task接口(这里无法重载create_go_task接口),可以创建带时间限制的go task:
~~~cpp
class WFTaskFactory
{
    /* Create 'Go' task with running time limit in seconds plus nanoseconds.
     * If time exceeded, state WFT_STATE_SYS_ERROR and error ETIMEDOUT will be got in callback. */
    template<class FUNC, class... ARGS>
    static WFGoTask *create_timedgo_task(time_t seconds, long nanoseconds,
                                         const std::string& queue_name,
                                         FUNC&& func, ARGS&&... args);
};
~~~
相比创建普通的go task,create_timedgo_task函数需要多传两个参数,seconds和nanoseconds。  
如果func的运行时间到达seconds+nanosconds时限,task直接callback,且state为WFT_STATE_SYS_ERROR,error为ETIMEDOUT。  
注意,框架无法中断用户执行中的任务。func依然会继续执行到结束,但不会再次callback。另外,nanoseconds取值区间在\[0,10亿)。  
另外,当我们给go task加上了运行时间限制,callback的时机可能会先于func函数的结束,任务所在series可能也会先于func结束。  
如果我们在func里访问series,可能就是一个错误了。例如:
~~~cpp
void f(SeriesWork *series)
{
    series->set_context(...);   // 错误。当f是一个带超时的go task,此时series可能已经失效了。
}

int http_callback(WFHttpTask *task)
{
    SeriesWork *series = series_of(task);
    WFGoTask *go = WFTaskFactory::create_timedgo_task(1, 0, "test", f, series);  // 1秒超时的go task
    series_of(task)->push_back(go);
}
~~~
这也是为什么,我们不推荐在计算任务的执行函数里,对任务所在的series进行操作。对series的操作,应该在callback里进行,例如:
~~~cpp
int main()
{
    WFGoTask *task = WFTaskFactory::create_timedgo_task(1, 0, "test", f);
    task->set_callback([](WFGoTask *task) {
        SeriesWork *series = series_of(task):
        void *context = series->get_context();
        if (task->get_state() == WFT_STATE_SUCCESS) // 成功执行完
        {
             ...
        }
        else // state == WFT_STATE_SYS_ERROR && error == ETIMEDOUT  // 超过运行时间限制
        {
             ...
        }
    });
}
~~~
但是,在计算函数里使用task,是安全的。所以,可以使用task->user_data,在计算函数和callback之间传递数据。例如:
~~~cpp
int main()
{
    WFGoTask *task = WFTaskFactory::create_timedgo_task(1, 0, "test", [&task]() {
        task->user_data = (void *)123;
    });
    task->set_callback([](WFGoTask *task) {
        SeriesWork *series = series_of(task):
        void *context = series->get_context();
        if (task->get_state() == WFT_STATE_SUCCESS) // 成功执行完
        {
		    int result = (int)task->user_data;
        }
        else // state == WFT_STATE_SYS_ERROR && error == ETIMEDOUT    // 超过运行时间限制
        {
		    ...
        }
    });
    task->start();
    ...
}
~~~~
# 重置go task的执行函数
在某些时候,我们想在go task的执行函数里访问task,如上面的例子,将计算结果写入task的user_data域。  
上例中,我们使用了引用捕获。但明显引用捕获会有一些问题。比如task本身的生命周期。我们更希望在执行函数里直接捕获go task指针。  
直接进行值捕获明显是错误的,例如:
~~~cpp
WFGoTask *task = WFTaskFactory::create_timedgo_task(1, 0, "test", [task]() {
        task->user_data = (void *)123;
    });
~~~
这段代码并不能在lambda函数里得到task指针,因为捕获执行时,task还没有赋值。但我们可以通过以下的代码,实现这个需求:
~~~cpp
WFGoTask *task = WFTaskFactory::create_timedgo_task(1, 0, "test", nullptr);  // 执行函数可以初始化为nullptr
WFTaskFactory::reset_go_task(task, [task]() {
        task->user_data = (void *)123;
    });
~~~
WFTaskFactory::reset_get_task()函数,用于重置go task的执行函数。  
因为task已经创建完毕,这时候在lambda函数里捕获task,就是一个正确的行为了。



================================================
FILE: docs/about-module.md
================================================
# 关于模块任务

我们的任务流是以task为元素。但很多情况下,用户需要模块级的封装,比如几个task完成一个特定的功能。  
用原有的方法,就不得不让最后一个task的callback来衔接下一个任务,或者填写server任务的resp。这样不太合理。  
因此,我们引入了WFModuleTask,方便用户封装模块,降低不同功能模块之间task的耦合。

# 模块任务的创建

我们把模块定义成一种特殊的任务,WFModuleTask。模块的内部包括一个sub_series用于运行模块内的任务。  
对任务来讲,它无需关心自己是否运行在模块内。因为模块内的sub_series和普通series没有任何区别。  
在[WFTaskFactory.h](/src/factory/WFTaskFactory.h)里,包括了包括了模块任务的创建接口:
~~~cpp
using module_callback_t = std::function<void (const WFModuleTask *)>;

class WFTaskFactory
{
    static WFModuleTask *create_module_task(SubTask *first, module_callback_t callback);
};
~~~
create_module_task()的第一个参数first代表模块首任务,这与创建series类似。  
module callback参数要求是const指针。这主要是防止用户在callback里,继续向module中添加任务。  

# WFModuleTask的主要接口

因为我们把模块也定义成这一种任务,所以,可以像使用其它任务一样使用模块。但模块没有state和error域。  
在[WFTask.h](/src/factory/WFTask.h)里,定义了WFModuleTask类。
~~~cpp
class ModuleTask : public ParallelTask, protected SeriesWork // 不必关注这个派生关系
{
public:
    void start() { .. }
    void dismiss() { ... }

public:
    SeriesWork *sub_series() { return this; }
    const SeriesWork *sub_series() const { return this; }

public:
    void *user_data;
};
~~~
module特有的sub_series接口返回module内任务运行的series。module本质上是一个子任务流。  
sub_series也是一个普通的series,用户可以调用它的set_context(),get_context(),push_back()等函数。  
但我们不太建议给sub_series设置callback,因为没有什么必要,使用module的callback就可以了。  
注意,在module的callback参数表,是const WFModuleTask \*,也就只能得到一个const的sub_series。  
因此,在模块任务的callback里,只能调用sub_series的get_context()得到series上下文。  

# 示例

在一个http server的处理逻辑中,我们把所有处理逻辑设计成一个模块。
~~~cpp
struct ModuleCtx
{
    std::string body;
};

void http_callback(WFHttpTask *http_task)
{
    SeriesWork *series = series_of(http_task);    // 这个series就是module的sub_series。
    struct ModuleCtx *ctx = (struct ModuleCtx *)series->get_context();
    const void *body;
    size_t size;

    if (http_task->get_resp()->get_parsed_body(&body, &size))
    {
        ctx->body.assign(body, size);
    }

    ParallelWork *pwork = Workflow::create_parallel_work(…);// 做一些别的操作
    series->push_back(pwork);
}

void process(WFHttpTask *server_task)
{
    WFHttpTask *http_task = WFTaskFactory::create_http_task(…, http_callback);
    WFModuleTask *module = WFTaskFactory::create_module_task(http_task, [server_task](const WFModuleTask *mod) {
        struct ModuleCxt *ctx = (struct ModuleCtx *)mod->sub_series()->get_context();
        server_task->get_resp()->append_output_body(ctx->body);
        delete ctx;
    });
    module->sub_series()->set_context(new ModuleCtx);
    series_of(server_task)->push_back(module);
}
~~~
通过这个方法,module里的任务只需操作series context,最终由module的callback汇总填写resp。任务耦合性大幅降低。


================================================
FILE: docs/about-resource-pool.md
================================================
# 资源池

在我们用workflow写异步程序时经常会遇到这样一些场景:
* 任务运行时需要先从某个池子里获得一个资源。任务运行结束,则会把资源放回池子,让下一个需要资源的任务运行。
* 网络通信时需要对某一个或一些通信目标做总的并发度限制,但又不希望占用线程等待。
* 我们有许多随机到达的任务,处在不同的series里。但这些任务必须**串行**的运行。

所有这些需求,都可以用资源池模块来解决。我们的[WFDnsResolver](https://github.com/sogou/workflow/blob/master/src/nameservice/WFDnsResolver.cc)就是通过这个方法来实现对dns server的并发度控制的。

# 资源池的接口
在[WFResourcePool.h](https://github.com/sogou/workflow/blob/master/src/factory/WFResourcePool.h)里,定义了资源池模块的接口:
~~~cpp
class WFResourcePool
{
public:
    WFConditional *get(SubTask *task, void **resbuf);
    WFConditional *get(SubTask *task);
    void post(void *res);
    ...

protected:
    virtual void *pop()
    {
        return this->data.res[this->data.index++];
    }

    virtual void push(void *res)
    {
        this->data.res[--this->data.index] = res;
    }
    ...

public:
    WFResourcePool(void *const *res, size_t n);
    WFResourcePool(size_t n);
    ...
};
~~~
#### 构造函数
第一个构造函数接受一个资源数组,长度为n。数组每个元素为一个void \*。内部会再分配一份相同大小的内存,把数组复制走。  
如果你的初始资源都是nullptr,那么你可以使用第二个构造函数,只需要传n,而无需先建立一个全部为nullptr的指针数组。  
大概看看内部实现就明白了:
~~~cpp
void WFResourcePool::create(size_t n)
{
    this->data.res = new void *[n];
    this->data.value = n;
    ...
}

WFResourcePool::WFResourcePool(void *const *res, size_t n)
{
    this->create(n);
    memcpy(this->data.res, res, n * sizeof (void *));
}

WFResourcePool::WFResourcePool(size_t n)
{
    this->create(n);
    memset(this->data.res, 0, n * sizeof (void *));
}
~~~

#### 使用接口
用户使用get()接口,把任务打包成一个conditional。conditional是一个条件任务,条件满足时运行其包装的任务。  
get()接口可包含第二个参数是一个void \*\*resbuf,用于保存所获得的资源。  
接下来,用户只需要用这个conditional取代原来的任务使用就好了,可以start或串进任务流。  
注意conditional是在它被执行时去尝试获得资源的,而不是在它被创建的时候。要不然的话,以下代码就会被卡死:
~~~cpp
WFResourcePool pool(1);

int f()
{
    WFHttpTask *t1 = WFTaskFactory::create_http_task(..., [](void *){pool.post(nullptr);});
    WFHttpTask *t2 = WFTaskFactory::create_http_task(..., [](void *){pool.post(nullptr);});

    WFConditional *c1 = pool.get(t1, &t1->user_data);  // 用user_data来保存res是一种实用方法。
    WFConditional *c2 = pool.get(t2, &t2->user_data);

    c2->start();
    // wait for t2 finish here.
    ...
    c1->start();
    ...
}
~~~
以上代码c1先创建,等待t2结束后才运行。这里并不会出现c2卡死,因为conditional是在执行时才获得资源的。  
当用户对资源使用完毕(一般在任务callback里),需要通过post()接口把资源放回池子。  
post()时的res参数,**无需**与get()得到res的一致。  

#### 派生
从上面的pop()和push()函数我们可以看到,我们对资源的使用默认是FILO,即先进后出的。  
使用FILO的原因是,大多数场景下,刚刚被释放的资源应该优先被复用。  
但是,用户可以通过派生的方式,非常简单的实现一个FIFO资源池。只需要重写pop()和push()两个virtual函数即可。  
如果需要,你还可以实现可动态扩展和收缩的资源池。

# 示例
我们准备抓取一份URL列表,但要求总的并发度不超过max_p。我们当然可以用parallel来实现,但使用资源池可以更简单:
~~~cpp
int fetch_with_max(std::vector<std::string>& url_list, size_t max_p)
{
    WFResourcePool pool(max_p);

    for (std::string& url : url_list)
    {
        WFHttpTask *task = WFTaskFactory::create_http_task(url, [&pool](WFHttpTask *task) {
            pool.post(nullptr);
        });
        WFConditional *cond = pool.get(task);  // 无需保存res,可以不传resbuf参数。
        cond->start();
    }

    // wait_here...
}
~~~

# 消息队列

消息队列是一种比资源使用方法类似的组件。它们的区别在于:
* 资源池的总资源数量是固定的,在创建时就已经确定。而消息队列的长度则不受限制。
* 资源池的存取方式是先进后出,刚刚释放的资源会先被复用。而消息队列则是先进先出。
* 资源池使用方式是先获取,后归还。没有获取就直接归还资源,可能导致缓冲区溢出。消息队列没有这样的约束。
* 实现上,资源池使用的是数组,消息队列使用链表。总体来讲,在实现和使用上,消息队列都比资源池简单一些。

# 消息队列接口

在[WFMessageQueue.h](https://github.com/sogou/workflow/blob/master/src/factory/WFMessageQueue.h)里,定义了消息队列模块的接口:
~~~cpp
class WFMessageQueue
{
public:
    WFConditional *get(SubTask *task, void **msgbuf);
    WFConditional *get(SubTask *task);
    void post(void *msg);
    ...

public:
    WFMessageQueue();
    ...
};
~~~
由于了解过资源池的用法,消息队列的使用方式我们也就无需再详细展开。模式和资源池一样,都是在获得消息(或资源)时,任务被拉起。  
消息队列的get和post接口,无需像资源池一样遵循先获取再放回的原则,任何任务都可以随时从队列中存取消息。  
如果有需要,用户同样可以派生WFMessageQueue类,实现先进后出的消息读取模式。  


================================================
FILE: docs/about-selector.md
================================================
# 关于Selector任务

我们业务中经常有一些需求,从几个异步分支中选择第一个成功完成的结果进行处理,丢弃其它结果。  
Selector任务就是为了上述这种多选一场景而设计的。  

# Selector解决的问题
常见的多选一场景例如:  
* 向多个下游发送网络请求,只要任意一个下游返回正确结果,工作流程就可以继续。
* 执行一组复杂的操作,操作执行完成或整体超时,流程都会继续。
* 并行计算中,任何一个线程计算出预期的结果即完成,例如MD5碰撞计算。
* 网络应用中的‘backup request’,也可以用selector配合timer来实现。

在selector任务被引入之前,这些场景很难被很好解决,涉及到任务生命周期以及丢弃结果的资源回收等问题。    

# 创建Selector任务
Selector也是一种任务,所以一般由WFTaskFactory里的工厂函数产生:
~~~cpp
using selector_callback_t = std::function<void (WFSelectorTask *)>;

class WFTaskFactory
{
public:
    static WFSelectorTask *create_selector_task(size_t candidates,
                                                selector_callback_t callback);
};
~~~
其中,candidates参数代表从多少个候选路径中选择。Selector任务创建后,必须有candidates次被提交才会被销毁。  
因此,用户可以放心的(也是必须的)向selector提交candidates次,无需要担心selector的生命周期问题。  

# Selector类的接口
WFSelectorTask类包括两个主要接口。其中,对提交者来讲,只需要关注submit函数。对于等待者,只需使用到get_message。  
~~~cpp
class WFSelectorTask : public WFGenericTask
{
public:
    virtual int submit(void *msg);

    void *get_message() const;
};
~~~
当第一个非空指针的msg被提交,submit函数返回1表示接受。随后的submit调用都返回0代表消息被拒绝。  
Selector运行后接收到一个有效消息就进入callback了,但在收到所有submit之前,不会被销毁。  
注意空指针永远不会被接受,所以submit一个NULL将返回0。一般来讲,submit(NULL)用于表示这个分支失败了。  
如果所有候选都提交了NULL,selector运行到callback时,state=WFT_STATE_SYS_ERROR, error=ENOMSG。  
作为等待者,在selector的callback里调用另外一个接口get_message()就可以得到被成功接受的消息了。  

# 示例
我们同时抓取两个http网页,并设置一个超时。当任意一个先抓取成功或超时,打印出抓取成功的URL或出错信息。  
示例中使用wait group来保证两个抓取任务已经结束才退出程序。而timer可以被程序退出打断,无需等待。  
~~~cpp
#include <stdlib.h>
#include <stdio.h>
#include "workflow/WFTaskFactory.h"
#include "workflow/WFFacilities.h"

WFSelectorTask *selector;
WFFacilities::WaitGroup wait_group(2);

void http_callback(WFHttpTask *t)
{
    if (t->get_state() == WFT_STATE_SUCCESS)
        selector->submit(t->user_data);
    else
        selector->submit(NULL);

    wait_group.done();
}

int main(int argc, char *argv[])
{
    if (argc != 4)
    {
        fprintf(stderr, "USAGE: %s <http URL1> <http URL2> <timeout>\n", argv[0]);
        exit(1); 
    }

    selector = WFTaskFactory::create_selector_task(3, [](WFSelectorTask *selector) {
        void *msg = selector->get_message();
        if (msg)
            printf("%s\n", (char *)msg);
        else
            printf("failed\n");
    });

    auto *t = WFTaskFactory::create_http_task(argv[1], 0, 0, http_callback);
    t->user_data = argv[1];
    t->start();

    t = WFTaskFactory::create_http_task(argv[2], 0, 0, http_callback);
    t->user_data = argv[2];
    t->start();

    auto *timer = WFTaskFactory::create_timer_task(atoi(argv[3]), 0, [](WFTimerTask *timer){
        if (timer->get_state() == WFT_STATE_SUCCESS)
            selector->submit((void *)"timeout");
        else
            selector->submit(NULL);
    });
    timer->start();

    selector->start();

    wait_group.wait();
    return 0;
}
~~~





================================================
FILE: docs/about-service-governance.md
================================================
# 关于服务治理

我们拥有一套完整的机制,来管理我们所依赖的服务。这套机制包括以下的几个功能:
* 用户级DNS。
* 服务地址的选取
  * 包括多种选取机制,如权重随机,一致性哈希,用户指定选取方式等。
* 服务的熔断与恢复。
* 负载均衡。
* 单个服务的独立参数配置。
* 服务的主备关系等。

所有这些功能都依赖于我们的upstream子系统。利用好这个系统,我们可以轻易地实现更复杂的服务网格功能。

# upstream名

upstream名相当于程序内部的域名,但相比一般的域名,upstream拥有更多的功能,包括:
* 域名通常只能指向一组ip地址,upstream名可以指向一组ip地址或域名。
* upstream指向的对象(域名或ip),可以包括端口信息。
* upstream有管理和选择目标的强大功能,每个目标可以包含大量属性。
* upstream的更新,是实时而且完全线程安全的,而域名的DNS信息,并不能实时更新。

实现上,如果无需访问外网,用upstream可以完全代替域名和DNS。

# upstream的创建与删除

在[UpstreamManager.h](../src/manager/UpstreamManager.h)里,包括几个upstream创建接口:
~~~cpp
using upstream_route_t = std::function<unsigned int (const char *, const char *, const char *)>;

class UpstreamManager
{
public:
    static int upstream_create_consistent_hash(const std::string& name,
                                               upstream_route_t consitent_hash);

    static int upstream_create_weighted_random(const std::string& name,
                                               bool try_another);

    static int upstream_create_manual(const std::string& name,
                                      upstream_route_t select,
                                      bool try_another,
                                      upstream_route_t consitent_hash);

    static int upstream_delete(const std::string& name);
    ...
};
~~~
三个函数创建分别为3种类型的upstream:一致性hash,权重随机和用户手动选取。  
参数name为upstream名,创建之后,就和域名一样的使用了。  
consistent_hash和select参数,都是一个类型为upstream_route_t的std::function,用于指定路由方式。  
而try_another表示,如果选取到的目标不可用(熔断),是否继续尝试找到一个可用目标。consistent_hash模式没有这个属性。  
upstream_route_t参数接收的3个参数分别是url里的path, query和fragment部分。例如URL为:http://abc.com/home/index.html?a=1#bottom  
则这三个参数分别为"/home/index.html", "a=1"和"bottom"。用户可以根据这三个部分,选择目标服务器,或者进行一致性hash。  
注意,以上接口中,consistent_hash参数都可以传nullptr,我们将使用默认的一致性哈希算法。  

# 示例1:权重分配

我们想把50%访问www.sogou.com的请求,打到127.0.0.1:8000和127.0.0.1:8080两个地址,并且让他们的负载为1:4。  
我们无需要关心域名www.sogou.com之下,有多少个ip地址。总之实际域名会接收50%的请求。
~~~cpp
#include "workflow/UpstreamManager.h"
#include "workflow/WFTaskFactory.h"

int main()
{
    UpstreamManager::upstream_create_weighted_random("www.sogou.com", false);
    struct AddressParams params = ADDRESS_PARAMS_DEFAULT;

    params.weight = 5;
    UpstreamManager::upstream_add_server("www.sogou.com", "www.sogou.com", &params);
    params.weight = 1;
    UpstreamManager::upstream_add_server("www.sogou.com", "127.0.0.1:8000", &params);
    params.weight = 4;
    UpstreamManager::upstream_add_server("www.sogou.com", "127.0.0.1:8080", &params);

    WFHttpTask *task = WFTaskFactory::create_http_task("http://www.sogou.com/index.html", ...);
    ...
}
~~~
请注意,以上这些函数可以在任何场景下调用,完全线程安全,并实时生效。  
另外,由于我们一切协议,包括用户自定义协议都有URL,所以upstream功能可作用于一切协议。

# 示例2:手动选择

同样是上面的例子,我们想让url里,query为"123"的请求,打到127.0.0.1:8000,如果是"abc",打到8080端口,其它打正常域名。  
~~~cpp
#include "workflow/UpstreamManager.h"
#include "workflow/WFTaskFactory.h"

int my_select(const char *path, const char *query, const char *fragment)
{
    if (strcmp(query, "123") == 0)
        return 1;
    else if (strcmp(query, "abc") == 0)
        return 2;
    else
        return 0;
}

int main()
{
    UpstreamManager::upstream_create_manual("www.sogou.com", my_select, false, nullptr);

    UpstreamManager::upstream_add_server("www.sogou.com", "www.sogou.com");
    UpstreamManager::upstream_add_server("www.sogou.com", "127.0.0.1:8000");
    UpstreamManager::upstream_add_server("www.sogou.com", "127.0.0.1:8080");

    /* This URL will route to 127.0.0.1:8080 */
    WFHttpTask *task = WFTaskFactory::create_http_task("http://www.sogou.com/index.html?abc", ...);
    ...
}
~~~
由于我们原生提供了redis和mysql协议,用这个方法,可以极其方便的实现数据库的读写分离功能(注:非事务的操作)。  
以上两个例子,upstream名用的是www.sogou.com,这本身也是一个域名。当然用户可以更简单的用字符串sogou,这样创建任务时:
~~~cpp
    WFHttpTask *task = WFTaskFactory::create_http_task("http://sogou/home/1.html?abc", ...);
~~~
总之url的host部分,如果是一个已经创建的upstream,则会被当作upstream使用。  

# 示例3:一致性hash

这个场景里,我们要从10个redis实例中,随机选择一台机器通信。但保证同一个url肯定访问一个确定的目标。方法很简单:
~~~cpp
int main()
{
    UpstreamManager::upstream_create_consistent_hash("redis.name", nullptr);

    UpstreamManager::upstream_add_server("redis.name", "10.135.35.53");
    UpstreamManager::upstream_add_server("redis.name", "10.135.35.54");
    UpstreamManager::upstream_add_server("redis.name", "10.135.35.55");
    ...
    UpstreamManager::upstream_add_server("redis.name", "10.135.35.62");

    auto *task = WFTaskFactory::create_redis_task("redis://:mypassword@redis.name/2?a=hello#111", ...);
    ...
}
~~~
我们的redis任务并不识别query部分,用户可以随意填写。path部分的2为redis库号。  
这个时候,consistent_hash函数将得到"/2","a=hello"和"111"三个参数,但因为我们用nullptr,默认一致性hash将被调用。  
upstream里的服务器没有指定端口号,于是将使用url里的端口。redis默认为6379。  
consitent_hash并没有try_another选项,如果目标熔断,将自动选取另一个。相同url还将得到相同选择(cache友好)。  

# upstream server的参数

示例1中,我们通过params参数设置了server的权重。当然server参数远不止权重一项。这个结构定义如下:
~~~cpp
// In EndpointParams.h
struct EndpointParams
{
    size_t max_connections;
    int connect_timeout;
    int response_timeout;
    int ssl_connect_timeout;
    bool use_tls_sni;
};

// In ServiceGovernance.h
struct AddressParams
{
    struct EndpointParams endpoint_params; ///< Connection config
    unsigned int dns_ttl_default;          ///< in seconds, DNS TTL when network request success
    unsigned int dns_ttl_min;              ///< in seconds, DNS TTL when network request fail
/**
 * - The max_fails directive sets the number of consecutive unsuccessful attempts to communicate with the server.
 * - After 30s following the server failure, upstream probe the server with some live client’s requests.
 * - If the probes have been successful, the server is marked as a live one.
 * - If max_fails is set to 1, it means server would out of upstream selection in 30 seconds when failed only once
 */
    unsigned int max_fails;                ///< [1, INT32_MAX] max_fails = 0 means max_fails = 1
    unsigned short weight;                 ///< [1, 65535] weight = 0 means weight = 1. only for main server
    int server_type;                       ///< 0 for main and 1 for backup
    int group_id;                          ///< -1 means no group. Backup without group will backup for any main node
};
~~~
大多数参数的作用一眼了然。其中endpoint_params和dns相关参数,可以覆盖全局的配置。  
例如,全局对每个目标ip最大连接数为200,但我想为10.135.35.53设置最多1000连接数,可以这么做:
~~~cpp
    UpstreamManager::upstream_create_weighted_random("10.135.35.53", false);
    struct AddressParams params = ADDRESS_PARAMS_DEFAULT;
    params.endpoint_params.max_connections = 1000;
    UpstreamManager::upstream_add_server("10.135.35.53", "10.135.35.53", &params);
~~~
max_fails参数为最大出错次数,如果选取目标连续出错达到max_fails则熔断,如果upstream的try_another属性为false,则任务失败,  
在任务callback里,get_state()=WFT_STATE_TASK_ERROR,get_error()=WFT_ERR_UPSTREAM_UNAVAILABLE。  
如果try_another为true,并且所有server都熔断的话,会得到同样错误。熔断时间为30秒。  
server_type和group_id用于主备功能。所有upstream必需有type为0(主节点)的server,否则upstream不可用。  
类型为1(备份节点)的server,会在同group_id的主节点熔断情况下被使用。  

更多upstream功能查询:[about-upstream.md](./about-upstream.md)。


================================================
FILE: docs/about-timeout.md
================================================
# 关于超时

为了让所有通信任务可以在用户的预期下精确运行,我们提供了大量的超时配置功能,并且确保这些超时的准确性。  
这些超时配置里,有些是全局的,比如连接超时,但你又可以通过upstream功能,给某个域名配置自己的连接超时。  
有一些超时是任务级的,比如完整发送一条消息的超时。因为用户需要根据消息大小,动态配置这个值。  
当然对server来讲,又有自己的超时整体配置。总之,超时是一件很复杂的事,我们会做得很精确。  
所有超时都采用poll风格,也就是int型,毫秒级,-1表示无限。  
另外,正如我们在项目介绍里说的,所有的配置你都可以忽略,可以等遇到实际需求了再进行调整。

### 基础通信超时配置

在[EndpointParams.h](../src/manager/EndpointParams.h)文件里,可以看到:
~~~cpp
struct EndpointParams
{
    size_t max_connections;
    int connect_timeout;
    int response_timeout;
    int ssl_connect_timeout;
};

static constexpr struct EndpointParams ENDPOINT_PARAMS_DEFAULT =
{
    .max_connections        = 200,
    .connect_timeout        = 10 * 1000,
    .response_timeout       = 10 * 1000,
    .ssl_connect_timeout    = 10 * 1000,
};
~~~
其中,与超时相关的配置包括以下3项。
  * connect_timeout: 与目标建立连接的超时。默认为10秒。
  * response_timeout: 等待目标响应的超时,默认为10秒。代表成功发送到目标、或从目标读取到一块数据的超时。
  * ssl_connect_timeout: 与目标完成SSL握手的超时。默认为10秒。

这个结构体是通信连接的最基础的配置,后续几乎所有的通信配置都会含有这个结构体。

### 全局超时配置

在[WFGlobal.h](../src/manager/WFGlobal.h)文件里,可以看到我们一个全局配置信息:
~~~cpp
struct WFGlobalSettings
{
    EndpointParams endpoint_params;
    unsigned int dns_ttl_default;
    unsigned int dns_ttl_min;
    int dns_threads;
    int poller_threads;
    int handler_threads;
    int compute_threads;
};

static constexpr struct WFGlobalSettings GLOBAL_SETTINGS_DEFAULT =
{
    .endpoint_params    =    ENDPOINT_PARAMS_DEFAULT,
    .dns_ttl_default    =    12 * 3600,    /* in seconds */
    .dns_ttl_min        =    180,          /* reacquire when communication error */
    .dns_threads        =    8,
    .poller_threads     =    2,
    .handler_threads    =    20,
    .compute_threads    =    -1
};
//compute_threads<=0 means auto-set by system cpu number
~~~
其中,与超时相关的配置就是EndpointParams endpoint_params这一项

修改全局配置的方法是,调用我们任何工厂函数之前,执行类似下面的操作:
~~~cpp
int main()
{
    struct WFGlobalSettings settings = GLOBAL_SETTINGS_DEFAULT;
    settings.endpoint_params.connect_timeout = 2 * 1000;
    settings.endpoint_params.response_timeout = -1;
    WORKFLOW_library_init(&settings);
}
~~~
上例把连接超时修改为2秒,远程服务器响应超时为无限。这种配置下,每次任务里都必须配置接收完整消息的超时,否则可能陷入无限的等待。  
全局的超时配置,可以通过upstream功能,被单独的地址配置覆盖,比如你可以指定某个域名的连接超时。  
Upstream每一个AddressParams也有一个EndpointParams endpoint_params项,使用方式与Global相仿。  
具体结构详见[upstream文档](tutorial-10-upstream.md#Address属性)

### Server超时配置

在[http_proxy](./tutorial-05-http_proxy.md)示例的里,我们介绍过server启动配置。其中超时相关的配置包括:
  * peer_response_timeout: 这个的定义和全局的response_timeout一样,指的是远程client的响应超时,默认为10秒。
  * receive_timeout: 接收一条完整请求的超时,默认为-1。
  * keep_alive_timeout: 连接保持时间。默认1分钟。redis server为5分钟。
  * ssl_accept_timeout: 完成ssl握手的超时,默认为10秒。

在这个默认配置下,client可以每9秒发送一个字节,让server一直接收而不引起超时。所以,如果服务用于公网,需要配置receive_timeout。  

### 任务级别的超时配置

任务级别的超时配置通过网络任务的几个接口调用来完成:
~~~cpp
template <class REQ, class RESP>
class WFNetworkTask : public CommRequest
{
...
public:
    /* All in milliseconds. timeout == -1 for unlimited. */
    void set_send_timeout(int timeout) { this->send_timeo = timeout; }
    void set_receive_timeout(int timeout) { this->receive_timeo = timeout; }
    void set_keep_alive(int timeout) { this->keep_alive_timeo = timeout; }
    void set_watch_timeout(int timeout) { this->watch_timeo = timeout; }
...
}
~~~
其中,set_send_timeout()设置发送完整消息的超时,默认值为-1。  
set_receive_timeout()只对client任务有效,指接收完整server回复的超时,默认值为-1。  
  * server任务的receive_timeout在server启动配置里。对server任务设置receive_timeout没有意义,因为消息已经接收完成。

set_keep_alive()接口设置连接保持超时。一般来讲,框架能很好的处理连接保持的问题,用户不需要调用。  
如果是http协议,client或server想要使用短连接,可通过添加HTTP header来完成,尽量不要用这个接口去修改。  
如果一个redis client想要在请求之后关闭连接,则需要用这个接口。显然,在callback里set_keep_alive()是无效的(连接已经被复用)。  

set_watch_timeout()接口为client任务专有,代表一个client任务的请求发出之后,接收到第一个返回包的最大等待时间。  
利用watch timeout,可以避免一些需要等待数据推送的client任务受到response timeout和receive timeout的约束而超时。  
设置了watch timeout之后,从接收到第一个数据包再开始计算receive timeout。

### 任务的同步等待超时

有一个非常特殊的超时配置,是全局唯一一个同步等待超时。我们并不鼓励使用,但在某些应用场景下能得到很好的效果。  
目前框架里,目标服务器是有连接上限的(全局和upstream都可以配置)。如果连接已经达到上限,默认的情况下,client任务失败返回。  
callback里task->get_state()得到WFT_STATE_SYS_ERROR, task->get_error()得到EAGAIN。如果任务配置了retry,会自动发起重试。  
在这里,我们允许通过task->set_wait_timeout()接口,配置一个同步等待超时,如果在这段时间内,有连接被释放,则任务可以占用这个连接。  
如果用户配置了wait_timeout,并且在超时之前没有拿到连接,则callback得到WFT_STATE_SYS_ERROR状态和ETIMEDOUT错误。
~~~cpp
class CommRequest : public SubTask, public CommSession
{
public:
    ...
    void set_wait_timeout(int wait_timeout) { this->wait_timeout = wait_timeout; }
}
~~~

### 超时的原因查看

通信task包含一个get_timeout_reason()接口,用于返回超时原因,但不是很细致,包括以下几个返回值:
  * TOR_NOT_TIMEOUT: 不是超时。
  * TOR_WAIT_TIMEOUT: 同步等待超时。
  * TOR_CONNECT_TIMEOUT: 连接超时。包括TCP,SCTP等协议的连接和SSL连接超时,都是这个值。
  * TOR_TRANSMIT_TIMEOUT: 一切传输超时。不能进一步区分是发送阶段还是接收阶段。以后可能会细化。
    * server任务,超时原因一定是TRANSMIT_TIMEOUT,并且一定是发送回复的阶段。

### 超时功能的实现

框架内部,需要处理的超时种类比我们在这里展现的还要更多。除了wait_timeout,全都是依赖于Linux的timerfd或kqueue的timer事件。  
每个poller线程包含一个timerfd,默认配置下,poller线程数为4,可以满足大多数应用的需要了。  
目前的超时算法利用了链表+红黑树的数据结构,时间复杂度在O(1)和O(logn)之间,其中n为poller线程的fd数量。  
超时处理目前看不是瓶颈所在,因为Linux内核epoll相关调用也是O(logn)时间复杂度,我们把超时都做到O(1)也区别不大。


================================================
FILE: docs/about-timer.md
================================================
# 关于定时器

定时器的作用是不占线程的等待一个确定时间,同样通过callback来通知定时器到期。

# 定时器的创建

WFTaskFactory类里包括四个定时相关的接口:
~~~cpp
using timer_callback_t = std::function<void (WFTimerTask *)>;

class WFTaskFactory
{
    ...
public:
    static WFTimerTask *create_timer_task(time_t seconds, long nanoseconds,
                                          timer_callback_t callback);

    static WFTimerTask *create_timer_task(const std::string& timer_name,
                                          time_t seconds, long nanoseconds,
                                          timer_callback_t callback);

    static int cancel_by_name(const std::string& timer_name)
    {
        cancel_by_name(const std::string& timer_name, (size_t)-1);
    }

    static int cancel_by_name(const std::string& timer_name, size_t max);
};
~~~
我们通过seconds和nanoseconds两个参数来指定一个定时器的定时时间。其中,seconds指定秒数而nanoseconds为纳秒数。  
* seconds参数可以传递-1,产生一个无限时长的定时器,一般用于命名定时器,为了将来调用cancel取消定时。  
* nanoseconds的取值范围在[0,1000000000),否则timer运行之后会立刻错误返回,错误码为EINVAL。

在创建定时器任务时,可以传入一个timer_name作为定时器名,用于cancel_by_name接口取消定时。  
定时器也是一种任务,因此使用方式与其它类型任务无异,同样有user_data域可以利用。  

# 取消定时

如果在创建定时器任务时传入一个名称,那么这个定时器就可以在被提前中断。  
中断一个定时任务的方法是通过WFTaskFactory::cancel_by_name这个接口,这个接口默认情况下,会取消这个名称下的所有定时器。  
因此,我们也支持传入一个max参数,让操作最多取消max个定时器。无论哪个接口,返回值都是代表实际被取消的定时器个数。  
如果没有这个名称下的定时器,cancel操作不会产生任何效果,并返回0。  
定时器在被创建之后就可取消,并非一定要等它被启动之后。以这个代码为例:
~~~cpp
#include <stdio.h>
#include "workflow/WFTaskFactory.h"

int main()
{
    WFTimerTask *timer = WFTaskFactory::create_timer_task("test", 10000, 0, [](WFTimerTask *){
        printf("timer callback, state = %d, error = %d.\n", task->get_state(), task->get_error());
    });

    WFTaskFactory::cancel_by_name("test");

    timer->start();

    getchar();
    return 0;
}
~~~
程序会在立即打印出'timer callback, state = 1, error = 125.",因为定时器在运行之前就已经被取消了。所以,定时任务启动后立即callback,状态码为WFT_STATE_SYS_ERROR,错误码为ECANCELED。  
使用中需要注意的是,命名定时器比匿名定时器是会多出一些开销的,原因是我们需要维护查找表,会有加锁解锁等操作。如果你的定时器没有提前中断的需要,就不要在创建时传入timer_name了。  

# 程序退出打断定时器

在[关于程序退出](./about-exit.md)里讲到,main函数结束或exit()被调用的时候,所有任务必须里运行到callback,并且没有新的任务被调起。  
这时就可能出现一个问题,定时器的定时周期可以非常长,如果是不可中断的定时器,那么等待定时器到期,程序退出需要很长时间。  
而实现上,程序退出是可以打断定时器,让定时器回到callback的。如果定时器被程序退出打断,get_state()会得到一个WFT_STATE_ABORTED状态。  
当然如果定时器被程序退出打断,则不能再调起新的任务。  
以下这个程序,每间隔一秒抓取一个一个http页面。当所有url抓完毕,程序直接退出,不用等待timer回到callback,退出不会有延迟。  
~~~cpp
bool program_terminate = false;

void timer_callback(WFTimerTask *timer)
{
    mutex.lock();
    if (!program_terminate)
    {
        WFHttpTask *task;
        if (urls_to_fetch > 0)
        {
            task = WFTaskFactory::create_http_task(...);
            series_of(timer)->push_back(task);
        }

        series_of(timer)->push_back(WFTaskFactory::create_timer_task(1, 0, timer_callback));
    }
    mutex.unlock();
}

...
int main()
{
    ....
    /* all urls done */
    mutex.lock();
    program_terminate = true;
    mutex.unlock();
    return 0;
}
~~~
以上程序,timer_callback必须在锁里判断program_terminate条件,否则可能在程序已经结束的情况下又调起新任务。


================================================
FILE: docs/about-tlv-message.md
================================================
# 关于TLV(Type-Length-Value)格式的消息
TLV消息是一种由类型,长度,内容组成的消息。由于其结构简单通用,而且方便嵌套和扩展,特别适用于定义通信消息。  
为方便用户实现自定义协议,我们内置了TLV消息的支持。  

# TLV消息的结构
TLV消息并没有具体规定Type和Length这两个字段占的字节数据。在我们的协议里,它们分别占4字节(网络序)。  
也就是说,我们的消息有8字节的消息头,以及不超过32GB的Value内容。Type和Value域的含义我们不做规定。  

# TLVMessage类
由于TLV的定义内容很少,所以[TLVMessage](/src/protocol/TLVMessage.h)需要用到的接口很少。
~~~cpp
namespace protocol
{
class TLVMessage : public ProtocolMessage
{
public:
    int get_type() const { return this->type; }
    void set_type(int type) { this->type = type; }

    std::string *get_value() { return &this->value; }
    void set_value(std::string value) { this->value = std::move(value); }

protected:
    int type;
    std::string value;

    ...
};

using TLVRequest = TLVMessage;
using TLVResposne = TLVMessage;
}
~~~
用户直接使用TLV消息来做数据传输的话,只需要用到上面的几个接口。分别为设置和获取Type与Value。  
Value直接以std::string返回,方便用户必要的时候直接通过std::move移动数据。  

# 基于TLV消息的echo server/client
以下代码,直接启动一个基于TLV消息的server,并通过命令行产生client task进行交互。建议运行一下:
~~~cpp
#include <stdio.h>
#include <string>
#include <iostream>
#include "workflow/WFGlobal.h"
#include "workflow/WFFacilities.h"
#include "workflow/TLVMessage.h"
#include "workflow/WFTaskFactory.h"
#include "workflow/WFServer.h"

using namespace protocol;

using WFTLVServer = WFServer<TLVRequest, TLVResponse>;
using WFTLVTask = WFNetworkTask<TLVRequest, TLVResponse>;
using tlv_callback_t = std::function<void (WFTLVTask *)>;

WFTLVTask *create_tlv_task(const char *host, unsigned short port, tlv_callback_t callback)
{
    auto *task = WFNetworkTaskFactory<TLVRequest, TLVResponse>::create_client_task(
                                       TT_TCP, host, port, 0, std::move(callback));
    task->set_keep_alive(60 * 1000);
    return task;
}

int main()
{
    WFTLVServer server([](WFTLVTask *task) {
        *task->get_resp() = std::move(*task->get_req());
    });

    if (server.start(8888) != 0) {
        perror("server.start");
        exit(1);
    }

    auto&& create = [](WFRepeaterTask *)->SubTask * {
        std::string string;
        printf("Input string (Ctrl-D to exit): ");
        std::cin >> string;
        if (string.empty())
            return NULL;

        auto *task = create_tlv_task("127.0.0.1", 8888, [](WFTLVTask *task) {
            if (task->get_state() == WFT_STATE_SUCCESS)
                printf("Server Response: %s\n", task->get_resp()->get_value()->c_str());
            else {
                const char *str = WFGlobal::get_error_string(task->get_state(), task->get_error());
                fprintf(stderr, "Error: %s\n", str);
            }
        });

        task->get_req()->set_value(std::move(string));
        return task;
    };

    WFFacilities::WaitGroup wait_group(1);
    WFRepeaterTask *repeater = WFTaskFactory::create_repeater_task(std::move(create), nullptr);
    Workflow::start_series_work(repeater, [&wait_group](const SeriesWork *) {
        wait_group.done();
    });

    wait_group.wait();
    server.stop();
    return 0;
}

~~~

# 派生TLVMessage
上面的echo server实例,我们直接使用了原始的TLVMessage。但建议在具体的应用中,用户可以对消息进行派生。  
在派生类里,提供更加丰富的接口来设置和提取消息内容,避免直接操作原始Value域,并形成自己的二级协议。  
例如,我们实现一个JSON的协议,可以:
~~~cpp
#include "workflow/json-parser.h"    // 内置的json解析器

class JsonMessage : public TLVMessage
{
public:
    void set_json_value(const json_value_t *val)
    {
        this->type = JSON_TYPE;
        this->json_to_string(val, &this->value);  // 需要实现一下
    }

    json_value_t *get_json_value() const
    {
        if (this->type == JSON_TYPE)
            return json_parser_parse(this->value.c_str());  // json-parser的函数
        else
            return NULL;
    }
};

using JsonRequest = JsonMessage;
using JsonResponse = JsonMessage;

using JsonServer = WFServer<JsonRequest, JsonResponse>;
~~~
这个例子只是为了说明派生的重要性,实际应用中,派生类可能要远远比这个复杂。  


================================================
FILE: docs/about-upstream.md
================================================
# 关于Upstream

在nginx里,Upstream代表了反向代理的负载均衡配置。在这里,我们扩充Upstream的含义,让其具备以下几个特点:
1. 每一个Upstream都是一个独立的反向代理
2. 访问一个Upstream等价于,在一组服务/目标/上下游,使用合适的策略选择其中一个进行访问
3. Upstream具备负载均衡、出错处理、熔断和其他服务治理能力
4. 对于同一个请求的多次重试,Upstream可以避开已试过的目标
5. 通过Upstream可以对不同下游配置不同的连接参数
6. 动态增删目标地址实时生效,方便对接任意的服务发现系统

### Upstream相对于域名DNS解析的优势

Upstream和域名DNS解析都可以将一组ip配置到一个Host,但是
1. DNS域名解析是不针对于端口号的,相同ip不同端口的服务DNS域名是不能配置到一起的;但Upstream可以
2. DNS域名解析对应的一组address,必定是ip;Upstream对应的一组address,可以是ip、域名或unix-domain-socket
3. 通常情况下,DNS域名解析会被操作系统或网络上DNS服务器所缓存,更新时间受到ttl的限制;Upstream可以做到实时更新实时生效
4. DNS域名解析消耗比Upstream解析和选取大很多

### Workflow的Upstream

这是一个本地反向代理模块,代理配置对server和client都生效。  

支持动态配置,可用于服务发现系统,目前[workflow-k8s](https://github.com/sogou/workflow-k8s)可以对接Kubernetes的API Server。  

Upstream名不包括端口,但Upstream请求支持指定端口(如果使用非内置协议,Upstream名暂时需要加上端口号以保证构造时的解析成功)。  

每一个Upstream配置自己的独立名称UpstreamName,并添加设定着一组Address,这些Address可以是:
1. ip4
2. ip6
2. 域名
3. unix-domain-socket

### 为什么要替代nginx的Upstream

#### nginx的Upstream工作方式
1. 只支持http/https协议
2. 需要搭建一个nginx服务,启动进程占用socket等其他资源
3. 请求先打到nginx上,nginx再向远端转发请求,这会多一次通信开销

#### workflow本地Upstream工作方式
1. 协议无关,你甚至可以通过upstream访问mysql、redis、mongodb等等
2. 无需额外启动其他进程或端口,直接在进程内模拟反向代理的功能
3. 选取过程是基本的计算和查表,不会有额外的通信开销

# 使用Upstream

### 常用接口
~~~cpp
class UpstreamManager
{
public:
    static int upstream_create_consistent_hash(const std::string& name,
                                               upstream_route_t consitent_hash);
    static int upstream_create_weighted_random(const std::string& name,
                                               bool try_another);
    static int upstream_create_manual(const std::string& name,
                                      upstream_route_t select,
                                      bool try_another,
                                      upstream_route_t consitent_hash);
    static int upstream_create_vnswrr(const std::string& name);
    static int upstream_delete(const std::string& name);

public:
    static int upstream_add_server(const std::string& name,
                                   const std::string& address);
    static int upstream_add_server(const std::string& name,
                                   const std::string& address,
                                   const struct AddressParams *address_params);
    static int upstream_remove_server(const std::string& name,
                                      const std::string& address);
    ...
}
~~~

### 例1 在多个目标中随机访问
配置一个本地反向代理,将本地发出的my_proxy.name所有请求均匀的打到6个目标server上
~~~cpp
UpstreamManager::upstream_create_weighted_random(
    "my_proxy.name",
    true);//如果遇到熔断机器,再次尝试直至找到可用或全部熔断

UpstreamManager::upstream_add_server("my_proxy.name", "192.168.2.100:8081");
UpstreamManager::upstream_add_server("my_proxy.name", "192.168.2.100:8082");
UpstreamManager::upstream_add_server("my_proxy.name", "192.168.10.10");
UpstreamManager::upstream_add_server("my_proxy.name", "test.sogou.com:8080");
UpstreamManager::upstream_add_server("my_proxy.name", "abc.sogou.com");
UpstreamManager::upstream_add_server("my_proxy.name", "abc.sogou.com");
UpstreamManager::upstream_add_server("my_proxy.name", "/dev/unix_domain_scoket_sample");

auto *http_task = WFTaskFactory::create_http_task("http://my_proxy.name/somepath?a=10", 0, 0, nullptr);
http_task->start();
~~~
基本原理
1. 随机选择一个目标
2. 如果try_another配置为true,那么将在所有存活的目标中随机选择一个
3. 仅在main中选择,选中目标所在group的主备和无group的备都视为有效的可选对象

### 例2 在多个目标中按照权重大小随机访问
配置一个本地反向代理,将本地发出的weighted.random所有请求按照5/20/1的权重分配打到3个目标server上
~~~cpp
UpstreamManager::upstream_create_weighted_random(
    "weighted.random",
    false);//如果遇到熔断机器,不再尝试,这种情况下此次请求必定失败

AddressParams address_params = ADDRESS_PARAMS_DEFAULT;
address_params.weight = 5;//权重为5
UpstreamManager::upstream_add_server("weighted.random", "192.168.2.100:8081", &address_params);//权重5
address_params.weight = 20;//权重为20
UpstreamManager::upstream_add_server("weighted.random", "192.168.2.100:8082", &address_params);//权重20
UpstreamManager::upstream_add_server("weighted.random", "abc.sogou.com");//权重1

auto *http_task = WFTaskFactory::create_http_task("http://weighted.random:9090", 0, 0, nullptr);
http_task->start();
~~~
基本原理
1. 按照权重分配,随机选择一个目标,权重越大概率越大
2. 如果try_another配置为true,那么将在所有存活的目标中按照权重分配随机选择一个
3. 仅在main中选择,选中目标所在group的主备和无group的备都视为有效的可选对象

### 例3 在多个目标中按照框架默认的一致性哈希访问
~~~cpp
UpstreamManager::upstream_create_consistent_hash(
    "abc.local",
    nullptr);//nullptr代表使用框架默认的一致性哈希函数

UpstreamManager::upstream_add_server("abc.local", "192.168.2.100:8081");
UpstreamManager::upstream_add_server("abc.local", "192.168.2.100:8082");
UpstreamManager::upstream_add_server("abc.local", "192.168.10.10");
UpstreamManager::upstream_add_server("abc.local", "test.sogou.com:8080");
UpstreamManager::upstream_add_server("abc.local", "abc.sogou.com");

auto *http_task = WFTaskFactory::create_http_task("http://abc.local/service/method", 0, 0, nullptr);
http_task->start();
~~~
基本原理
1. 每1个main视为16个虚拟节点
2. 框架会使用std::hash对所有节点的address+虚拟index+此address加到此upstream的次数进行运算,作为一致性哈希的node值
3. 框架会使用std::hash对path+query+fragment进行运算,作为一致性哈希data值
4. 每次都选择存活node最近的值作为目标
5. 对于每一个main、只要有存活group内main/有存活group内backup/有存活no group backup,即视为存活
6. 如果upstream_add_server()时加上AddressParams,并配上权重weight,则每1个main视为16 * weight个虚拟节点,适用于带权一致性哈希或者希望一致性哈希标准差更小的场景

### 例4 自定义一致性哈希函数
~~~cpp
UpstreamManager::upstream_create_consistent_hash(
    "abc.local",
    [](const char *path, const char *query, const char *fragment) -> unsigned int {
        unsigned int hash = 0;

        while (*path)
            hash = (hash * 131) + (*path++);

        while (*query)
            hash = (hash * 131) + (*query++);

        while (*fragment)
            hash = (hash * 131) + (*fragment++);

        return hash;
    });

UpstreamManager::upstream_add_server("abc.local", "192.168.2.100:8081");
UpstreamManager::upstream_add_server("abc.local", "192.168.2.100:8082");
UpstreamManager::upstream_add_server("abc.local", "192.168.10.10");
UpstreamManager::upstream_add_server("abc.local", "test.sogou.com:8080");
UpstreamManager::upstream_add_server("abc.local", "abc.sogou.com");

auto *http_task = WFTaskFactory::create_http_task("http://abc.local/sompath?a=1#flag100", 0, 0, nullptr);
http_task->start();
~~~
基本原理
1. 框架会使用用户自定义的一致性哈希函数作为data值
2. 其余与上例原理一致

### 例5 自定义选取策略
~~~cpp
UpstreamManager::upstream_create_manual(
    "xyz.cdn",
    [](const char *path, const char *query, const char *fragment) -> unsigned int {
        return atoi(fragment);
    },
    true,//如果选择到已经熔断的目标,将进行二次选取
    nullptr);//nullptr代表二次选取时使用框架默认的一致性哈希函数

UpstreamManager::upstream_add_server("xyz.cdn", "192.168.2.100:8081");
UpstreamManager::upstream_add_server("xyz.cdn", "192.168.2.100:8082");
UpstreamManager::upstream_add_server("xyz.cdn", "192.168.10.10");
UpstreamManager::upstream_add_server("xyz.cdn", "test.sogou.com:8080");
UpstreamManager::upstream_add_server("xyz.cdn", "abc.sogou.com");

auto *http_task = WFTaskFactory::create_http_task("http://xyz.cdn/sompath?key=somename#3", 0, 0, nullptr);
http_task->start();
~~~
基本原理
1. 框架首先依据用户提供的普通选取函数、按照取模,在main列表中确定选取
2. 对于每一个main、只要有存活group内main/有存活group内backup/有存活no group backup,即视为存活
3. 如果选中目标不再存活且try_another设为true,将再使用一致性哈希函数进行二次选取
4. 如果触发二次选取,一致性哈希将保证一定会选择一个存活目标、除非全部机器都被熔断掉

### 例6 简单的主备模式
~~~cpp
UpstreamManager::upstream_create_weighted_random(
    "simple.name",
    true);//一主一备这项设什么没区别

AddressParams address_params = ADDRESS_PARAMS_DEFAULT;
address_params.server_type = 0;
UpstreamManager::upstream_add_server("simple.name", "main01.test.ted.bj.sogou", &address_params);//主
address_params.server_type = 1;
UpstreamManager::upstream_add_server("simple.name", "backup01.test.ted.gd.sogou", &address_params);//备

auto *http_task = WFTaskFactory::create_http_task("http://simple.name/request", 0, 0, nullptr);
auto *redis_task = WFTaskFactory::create_redis_task("redis://simple.name/2", 0, nullptr);
redis_task->get_req()->set_query("MGET", {"key1", "key2", "key3", "key4"});
(*http_task * redis_task).start();
~~~
基本原理
1. 主备模式与前面所展示的任何模式都不冲突,可以同时生效
2. 主备数量各自独立,没有限制。主和主之间平等,备与备之间平等,主备之间不平等。
3. 只要有主活着,请求一直会使用某一个主
4. 如果主都被熔断,备将作为替代目标接管请求直至有主恢复正常
5. 在每一个策略中,存活的备都可以作为主的存活依据

### 例7 主备+一致性哈希+分组
~~~cpp
UpstreamManager::upstream_create_consistent_hash(
    "abc.local",
    nullptr);//nullptr代表使用框架默认的一致性哈希函数

AddressParams address_params = ADDRESS_PARAMS_DEFAULT;
address_params.server_type = 0;
address_params.group_id = 1001;
UpstreamManager::upstream_add_server("abc.local", "192.168.2.100:8081", &address_params);//main in group 1001
address_params.server_type = 1;
address_params.group_id = 1001;
UpstreamManager::upstream_add_server("abc.local", "192.168.2.100:8082", &address_params);//backup for group 1001
address_params.server_type = 0;
address_params.group_id = 1002;
UpstreamManager::upstream_add_server("abc.local", "main01.test.ted.bj.sogou", &address_params);//main in group 1002
address_params.server_type = 1;
address_params.group_id = 1002;
UpstreamManager::upstream_add_server("abc.local", "backup01.test.ted.gd.sogou", &address_params);//backup for group 1002
address_params.server_type = 1;
address_params.group_id = -1;
UpstreamManager::upstream_add_server("abc.local", "test.sogou.com:8080", &address_params);//backup for no group mean backup for all group and no group
UpstreamManager::upstream_add_server("abc.local", "abc.sogou.com");//main, no group

auto *http_task = WFTaskFactory::create_http_task("http://abc.local/service/method", 0, 0, nullptr);
http_task->start();
~~~
基本原理
1. 组号-1代表无组,这种目标不属于任何组
2. 无组的main之间是平等的,甚至可以视为同一个组。但与有组的main之间是隔离的
3. 无组的backup可以为全局任何组目标/任何无组目标作为备
4. 组号可以区分哪些主备是在一起工作的
5. 不同组之间的备是相互隔离的,只为本组的main服务
6. 添加目标的默认组号-1,type为0,表示主节点。

### 例8 NVSWRR平滑按权重选取策略
~~~cpp
UpstreamManager::upstream_create_vnswrr("nvswrr.random");

AddressParams address_params = ADDRESS_PARAMS_DEFAULT;
address_params.weight = 3;//权重为3
UpstreamManager::upstream_add_server("nvswrr.random", "192.168.2.100:8081", &address_params);//权重3
address_params.weight = 2;//权重为2
UpstreamManager::upstream_add_server("nvswrr.random", "192.168.2.100:8082", &address_params);//权重2
UpstreamManager::upstream_add_server("nvswrr.random", "abc.sogou.com");//权重1

auto *http_task = WFTaskFactory::create_http_task("http://nvswrr.random:9090", 0, 0, nullptr);
http_task->start();
~~~
基本原理
1. 虚拟节点初始化顺序按照[SWRR算法](https://github.com/nginx/nginx/commit/52327e0627f49dbda1e8db695e63a4b0af4448b1)选取
2. 虚拟节点运行时分批初始化,避免密集型计算集中,每批次虚拟节点使用完后再进行下一批次虚拟节点列表初始化
3. 兼具[SWRR算法](https://github.com/nginx/nginx/commit/52327e0627f49dbda1e8db695e63a4b0af4448b1)的平滑、分散特点,又能具备O(1)的时间复杂度
4. 算法具体细节参见[tengine](https://github.com/alibaba/tengine/pull/1306)

# Upstream选择策略

当发起请求的url的URIHost填UpstreamName时,视做对与名字对应的Upstream发起请求,接下来将会在Upstream记录的这组Address中进行选择:
1. 权重随机策略:按照权重随机选择
2. 一致性哈希策略:框架使用标准的一致性哈希算法,用户可以自定义对请求uri的一致性哈希函数consistent_hash
3. 手动策略:根据用户提供的对请求uri的select函数进行确定的选择,如果选中了已经熔断的目标:
  a. 如果try_another为false,这次请求将返回失败
  b. 如果try_another为true,框架使用标准的一致性哈希算法重新选取,用户可以自定义对请求uri的一致性哈希函数consistent_hash
4. 主备策略:按照先主后备的优先级,只要主可以用就选择主。此策略可以与[1]、[2]、[3]中的任何一个同时生效,相互影响。

round-robin/weighted-round-robin:视为与[1]等价,暂不提供  
框架建议普通用户使用策略[2],可以保证集群具有良好的容错性和可扩展性  
对于复杂需求场景,高级用户可以使用策略[3],订制复杂的选择逻辑

# Address属性

~~~cpp
struct EndpointParams
{
    size_t max_connections;
    int connect_timeout;
    int response_timeout;
    int ssl_connect_timeout;
    bool use_tls_sni;
};

static constexpr struct EndpointParams ENDPOINT_PARAMS_DEFAULT =
{
    .max_connections        = 200,
    .connect_timeout        = 10 * 1000,
    .response_timeout       = 10 * 1000,
    .ssl_connect_timeout    = 10 * 1000,
    .use_tls_sni            = false,
};

struct AddressParams
{
    struct EndpointParams endpoint_params;
    unsigned int dns_ttl_default;
    unsigned int dns_ttl_min;
    unsigned int max_fails;
    unsigned short weight;
    int server_type;
    int group_id;
};

static constexpr struct AddressParams ADDRESS_PARAMS_DEFAULT =
{
    .endpoint_params    =    ENDPOINT_PARAMS_DEFAULT,
    .dns_ttl_default    =    12 * 3600,
    .dns_ttl_min        =    180,
    .max_fails          =    200,
    .weight             =    1,    //only for main of UPSTREAM_WEIGHTED_RANDOM
    .server_type        =    0,
    .group_id           =    -1,
};
~~~
每个Addreess都可以配置自己的自定义参数:
  * EndpointParams的max_connections, connect_timeout, response_timeout, ssl_connect_timeout:连接相关的参数
  * dns_ttl_default:dns cache中默认的ttl,单位秒,默认12小时,dns cache是针对当前进程的,即进程退出就会消失,配置也仅对当前进程有效
  * dns_ttl_min:dns最短生效时间,单位秒,默认3分钟,用于在通信失败重试时是否进行重新dns的决策
  * max_fails:触发熔断的【连续】失败次数(注:每次通信成功,计数会清零)
  * weight:权重,默认1,仅对main有效,用于Upstream随机策略选取和一致性哈希选取,权重大越容易被选中
  * server_type:主备配置,默认主。无论什么时刻,同组的主优先级永远高于其他的备
  * group_id:分组依据,默认-1。-1代表无分组(游离),游离的备可视为任何主的备,有组的备优先级永远高于游离的备。

# 关于熔断

## MTTR

平均修复时间(Mean time to repair,MTTR),是描述产品由故障状态转为工作状态时修理时间的平均值。

## 服务雪崩效应

服务雪崩效应是一种因“服务提供者的故障”(原因),导致“服务调用者故障”(结果),并将不可用逐渐/逐级放大的现象  
若不加以有效控制,效应不会收敛,而且会以几何级放大,犹如雪崩,雪崩效应因此得名  
日常表现通常为:起初只是一个很小的服务or模块异常/超时,引起下游其他依赖的服务随之异常/超时,产生连锁反应,最终导致绝大多数甚至全部的服务陷入瘫痪  
随着故障的修复,效应随之消失,所以效应持续时间通常等于MTTR

## 熔断机制

当某一个目标的错误or异常触达到预先设定的阈值条件时,暂时认为这个目标不可用,剔除目标,即熔断开启进入熔断期  
在熔断持续时间达到MTTR时长后,会进入半熔断状态,(尝试)恢复目标
如果恢复的时候发现其他所有目标都被熔断,会同一时间把所有目标恢复
熔断机制策略可以有效阻止雪崩效应

## Upstream熔断保护机制

MTTR=30秒,暂时不可配置,后续会考虑开放给用户自行配置  
当某一个Addrees连续失败次数达到设定上限(默认200次),这个Address会被熔断MTTR=30秒  
Address在熔断期间,一旦被策略选中,Upstream会根据具体配置决定是否尝试其他Address、如何尝试  

请注意满足下面1-4的某个情景,通信任务将得到一个WFT_ERR_UPSTREAM_UNAVAILABLE = 1004的错误:
1. 权重随机策略,全部目标都处于熔断期
2. 一致性哈希策略,全部目标都处于熔断期
3. 手动策略 && try_another==true,全部目标都处于熔断期  
4. 手动策略 && try_another==false,且同时满足下面三个条件:  
  1). select函数选中的main处于熔断期,,且游离的备都处于熔断期  
  2). 这个main是游离的主,或者这个main所在的group其他目标都处于熔断期  
  3). 所有游离的备都处于熔断期  

# Upstream端口优先级

1. 优先选择显式配置在Upstream Address上的端口号
2. 若没有,再选择显式配置在请求url中的端口号
3. 若都没有,使用协议默认端口号

~~~text
配置 UpstreamManager::upstream_add_server("my_proxy.name", "192.168.2.100:8081");
请求 http://my_proxy.name:456/test.html => http://192.168.2.100:8081/test.html
请求 http://my_proxy.name/test.html => http://192.168.2.100:8081/test.html
~~~

~~~text
配置 UpstreamManager::upstream_add_server("my_proxy.name", "192.168.10.10");
请求 http://my_proxy.name:456/test.html => http://192.168.10.10:456/test.html
请求 http://my_proxy.name/test.html => http://192.168.10.10:80/test.html
~~~



================================================
FILE: docs/benchmark.md
================================================
# 性能测试

Sogou C++ Workflow是一款性能优异的网络框架,本文介绍我们进行的性能测试,
包括方案、代码、结果,以及与其他同类产品的对比。

更多场景下的实验正在进行中,本文将持续更新。

## HTTP Server

HTTP Client/Server是Sogou C++ Workflow常见的应用场景,
我们首先对Server端进行实验。

### 环境

我们部署了两台相同机器作为Server和Client,软硬件配置如下:

| 软硬件 | 配置 |
|:---:|:---|
| CPU | 40 Cores, x86_64, Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz |
| Memory | 192GB |
| NIC | 25000Mbps |
| OS | CentOS 7.8.2003 |
| Kernel | Linux version 3.10.0-1127.el7.x86_64 |
| GCC | 4.8.5 |

两者间`ping`测得的RTT为0.1ms左右。

### 对照组

我们选择nginx和brpc作为对照组。
选择前者是因为它在生产中部署十分广泛,性能不俗;
对于后者,我们在本次实验中只关注HTTP Server方面的能力,
其他的特性已有[单独的实验][Sogou RPC Benchmark]进行更为详尽的测试。

事实上,我们也对此二者之外的其他某些框架同时进行了实验,
但结果其性能表现相差较远,因此未在本文中体现。

后续我们将选取更多合适的框架加入对比测试中。

### Client工具

本次实验我们使用的压测工具为[wrk][wrk]和[wrk2][wrk2]。
前者适合测试特定并发下的QPS极限和延时,
后者适合在特定QPS下测试延时分布。

我们也尝试过使用其他测试工具,例如[ab][ab]等,但无法打出足够的压力。
有鉴于此,我们也在着手开发基于Sogou C++ Workflow的benchmark工具。

### 变量和指标

一般而言,对网络框架的性能测试,切入的角度可谓纷繁多样。
通过控制不同的变量、观测不同的指标,可以探究程序在不同场景下的适应能力。

本次实验,我们选择其中最普遍常见的变量和指标:
通过控制Client并发度和承载数据的大小,来测试QPS和延时的变化情况。
另外,我们还测试了在掺杂慢请求的正常请求的延时分布。

下面依次介绍两个测试场景。

### 不同并发度和数据长度下的QPS和延时

#### 代码和配置

我们搭建了一个极其简约的HTTP服务器,
忽略掉所有的业务逻辑,
将测试点聚焦在纯粹的网络框架性能上。

代码片段如下,
完整代码移步[这里][benchmark-01 Code]。

```cpp
// ...

auto * resp = task->get_resp();
resp->add_header_pair("Date", timestamp);
resp->add_header_pair("Content-Type", "text/plain; charset=UTF-8");
resp->append_output_body_nocopy(content.data(), content.size());

// ...
```

可以从上述代码中看到,
对于到来的任何HTTP请求,
我们都会返回一段固定的内容作为Body,
并设置必要的Header,
包括代码中指明的`content-type`、`date`,
以及自动填充的`connection`和`content-length`。

HTTP Body的固定内容是在Server启动时随机生成的ASCII字符串,
其长度可以通过启动参数配置。
同时可以配置的还有使用的poller线程数和监听的端口号。
前者我们在本次测试中固定为16,
因此Sogou C++ Workflow将使用16个poller线程和20个handler线程(默认配置)。

对于nginx和brpc,
我们也构建了相同的返回内容,
并为nginx配置了40个进程、
brpc配置了40个线程。


#### 变量

我们控制并发度在`[1, 2K]`之间翻倍增长,
数据长度在`[16B, 64KB]`之间翻倍增长,
两者正交。

#### 指标

鉴于并发度和数据长度组合之后数量较多,
我们选择其中部分数据绘制为曲线。

##### 固定数据长度下QPS与并发度关系

![Concurrency and QPS][Con-QPS]

上图可以看出,当数据长度保持不变,
QPS随着并发度提高而增大,后趋于平稳。
此过程中Sogou C++ Workflow一直有明显优势,
高于brpc和nginx。
特别是数据长度为64和512的两条曲线,
并发度足够的时候,可以保持500K的QPS。

注意上图中nginx-64与nginx-512的曲线重叠度很高,
不易辨识。

##### 固定并发度下QPS与数据长度关系

![Body Length and QPS][Len-QPS]

上图可以看出,当并发度保持不变,
随着数据长度的增长,
QPS保持平稳至4K时下降。
此过程中,Sogou C++ Workflow也一直保持优势。

##### 固定数据长度下延时与并发度关系

![Concurrency and Latency][Con-Lat]

上图可以看出,保持数据长度不变,
延时随并发度提高而有所上升。
此过程中,Sogou C++ Workflow略好于brpc,
大好于nginx。

##### 固定并发度下延时与数据长度关系

![Body Length and Latency][Len-Lat]

上图可以看出,并发度保持不变时,
增大数据长度,造成延时上升。
此过程中,Sogou C++ Workflow好于nginx,
好于brpc。

### 掺杂慢请求的延时分布

#### 代码

我们在上一个测试的基础上,简单添加了一个慢请求的逻辑,
模拟业务场景中可能出现的特殊情况。

代码片段如下,
完整代码请移步[这里][benchmark-02 Code]。

```cpp
// ...

if (std::strcmp(uri, "/long_req/") == 0)
{
    auto timer_task = WFTaskFactory::create_timer_task(microseconds, nullptr);
    series_of(task)->push_back(timer_task);
}
// ...
```

我们在Server的process里进行判断,
如果访问的是特定的路径,
则添加一个`WFTimerTask`到Series的末尾,
能够模拟一个异步耗时处理过程。
类似地,对brpc使用`bthread_usleep()`函数进行异步睡眠。

#### 配置

在本次实验中,我们固定并发度为1024,数据长度为1024字节,
分别以QPS为20K、100K和200K进行正常请求测试,
测绘延时;
与此同时,有另一路压力,进行慢请求,
QPS是上述QPS的1%,
数据不计入统计。
慢请求的时长固定为5ms。

#### 延时CDF图

![Latency CDF][Lat CDF]

从上图可以看出,当QPS为20K时,
Sogou C++ Workflow略次于brpc;
当QPS为100K时,两者几乎相当;
当QPS为200K时,Sogou C++ Workflow略好于brpc。
总之,可以认为两者在这方面旗鼓相当。


[Sogou RPC Benchmark]: https://github.com/holmes1412/sogou-rpc-benchmark
[wrk]: https://github.com/wg/wrk
[wrk2]: https://github.com/giltene/wrk2
[ab]: https://httpd.apache.org/docs/2.4/programs/ab.html
[benchmark-01 Code]: ../benchmark/benchmark-01-http_server.cc
[benchmark-02 Code]: ../benchmark/benchmark-02-http_server_long_req.cc
[Con-QPS]: https://raw.githubusercontent.com/wiki/sogou/workflow/img/benchmark-01.png
[Len-QPS]: https://raw.githubusercontent.com/wiki/sogou/workflow/img/benchmark-02.png
[Con-Lat]: https://raw.githubusercontent.com/wiki/sogou/workflow/img/benchmark-03.png
[Len-Lat]: https://raw.githubusercontent.com/wiki/sogou/workflow/img/benchmark-04.png
[Lat CDF]: https://raw.githubusercontent.com/wiki/sogou/workflow/img/benchmark-05.png


================================================
FILE: docs/bugs.md
================================================
# 已知BUG列表

### OpenSSL 1.1.1及以下,出现网络任务状态为WFT_STATE_SYS_ERROR,错误为0。
这是OpenSSL 1.1.1及以下的bug,在SSL_get_error()为SSL_ERROR_SYSCALL时,errno被置为0。由于框架会把SSL_ERROR_SYSCALL转为系统错误,这会导致我们得到一个错误码0的系统错误:
~~~cpp
void callback(WFHttpTask *task)
{
    int state = task->get_state();
    int error = task->get_error();
    printf("%d, %d\n", state, error);  // 此处得到1,0,其中1是WFT_STATE_SYS_ERROR。
}
~~~
显然只有在SSL通信下可能出现在这个问题。这个bug在OpenSSL 3.0里被修复,建议升级到OpenSSL 3.0或以上。  
相关issue:https://github.com/openssl/openssl/issues/12416
### 访问HTTPS网页,当打开TLS SNI并使用upstream时出现SSL error。
当我们创建Http任务,http header里的Host域填写的是原始URL里的host部分。例如:
~~~cpp
void f()
{
    auto *task = WFTaskFactory::create_http_task("https://sogou/index.html", 0, 0, nullptr);
}
~~~
这时候http request里的Host必然填写的是"sogou"。此时如果sogou是一个upstream名,指向域名www.sogou.com。并且我们开启了TLS SNI,那么SNI server name信息就是www.sogou.com,与http header里的Host是不一致的,会导致SSL错误。  
要解决这个问题,用户可以在通过设置prepare函数,在发送请求前修改Host,让它与最终URL里的一致:
~~~cpp
void f();
{
    auto *task = WFTaskFactory::create_http_task("https://sogou/index.html", 0, 0, nullptr);
    task->set_prepare([](WFHttpTask *task){
        auto *t = static_cast<WFComplexClientTask<protocol::HttpRequest, protocol::HttpResponse> *>(task);
        task->get_req()->set_header_pair("Host", t->get_current_uri()->host);  // 这里得到实际uri里的host。
    });
}
~~~
只有打开了TLS SNI功能并使用upstream会出这个不一致问题。当然,很多时候我们配置upstream来访问http网站,也需要做这个修改,否则对方可能不会接受你的Host信息。


================================================
FILE: docs/en/CONTRIBUTING.md
================================================
# Contribution Guide

Sogou C++ Workflow is community-driven and welcomes any contributor. 

This document outlines some conventions about development steps, commit message formatting and contact points to make it easier to get your contribution accepted. 

-   [Code of Conduct](#code-of-conduct)
-   [Getting started](#getting-started)
-   [First Contribution](#first-contribution)
    -   [Find a good first topic](#find-a-good-first-topic)
    -   [Work on an existed issue](#work-on-an-existed-issue)
    -   [File a new issue](#file-a-new-issue)
-   [Contributor workflow](#contributor-workflow)
    -   [Creating Pull Requests](#creating-pull-requests)
    -   [Code Review](#code-review)
    -   [Testing and building](#testing-and-building)

# Code of Conduct

Please make sure to read and observe our [Code of Conduct](/CODE_OF_CONDUCT.md).

# Getting started

- Fork the repository on GitHub.
- Make your changes on your fork repository.
- Submit a PR.

# First Contribution

We will help you to contribute in different areas like filing issues, developing features, fixing critical bugs and getting your work reviewed and merged.

If you have questions about the development process, feel free to [file an issue](https://github.com/sogou/workflow/issues/new/choose).

We are always in need of help, be it fixing documentation, reporting bugs or writing some code.
Look at places where you feel best coding practices aren't followed, code refactoring is needed or tests are missing.
Here is how you get started.

### Find a good first topic

You can start by finding an existing issue with the 
[help-wanted](https://github.com/sogou/workflow/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) and
[good first issue](https://github.com/sogou/workflow/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
 label in this repository. These issues are well suited for new contributors as a beginner-friendly issues.

We can help new contributors who wish to work on such issues.

Another good way to contribute is to find a documentation improvement, such as a missing/broken link.

#### Work on an existed issue

When you are willing to take on an issue, just reply on the issue. The maintainer will assign it to you.

### File a new issue

While we encourage everyone to contribute code, it is also appreciated when someone reports an issue.

Please follow the prompted submission guidelines while opening an issue.

# Contributor workflow

To contribute to the code base, please follow the workflow as defined in this section.

1. Create a topic branch from where you want to base your work. This is usually master.
2. Make commits of logical units and add test case if the change fixes a bug or adds new functionality.
3. Run tests and make sure all the tests are passed.
4. Make sure your commit messages are in the proper format.
5. Push your changes to a topic branch in your fork of the repository.
6. Submit a pull request.

This is a rough outline of what a contributor's workflow looks like. For more details, you are encouraged to communicate with the reviewers before sending a pull request.

Thanks for your contributions!

## Creating Pull Requests

Our project generally follows the standard [github pull request](https://help.github.com/articles/about-pull-requests/) process.
To submit a proposed change, please develop the code/fix and add new test cases.
After that, run these local verifications before submitting pull request to predict the pass or fail of continuous integration.

## Code Review

To make it easier for your Pull Request to receive reviews, break large changes into a logical series of smaller patches which individually make easily understandable changes, and in aggregate solve a broader issue.

If this is an independent modification, then it is recommended that you provide a tutorial and corresponding documents, and communicate with us. 

## Testing and building

Make sure the  the [travis-ci](https://travis-ci.com/github/sogou/workflow/pull_requests) passed.

Once Your PR has been merged, you become a contributor. Thank you for your contribution!


================================================
FILE: docs/en/about-config.md
================================================
# About global configuration

Global configuration is used to configure default global parameters to meet the actual business requirements and improve performance. The change of the global configuration must be made before you call any intefaces in the framework. Otherwise the change may not take effect. In addition, some global configuration items can be overridden in the upstream configuration. Please see upstream documents for reference.

# Changing default configuration

[WFGlobal.h](/src/manager/WFGlobal.h) defines the struts and the default values of the global configuration.

~~~cpp
struct WFGlobalSettings
{
    struct EndpointParams endpoint_params;
    struct EndpointParams dns_server_params;
    unsigned int dns_ttl_default;   ///< in seconds, DNS TTL when network request success
    unsigned int dns_ttl_min;       ///< in seconds, DNS TTL when network request fail
    int dns_threads;
    int poller_threads;
    int handler_threads;
    int compute_threads;            ///< auto-set by system CPU number if value<=0
    int fio_max_events;
    const char *resolv_conf_path;
    const char *hosts_path;
};


static constexpr struct WFGlobalSettings GLOBAL_SETTINGS_DEFAULT =
{
    .endpoint_params    =   ENDPOINT_PARAMS_DEFAULT,
    .dns_server_params  =   ENDPOINT_PARAMS_DEFAULT,
    .dns_ttl_default    =   12 * 3600,
    .dns_ttl_min        =   180,
    .dns_threads        =   4,
    .poller_threads     =   4,
    .handler_threads    =   20,
    .compute_threads    =   -1,
	.fio_max_events     =   4096,
    .resolv_conf_path   =   "/etc/resolv.conf",
    .hosts_path         =   "/etc/hosts",
};
~~~

[EndpointParams.h](/src/manager/EndpointParams.h) defines the struture of EndpointParams and the default values.

~~~cpp

struct EndpointParams
{
    size_t max_connections;
    int connect_timeout;
    int response_timeout;
    int ssl_connect_timeout;
    bool use_tls_sni;
};

static constexpr struct EndpointParams ENDPOINT_PARAMS_DEFAULT =
{
    .max_connections        = 200,
    .connect_timeout        = 10 * 1000,
    .response_timeout       = 10 * 1000,
    .ssl_connect_timeout    = 10 * 1000,
    .use_tls_sni            = false,
};
~~~

If you want to change the default connecting timeout to 5 seconds, the default TTL for DNS to 1 hour and increase the number of poller threads for message deserialization to 10, you can follow the example below:

~~~cpp
#include "workflow/WFGlobal.h"

int main()
{
    struct WFGlobalSettings settings = GLOBAL_SETTINGS_DEFAULT;

    settings.endpoint_params.connect_timeout = 5 * 1000;
    settings.dns_ttl_default = 3600;
    settings.poller_threads = 10;
    WORKFLOW_library_init(&settings);

    ...
}

~~~

Most of the parameters are self-explanatory. Note: the ttl and related parameters in DNS configuration are in **seconds**. The timeout for endpoint is in **milliseconds**, and -1 indicates an infinite timeout.   
dns\_threads indicates the total number of threads accessing DNS in parallel, but by default, we use asynchronous DNS resolving and don't create any dns threads (Except windows platform).  
dns\_server\_params indicates parameters that we access DNS server, including the maximum cocurrent connections, and the DNS server's connecting and response timeout.  
compute\_threads indicates the number of threads used for computation. The default value is -1, meaning the number of threads is the same as the number of CPU cores in the current node.   
fio\_max\_events indicates the maximum number of concurrent asynchronous file IO events.  
resolv\_conf\_path indicates the path of dns resolving configuration file. The default value is "/etc/resolv.conf" on unix platforms and NULL on windows. On the windows platform, we still use multi-threaded dns resolving by default.  
hosts_path indicates the path of the **hosts** file. The default value is "/etc/hosts" on unix platforms. If resolv_conf_path is NULL, this configuration will be ignored.  
poller\_threads and handler\_threads are the two parameters for tuning network performance:

* poller\_threads is mainly used for epoll (kqueue) and message deserialization.
* handler\_threads is the number of threads for the callback and the process of a network task.

All resources required by the framework are applied for when they are used for the first time. For example, if a user task does not involve DNS resolution, the asynchronous DNS resolver or DNS threads will not be created.


================================================
FILE: docs/en/about-connection-context.md
================================================
# About connection context

Connection context is an advanced programming topic in this framework.  
From the previous examples, we can see that we cannot assign one specific connection for a client task or a server task.   
However, in some business scenarios, especially for the server, we may need to maintain the connection status. In other words, we need to bind a context to a connection.   
In the framework, we provide a connection context for users.

# Application senarios for connection context

HTTP is a completely stateless protocol, and HTTP session is realized with cookies. HTTP, Kafka and other stateless protocols are most friendly with our framework.   
The connection used by Redis and MySQL is obviously stateful. Redis specifies the database ID on the current connection with the SELECT command. MySQL uses a completely stateful connection.   
When you use Redis or non-transactional MySQL client tasks in the framework, the URL already contains all the information related to the connection, including:

* username and password
* database name or database ID
* the character set for MySQL

The framework will automatically log in or select  a reusable connection based on the above information, and you do not need to care about the connection context.   
Due to this limitation, in the framework, you cannot use the SELECT command of Redis and the USE command of MySQL. If you want to switch databases, you should use a new URL to create the task.   
Transactional MySQL tasks can use fixed connections. Please see MySQL documentations for relevant details.   
However, if you implement a server based on Redis protocol, you need to know the current connection status.

By using the deleter function of connection context, users can also get notified when the connection was closed by the peer.

# How to use connection context

Note: generally, only the server tasks need to use the connection context, and the connection context is used only inside the process function, which is also the safest and simplest.   
You can also use or modify the connection context in the callback, but you should consider the concurrency problem. We’ll discuss the related issues in details.   
You can obtain the connection object in any network task through interfaces, and then obtain or modify the connection context. [WFTask.h](../src/factory/WFTask.h) contains a sample call:

~~~cpp
template<class REQ, class, RESP>
class WFNetworkTask : public CommRequest
{
public:
    virtual WFConnection *get_connection() const = 0;
    ...
};
~~~

[WFConneciton.h ](../src/factory/WFConnection.h)contains the interfaces for performing operations on the connection objects:

~~~cpp
class WFConnection : public CommConnection
{
public:
    void *get_context() const;
    void set_context(void *context, std::function<void (void *)> deleter);
    void set_context(void *context);
    void *test_set_context(void *test_context, void *new_context,
                           std::function<void (void *)> deleter);
    void *test_set_context(void *test_context, void *new_context);
};
~~~

**get\_connection()** can only be called in a process or a callback. If you call it in the callback, please check whether the return value is NULL.   
If you get the WFConnection object successfully, you can perform operations on the connection context. A connection context is a void \* pointer. When the connection is closed, the deleter is automatically called. When using the setting context functions without ``deleter`` argument, the original deleter will be kept unchanged.

# Timing and concurrency for accessing connection context

When a client task is created, the connection object is not determined. Thus, for all client tasks, you can only use the connection context in the callback.   
For server tasks, you may use connection context in the process or the callback.   
When you use connection context in a callback, you need to consider concurrency, because the same connection may be reused by multiple tasks and reach the callbacks at the same time.   
Therefore, we recommend that the connection context should be accessed or modified only in the process function, because the connection will not be reused or released in the process, which is the simplest and safest.   
Note: the process in the above paragraphs means only the places inside the process function. In the places after the process function and before the callback, get\_connection() always returns NULL.   
**test\_set\_context()** in the WFConnection is used to solve the concurrency issues for using connection context in the callback, but it is not recommended.   
In a word, if you are not very familiar with the system implementation, please use the connection context only in the process function of the server tasks.

# Example: how to reduce the request header fields in HTTP/1.1

HTTP protocol is a stateless connection protocol, and a complete header must be sent for every request on the same connection.   
If the cookie in the request is very large, it will obviously increase the data transmission overload. You can use the server-side connection context to solve this issue.   
You can specify that the cookie in the HTTP request is valid for all subsequent requests on the same connection, and omit the cookie in the subsequent request headers.   
Please see the following codes on the server side:

~~~cpp
void process(WFHttpTask *server_task)
{
    protocol::HttpRequest *req = server_task->get_req();
    protocol::HttpHeaderCursor cursor(req);
    WFConnection *conn = server_task->get_connection();
    void *context = conn->get_context();
    std::string cookie;

    if (cursor.find("Cookie", cookie))
    {
        if (context)
            delete (std::string *)context;
        context = new std::string(cookie);
        conn->set_context(context, [](void *p) { delete (std::string *)p; });
    }
    else if (context)
        cookie = *(std::string *)context;

    ...
}
~~~

In this way, if you arrange with the client that the cookie is transmitted only at the first request of the connection, the traffic can be reduced.   
The implementation in the client side needs to use a new **prepare** function. Please see the codes below:

~~~cpp

using namespace protocol;

void prepare_func(WFHttpTask *task)
{
    if (task->get_task_seq() == 0)
        task->get_req()->add_header_pair("Cookie", my_cookie);
}

int some_function()
{
    WFHttpTask *task = WFTaskFactory::create_http_task(...);
    task->set_prepare(prepare_func);
    ...
}
~~~

In the example, when the HTTP task is the first request on the connection, the cookie is set. If it is not the first request, according to our arrangement, we do not set the cookie.   
In addition, you may use the connection context safely in the **prepare** function. **prepare** will not be concurrent on the same connection.  


================================================
FILE: docs/en/about-counter.md
================================================
# About counter

Counters are very important basic tasks in our framework. A counter is essentially a semaphore that does not occupy thread.   
Counters are mainly used for workflow control. It includes anonymous counters and named counters, and can realize very complex business logic.

# Creating a counter

As a counter is also a task, it is created through WFTaskFactory. You can create a counter with one of the following two methods:

~~~cpp
using counter_callback_t = std::function<void (WFCounterTask *)>;

class WFTaskFactory
{
    ...
    static WFCounterTask *create_counter_task(unsigned int target_value,
                                              counter_callback_t callback);
                                              
    static WFCounterTask *create_counter_task(const std::string& counter_name,
                                              unsigned int target_value,
                                              counter_callback_t callback);

    ...
};
~~~

Each counter contains a target\_value. When the count in the counter reaches the target\_value, its callback is called.   
The above two interfaces generate a anonymous counter and a named counter respectively. The anonymous counter directly increases the count through the count method in the WFCounterTask:

~~~cpp
class WFCounterTask
{
public:
    virtual void count()
    {
        ...
    }
    ...
}
~~~

If a counter\_name is passed when you create a counter, a named counter is generated, and the count can be increased with the count\_by\_name function.

# Creating parallel tasks with anonymous counters

In the example of [parallel wget](/docs/en/tutorial-06-parallel_wget.md), we created a ParallelWork to achieve the parallel execution of several series.   
With the combination of ParallelWork and SeriesWork, you can build series-parallel graphs in any form, which can meet the requirements in most scenarios.   
Counters allow us to build more complex dependencies between the tasks, such as a fully connected neural network.   
The following simple code can replace ParallelWork to realize parallel HTTP crawling.

~~~cpp
void http_callback(WFHttpTask *task)
{
    /* Save http page. */
    ...

    WFCounterTask *counter = (WFCounterTask *)task->user_data;
    counter->count();
}

std::mutex mutex;
std::condition_variable cond;
bool finished = false;

void counter_callback(WFCounterTask *counter)
{
    mutex.lock();
    finished = true;
    cond.notify_one();
    mutex.unlock();
}

int main(int argc, char *argv[])
{
    WFCounterTask *counter = create_counter_task(url_count, counter_callback);
    WFHttpTask *task;
    std::string url[url_count];

    /* init urls */
    ...

    for (int i = 0; i < url_count; i++)
    {
        task = create_http_task(url[i], http_callback);
        task->user_data = counter;
        task->start();
    }

    counter->start();
    std::unique_lock<std:mutex> lock(mutex);
    while (!finished)   
        cond.wait(lock);
    lock.unlock();
    return 0;
}
~~~

The above code creates a counter with the target value as url\_count, and calls the count once after each HTTP task is completed.   
Note that the times **count()** a anonymous counter cannot exceed it's target value. Otherwise the counter may have been destroyed after the callback, and the program behavior is undefined.   
The call of **counter->start()** can be placed before the for loop. After a counter is created, you can call its count interface, no matter whether the counter has been started or not.   
You can also use **counter->WFCounterTask::count()** to call the count interface of an anonymous counter; this can be used in performance-sensitive applications.

# Using a server together with other asynchronous engines

In some cases, our server may need to call asynchronous clients in other frameworks and wait for the results. A simple method is that we wait synchronously in the process and then are waken up through conditional variables.   
Its disadvantage is that we occupy a processing thread and turn asynchronous clients in other frameworks into synchronous clients. But with the counter method, we can wait without occupying threads. The method is very simple:

~~~cpp

void some_callback(void *context)
{
    protocol::HttpResponse *resp = get_resp_from_context(context);
    WFCounterTask *counter = get_counter_from_context(context);
    /* write data to resp. */
    ...
    counter->count();
}

void process(WFHttpTask *task)
{
    WFCounterTask *counter = WFTaskFactory::create_counter_task(1, nullptr);

    SomeOtherAsyncClient client(some_callback, context);

    *series_of(task) << counter;
}
~~~

Here, we can consider the series of a server task as a coroutine, and the counter whose target value is 1 can be considered as a conditional variable.

# Named counters

When the count operation is executed on the anonymous counter, the counter object pointer is directly accessed. This inevitably requires that the number of calls to count should not exceed the target value during operation.   
But imagine an application scenario where we start four tasks at the same time, and as long as any three tasks are completed, the workflow can continue.   
We can use a counter with a target value of 3, and count once after each task is completed. As long as three tasks are completed, the callback of the counter will be executed.   
But the problem is that when the fourth task is finished and **counter->count()** is called again, the counter is already a wild pointer and the program crashes.   
In this case, we can use named counters to solve this problem. By naming the counter and counting by name, we can have the following implementation:

~~~cpp
void counter_callback(WFCounterTask *counter)
{
    WFRedisTask *next = WFTaskFactory::create_redis_task(...);
    series_of(counter)->push_back(next);
}

int main(void)
{
    WFHttpTask *tasks[4];
    WFCounterTask *counter;

    counter = WFTaskFactory::create_counter_task("c1", 3, counter_callback);
    counter->start();

    for (int i = 0; i < 4; i++)
    {
        tasks[i] = WFTaskFactory::create_http_task(..., [](WFHttpTask *task){
                                            WFTaskFactory::count_by_name("c1"); });
        tasks[i]->start();
    }

    ...

}
~~~

In this example, four concurrent HTTP tasks are started, three of which are completed, and a Redis task is started immediately. In the practical application, you may need to add the code of data transmission.   
In the example, a counter named "c1" is created, and in the HTTP callback, call **WFTaskFactory::count\_by\_name()** to increase the count.

~~~cpp
class WFTaskFactory
{
    ...
    static int count_by_name(const std::string& counter_name);

    static int count_by_name(const std::string& counter_name, unsigned int n);
    ...
};
~~~

You can pass an integer n to **WFTaskFactory::count\_by\_name**, indicating the count value to be increased in this operation. Obviously:   
**count\_by\_name("c1")** is equivalent to **count\_by\_name("c1", 1)**.   
If the "c1" counter does not exist (not created or already completed), the operation on "c1" will have no effect, so the wild pointer problem in an anonymous counter will not happen here.  
The **count\_by\_name()** function returns the number of counters that was waked up by the operation. When **n** is greater that 1, more than one counter may reach target value.

# Definition of the detailed behaviors of named counters

When you **call WFTaskFactory::count\_by\_name(name, n)**:

* if the name does not exist (not created or already completed), there is no action.
* if there is only one counter with that name:
  * if the remaining value of the counter is less than or equal to n, the counting is completed, the callback is called, and the counter is destroyed. end.
  * if the remaining value of the counter is greater than n, the count value is increased by n. end.
* if there are multiple counters with that name:
  * according to the order of creation, take the first counter and assume that its remaining value is m:
    * if m is greater than n, the count value is increased by n. end (the remaining value is m-n).
    * if m is less than or equal to n, the counting is completed, the callback is called, and the counter is destroyed. set n = n-m.
      * If n is 0, the procedure ends.
      * If n is greater than 0, take out the next counter with the same name and repeat the whole operation.

Although the description is very complicated, it can be summed up in one sentence. Access all counters with that name according to the order of creation one by one until n is 0.   
In other words, one **count\_by\_name(name, n)** may wake up multiple counters.   
The counters can be used to realize very complex business logic if you can use them well. In our framework, counters are often used to implement asynchronous locks or to build channels between tasks. It is more like a control task in form.


================================================
FILE: docs/en/about-dns.md
================================================
# About DNS
When using a domain name to request the network, you first need to obtain the server address through domain name resolution, and then use the network address to make subsequent requests. Workflow has implemented a complete domain name resolution and caching system. Generally speaking, users can initiate network tasks smoothly without knowing the internal mechanism.

## DNS related configuration
Global configuration in Workflow includes

~~~cpp
struct WFGlobalSettings
{
    struct EndpointParams endpoint_params;
    struct EndpointParams dns_server_params;
    unsigned int dns_ttl_default;
    unsigned int dns_ttl_min;
    int dns_threads;
    int poller_threads;
    int handler_threads;
    int compute_threads;
    int fio_max_events;
    const char *resolv_conf_path;
    const char *hosts_path;
};
~~~

Among them, the configuration items related to domain name resolution include

* dns_server_params
  * address_family: This item will be explained later
  * max_connections: The maximum number of concurrent requests sent to the DNS server, the default is 200
  * connect_timeout/response_timeout/ssl_connect_timeout: refer to [timeout](about-timeout.md) for related instructions
* dns_threads: When using synchronous mode to implement domain name resolution, the resolution operation will be executed in an independent thread pool. This item specifies the number of threads in the thread pool. The default is 4.
* dns_ttl_default: The result of successful domain name resolution will be placed in the domain name cache. This item specifies its survival time in seconds. The default value is 1 hour. When the resolution result expires, it will be re-parsed to obtain the latest content.
* dns_ttl_min: When communication fails, the cached result may have expired. This item specifies a shorter survival time. When communication fails, the cache is updated at a more frequent rate. The unit is seconds. The default value is 1 minute.
* resolv_conf_path: This file saves the configuration related to accessing DNS. It is usually located in `/etc/resolv.conf` on common Linux distributions. If this item is configured as `NULL`, it means using multi-threaded synchronous resolution mode.
* hosts_path: This file is a local domain name lookup table. If the resolved domain name hits this table, it will not initiate a request to DNS. It is usually located in `/etc/hosts` on common Linux distributions. If this item is configured as `NULL` means not to use the lookup table

### resolv.conf extensions
Workflow has extended the `resolv.conf` configuration file. Users can modify the configuration to support the `DNS over TLS(DoT)`. **Note** directly modifying `/etc/resolv.conf` will affect other processes. You can make a copy of the file for modification, and modify the `resolv_conf_path` configuration of Workflow to the path of the new file. For example, a `nameserver` using the `dnss` protocol will connect via SSL

~~~bash
nameserver dnss://8.8.8.8/
nameserver dnss://[2001:4860:4860::8888]/
~~~

### Address Family
In some network environments, although the machine supports IPv6, it cannot communicate with the outside because it has not been assigned a public IPv6 address (for example, the local IPv6 address starts with `fe80`). At this time, you can set `endpoint_params.address_family` to `AF_INET` to force only IPv4 addresses to be resolved during domain name resolution. Similarly, the `resolv.conf` file may specify both the IPv4 address and the IPv6 address of the `nameserver`. In this case, you can set `dns_server_params.address_family` to `AF_INET` or `AF_INET6` to force the use of only IPv4 or IPv6 addresses to access DNS.

### Use Upstream configuration
The global configuration takes effect for each domain name by default. If you need to specify different configurations for certain domain names, you can use the [Upstream](./about-upstream.md#Address attribute) function. Using Upstream, you can individually specify the `dns_ttl_default` and `dns_ttl_min` configuration items, and individually specify the IP address family used by the domain name through `endpoint_params.address_family`.


## Domain name resolution and caching strategy
Network tasks usually require domain name resolution to obtain the IP address that needs to be accessed. The relevant strategies for domain name resolution in Workflow are as follows:

1. Check whether the domain name cache has the IP address corresponding to the domain name. If there is a cache and it has not expired, use this set of IP addresses.
2. Check whether the domain name is an IPv4, IPv6 address or `Unix Domain Socket`. If so, use the address directly without initiating domain name resolution.
3. Check whether the `hosts_path` file contains the IP address corresponding to the domain name. If so, use the address directly.
4. Obtain an asynchronous lock to ensure that a resolution request for the same domain name is only initiated once at the same time, and initiate a resolution request to DNS
5. After successful parsing, the parsing result will be saved to the domain name cache of the current process for next use, and the asynchronous lock will be released.
6. After the parsing fails, the asynchronous lock will be released and the failure reason will be notified to all tasks waiting on the same asynchronous lock. New tasks initiated after the notification is completed will request DNS again.

Many scenarios that require a large number of network requests will be equipped with a domain name caching component. If a resolution request is sent to the DNS every time a network task is initiated, the DNS will inevitably be overwhelmed. Workflow sets the cache survival time (dns_ttl_default and dns_ttl_min) to ensure that the cache will expire after a reasonable period of time and the domain name resolution results can be updated in a timely manner. When a cache item of a domain name expires, the first task found to be expired will extend its survival time by 5 seconds and initiate a resolution request to DNS. Requests on the same domain name within 5 seconds will directly use the cached DNS resolution results without waiting.

The asynchronous lock mechanism can ensure that the resolution request for the **same domain name** is only initiated once at the same time. Without lock protection, if a large number of network tasks are initiated for the same domain name in a short period of time, each task will be unable to be retrieved from the cache. Too many resolution request to DNS will place a large and unnecessary burden on DNS. The same domain name here represents the `(host, port, family)` triplet. If a domain name is required to only use IPv4 and IPv6 through Upstream, they will be protected by different asynchronous locks, and it is possible to request DNS at the same time.


### Asynchronous domain name resolution
Workflow implements a complete DNS task. If the `resolv_conf_path` configuration item is specified, an asynchronous request will be used when initiating domain name resolution to DNS. Under Unix-like systems, Workflow uses `/etc/resolv.conf` as the value of this configuration by default. Asynchronous domain name resolution does not block any threads or monopolize the thread pool, and can complete the task of domain name resolution more efficiently.

### Synchronous domain name resolution
If `resolv_conf_path` is specified as `NULL`, synchronous domain name resolution will be achieved by calling the `getaddrinfo` function. This method will use an independent thread pool, and the number of threads is configured through the `dns_threads` parameter. If a large number of domain name resolution requests need to be initiated in a short period of time, the synchronization method will cause a large delay.


================================================
FILE: docs/en/about-error.md
================================================
# About error handling

Error handling is an important and complex problem in any software system. Within our framework, error handling is ubiquitous and extremely cumbersome.   
In the interfaces we exposed to users, we try to make things as simple as possible, but users still inevitably need to know some error messages.

### Disabling C++ exceptions

C++ exceptions are not used in our framework. When you compile your own code, it is best to add **-fno-exceptions** flag to reduce the code size.   
According to the common practice in the industry, we ignore the possibility of the failure of **new** operation, and avoid using new to allocate large blocks of memory internally. And there are error checks in memory allocation in C style.

### About factory functions

From the previous examples, you can see that all task and series are generated from two factory classes, WFTaskFactory or Workflow.   
These factory classes, as well as more factory class interfaces that we may encounter in the future, ensure success. In other words, they never return NULL. And you do not need to check the return value.   
To achieve this goal, even when the URL is illegal, the factory still generates the task normally. And you will get the error in the callback of the task.

### States and error codes of a task

In the previous examples, you often see such codes in the callback:

~~~cpp
void callback(WFXxxTask *task)
{
    int state = task->get_state();
    int error = task->get_error();
    ...
}
~~~

in which, the state indicates the end state of a task. [WFTask.h](/src/factory/WFTask.h) contains all possible states:

~~~cpp
enum
{
    WFT_STATE_UNDEFINED = -1,
    WFT_STATE_SUCCESS = CS_STATE_SUCCESS,
    WFT_STATE_TOREPLY = CS_STATE_TOREPLY,        /* for server task only */
    WFT_STATE_NOREPLY = CS_STATE_TOREPLY + 1,    /* for server task only */
    WFT_STATE_SYS_ERROR = CS_STATE_ERROR,
    WFT_STATE_SSL_ERROR = 65,
    WFT_STATE_DNS_ERROR = 66,                    /* for client task only */
    WFT_STATE_TASK_ERROR = 67,
    WFT_STATE_ABORTED = CS_STATE_STOPPED         /* main process terminated */
};
~~~

##### Please note the following states:

* SUCCESS: the task is successfully completed. The client receives the complete reply, or the server writes the reply completely into the send buffer (but there is no guarantee that the peer will receive it).
* SYS\_ERROR: system error. In this case, use **task->get\_error()** to get the system error code **errno**.
  * When **get\_error()** gets ETIMEDOUT, you can call **task->get\_timeout\_reason()** to get the timeout reasons.
* DNS\_ERROR: DNS resolution error. Use **get\_error()** to get the return code of **getaddrinfo()**. For DNS, please see the article for details [about-dns.md](/docs/en/about-dns.md). 
  * The server task never has a DNS\_ERROR.
* SSL\_ERROR: SSL error. Use **get\_error()** to get the return value of **SSL\_get\_error()**.
  * Currently SSL error information is not complete, and you can not get the value of **ERR\_get\_error()**. Therefore, basically there are three possible return value of **get\_error()**:
    * SSL\_ERROR\_ZERO\_RETURN, SSL\_ERROR\_X509\_LOOKUP, SSL\_ERROR\_SSL.
  * We will consider adding more detailed SSL error information in the future versions.
* TASK\_ERROR: task errors. Common errors include illegal URL, login failure, etc. [WFTaskError.h](/src/factory/WFTaskError.h) lists the return values of **get\_error()**.

##### You do not need to pay attention to the following states:

* UNDEFINED: Client tasks that have just been created and have not yet been run are in UNDEFINED state.
* TOREPLY: Server tasks that have not sent replies or called **task->noreply()** are in TOREPLY state.
* NOREPLY: Server tasks that have called **task->noreply()** are always in NOREPLY state. The callback of these tasks are also in NOREPLY state. And the connection will be closed.

### Other error handling requirements

In addition to the error handling of the task itself, you also need to check the errors of the message interfaces of various protocols. Generally, these interfaces indicate errors by returning false, and show the error reasons in the errno.   
In addition, you may encounter more complicated error messages when you use some complex operations. You will learn them in detailed documents.


================================================
FILE: docs/en/about-exit.md
================================================
# About exit

As most of our calls are non-blocking, we need some mechanisms to prevent the main function from exiting early in the previous examples.   
For example, in the wget example, we wait for the user's Ctrl-C, or in the parallel\_wget example, we wake up the main thread after all crawling tasks are finished.   
In several server examples, the **stop()** operation is blocking, which can ensure the normal end of all server tasks and the safe exit of the main thread.

# Principles on the safe exit

Generally, as long as you writes the program normally and follows the methods in the examples, there is little doubt about exit. However, it is still necessary to define the conditions for normal program exit.

* You can't call **exit()** of the system in any callback functions such as the callback or t
Download .txt
gitextract_sj2a21rt/

├── .editorconfig
├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── xmake.yml
├── .gitignore
├── BUILD
├── CMakeLists.txt
├── CMakeLists_Headers.txt
├── CODE_OF_CONDUCT.md
├── GNUmakefile
├── LICENSE
├── LICENSE_GPLV2
├── README.md
├── README_cn.md
├── WORKSPACE
├── benchmark/
│   ├── CMakeLists.txt
│   ├── GNUmakefile
│   ├── README.md
│   ├── benchmark-01-http_server.cc
│   ├── benchmark-02-http_server_long_req.cc
│   ├── util/
│   │   ├── args.h
│   │   ├── content.h
│   │   └── date.h
│   └── xmake.lua
├── docs/
│   ├── about-conditional.md
│   ├── about-config.md
│   ├── about-connection-context.md
│   ├── about-counter.md
│   ├── about-dns.md
│   ├── about-error.md
│   ├── about-exit.md
│   ├── about-go-task.md
│   ├── about-module.md
│   ├── about-resource-pool.md
│   ├── about-selector.md
│   ├── about-service-governance.md
│   ├── about-timeout.md
│   ├── about-timer.md
│   ├── about-tlv-message.md
│   ├── about-upstream.md
│   ├── benchmark.md
│   ├── bugs.md
│   ├── en/
│   │   ├── CONTRIBUTING.md
│   │   ├── about-config.md
│   │   ├── about-connection-context.md
│   │   ├── about-counter.md
│   │   ├── about-dns.md
│   │   ├── about-error.md
│   │   ├── about-exit.md
│   │   ├── about-go-task.md
│   │   ├── about-module.md
│   │   ├── about-resource-pool.md
│   │   ├── about-service-governance.md
│   │   ├── about-timeout.md
│   │   ├── about-timer.md
│   │   ├── about-tlv-message.md
│   │   ├── about-upstream.md
│   │   ├── tutorial-01-wget.md
│   │   ├── tutorial-02-redis_cli.md
│   │   ├── tutorial-03-wget_to_redis.md
│   │   ├── tutorial-04-http_echo_server.md
│   │   ├── tutorial-05-http_proxy.md
│   │   ├── tutorial-06-parallel_wget.md
│   │   ├── tutorial-07-sort_task.md
│   │   ├── tutorial-08-matrix_multiply.md
│   │   ├── tutorial-09-http_file_server.md
│   │   ├── tutorial-10-user_defined_protocol.md
│   │   ├── tutorial-11-graph_task.md
│   │   ├── tutorial-12-mysql_cli.md
│   │   ├── tutorial-13-kafka_cli.md
│   │   └── xmake.md
│   ├── tutorial-01-wget.md
│   ├── tutorial-02-redis_cli.md
│   ├── tutorial-03-wget_to_redis.md
│   ├── tutorial-04-http_echo_server.md
│   ├── tutorial-05-http_proxy.md
│   ├── tutorial-06-parallel_wget.md
│   ├── tutorial-07-sort_task.md
│   ├── tutorial-08-matrix_multiply.md
│   ├── tutorial-09-http_file_server.md
│   ├── tutorial-10-user_defined_protocol.md
│   ├── tutorial-11-graph_task.md
│   ├── tutorial-12-mysql_cli.md
│   ├── tutorial-13-kafka_cli.md
│   ├── tutorial-15-name_service.md
│   ├── tutorial-17-dns_cli.md
│   ├── tutorial-18-redis_subscriber.md
│   ├── tutorial-19-dns_server.md
│   └── xmake.md
├── src/
│   ├── CMakeLists.txt
│   ├── client/
│   │   ├── CMakeLists.txt
│   │   ├── WFConsulClient.cc
│   │   ├── WFConsulClient.h
│   │   ├── WFDnsClient.cc
│   │   ├── WFDnsClient.h
│   │   ├── WFHttpChunkedClient.cc
│   │   ├── WFHttpChunkedClient.h
│   │   ├── WFKafkaClient.cc
│   │   ├── WFKafkaClient.h
│   │   ├── WFMySQLConnection.cc
│   │   ├── WFMySQLConnection.h
│   │   ├── WFRedisSubscriber.cc
│   │   ├── WFRedisSubscriber.h
│   │   └── xmake.lua
│   ├── factory/
│   │   ├── CMakeLists.txt
│   │   ├── DnsTaskImpl.cc
│   │   ├── FileTaskImpl.cc
│   │   ├── HttpTaskImpl.cc
│   │   ├── HttpTaskImpl.inl
│   │   ├── KafkaTaskImpl.cc
│   │   ├── KafkaTaskImpl.inl
│   │   ├── MySQLTaskImpl.cc
│   │   ├── RedisTaskImpl.cc
│   │   ├── RedisTaskImpl.inl
│   │   ├── WFAlgoTaskFactory.h
│   │   ├── WFAlgoTaskFactory.inl
│   │   ├── WFConnection.h
│   │   ├── WFGraphTask.cc
│   │   ├── WFGraphTask.h
│   │   ├── WFMessageQueue.cc
│   │   ├── WFMessageQueue.h
│   │   ├── WFOperator.h
│   │   ├── WFResourcePool.cc
│   │   ├── WFResourcePool.h
│   │   ├── WFTask.h
│   │   ├── WFTask.inl
│   │   ├── WFTaskError.h
│   │   ├── WFTaskFactory.cc
│   │   ├── WFTaskFactory.h
│   │   ├── WFTaskFactory.inl
│   │   ├── Workflow.cc
│   │   ├── Workflow.h
│   │   └── xmake.lua
│   ├── kernel/
│   │   ├── CMakeLists.txt
│   │   ├── CommRequest.cc
│   │   ├── CommRequest.h
│   │   ├── CommScheduler.cc
│   │   ├── CommScheduler.h
│   │   ├── Communicator.cc
│   │   ├── Communicator.h
│   │   ├── ExecRequest.h
│   │   ├── Executor.cc
│   │   ├── Executor.h
│   │   ├── IORequest.h
│   │   ├── IOService_linux.cc
│   │   ├── IOService_linux.h
│   │   ├── IOService_thread.cc
│   │   ├── IOService_thread.h
│   │   ├── SleepRequest.h
│   │   ├── SubTask.cc
│   │   ├── SubTask.h
│   │   ├── list.h
│   │   ├── mpoller.c
│   │   ├── mpoller.h
│   │   ├── msgqueue.c
│   │   ├── msgqueue.h
│   │   ├── poller.c
│   │   ├── poller.h
│   │   ├── rbtree.c
│   │   ├── rbtree.h
│   │   ├── thrdpool.c
│   │   ├── thrdpool.h
│   │   └── xmake.lua
│   ├── manager/
│   │   ├── CMakeLists.txt
│   │   ├── DnsCache.cc
│   │   ├── DnsCache.h
│   │   ├── EndpointParams.h
│   │   ├── RouteManager.cc
│   │   ├── RouteManager.h
│   │   ├── UpstreamManager.cc
│   │   ├── UpstreamManager.h
│   │   ├── WFFacilities.h
│   │   ├── WFFacilities.inl
│   │   ├── WFFuture.h
│   │   ├── WFGlobal.cc
│   │   ├── WFGlobal.h
│   │   └── xmake.lua
│   ├── nameservice/
│   │   ├── CMakeLists.txt
│   │   ├── UpstreamPolicies.cc
│   │   ├── UpstreamPolicies.h
│   │   ├── WFDnsResolver.cc
│   │   ├── WFDnsResolver.h
│   │   ├── WFNameService.cc
│   │   ├── WFNameService.h
│   │   ├── WFServiceGovernance.cc
│   │   ├── WFServiceGovernance.h
│   │   └── xmake.lua
│   ├── protocol/
│   │   ├── CMakeLists.txt
│   │   ├── ConsulDataTypes.h
│   │   ├── DnsMessage.cc
│   │   ├── DnsMessage.h
│   │   ├── DnsUtil.cc
│   │   ├── DnsUtil.h
│   │   ├── HttpMessage.cc
│   │   ├── HttpMessage.h
│   │   ├── HttpUtil.cc
│   │   ├── HttpUtil.h
│   │   ├── KafkaDataTypes.cc
│   │   ├── KafkaDataTypes.h
│   │   ├── KafkaMessage.cc
│   │   ├── KafkaMessage.h
│   │   ├── KafkaResult.cc
│   │   ├── KafkaResult.h
│   │   ├── MySQLMessage.cc
│   │   ├── MySQLMessage.h
│   │   ├── MySQLMessage.inl
│   │   ├── MySQLResult.cc
│   │   ├── MySQLResult.h
│   │   ├── MySQLResult.inl
│   │   ├── MySQLUtil.cc
│   │   ├── MySQLUtil.h
│   │   ├── PackageWrapper.cc
│   │   ├── PackageWrapper.h
│   │   ├── ProtocolMessage.h
│   │   ├── RedisMessage.cc
│   │   ├── RedisMessage.h
│   │   ├── SSLWrapper.cc
│   │   ├── SSLWrapper.h
│   │   ├── TLVMessage.cc
│   │   ├── TLVMessage.h
│   │   ├── dns_parser.c
│   │   ├── dns_parser.h
│   │   ├── dns_types.h
│   │   ├── http_parser.c
│   │   ├── http_parser.h
│   │   ├── kafka_parser.c
│   │   ├── kafka_parser.h
│   │   ├── mysql_byteorder.c
│   │   ├── mysql_byteorder.h
│   │   ├── mysql_parser.c
│   │   ├── mysql_parser.h
│   │   ├── mysql_stream.c
│   │   ├── mysql_stream.h
│   │   ├── mysql_types.h
│   │   ├── redis_parser.c
│   │   ├── redis_parser.h
│   │   └── xmake.lua
│   ├── server/
│   │   ├── CMakeLists.txt
│   │   ├── WFDnsServer.h
│   │   ├── WFHttpServer.h
│   │   ├── WFMySQLServer.cc
│   │   ├── WFMySQLServer.h
│   │   ├── WFRedisServer.h
│   │   ├── WFServer.cc
│   │   ├── WFServer.h
│   │   └── xmake.lua
│   ├── util/
│   │   ├── CMakeLists.txt
│   │   ├── EncodeStream.cc
│   │   ├── EncodeStream.h
│   │   ├── LRUCache.h
│   │   ├── StringUtil.cc
│   │   ├── StringUtil.h
│   │   ├── URIParser.cc
│   │   ├── URIParser.h
│   │   ├── crc32c.c
│   │   ├── crc32c.h
│   │   ├── json_parser.c
│   │   ├── json_parser.h
│   │   └── xmake.lua
│   └── xmake.lua
├── test/
│   ├── CMakeLists.txt
│   ├── GNUmakefile
│   ├── algo_unittest.cc
│   ├── dns_unittest.cc
│   ├── facilities_unittest.cc
│   ├── graph_unittest.cc
│   ├── http_unittest.cc
│   ├── memory_unittest.cc
│   ├── mysql_unittest.cc
│   ├── redis_unittest.cc
│   ├── resource_unittest.cc
│   ├── task_unittest.cc
│   ├── upstream_unittest.cc
│   ├── uriparser_unittest.cc
│   └── xmake.lua
├── tutorial/
│   ├── CMakeLists.txt
│   ├── GNUmakefile
│   ├── tutorial-00-helloworld.cc
│   ├── tutorial-01-wget.cc
│   ├── tutorial-02-redis_cli.cc
│   ├── tutorial-03-wget_to_redis.cc
│   ├── tutorial-04-http_echo_server.cc
│   ├── tutorial-05-http_proxy.cc
│   ├── tutorial-06-parallel_wget.cc
│   ├── tutorial-07-sort_task.cc
│   ├── tutorial-08-matrix_multiply.cc
│   ├── tutorial-09-http_file_server.cc
│   ├── tutorial-10-user_defined_protocol/
│   │   ├── client-uds.cc
│   │   ├── client.cc
│   │   ├── message.cc
│   │   ├── message.h
│   │   ├── server-uds.cc
│   │   ├── server.cc
│   │   └── xmake.lua
│   ├── tutorial-11-graph_task.cc
│   ├── tutorial-12-mysql_cli.cc
│   ├── tutorial-13-kafka_cli.cc
│   ├── tutorial-14-consul_cli.cc
│   ├── tutorial-15-name_service.cc
│   ├── tutorial-16-graceful_restart/
│   │   ├── bootstrap.c
│   │   ├── server.cc
│   │   └── xmake.lua
│   ├── tutorial-17-dns_cli.cc
│   ├── tutorial-18-redis_subscriber.cc
│   ├── tutorial-19-dns_server.cc
│   └── xmake.lua
├── workflow-config.cmake.in
└── xmake.lua
Download .txt
SYMBOL INDEX (1863 symbols across 177 files)

FILE: benchmark/benchmark-01-http_server.cc
  function signal_handler (line 13) | void signal_handler(int)
  function main (line 18) | int main(int argc, char ** argv)

FILE: benchmark/benchmark-02-http_server_long_req.cc
  function signal_handler (line 14) | void signal_handler(int)
  function main (line 19) | int main(int argc, char ** argv)

FILE: benchmark/util/args.h
  function namespace (line 8) | namespace details
  function parse_args (line 90) | static size_t parse_args(int & argc, char ** argv, ARGS & ... args)

FILE: benchmark/util/date.h
  function date (line 6) | static inline void date(char * buf, size_t n)

FILE: src/client/WFConsulClient.cc
  type protocol::ConsulService (line 53) | struct protocol::ConsulService
  type ConsulServiceInstance (line 69) | struct ConsulServiceInstance
  type ConsulServiceTags (line 71) | struct ConsulServiceTags
  type ConsulServiceInstance (line 74) | struct ConsulServiceInstance
  type ConsulServiceTags (line 102) | struct ConsulServiceTags
  function SubTask (line 183) | SubTask *WFConsulTask::done()
  function convert_time_to_str (line 198) | static std::string convert_time_to_str(int milliseconds)
  function WFHttpTask (line 233) | WFHttpTask *WFConsulTask::create_discover_task()
  function WFHttpTask (line 245) | WFHttpTask *WFConsulTask::create_list_service_task()
  type ConsulService (line 266) | struct ConsulService
  function WFHttpTask (line 269) | WFHttpTask *WFConsulTask::create_register_task()
  function WFHttpTask (line 305) | WFHttpTask *WFConsulTask::create_deregister_task()
  function WFConsulTask (line 429) | WFConsulTask *WFConsulClient::create_discover_task(
  function WFConsulTask (line 444) | WFConsulTask *WFConsulClient::create_list_service_task(
  function WFConsulTask (line 458) | WFConsulTask *WFConsulClient::create_register_task(
  function WFConsulTask (line 474) | WFConsulTask *WFConsulClient::create_deregister_task(
  function create_tagged_address (line 489) | static bool create_tagged_address(const ConsulAddress& consul_address,
  function create_health_check (line 514) | static bool create_health_check(const ConsulConfig& config, json_object_...
  function create_register_request (line 608) | static bool create_register_request(const json_value_t *root,
  function parse_list_service_result (line 707) | static bool parse_list_service_result(const json_value_t *root,
  function parse_discover_node (line 745) | static bool parse_discover_node(const json_object_t *obj,
  function parse_tagged_address (line 827) | static bool parse_tagged_address(const char *name,
  function parse_service (line 864) | static bool parse_service(const json_object_t *obj,
  function parse_health_check (line 977) | static bool parse_health_check(const json_object_t *obj,
  function parse_discover_result (line 1083) | static bool parse_discover_result(const json_value_t *root,
  function print_json_string (line 1114) | static void print_json_string(const char *str, std::string& json_str)
  function print_json_number (line 1158) | static void print_json_number(double number, std::string& json_str)
  function print_json_object (line 1168) | static void print_json_object(const json_object_t *obj, int depth,
  function print_json_array (line 1198) | static void print_json_array(const json_array_t *arr, int depth,
  function print_json_value (line 1225) | static void print_json_value(const json_value_t *val, int depth,

FILE: src/client/WFConsulClient.h
  function class (line 43) | class WFConsulTask : public WFGenericTask
  function class (line 132) | class WFConsulClient

FILE: src/client/WFDnsClient.cc
  class DnsParams (line 33) | class DnsParams
    type dns_params (line 36) | struct dns_params
    method DnsParams (line 46) | DnsParams()
    method DnsParams (line 52) | DnsParams(const DnsParams& p)
    method DnsParams (line 59) | DnsParams& operator=(const DnsParams& p)
    method dns_params (line 73) | const dns_params *get_params() const { return this->params; }
    method dns_params (line 74) | dns_params *get_params() { return this->params; }
    method incref (line 77) | void incref() { (*this->ref)++; }
    method decref (line 78) | void decref()
  type DnsStatus (line 99) | struct DnsStatus
  function __get_ndots (line 110) | static int __get_ndots(const std::string& s)
  function __has_next_name (line 118) | static bool __has_next_name(const DnsParams::dns_params *p,
  function __callback_internal (line 148) | static void __callback_internal(WFDnsTask *task, const DnsParams& params,
  function WFDnsTask (line 240) | WFDnsTask *WFDnsClient::create_dns_task(const std::string& name,

FILE: src/client/WFDnsClient.h
  function class (line 28) | class WFDnsClient

FILE: src/client/WFHttpChunkedClient.cc
  function WFHttpChunkedTask (line 49) | WFHttpChunkedTask *
  function WFHttpChunkedTask (line 62) | WFHttpChunkedTask *

FILE: src/client/WFHttpChunkedClient.h
  function class (line 29) | class WFHttpChunkedTask : public WFGenericTask
  function virtual (line 133) | virtual SubTask *done()
  function virtual (line 162) | virtual ~WFHttpChunkedTask()
  function class (line 171) | class WFHttpChunkedClient

FILE: src/client/WFKafkaClient.cc
  class KafkaMember (line 52) | class KafkaMember
    method KafkaMember (line 55) | KafkaMember() : scheme("kafka://"), ref(1)
    method incref (line 66) | void incref()
    method decref (line 71) | void decref()
    type TransportType (line 77) | enum TransportType
  class KafkaClientTask (line 98) | class KafkaClientTask : public WFKafkaTask
    method KafkaClientTask (line 101) | KafkaClientTask(const std::string& query, int retry_max,
    method KafkaBroker (line 196) | KafkaBroker *get_broker(int node_id)
    method get_userinfo (line 206) | std::string get_userinfo() { return this->userinfo; }
  type list_head (line 619) | struct list_head
  function check_replace_toppar (line 1301) | static bool check_replace_toppar(KafkaTopparList *toppar_list, KafkaTopp...
  function SubTask (line 1558) | SubTask *WFKafkaTask::done()
  function WFKafkaTask (line 1662) | WFKafkaTask *WFKafkaClient::create_kafka_task(const std::string& query,
  function WFKafkaTask (line 1670) | WFKafkaTask *WFKafkaClient::create_kafka_task(int retry_max,
  function WFKafkaTask (line 1677) | WFKafkaTask *WFKafkaClient::create_leavegroup_task(int retry_max,
  function KafkaMetaList (line 1690) | KafkaMetaList *WFKafkaClient::get_meta_list()
  function KafkaMetaList (line 1695) | const KafkaMetaList *WFKafkaClient::get_meta_list() const

FILE: src/client/WFKafkaClient.h
  function class (line 39) | class WFKafkaTask : public WFGenericTask
  function class (line 147) | class WFKafkaClient

FILE: src/client/WFMySQLConnection.h
  function class (line 29) | class WFMySQLConnection

FILE: src/client/WFRedisSubscriber.cc
  function WFRedisTask (line 98) | WFRedisTask *
  function WFRedisSubscribeTask (line 110) | WFRedisSubscribeTask *
  function WFRedisSubscribeTask (line 120) | WFRedisSubscribeTask *

FILE: src/client/WFRedisSubscriber.h
  function class (line 34) | class WFRedisSubscribeTask : public WFGenericTask
  function virtual (line 165) | virtual SubTask *done()
  function virtual (line 195) | virtual ~WFRedisSubscribeTask()
  function class (line 204) | class WFRedisSubscriber

FILE: src/factory/DnsTaskImpl.cc
  class ComplexDnsTask (line 33) | class ComplexDnsTask : public WFComplexClientTask<DnsRequest, DnsResponse,
    type addrinfo (line 36) | struct addrinfo
    method ComplexDnsTask (line 40) | ComplexDnsTask(int retry_max, dns_callback_t&& cb):
  type addrinfo (line 55) | struct addrinfo
  function CommMessageOut (line 64) | CommMessageOut *ComplexDnsTask::message_out()
  type TransportType (line 93) | enum TransportType
  type addrinfo (line 94) | struct addrinfo
  type TransportType (line 147) | enum TransportType
  function WFDnsTask (line 160) | WFDnsTask *WFTaskFactory::create_dns_task(const std::string& url,
  function WFDnsTask (line 170) | WFDnsTask *WFTaskFactory::create_dns_task(const ParsedURI& uri,
  class WFDnsServerTask (line 193) | class WFDnsServerTask : public WFServerTask<DnsRequest, DnsResponse>
    method WFDnsServerTask (line 196) | WFDnsServerTask(CommService *service,
    method CommMessageIn (line 204) | virtual CommMessageIn *message_in()
    method CommMessageOut (line 210) | virtual CommMessageOut *message_out()
    type TransportType (line 219) | enum TransportType
  function WFDnsTask (line 243) | WFDnsTask *WFServerTaskFactory::create_dns_task(CommService *service,

FILE: src/factory/FileTaskImpl.cc
  class WFFilepreadTask (line 26) | class WFFilepreadTask : public WFFileIOTask
    method WFFilepreadTask (line 29) | WFFilepreadTask(int fd, void *buf, size_t count, off_t offset,
    method prepare (line 40) | virtual int prepare()
  class WFFilepwriteTask (line 48) | class WFFilepwriteTask : public WFFileIOTask
    method WFFilepwriteTask (line 51) | WFFilepwriteTask(int fd, const void *buf, size_t count, off_t offset,
    method prepare (line 62) | virtual int prepare()
  class WFFilepreadvTask (line 70) | class WFFilepreadvTask : public WFFileVIOTask
    method WFFilepreadvTask (line 73) | WFFilepreadvTask(int fd, const struct iovec *iov, int iovcnt, off_t of...
    method prepare (line 84) | virtual int prepare()
  class WFFilepwritevTask (line 92) | class WFFilepwritevTask : public WFFileVIOTask
    method WFFilepwritevTask (line 95) | WFFilepwritevTask(int fd, const struct iovec *iov, int iovcnt, off_t o...
    method prepare (line 106) | virtual int prepare()
  class WFFilefsyncTask (line 114) | class WFFilefsyncTask : public WFFileSyncTask
    method WFFilefsyncTask (line 117) | WFFilefsyncTask(int fd, IOService *service, fsync_callback_t&& cb) :
    method prepare (line 124) | virtual int prepare()
  class WFFilefdatasyncTask (line 131) | class WFFilefdatasyncTask : public WFFileSyncTask
    method WFFilefdatasyncTask (line 134) | WFFilefdatasyncTask(int fd, IOService *service, fsync_callback_t&& cb) :
    method prepare (line 141) | virtual int prepare()
  class __WFFilepreadTask (line 150) | class __WFFilepreadTask : public WFFilepreadTask
    method __WFFilepreadTask (line 153) | __WFFilepreadTask(const std::string& path, void *buf, size_t count,
    method prepare (line 161) | virtual int prepare()
    method SubTask (line 170) | virtual SubTask *done()
  class __WFFilepwriteTask (line 185) | class __WFFilepwriteTask : public WFFilepwriteTask
    method __WFFilepwriteTask (line 188) | __WFFilepwriteTask(const std::string& path, const void *buf, size_t co...
    method prepare (line 196) | virtual int prepare()
    method SubTask (line 205) | virtual SubTask *done()
  class __WFFilepreadvTask (line 220) | class __WFFilepreadvTask : public WFFilepreadvTask
    method __WFFilepreadvTask (line 223) | __WFFilepreadvTask(const std::string& path, const struct iovec *iov,
    method prepare (line 232) | virtual int prepare()
    method SubTask (line 241) | virtual SubTask *done()
  class __WFFilepwritevTask (line 256) | class __WFFilepwritevTask : public WFFilepwritevTask
    method __WFFilepwritevTask (line 259) | __WFFilepwritevTask(const std::string& path, const struct iovec *iov,
    method prepare (line 268) | virtual int prepare()
    method SubTask (line 278) | virtual SubTask *done()
  function WFFileIOTask (line 295) | WFFileIOTask *WFTaskFactory::create_pread_task(int fd,
  function WFFileIOTask (line 306) | WFFileIOTask *WFTaskFactory::create_pwrite_task(int fd,
  function WFFileVIOTask (line 317) | WFFileVIOTask *WFTaskFactory::create_preadv_task(int fd,
  function WFFileVIOTask (line 328) | WFFileVIOTask *WFTaskFactory::create_pwritev_task(int fd,
  function WFFileSyncTask (line 339) | WFFileSyncTask *WFTaskFactory::create_fsync_task(int fd,
  function WFFileSyncTask (line 347) | WFFileSyncTask *WFTaskFactory::create_fdatasync_task(int fd,
  function WFFileIOTask (line 357) | WFFileIOTask *WFTaskFactory::create_pread_task(const std::string& path,
  function WFFileIOTask (line 368) | WFFileIOTask *WFTaskFactory::create_pwrite_task(const std::string& path,
  function WFFileVIOTask (line 379) | WFFileVIOTask *WFTaskFactory::create_preadv_task(const std::string& path,
  function WFFileVIOTask (line 390) | WFFileVIOTask *WFTaskFactory::create_pwritev_task(const std::string& path,

FILE: src/factory/HttpTaskImpl.cc
  function __encode_auth (line 45) | static int __encode_auth(const char *p, std::string& auth)
  class ComplexHttpTask (line 62) | class ComplexHttpTask : public WFComplexClientTask<HttpRequest, HttpResp...
    method ComplexHttpTask (line 65) | ComplexHttpTask(int redirect_max,
  function CommMessageOut (line 98) | CommMessageOut *ComplexHttpTask::message_out()
  function CommMessageIn (line 182) | CommMessageIn *ComplexHttpTask::message_in()
  type HttpMessageHeader (line 201) | struct HttpMessageHeader
  type HttpMessageHeader (line 420) | struct HttpMessageHeader
  function SSL (line 439) | static SSL *__create_ssl(SSL_CTX *ssl_ctx)
  class ComplexHttpProxyTask (line 467) | class ComplexHttpProxyTask : public ComplexHttpTask
    method ComplexHttpProxyTask (line 470) | ComplexHttpProxyTask(int redirect_max,
    method set_user_uri (line 477) | void set_user_uri(ParsedURI&& uri) { user_uri_ = std::move(uri); }
    method set_user_uri (line 478) | void set_user_uri(const ParsedURI& uri) { user_uri_ = uri; }
    method ParsedURI (line 480) | virtual const ParsedURI *get_current_uri() const { return &user_uri_; }
    method WFConnection (line 491) | virtual WFConnection *get_connection() const
    type SSLConnection (line 502) | struct SSLConnection : public WFConnection
      method SSLConnection (line 507) | SSLConnection(SSL *ssl) : handshaker(ssl), wrapper(&wrapper, ssl)
    method SSLHandshaker (line 513) | SSLHandshaker *get_ssl_handshaker() const
    method SSLWrapper (line 518) | SSLWrapper *get_ssl_wrapper(ProtocolMessage *msg) const
  function CommMessageOut (line 560) | CommMessageOut *ComplexHttpProxyTask::message_out()
  function CommMessageIn (line 596) | CommMessageIn *ComplexHttpProxyTask::message_in()
  type HttpMessageHeader (line 828) | struct HttpMessageHeader
  class ComplexHttpChunkedTask (line 848) | class ComplexHttpChunkedTask : public ComplexHttpTask
    method keep_alive_timeout (line 853) | virtual int keep_alive_timeout()
    method finish_once (line 858) | virtual bool finish_once()
    class ChunkWrapper (line 864) | class ChunkWrapper : public PackageWrapper
      method ChunkWrapper (line 873) | ChunkWrapper(ComplexHttpChunkedTask *task) :
    method ComplexHttpChunkedTask (line 890) | ComplexHttpChunkedTask(int redirect_max,
  function CommMessageIn (line 902) | CommMessageIn *ComplexHttpChunkedTask::message_in()
  function ProtocolMessage (line 914) | ProtocolMessage *
  function WFHttpTask (line 971) | WFHttpTask *WFTaskFactory::create_http_task(const std::string& url,
  function WFHttpTask (line 987) | WFHttpTask *WFTaskFactory::create_http_task(const ParsedURI& uri,
  function WFHttpTask (line 1001) | WFHttpTask *WFTaskFactory::create_http_task(const std::string& url,
  function WFHttpTask (line 1021) | WFHttpTask *WFTaskFactory::create_http_task(const ParsedURI& uri,
  function WFHttpTask (line 1038) | WFHttpTask *__WFHttpTaskFactory::create_chunked_task(const std::string& ...
  function WFHttpTask (line 1054) | WFHttpTask *__WFHttpTaskFactory::create_chunked_task(const ParsedURI& uri,
  class WFHttpServerTask (line 1070) | class WFHttpServerTask : public WFServerTask<protocol::HttpRequest,
    method WFHttpServerTask (line 1077) | WFHttpServerTask(CommService *service, std::function<void (TASK *)>& p...
  type HttpMessageHeader (line 1099) | struct HttpMessageHeader
  function CommMessageOut (line 1118) | CommMessageOut *WFHttpServerTask::message_out()
  function WFHttpTask (line 1228) | WFHttpTask *WFServerTaskFactory::create_http_task(CommService *service,

FILE: src/factory/KafkaTaskImpl.cc
  function KafkaCgroup (line 35) | static KafkaCgroup __create_cgroup(const KafkaCgroup *c)
  class __ComplexKafkaTask (line 50) | class __ComplexKafkaTask : public WFComplexClientTask<KafkaRequest, Kafk...
    method __ComplexKafkaTask (line 53) | __ComplexKafkaTask(int retry_max, __kafka_callback_t&& callback) :
    type KafkaConnectionInfo (line 68) | struct KafkaConnectionInfo
      method KafkaConnectionInfo (line 74) | KafkaConnectionInfo()
      method init (line 86) | bool init(const char *mechanisms)
  function CommMessageOut (line 136) | CommMessageOut *__ComplexKafkaTask::message_out()
  function CommMessageIn (line 277) | CommMessageIn *__ComplexKafkaTask::message_in()
  type TransportType (line 304) | enum TransportType
  function __WFKafkaTask (line 694) | __WFKafkaTask *__WFKafkaTaskFactory::create_kafka_task(const std::string...
  function __WFKafkaTask (line 709) | __WFKafkaTask *__WFKafkaTaskFactory::create_kafka_task(const ParsedURI& ...
  function __WFKafkaTask (line 722) | __WFKafkaTask *__WFKafkaTaskFactory::create_kafka_task(enum TransportTyp...

FILE: src/factory/MySQLTaskImpl.cc
  class ComplexMySQLTask (line 41) | class ComplexMySQLTask : public WFComplexClientTask<MySQLRequest, MySQLR...
    method WFConnection (line 53) | virtual WFConnection *get_connection() const
    type ConnState (line 68) | enum ConnState
    type MyConnection (line 82) | struct MyConnection : public WFConnection
      type ConnState (line 86) | enum ConnState
      method MyConnection (line 90) | MyConnection(SSL *ssl) : wrapper(&wrapper, ssl)
    type MySSLWrapper (line 99) | struct MySSLWrapper : public SSLWrapper
      method MySSLWrapper (line 101) | MySSLWrapper(ProtocolMessage *msg, SSL *ssl) :
      method ProtocolMessage (line 104) | ProtocolMessage *get_msg() const { return this->message; }
    method ComplexMySQLTask (line 120) | ComplexMySQLTask(int retry_max, mysql_callback_t&& callback):
  function SSL (line 153) | static SSL *__create_ssl(SSL_CTX *ssl_ctx)
  function CommMessageOut (line 181) | CommMessageOut *ComplexMySQLTask::message_out()
  function CommMessageIn (line 283) | CommMessageIn *ComplexMySQLTask::message_in()
  function __mysql_get_character_set (line 577) | static int __mysql_get_character_set(const std::string& charset)
  function WFMySQLTask (line 769) | WFMySQLTask *WFTaskFactory::create_mysql_task(const std::string& url,
  function WFMySQLTask (line 786) | WFMySQLTask *WFTaskFactory::create_mysql_task(const ParsedURI& uri,
  class WFMySQLServerTask (line 803) | class WFMySQLServerTask : public WFServerTask<MySQLRequest, MySQLResponse>
    method WFMySQLServerTask (line 806) | WFMySQLServerTask(CommService *service,
  function SubTask (line 817) | SubTask *WFMySQLServerTask::done()
  function CommMessageOut (line 825) | CommMessageOut *WFMySQLServerTask::message_out()
  function CommMessageIn (line 835) | CommMessageIn *WFMySQLServerTask::message_in()
  function WFMySQLTask (line 847) | WFMySQLTask *WFServerTaskFactory::create_mysql_task(CommService *service,

FILE: src/factory/RedisTaskImpl.cc
  class ComplexRedisTask (line 38) | class ComplexRedisTask : public WFComplexClientTask<RedisRequest, RedisR...
    method ComplexRedisTask (line 41) | ComplexRedisTask(int retry_max, redis_callback_t&& callback):
  function CommMessageOut (line 86) | CommMessageOut *ComplexRedisTask::message_out()
  function CommMessageIn (line 124) | CommMessageIn *ComplexRedisTask::message_in()
  type TransportType (line 157) | enum TransportType
  class ComplexRedisSubscribeTask (line 298) | class ComplexRedisSubscribeTask : public ComplexRedisTask
    method push (line 301) | virtual int push(const void *buf, size_t size)
    method CommMessageIn (line 319) | virtual CommMessageIn *message_in()
    method first_timeout (line 327) | virtual int first_timeout()
    class SubscribeWrapper (line 333) | class SubscribeWrapper : public PackageWrapper
      method SubscribeWrapper (line 342) | SubscribeWrapper(ComplexRedisSubscribeTask *task) :
    method ComplexRedisSubscribeTask (line 356) | ComplexRedisSubscribeTask(std::function<void (WFRedisTask *)>&& extract,
  function ProtocolMessage (line 367) | ProtocolMessage *
  function WFRedisTask (line 399) | WFRedisTask *WFTaskFactory::create_redis_task(const std::string& url,
  function WFRedisTask (line 412) | WFRedisTask *WFTaskFactory::create_redis_task(const ParsedURI& uri,
  function WFRedisTask (line 423) | WFRedisTask *
  function WFRedisTask (line 437) | WFRedisTask *

FILE: src/factory/WFAlgoTaskFactory.h
  function namespace (line 27) | namespace algorithm
  function class (line 183) | class WFAlgoTaskFactory

FILE: src/factory/WFConnection.h
  function class (line 27) | class WFConnection : public CommConnection

FILE: src/factory/WFGraphTask.cc
  function SubTask (line 23) | SubTask *WFGraphNode::done()
  function WFGraphNode (line 53) | WFGraphNode& WFGraphTask::create_graph_node(SubTask *task)
  function SubTask (line 79) | SubTask *WFGraphTask::done()

FILE: src/factory/WFGraphTask.h
  function class (line 28) | class WFGraphNode : protected WFCounterTask
  function class (line 76) | class WFGraphTask : public WFGenericTask

FILE: src/factory/WFMessageQueue.cc
  class __MQConditional (line 23) | class __MQConditional : public WFConditional
    type list_head (line 26) | struct list_head
    type WFMessageQueue::Data (line 27) | struct WFMessageQueue::Data
    method signal (line 31) | virtual void signal(void *msg) { }
    method __MQConditional (line 34) | __MQConditional(SubTask *task, void **msgbuf,
    method __MQConditional (line 41) | __MQConditional(SubTask *task,
  type WFMessageQueue::Data (line 51) | struct WFMessageQueue::Data
  function WFConditional (line 63) | WFConditional *WFMessageQueue::get(SubTask *task, void **msgbuf)
  function WFConditional (line 68) | WFConditional *WFMessageQueue::get(SubTask *task)
  type WFMessageQueue::Data (line 75) | struct WFMessageQueue::Data

FILE: src/factory/WFMessageQueue.h
  function class (line 26) | class WFMessageQueue
  function push (line 37) | void push(void *msg) { this->queue->push(msg); }
  type list_head (line 39) | struct list_head
  type list_head (line 40) | struct list_head
  type MessageEntry (line 46) | struct MessageEntry
  type MessageEntry (line 55) | struct MessageEntry
  function virtual (line 66) | virtual void push(void *msg)
  type Data (line 74) | struct Data
  function virtual (line 84) | virtual ~WFMessageQueue() { }

FILE: src/factory/WFResourcePool.cc
  class __RPConditional (line 25) | class __RPConditional : public WFConditional
    type list_head (line 28) | struct list_head
    type WFResourcePool::Data (line 29) | struct WFResourcePool::Data
    method signal (line 33) | virtual void signal(void *res) { }
    method __RPConditional (line 36) | __RPConditional(SubTask *task, void **resbuf,
    method __RPConditional (line 43) | __RPConditional(SubTask *task,
  type WFResourcePool::Data (line 53) | struct WFResourcePool::Data
  function WFConditional (line 65) | WFConditional *WFResourcePool::get(SubTask *task, void **resbuf)
  function WFConditional (line 70) | WFConditional *WFResourcePool::get(SubTask *task)
  type WFResourcePool::Data (line 98) | struct WFResourcePool::Data

FILE: src/factory/WFResourcePool.h
  function class (line 27) | class WFResourcePool
  function push (line 38) | void push(void *res) { this->pool->push(res); }
  type list_head (line 43) | struct list_head
  function virtual (line 54) | virtual void push(void *res)
  type Data (line 60) | struct Data
  function virtual (line 68) | virtual ~WFResourcePool() { delete []this->data.res; }

FILE: src/factory/WFTask.h
  function dismiss (line 61) | void dismiss()
  function OUTPUT (line 69) | OUTPUT *get_output() { return &this->output; }
  function INPUT (line 71) | const INPUT *get_input() const { return &this->input; }
  function OUTPUT (line 72) | const OUTPUT *get_output() const { return &this->output; }
  function dismiss (line 130) | void dismiss()
  function RESP (line 138) | RESP *get_resp() { return &this->resp; }
  function REQ (line 140) | const REQ *get_req() const { return &this->req; }
  function RESP (line 141) | const RESP *get_resp() const { return &this->resp; }
  type sockaddr (line 168) | struct sockaddr
  function set_receive_timeout (line 175) | void set_receive_timeout(int timeout) { this->receive_timeo = timeout; }
  function set_keep_alive (line 176) | void set_keep_alive(int timeout) { this->keep_alive_timeo = timeout; }
  function set_watch_timeout (line 177) | void set_watch_timeout(int timeout) { this->watch_timeo = timeout; }
  function virtual (line 188) | virtual int push(const void *buf, size_t size)
  function virtual (line 230) | virtual int receive_timeout() { return this->receive_timeo; }
  function virtual (line 231) | virtual int keep_alive_timeout() { return this->keep_alive_timeo; }
  function virtual (line 232) | virtual int first_timeout() { return this->watch_timeo; }
  function virtual (line 261) | virtual ~WFNetworkTask() { }
  function class (line 264) | class WFTimerTask : public SleepRequest
  function dismiss (line 332) | void dismiss()
  function ARGS (line 341) | const ARGS *get_args() const { return &this->args; }
  function class (line 395) | class WFGenericTask : public SubTask
  function class (line 446) | class WFCounterTask : public WFGenericTask
  function virtual (line 528) | virtual SubTask *done()
  function virtual (line 627) | virtual SubTask *done()
  function flag (line 690) | flag(false)
  function class (line 703) | class WFGoTask : public ExecRequest
  function class (line 759) | class WFRepeaterTask : public WFGenericTask
  function dismiss (line 829) | void dismiss()
  function SeriesWork (line 838) | const SeriesWork *sub_series() const { return this; }

FILE: src/factory/WFTaskFactory.cc
  class __WFTimerTask (line 31) | class __WFTimerTask : public WFTimerTask
    method duration (line 34) | virtual int duration(struct timespec *value)
    method __WFTimerTask (line 46) | __WFTimerTask(time_t seconds, long nanoseconds, CommScheduler *scheduler,
  class __WFCanceledTimerTask (line 55) | class __WFCanceledTimerTask : public __WFTimerTask
    method dispatch (line 58) | virtual void dispatch()
    method __WFCanceledTimerTask (line 67) | __WFCanceledTimerTask(CommScheduler *scheduler, timer_callback_t&& cb) :
  function WFTimerTask (line 73) | WFTimerTask *WFTaskFactory::create_timer_task(time_t seconds, long nanos...
  function WFTimerTask (line 80) | WFTimerTask *WFTaskFactory::create_timer_task(timer_callback_t callback)
  function WFTimerTask (line 87) | WFTimerTask *WFTaskFactory::create_timer_task(unsigned int microseconds,
  type __NamedObjectList (line 98) | struct __NamedObjectList
    method __NamedObjectList (line 100) | __NamedObjectList(const std::string& str):
    method push_back (line 106) | void push_back(T *node)
    method empty (line 111) | bool empty() const
    method del (line 116) | bool del(T *node, rb_root *root)
    type rb_node (line 128) | struct rb_node
    type list_head (line 129) | struct list_head
  function T (line 134) | static T *__get_object_list(const std::string& name, struct rb_root *root,
  class __WFNamedTimerTask (line 168) | class __WFNamedTimerTask
    method __WFNamedTimerTask (line 206) | __WFNamedTimerTask(time_t seconds, long nanoseconds,
    method push_to (line 215) | void push_to(__NamedTimerMap::TimerList *timers)
    type __timer_node (line 242) | struct __timer_node
  type __timer_node (line 170) | struct __timer_node
    type list_head (line 172) | struct list_head
  class __NamedTimerMap (line 176) | class __NamedTimerMap
    type __timer_node (line 179) | struct __timer_node
    type rb_root (line 191) | struct rb_root
    method __NamedTimerMap (line 195) | __NamedTimerMap()
  class __WFNamedTimerTask (line 203) | class __WFNamedTimerTask : public __WFTimerTask
    method __WFNamedTimerTask (line 206) | __WFNamedTimerTask(time_t seconds, long nanoseconds,
    method push_to (line 215) | void push_to(__NamedTimerMap::TimerList *timers)
    type __timer_node (line 242) | struct __timer_node
  function WFTimerTask (line 295) | WFTimerTask *__NamedTimerMap::create(const std::string& name,
  type __timer_node (line 310) | struct __timer_node
    type list_head (line 172) | struct list_head
  function WFTimerTask (line 347) | WFTimerTask *WFTaskFactory::create_timer_task(const std::string& name,
  class __WFNamedCounterTask (line 363) | class __WFNamedCounterTask
    method __WFNamedCounterTask (line 411) | __WFNamedCounterTask(unsigned int target_value, counter_callback_t&& c...
    method push_to (line 418) | void push_to(__NamedCounterMap::CounterList *counters)
    method count (line 424) | virtual void count()
    type __counter_node (line 436) | struct __counter_node
  type __counter_node (line 365) | struct __counter_node
    type list_head (line 367) | struct list_head
  class __NamedCounterMap (line 372) | class __NamedCounterMap
    type __counter_node (line 375) | struct __counter_node
    type __counter_node (line 382) | struct __counter_node
    method remove (line 384) | void remove(CounterList *counters, struct __counter_node *node)
    type list_head (line 397) | struct list_head
    type rb_root (line 398) | struct rb_root
    method __NamedCounterMap (line 402) | __NamedCounterMap()
  class __WFNamedCounterTask (line 408) | class __WFNamedCounterTask : public WFCounterTask
    method __WFNamedCounterTask (line 411) | __WFNamedCounterTask(unsigned int target_value, counter_callback_t&& c...
    method push_to (line 418) | void push_to(__NamedCounterMap::CounterList *counters)
    method count (line 424) | virtual void count()
    type __counter_node (line 436) | struct __counter_node
  function WFCounterTask (line 440) | WFCounterTask *__NamedCounterMap::create(const std::string& name,
  type list_head (line 455) | struct list_head
  type __counter_node (line 457) | struct __counter_node
    type list_head (line 367) | struct list_head
  type __counter_node (line 486) | struct __counter_node
    type list_head (line 367) | struct list_head
  type __counter_node (line 512) | struct __counter_node
    type list_head (line 367) | struct list_head
  function WFCounterTask (line 532) | WFCounterTask *WFTaskFactory::create_counter_task(const std::string& name,
  class __WFNamedMailboxTask (line 546) | class __WFNamedMailboxTask
    method __WFNamedMailboxTask (line 594) | __WFNamedMailboxTask(void **mailbox, mailbox_callback_t&& cb) :
    method __WFNamedMailboxTask (line 600) | __WFNamedMailboxTask(mailbox_callback_t&& cb) :
    method push_to (line 606) | void push_to(__NamedMailboxMap::MailboxList *mailboxes)
    method send (line 612) | virtual void send(void *msg)
    type __mailbox_node (line 624) | struct __mailbox_node
  type __mailbox_node (line 548) | struct __mailbox_node
    type list_head (line 550) | struct list_head
  class __NamedMailboxMap (line 554) | class __NamedMailboxMap
    type __mailbox_node (line 557) | struct __mailbox_node
    type __mailbox_node (line 565) | struct __mailbox_node
    method remove (line 567) | void remove(MailboxList *mailboxes, struct __mailbox_node *node)
    type list_head (line 580) | struct list_head
    type rb_root (line 581) | struct rb_root
    method __NamedMailboxMap (line 585) | __NamedMailboxMap()
  class __WFNamedMailboxTask (line 591) | class __WFNamedMailboxTask : public WFMailboxTask
    method __WFNamedMailboxTask (line 594) | __WFNamedMailboxTask(void **mailbox, mailbox_callback_t&& cb) :
    method __WFNamedMailboxTask (line 600) | __WFNamedMailboxTask(mailbox_callback_t&& cb) :
    method push_to (line 606) | void push_to(__NamedMailboxMap::MailboxList *mailboxes)
    method send (line 612) | virtual void send(void *msg)
    type __mailbox_node (line 624) | struct __mailbox_node
  function WFMailboxTask (line 628) | WFMailboxTask *__NamedMailboxMap::create(const std::string& name,
  function WFMailboxTask (line 639) | WFMailboxTask *__NamedMailboxMap::create(const std::string& name,
  type list_head (line 650) | struct list_head
  type __mailbox_node (line 674) | struct __mailbox_node
    type list_head (line 550) | struct list_head
  type __mailbox_node (line 701) | struct __mailbox_node
    type list_head (line 550) | struct list_head
  function WFMailboxTask (line 715) | WFMailboxTask *WFTaskFactory::create_mailbox_task(const std::string& name,
  function WFMailboxTask (line 722) | WFMailboxTask *WFTaskFactory::create_mailbox_task(const std::string& name,
  class __WFNamedConditional (line 743) | class __WFNamedConditional
    method __WFNamedConditional (line 792) | __WFNamedConditional(SubTask *task, void **msgbuf) :
    method __WFNamedConditional (line 798) | __WFNamedConditional(SubTask *task) :
    method push_to (line 804) | void push_to(__NamedConditionalMap::ConditionalList *conds)
    method signal (line 810) | virtual void signal(void *msg)
    type __conditional_node (line 822) | struct __conditional_node
  type __conditional_node (line 745) | struct __conditional_node
    type list_head (line 747) | struct list_head
  class __NamedConditionalMap (line 751) | class __NamedConditionalMap
    type __conditional_node (line 754) | struct __conditional_node
    type __conditional_node (line 762) | struct __conditional_node
    method remove (line 765) | void remove(ConditionalList *conds, struct __conditional_node *node)
    type list_head (line 778) | struct list_head
    type rb_root (line 779) | struct rb_root
    method __NamedConditionalMap (line 783) | __NamedConditionalMap()
  class __WFNamedConditional (line 789) | class __WFNamedConditional : public WFConditional
    method __WFNamedConditional (line 792) | __WFNamedConditional(SubTask *task, void **msgbuf) :
    method __WFNamedConditional (line 798) | __WFNamedConditional(SubTask *task) :
    method push_to (line 804) | void push_to(__NamedConditionalMap::ConditionalList *conds)
    method signal (line 810) | virtual void signal(void *msg)
    type __conditional_node (line 822) | struct __conditional_node
  function WFConditional (line 826) | WFConditional *__NamedConditionalMap::create(const std::string& name,
  function WFConditional (line 836) | WFConditional *__NamedConditionalMap::create(const std::string& name,
  type list_head (line 848) | struct list_head
  type __conditional_node (line 872) | struct __conditional_node
    type list_head (line 747) | struct list_head
  type __conditional_node (line 899) | struct __conditional_node
    type list_head (line 747) | struct list_head
  function WFConditional (line 913) | WFConditional *WFTaskFactory::create_conditional(const std::string& name,
  function WFConditional (line 919) | WFConditional *WFTaskFactory::create_conditional(const std::string& name,
  class __WFNamedGuard (line 940) | class __WFNamedGuard
    method __WFNamedGuard (line 997) | __WFNamedGuard(SubTask *task) : WFConditional(task)
    method __WFNamedGuard (line 1002) | __WFNamedGuard(SubTask *task, void **msgbuf) : WFConditional(task, msg...
    method signal (line 1015) | virtual void signal(void *msg) { }
    type __guard_node (line 1018) | struct __guard_node
  type __guard_node (line 942) | struct __guard_node
    type list_head (line 944) | struct list_head
  class __NamedGuardMap (line 948) | class __NamedGuardMap
    type GuardList (line 951) | struct GuardList : public __NamedObjectList<struct __guard_node>
      type __guard_node (line 951) | struct __guard_node
      method GuardList (line 953) | GuardList(const std::string& name) : __NamedObjectList(name)
    type __guard_node (line 969) | struct __guard_node
    method unref (line 971) | void unref(GuardList *guards)
    type rb_root (line 984) | struct rb_root
    method __NamedGuardMap (line 988) | __NamedGuardMap()
  class __WFNamedGuard (line 994) | class __WFNamedGuard : public WFConditional
    method __WFNamedGuard (line 997) | __WFNamedGuard(SubTask *task) : WFConditional(task)
    method __WFNamedGuard (line 1002) | __WFNamedGuard(SubTask *task, void **msgbuf) : WFConditional(task, msg...
    method signal (line 1015) | virtual void signal(void *msg) { }
    type __guard_node (line 1018) | struct __guard_node
  function WFConditional (line 1038) | WFConditional *__NamedGuardMap::create(const std::string& name,
  function WFConditional (line 1049) | WFConditional *__NamedGuardMap::create(const std::string& name,
  type __guard_node (line 1060) | struct __guard_node
    type list_head (line 944) | struct list_head
  type __guard_node (line 1062) | struct __guard_node
    type list_head (line 944) | struct list_head
  function WFConditional (line 1092) | WFConditional *WFTaskFactory::create_guard(const std::string& name,
  function WFConditional (line 1098) | WFConditional *WFTaskFactory::create_guard(const std::string& name,
  type __guard_node (line 1106) | struct __guard_node
    type list_head (line 944) | struct list_head
  type __guard_node (line 1117) | struct __guard_node
    type list_head (line 944) | struct list_head
  function SubTask (line 1145) | SubTask *__WFTimedGoTask::done()

FILE: src/factory/WFTaskFactory.h
  type FileIOArgs (line 59) | struct FileIOArgs
  type FileVIOArgs (line 67) | struct FileVIOArgs
  type FileSyncArgs (line 75) | struct FileSyncArgs
  function class (line 110) | class WFTaskFactory
  function count_by_name (line 258) | static int count_by_name(const std::string& counter_name)
  function WFMailboxTask (line 276) | static WFMailboxTask *create_mailbox_task(mailbox_callback_t callback)
  function send_by_name (line 290) | static int send_by_name(const std::string& mailbox_name, void *msg)
  function WFConditional (line 315) | static WFConditional *create_conditional(SubTask *task)
  function signal_by_name (line 326) | static int signal_by_name(const std::string& cond_name, void *msg)
  function WFRepeaterTask (line 393) | static WFRepeaterTask *create_repeater_task(repeated_create_t create,
  function WFModuleTask (line 406) | static WFModuleTask *create_module_task(SubTask *first, SubTask *last,
  type TransportType (line 422) | enum TransportType
  type TransportType (line 428) | enum TransportType
  type TransportType (line 433) | enum TransportType
  type TransportType (line 438) | enum TransportType
  type sockaddr (line 439) | struct sockaddr
  type TransportType (line 444) | enum TransportType
  type sockaddr (line 445) | struct sockaddr

FILE: src/factory/Workflow.cc
  function SubTask (line 114) | SubTask *SeriesWork::pop()
  function SubTask (line 131) | SubTask *SeriesWork::pop_task()
  function SubTask (line 220) | SubTask *ParallelWork::done()

FILE: src/factory/Workflow.h
  function class (line 35) | class Workflow
  function class (line 65) | class SeriesWork
  function SeriesWork (line 162) | static inline SeriesWork *series_of(const SubTask *task)
  function SeriesWork (line 178) | inline SeriesWork *
  function start_series_work (line 184) | inline void
  function SeriesWork (line 191) | inline SeriesWork *
  function start_series_work (line 200) | inline void
  function class (line 209) | class ParallelWork : public ParallelTask
  function SeriesWork (line 259) | SeriesWork *const *end() { return this->all_series + this->subtasks_nr; }
  function SeriesWork (line 261) | const SeriesWork *const *begin() const
  function SeriesWork (line 266) | const SeriesWork *const *end() const
  function ParallelWork (line 299) | inline ParallelWork *
  function ParallelWork (line 305) | inline ParallelWork *
  function start_parallel_work (line 312) | inline void

FILE: src/kernel/CommRequest.h
  function CommSchedObject (line 38) | CommSchedObject *get_request_object() const { return this->object; }
  function set_request_object (line 39) | void set_request_object(CommSchedObject *object) { this->object = object; }
  function set_wait_timeout (line 41) | void set_wait_timeout(int timeout) { this->wait_timeout = timeout; }

FILE: src/kernel/CommScheduler.cc
  type timespec (line 30) | struct timespec
  type timespec (line 30) | struct timespec
  type sockaddr (line 47) | struct sockaddr
  function CommTarget (line 92) | CommTarget *CommSchedTarget::acquire(int wait_timeout)
  function CommTarget (line 396) | CommTarget *CommSchedGroup::acquire(int wait_timeout)

FILE: src/kernel/CommScheduler.h
  function class (line 28) | class CommSchedObject
  type sockaddr (line 51) | struct sockaddr
  type sockaddr (line 57) | struct sockaddr
  function class (line 83) | class CommSchedGroup : public CommSchedObject
  function class (line 111) | class CommScheduler

FILE: src/kernel/Communicator.cc
  type CommConnEntry (line 40) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  function __set_fd_nonblock (line 67) | static inline int __set_fd_nonblock(int fd)
  function __bind_sockaddr (line 77) | static int __bind_sockaddr(int sockfd, const struct sockaddr *addr,
  function __create_ssl (line 103) | static int __create_ssl(SSL_CTX *ssl_ctx, struct CommConnEntry *entry)
  function __ssl_writev (line 124) | static int __ssl_writev(SSL *ssl, struct iovec vectors[], int cnt)
  function __release_conn (line 175) | static void __release_conn(struct CommConnEntry *entry)
  type sockaddr (line 191) | struct sockaddr
  type sockaddr (line 196) | struct sockaddr
  type CommConnEntry (line 228) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type sockaddr (line 229) | struct sockaddr
  type sockaddr (line 267) | struct sockaddr
  type sockaddr (line 272) | struct sockaddr
  type CommConnEntry (line 304) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type list_head (line 305) | struct list_head
  class CommServiceTarget (line 339) | class CommServiceTarget : public CommTarget
    method incref (line 342) | void incref()
    method decref (line 347) | void decref()
  type CommConnEntry (line 374) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type timespec (line 436) | struct timespec
  type iovec (line 479) | struct iovec
  type CommConnEntry (line 480) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type iovec (line 572) | struct iovec
  type CommConnEntry (line 573) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_data (line 575) | struct poller_data
  type iovec (line 580) | struct iovec
  type iovec (line 580) | struct iovec
  type CommConnEntry (line 625) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type iovec (line 627) | struct iovec
  type iovec (line 628) | struct iovec
  type poller_result (line 647) | struct poller_result
  type CommConnEntry (line 649) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 649) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 725) | struct poller_result
  type CommConnEntry (line 727) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 727) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 805) | struct poller_result
  type CommConnEntry (line 807) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 807) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 818) | struct poller_result
  type CommConnEntry (line 820) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 820) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 881) | struct poller_result
  type CommConnEntry (line 883) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 883) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 933) | struct poller_result
  type CommConnEntry (line 935) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 935) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 944) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 947) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 952) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 953) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 977) | struct poller_result
  type CommConnEntry (line 979) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 979) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 1052) | struct poller_result
  type CommConnEntry (line 1055) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 1116) | struct poller_result
  type CommConnEntry (line 1119) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 1127) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 1166) | struct poller_result
  type CommConnEntry (line 1168) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 1168) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 1195) | struct poller_result
  type poller_result (line 1218) | struct poller_result
  type poller_result (line 1257) | struct poller_result
  type poller_result (line 1302) | struct poller_result
  type CommConnEntry (line 1309) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  function poller_message_t (line 1356) | poller_message_t *Communicator::create_request(void *context)
  function poller_message_t (line 1414) | poller_message_t *Communicator::create_reply(void *context)
  type CommConnEntry (line 1446) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 1501) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 1501) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type sockaddr (line 1510) | struct sockaddr
  type sockaddr (line 1534) | struct sockaddr
  type CommConnEntry (line 1538) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_result (line 1568) | struct poller_result
  type thrdpool_task (line 1576) | struct thrdpool_task
  type poller_params (line 1603) | struct poller_params
  type poller_result (line 1612) | struct poller_result
  type CommConnEntry (line 1689) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 1692) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 1699) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 1699) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type CommConnEntry (line 1736) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type list_head (line 1737) | struct list_head
  type CommConnEntry (line 1788) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type poller_data (line 1789) | struct poller_data
  type poller_data (line 1869) | struct poller_data
  type CommConnEntry (line 1918) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type list_head (line 1919) | struct list_head
  type CommConnEntry (line 1948) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type iovec (line 1950) | struct iovec
  type msghdr (line 1963) | struct msghdr
  type CommConnEntry (line 1982) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type list_head (line 1983) | struct list_head
  type CommConnEntry (line 2009) | struct CommConnEntry
    type list_head (line 42) | struct list_head
    type iovec (line 57) | struct iovec
  type timespec (line 2105) | struct timespec
  type poller_data (line 2137) | struct poller_data
  type poller_data (line 2191) | struct poller_data
  type thrdpool_task (line 2238) | struct thrdpool_task
  type thrdpool_task (line 2249) | struct thrdpool_task
  type poller_result (line 2265) | struct poller_result
  type poller_result (line 2268) | struct poller_result
  type poller_result (line 2269) | struct poller_result
  type poller_result (line 2282) | struct poller_result
  type poller_result (line 2282) | struct poller_result
  type poller_result (line 2287) | struct poller_result

FILE: src/kernel/Communicator.h
  function class (line 32) | class CommConnection
  function class (line 38) | class CommTarget
  function class (line 97) | class CommMessageOut
  function class (line 107) | class CommMessageIn : private poller_message_t
  function class (line 135) | class CommSession
  function class (line 172) | class CommService
  function class (line 246) | class SleepSession
  function class (line 267) | class CommEventHandler
  function class (line 278) | class Communicator

FILE: src/kernel/ExecRequest.h
  function ExecQueue (line 35) | ExecQueue *get_request_queue() const { return this->queue; }
  function set_request_queue (line 36) | void set_request_queue(ExecQueue *queue) { this->queue = queue; }

FILE: src/kernel/Executor.cc
  type ExecSessionEntry (line 26) | struct ExecSessionEntry
    type list_head (line 28) | struct list_head
  type thrdpool_task (line 67) | struct thrdpool_task
  type ExecSessionEntry (line 73) | struct ExecSessionEntry
    type list_head (line 28) | struct list_head
  type thrdpool_task (line 86) | struct thrdpool_task
  type thrdpool_task (line 99) | struct thrdpool_task
  type ExecSessionEntry (line 102) | struct ExecSessionEntry
    type list_head (line 28) | struct list_head
  type list_head (line 103) | struct list_head
  type ExecSessionEntry (line 119) | struct ExecSessionEntry
    type list_head (line 28) | struct list_head
  type ExecSessionEntry (line 122) | struct ExecSessionEntry
    type list_head (line 28) | struct list_head
  type ExecSessionEntry (line 122) | struct ExecSessionEntry
    type list_head (line 28) | struct list_head
  type thrdpool_task (line 131) | struct thrdpool_task

FILE: src/kernel/Executor.h
  function class (line 26) | class ExecQueue
  function class (line 45) | class ExecSession
  function class (line 62) | class Executor

FILE: src/kernel/IOService_linux.cc
  type io_context (line 31) | struct io_context
  type io_iocb_cmd (line 33) | enum io_iocb_cmd {
  type io_iocb_poll (line 91) | struct io_iocb_poll {
  type io_iocb_sockaddr (line 95) | struct io_iocb_sockaddr {
    type sockaddr (line 96) | struct sockaddr
  type io_iocb_common (line 100) | struct io_iocb_common {
  type io_iocb_vector (line 109) | struct io_iocb_vector {
    type iovec (line 110) | struct iovec
  type iocb (line 115) | struct iocb {
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type io_event (line 133) | struct io_event {
    type iocb (line 135) | struct iocb
  function io_setup (line 145) | static inline int io_setup(int maxevents, io_context_t *ctxp)
  function io_destroy (line 150) | static inline int io_destroy(io_context_t ctx)
  function io_submit (line 155) | static inline int io_submit(io_context_t ctx, long nr, struct iocb *ios[])
  function io_cancel (line 160) | static inline int io_cancel(io_context_t ctx, struct iocb *iocb,
  function io_getevents (line 166) | static inline int io_getevents(io_context_t ctx_id, long min_nr, long nr,
  function io_set_eventfd (line 173) | static inline void io_set_eventfd(struct iocb *iocb, int eventfd)
  type iocb (line 181) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iocb (line 181) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iocb (line 193) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iocb (line 193) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iovec (line 203) | struct iovec
  type iocb (line 206) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iocb (line 206) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iovec (line 216) | struct iovec
  type iocb (line 219) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iocb (line 219) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iocb (line 231) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iocb (line 231) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iocb (line 240) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iocb (line 240) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type io_event (line 289) | struct io_event
    type iocb (line 135) | struct iocb
  type iocb (line 322) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type iocb (line 322) | struct iocb
    type io_iocb_common (line 126) | struct io_iocb_common
    type io_iocb_vector (line 127) | struct io_iocb_vector
    type io_iocb_poll (line 128) | struct io_iocb_poll
    type io_iocb_sockaddr (line 129) | struct io_iocb_sockaddr
  type io_event (line 350) | struct io_event
    type iocb (line 135) | struct iocb

FILE: src/kernel/IOService_linux.h
  function class (line 31) | class IOSession
  function class (line 64) | class IOService

FILE: src/kernel/IOService_thread.cc
  type io_iocb_cmd (line 27) | enum io_iocb_cmd {
  type iovec (line 57) | struct iovec
  type iovec (line 67) | struct iovec
  type iovec (line 115) | struct iovec
  type iovec (line 121) | struct iovec
  type iovec (line 232) | struct iovec
  type iovec (line 236) | struct iovec
  type iovec (line 268) | struct iovec
  type iovec (line 291) | struct iovec

FILE: src/kernel/IOService_thread.h
  function class (line 31) | class IOSession
  function class (line 70) | class IOService
  type list_head (line 101) | struct list_head
  type iovec (line 110) | struct iovec
  type iovec (line 111) | struct iovec
  type iovec (line 114) | struct iovec
  type iovec (line 116) | struct iovec

FILE: src/kernel/SubTask.h
  function class (line 24) | class SubTask
  function class (line 54) | class ParallelTask : public SubTask

FILE: src/kernel/list.h
  type list_head (line 14) | struct list_head {
  function INIT_LIST_HEAD (line 30) | static inline void INIT_LIST_HEAD(struct list_head *list)
  function __list_add (line 42) | static inline void __list_add(struct list_head *entry,
  function list_add (line 60) | static inline void list_add(struct list_head *entry, struct list_head *h...
  function list_add_tail (line 73) | static inline void list_add_tail(struct list_head *entry,
  function __list_del (line 86) | static inline void __list_del(struct list_head *prev, struct list_head *...
  function list_del (line 98) | static inline void list_del(struct list_head *entry)
  function list_move (line 108) | static inline void list_move(struct list_head *entry, struct list_head *...
  function list_move_tail (line 119) | static inline void list_move_tail(struct list_head *entry,
  function list_empty (line 130) | static inline int list_empty(const struct list_head *head)
  function __list_splice (line 135) | static inline void __list_splice(const struct list_head *list,
  function list_splice (line 154) | static inline void list_splice(const struct list_head *list,
  function list_splice_init (line 168) | static inline void list_splice_init(struct list_head *list,
  type slist_node (line 227) | struct slist_node {
  type slist_head (line 231) | struct slist_head {
  function INIT_SLIST_HEAD (line 240) | static inline void INIT_SLIST_HEAD(struct slist_head *list)
  function slist_add_after (line 246) | static inline void slist_add_after(struct slist_node *entry,
  function slist_add_head (line 256) | static inline void slist_add_head(struct slist_node *entry,
  function slist_add_tail (line 262) | static inline void slist_add_tail(struct slist_node *entry,
  function slist_del_after (line 270) | static inline void slist_del_after(struct slist_node *prev,
  function slist_del_head (line 278) | static inline void slist_del_head(struct slist_head *list)
  function slist_empty (line 283) | static inline int slist_empty(const struct slist_head *list)
  function __slist_splice (line 288) | static inline void __slist_splice(const struct slist_head *list,
  function slist_splice (line 298) | static inline void slist_splice(const struct slist_head *list,
  function slist_splice_init (line 306) | static inline void slist_splice_init(struct slist_head *list,

FILE: src/kernel/mpoller.c
  type poller_params (line 24) | struct poller_params
  function __mpoller_create (line 27) | static int __mpoller_create(const struct poller_params *params,
  function mpoller_t (line 57) | mpoller_t *mpoller_create(const struct poller_params *params, size_t nth...
  function mpoller_start (line 79) | int mpoller_start(mpoller_t *mpoller)
  function mpoller_set_callback (line 98) | void mpoller_set_callback(void (*callback)(struct poller_result *, void *),
  function mpoller_stop (line 107) | void mpoller_stop(mpoller_t *mpoller)
  function mpoller_destroy (line 115) | void mpoller_destroy(mpoller_t *mpoller)

FILE: src/kernel/mpoller.h
  type mpoller_t (line 25) | typedef struct __mpoller mpoller_t;
  type poller_params (line 32) | struct poller_params
  type poller_result (line 34) | struct poller_result
  type __mpoller (line 43) | struct __mpoller
  function mpoller_add (line 50) | static inline int mpoller_add(const struct poller_data *data, int timeout,
  function mpoller_del (line 57) | static inline int mpoller_del(int fd, mpoller_t *mpoller)
  function mpoller_mod (line 63) | static inline int mpoller_mod(const struct poller_data *data, int timeout,
  function mpoller_set_timeout (line 70) | static inline int mpoller_set_timeout(int fd, int timeout, mpoller_t *mp...
  function mpoller_add_timer (line 76) | static inline int mpoller_add_timer(const struct timespec *value, void *...
  function mpoller_del_timer (line 85) | static inline int mpoller_del_timer(void *timer, int index, mpoller_t *m...

FILE: src/kernel/msgqueue.c
  type __msgqueue (line 24) | struct __msgqueue
  function msgqueue_set_nonblock (line 41) | void msgqueue_set_nonblock(msgqueue_t *queue)
  function msgqueue_set_block (line 50) | void msgqueue_set_block(msgqueue_t *queue)
  function msgqueue_put (line 55) | void msgqueue_put(void *msg, msgqueue_t *queue)
  function msgqueue_put_head (line 71) | void msgqueue_put_head(void *msg, msgqueue_t *queue)
  function __msgqueue_swap (line 101) | static size_t __msgqueue_swap(msgqueue_t *queue)
  function msgqueue_t (line 139) | msgqueue_t *msgqueue_create(size_t maxlen, int linkoff)
  function msgqueue_destroy (line 185) | void msgqueue_destroy(msgqueue_t *queue)

FILE: src/kernel/msgqueue.h
  type msgqueue_t (line 24) | typedef struct __msgqueue msgqueue_t;

FILE: src/kernel/poller.c
  type __poller_node (line 45) | struct __poller_node
  type __poller (line 64) | struct __poller
  function __poller_create_pfd (line 88) | static inline int __poller_create_pfd()
  function __poller_close_pfd (line 93) | static inline int __poller_close_pfd(int fd)
  function __poller_add_fd (line 98) | static inline int __poller_add_fd(int fd, int event, void *data,
  function __poller_del_fd (line 110) | static inline int __poller_del_fd(int fd, int event, poller_t *poller)
  function __poller_mod_fd (line 115) | static inline int __poller_mod_fd(int fd, int old_event,
  function __poller_create_timerfd (line 128) | static inline int __poller_create_timerfd()
  function __poller_close_timerfd (line 133) | static inline int __poller_close_timerfd(int fd)
  function __poller_add_timerfd (line 138) | static inline int __poller_add_timerfd(int fd, poller_t *poller)
  function __poller_set_timerfd (line 148) | static inline int __poller_set_timerfd(int fd, const struct timespec *ab...
  type __poller_event_t (line 158) | typedef struct epoll_event __poller_event_t;
  function __poller_wait (line 160) | static inline int __poller_wait(__poller_event_t *events, int maxevents,
  function __poller_create_pfd (line 173) | static inline int __poller_create_pfd()
  function __poller_close_pfd (line 178) | static inline int __poller_close_pfd(int fd)
  function __poller_add_fd (line 183) | static inline int __poller_add_fd(int fd, int event, void *data,
  function __poller_del_fd (line 191) | static inline int __poller_del_fd(int fd, int event, poller_t *poller)
  function __poller_mod_fd (line 198) | static inline int __poller_mod_fd(int fd, int old_event,
  function __poller_create_timerfd (line 208) | static inline int __poller_create_timerfd()
  function __poller_close_timerfd (line 213) | static inline int __poller_close_timerfd(int fd)
  function __poller_add_timerfd (line 218) | static inline int __poller_add_timerfd(int fd, poller_t *poller)
  function __poller_set_timerfd (line 223) | static int __poller_set_timerfd(int fd, const struct timespec *abstime,
  type __poller_event_t (line 255) | typedef struct kevent __poller_event_t;
  function __poller_wait (line 257) | static inline int __poller_wait(__poller_event_t *events, int maxevents,
  function __timeout_cmp (line 274) | static inline long __timeout_cmp(const struct __poller_node *node1,
  function __poller_tree_insert (line 285) | static void __poller_tree_insert(struct __poller_node *node, poller_t *p...
  function __poller_tree_erase (line 324) | static inline void __poller_tree_erase(struct __poller_node *node,
  function __poller_remove_node (line 337) | static int __poller_remove_node(struct __poller_node *node, poller_t *po...
  function __poller_append_message (line 359) | static int __poller_append_message(const void *buf, size_t *n,
  function __poller_handle_ssl_error (line 401) | static int __poller_handle_ssl_error(struct __poller_node *node, int ret,
  function __poller_handle_read (line 438) | static void __poller_handle_read(struct __poller_node *node,
  function __poller_handle_write (line 516) | static void __poller_handle_write(struct __poller_node *node,
  function __poller_handle_listen (line 599) | static void __poller_handle_listen(struct __poller_node *node,
  function __poller_handle_connect (line 651) | static void __poller_handle_connect(struct __poller_node *node,
  function __poller_handle_recvfrom (line 677) | static void __poller_handle_recvfrom(struct __poller_node *node,
  function __poller_handle_ssl_accept (line 729) | static void __poller_handle_ssl_accept(struct __poller_node *node,
  function __poller_handle_ssl_connect (line 757) | static void __poller_handle_ssl_connect(struct __poller_node *node,
  function __poller_handle_ssl_shutdown (line 785) | static void __poller_handle_ssl_shutdown(struct __poller_node *node,
  function __poller_handle_event (line 813) | static void __poller_handle_event(struct __poller_node *node,
  function __poller_handle_notify (line 875) | static void __poller_handle_notify(struct __poller_node *node,
  function __poller_handle_pipe (line 933) | static int __poller_handle_pipe(poller_t *poller)
  function __poller_handle_timeout (line 955) | static void __poller_handle_timeout(const struct __poller_node *time_node,
  function __poller_set_timer (line 1021) | static void __poller_set_timer(poller_t *poller)
  type __poller_node (line 1054) | struct __poller_node
  type __poller_node (line 1055) | struct __poller_node
  type __poller_node (line 1068) | struct __poller_node
  function __poller_open_pipe (line 1119) | static int __poller_open_pipe(poller_t *poller)
  function __poller_create_timer (line 1144) | static int __poller_create_timer(poller_t *poller)
  function poller_t (line 1162) | poller_t *__poller_create(void **nodes_buf, const struct poller_params *...
  function poller_t (line 1204) | poller_t *poller_create(const struct poller_params *params)
  function __poller_destroy (line 1221) | void __poller_destroy(poller_t *poller)
  function poller_destroy (line 1229) | void poller_destroy(poller_t *poller)
  function poller_start (line 1235) | int poller_start(poller_t *poller)
  function __poller_insert_node (line 1261) | static void __poller_insert_node(struct __poller_node *node,
  function __poller_node_set_timeout (line 1290) | static void __poller_node_set_timeout(int timeout, struct __poller_node ...
  function __poller_data_get_event (line 1302) | static int __poller_data_get_event(int *event, const struct poller_data ...
  type __poller_node (line 1342) | struct __poller_node
  type poller_data (line 1342) | struct poller_data
  type __poller_node (line 1345) | struct __poller_node
  type __poller_node (line 1346) | struct __poller_node
  type __poller_node (line 1362) | struct __poller_node
  type __poller_node (line 1362) | struct __poller_node
  type __poller_node (line 1367) | struct __poller_node
  type __poller_node (line 1367) | struct __poller_node
  function poller_add (line 1385) | int poller_add(const struct poller_data *data, int timeout, poller_t *po...
  function poller_del (line 1419) | int poller_del(int fd, poller_t *poller)
  function poller_mod (line 1465) | int poller_mod(const struct poller_data *data, int timeout, poller_t *po...
  function poller_set_timeout (line 1522) | int poller_set_timeout(int fd, int timeout, poller_t *poller)
  function poller_add_timer (line 1560) | int poller_add_timer(const struct timespec *value, void *context, void *...
  function poller_del_timer (line 1608) | int poller_del_timer(void *timer, poller_t *poller)
  function poller_set_callback (line 1638) | void poller_set_callback(void (*callback)(struct poller_result *, void *),
  function poller_stop (line 1644) | void poller_stop(poller_t *poller)

FILE: src/kernel/poller.h
  type poller_t (line 27) | typedef struct __poller poller_t;
  type poller_message_t (line 28) | typedef struct __poller_message poller_message_t;
  type __poller_message (line 30) | struct __poller_message
  type poller_data (line 36) | struct poller_data
  type poller_result (line 74) | struct poller_result
  type poller_params (line 88) | struct poller_params
  type poller_params (line 100) | struct poller_params
  type poller_data (line 102) | struct poller_data
  type poller_data (line 104) | struct poller_data
  type timespec (line 106) | struct timespec
  type poller_result (line 109) | struct poller_result

FILE: src/kernel/rbtree.c
  function __rb_rotate_left (line 25) | static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
  function __rb_rotate_right (line 45) | static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
  function rb_insert_color (line 65) | void rb_insert_color(struct rb_node *node, struct rb_root *root)
  function __rb_erase_color (line 130) | static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
  function rb_erase (line 222) | void rb_erase(struct rb_node *node, struct rb_root *root)
  type rb_node (line 299) | struct rb_node
  type rb_root (line 299) | struct rb_root
  type rb_node (line 301) | struct rb_node
  type rb_node (line 305) | struct rb_node
  type rb_node (line 311) | struct rb_node
  type rb_root (line 311) | struct rb_root
  type rb_node (line 313) | struct rb_node
  type rb_node (line 317) | struct rb_node
  type rb_node (line 323) | struct rb_node
  type rb_node (line 323) | struct rb_node
  type rb_node (line 346) | struct rb_node
  type rb_node (line 346) | struct rb_node
  function rb_replace_node (line 365) | void rb_replace_node(struct rb_node *victim, struct rb_node *newnode,

FILE: src/kernel/rbtree.h
  type rb_node (line 98) | struct rb_node
  type rb_root (line 109) | struct rb_root
  type rb_node (line 123) | struct rb_node
  type rb_root (line 123) | struct rb_root
  type rb_node (line 124) | struct rb_node
  type rb_root (line 124) | struct rb_root
  type rb_node (line 127) | struct rb_node
  type rb_node (line 127) | struct rb_node
  type rb_node (line 128) | struct rb_node
  type rb_node (line 128) | struct rb_node
  type rb_node (line 129) | struct rb_node
  type rb_root (line 129) | struct rb_root
  type rb_node (line 130) | struct rb_node
  type rb_root (line 130) | struct rb_root
  type rb_node (line 133) | struct rb_node
  type rb_node (line 133) | struct rb_node
  type rb_root (line 134) | struct rb_root
  function rb_link_node (line 140) | static inline void rb_link_node(struct rb_node *node, struct rb_node *pa...

FILE: src/kernel/thrdpool.c
  type __thrdpool (line 25) | struct __thrdpool
  type __thrdpool_task_entry (line 36) | struct __thrdpool_task_entry
  function __thrdpool_exit_routine (line 44) | static void __thrdpool_exit_routine(void *context)
  type __thrdpool_task_entry (line 66) | struct __thrdpool_task_entry
  type __thrdpool_task_entry (line 73) | struct __thrdpool_task_entry
  function __thrdpool_terminate (line 94) | static void __thrdpool_terminate(int in_pool, thrdpool_t *pool)
  function __thrdpool_create_threads (line 119) | static int __thrdpool_create_threads(size_t nthreads, thrdpool_t *pool)
  function thrdpool_t (line 157) | thrdpool_t *thrdpool_create(size_t nthreads, size_t stacksize)
  function __thrdpool_schedule (line 196) | void __thrdpool_schedule(const struct thrdpool_task *task, void *buf,
  function thrdpool_schedule (line 203) | int thrdpool_schedule(const struct thrdpool_task *task, thrdpool_t *pool)
  function thrdpool_in_pool (line 216) | int thrdpool_in_pool(thrdpool_t *pool)
  function thrdpool_increase (line 221) | int thrdpool_increase(thrdpool_t *pool)
  function thrdpool_decrease (line 252) | int thrdpool_decrease(thrdpool_t *pool)
  function thrdpool_exit (line 269) | void thrdpool_exit(thrdpool_t *pool)
  function thrdpool_destroy (line 275) | void thrdpool_destroy(void (*pending)(const struct thrdpool_task *),

FILE: src/kernel/thrdpool.h
  type thrdpool_t (line 24) | typedef struct __thrdpool thrdpool_t;
  type thrdpool_task (line 26) | struct thrdpool_task
  type thrdpool_task (line 38) | struct thrdpool_task
  type thrdpool_task (line 43) | struct thrdpool_task

FILE: src/manager/DnsCache.cc
  type addrinfo (line 57) | struct addrinfo

FILE: src/manager/DnsCache.h
  type DnsCacheValue (line 33) | struct DnsCacheValue
  function class (line 48) | class DnsCache

FILE: src/manager/EndpointParams.h
  type TransportType (line 30) | enum TransportType
  type EndpointParams (line 39) | struct EndpointParams
  type EndpointParams (line 49) | struct EndpointParams

FILE: src/manager/RouteManager.cc
  class RouteTargetUDP (line 47) | class RouteTargetUDP : public RouteManager::RouteTarget
    method create_connect_fd (line 50) | virtual int create_connect_fd()
  class RouteTargetSCTP (line 60) | class RouteTargetSCTP : public RouteManager::RouteTarget
    method create_connect_fd (line 64) | virtual int create_connect_fd()
    method create_connect_fd (line 73) | virtual int create_connect_fd()
  class RouteTargetTCP_TLS_SNI (line 84) | class RouteTargetTCP_TLS_SNI : public RouteTargetTCP_SSL
    method init_ssl (line 87) | virtual int init_ssl(SSL *ssl)
    method RouteTargetTCP_TLS_SNI (line 99) | RouteTargetTCP_TLS_SNI(const std::string& name) : hostname(name)
  class RouteTargetSCTP_TLS_SNI (line 106) | class RouteTargetSCTP_TLS_SNI : public RouteTargetSCTP_SSL
    method init_ssl (line 109) | virtual int init_ssl(SSL *ssl)
    method RouteTargetSCTP_TLS_SNI (line 121) | RouteTargetSCTP_TLS_SNI(const std::string& name) : hostname(name)
  type RouteParams (line 129) | struct RouteParams
    type TransportType (line 131) | enum TransportType
    type addrinfo (line 132) | struct addrinfo
  class RouteResultEntry (line 143) | class RouteResultEntry
    type rb_node (line 146) | struct rb_node
    type list_head (line 151) | struct list_head
    method RouteResultEntry (line 156) | RouteResultEntry():
    type RouteParams (line 166) | struct RouteParams
    type RouteParams (line 175) | struct RouteParams
    type addrinfo (line 176) | struct addrinfo
    type RouteParams (line 177) | struct RouteParams
  type __breaker_node (line 180) | struct __breaker_node
    type list_head (line 184) | struct list_head
  type RouteParams (line 188) | struct RouteParams
    type TransportType (line 131) | enum TransportType
    type addrinfo (line 132) | struct addrinfo
  type addrinfo (line 189) | struct addrinfo
  type RouteParams (line 232) | struct RouteParams
    type TransportType (line 131) | enum TransportType
    type addrinfo (line 132) | struct addrinfo
  type addrinfo (line 234) | struct addrinfo
  type RouteParams (line 274) | struct RouteParams
    type TransportType (line 131) | enum TransportType
    type addrinfo (line 132) | struct addrinfo
  type addrinfo (line 277) | struct addrinfo
  type list_head (line 325) | struct list_head
  type list_head (line 381) | struct list_head
  function __addr_cmp (line 404) | static inline int __addr_cmp(const struct addrinfo *x, const struct addr...
  function __addr_less (line 415) | static inline bool __addr_less(const struct addrinfo *x, const struct ad...
  function __fnv_hash (line 420) | static uint64_t __fnv_hash(const unsigned char *data, size_t size)
  function __generate_key (line 434) | static uint64_t __generate_key(enum TransportType type,
  type TransportType (line 503) | enum TransportType
  type addrinfo (line 504) | struct addrinfo
  type EndpointParams (line 506) | struct EndpointParams
  type rb_node (line 521) | struct rb_node
  type rb_node (line 522) | struct rb_node
  type RouteParams (line 547) | struct RouteParams
    type TransportType (line 131) | enum TransportType
    type addrinfo (line 132) | struct addrinfo

FILE: src/manager/RouteManager.h
  function class (line 33) | class RouteManager
  type TransportType (line 87) | enum TransportType
  type addrinfo (line 88) | struct addrinfo
  type EndpointParams (line 90) | struct EndpointParams
  type rb_root (line 103) | struct rb_root

FILE: src/manager/UpstreamManager.cc
  class __UpstreamManager (line 26) | class __UpstreamManager
    method __UpstreamManager (line 29) | static __UpstreamManager *get_instance()
    method add_upstream_policy (line 35) | void add_upstream_policy(UPSGroupPolicy *policy)
    method __UpstreamManager (line 43) | __UpstreamManager() :
  function __default_consistent_hash (line 76) | static unsigned int __default_consistent_hash(const char *path,
  type AddressParams (line 255) | struct AddressParams

FILE: src/manager/UpstreamManager.h
  function class (line 77) | class UpstreamManager

FILE: src/manager/WFFacilities.h
  function class (line 28) | class WFFacilities

FILE: src/manager/WFFuture.h
  function RES (line 52) | RES get()
  function set_value (line 79) | void set_value(const RES& value) { this->promise.set_value(value); }
  function set_value (line 80) | void set_value(RES&& value) { this->promise.set_value(std::move(value)); }
  function get (line 131) | void>::get()
  function set_value (line 152) | void set_value() { this->promise.set_value(); }

FILE: src/manager/WFGlobal.cc
  class __WFGlobal (line 42) | class __WFGlobal
    method __WFGlobal (line 45) | static __WFGlobal *get_instance()
    method register_scheme_port (line 69) | void register_scheme_port(const std::string& scheme, unsigned short port)
    method sync_operation_begin (line 76) | void sync_operation_begin()
    method sync_operation_end (line 90) | void sync_operation_end()
  class __SSLManager (line 176) | class __SSLManager
    method __SSLManager (line 179) | static __SSLManager *get_instance()
    method SSL_CTX (line 185) | SSL_CTX *get_ssl_client_ctx() { return ssl_client_ctx_; }
    method SSL_CTX (line 186) | SSL_CTX *new_ssl_server_ctx() { return SSL_CTX_new(SSLv23_server_metho...
    method __SSLManager (line 189) | __SSLManager()
  class __FileIOService (line 205) | class __FileIOService : public IOService
    method __FileIOService (line 208) | __FileIOService(CommScheduler *scheduler):
    method bind (line 213) | int bind()
    method deinit (line 227) | void deinit()
    method handle_unbound (line 238) | virtual void handle_unbound()
    method handle_stop (line 246) | virtual void handle_stop(int error)
  class __ThreadDnsManager (line 257) | class __ThreadDnsManager
    method __ThreadDnsManager (line 260) | static __ThreadDnsManager *get_instance()
    method ExecQueue (line 266) | ExecQueue *get_dns_queue() { return &dns_queue_; }
    method Executor (line 267) | Executor *get_dns_executor() { return &dns_executor_; }
    method __ThreadDnsManager (line 269) | __ThreadDnsManager()
  class __CommManager (line 293) | class __CommManager
    method __CommManager (line 296) | static __CommManager *get_instance()
    method CommScheduler (line 303) | CommScheduler *get_scheduler() { return &scheduler_; }
    method is_created (line 305) | static bool is_created() { return created_; }
    method __CommManager (line 308) | __CommManager():
  function IOService (line 343) | inline IOService *__CommManager::get_io_service()
  class __ExecManager (line 377) | class __ExecManager
    method __ExecManager (line 383) | static __ExecManager *get_instance()
    method Executor (line 390) | Executor *get_compute_executor() { return &compute_executor_; }
    method __ExecManager (line 393) | __ExecManager():
  function ExecQueue (line 424) | inline ExecQueue *__ExecManager::get_exec_queue(const std::string& queue...
  function __dns_server_url (line 458) | static std::string __dns_server_url(const std::string& url,
  function __split_merge_str (line 488) | static void __split_merge_str(const char *p, bool is_nameserver,
  function __set_options (line 532) | static void __set_options(const char *p,
  function __parse_resolv_conf (line 562) | static int __parse_resolv_conf(const char *path,
  class __DnsClientManager (line 598) | class __DnsClientManager
    method __DnsClientManager (line 601) | static __DnsClientManager *get_instance()
    method WFDnsClient (line 608) | WFDnsClient *get_dns_client() { return client_; }
    method WFResourcePool (line 609) | WFResourcePool *get_dns_respool() { return &respool_; }
    method __DnsClientManager (line 612) | __DnsClientManager() : respool_(WFGlobal::get_global_settings()->
  type WFGlobalSettings (line 652) | struct WFGlobalSettings
  function CommScheduler (line 663) | CommScheduler *WFGlobal::get_scheduler()
  function SSL_CTX (line 668) | SSL_CTX *WFGlobal::get_ssl_client_ctx()
  function SSL_CTX (line 673) | SSL_CTX *WFGlobal::new_ssl_server_ctx()
  function ExecQueue (line 678) | ExecQueue *WFGlobal::get_exec_queue(const std::string& queue_name)
  function Executor (line 683) | Executor *WFGlobal::get_compute_executor()
  function IOService (line 688) | IOService *WFGlobal::get_io_service()
  function ExecQueue (line 693) | ExecQueue *WFGlobal::get_dns_queue()
  function Executor (line 698) | Executor *WFGlobal::get_dns_executor()
  function WFDnsClient (line 703) | WFDnsClient *WFGlobal::get_dns_client()
  function WFResourcePool (line 708) | WFResourcePool *WFGlobal::get_dns_respool()
  function WORKFLOW_library_init (line 931) | void WORKFLOW_library_init(const struct WFGlobalSettings *settings)

FILE: src/manager/WFGlobal.h
  type WFGlobalSettings (line 48) | struct WFGlobalSettings
  type WFGlobalSettings (line 66) | struct WFGlobalSettings
  type WFGlobalSettings (line 85) | struct WFGlobalSettings
  function class (line 91) | class WFGlobal

FILE: src/nameservice/UpstreamPolicies.cc
  class EndpointGroup (line 28) | class EndpointGroup
    method EndpointGroup (line 31) | EndpointGroup(int group_id, UPSGroupPolicy *policy) :
    type rb_node (line 52) | struct rb_node
  type AddressParams (line 61) | struct AddressParams
  function EndpointAddress (line 203) | EndpointAddress *UPSGroupPolicy::check_and_get(EndpointAddress *addr,
  function EndpointAddress (line 228) | EndpointAddress *EndpointGroup::get_one(WFNSTracing *tracing)
  function EndpointAddress (line 268) | EndpointAddress *EndpointGroup::get_one_backup(WFNSTracing *tracing)
  function EndpointAddress (line 397) | EndpointAddress *UPSGroupPolicy::consistent_hash_with_group(unsigned int...
  function EndpointAddress (line 469) | EndpointAddress *UPSRoundRobinPolicy::first_strategy(const ParsedURI& uri,
  function EndpointAddress (line 475) | EndpointAddress *UPSRoundRobinPolicy::another_strategy(const ParsedURI& ...
  type TracingData (line 511) | struct TracingData
  type TracingData (line 511) | struct TracingData
  function EndpointAddress (line 524) | EndpointAddress *UPSWeightedRandomPolicy::first_strategy(const ParsedURI...
  function EndpointAddress (line 553) | EndpointAddress *UPSWeightedRandomPolicy::another_strategy(const ParsedU...
  function EndpointAddress (line 612) | EndpointAddress *UPSVNSWRRPolicy::first_strategy(const ParsedURI& uri,
  function EndpointAddress (line 675) | EndpointAddress *UPSConsistentHashPolicy::first_strategy(const ParsedURI...
  function EndpointAddress (line 698) | EndpointAddress *UPSManualPolicy::first_strategy(const ParsedURI& uri,
  function EndpointAddress (line 711) | EndpointAddress *UPSManualPolicy::another_strategy(const ParsedURI& uri,

FILE: src/nameservice/UpstreamPolicies.h
  function class (line 40) | class UPSAddrParams : public PolicyAddrParams
  function class (line 52) | class UPSGroupPolicy : public WFServiceGovernance
  function class (line 93) | class UPSRoundRobinPolicy : public UPSGroupPolicy
  function class (line 115) | class UPSWeightedRandomPolicy : public UPSGroupPolicy
  function class (line 143) | class UPSVNSWRRPolicy : public UPSWeightedRandomPolicy
  function class (line 166) | class UPSConsistentHashPolicy : public UPSGroupPolicy
  function class (line 184) | class UPSManualPolicy : public UPSGroupPolicy

FILE: src/nameservice/WFDnsResolver.cc
  class DnsInput (line 48) | class DnsInput
    method DnsInput (line 51) | DnsInput() :
    method DnsInput (line 57) | DnsInput(const std::string& host, unsigned short port,
    method reset (line 65) | void reset(const std::string& host, unsigned short port)
    method reset (line 73) | void reset(const std::string& host, unsigned short port,
    method get_port (line 83) | unsigned short get_port() const { return port_; }
    method is_numeric_host (line 84) | bool is_numeric_host() const { return numeric_host_; }
  class DnsOutput (line 95) | class DnsOutput
    method DnsOutput (line 98) | DnsOutput():
    method get_error (line 114) | int get_error() const { return error_; }
    type addrinfo (line 115) | struct addrinfo
    type addrinfo (line 118) | struct addrinfo
    type addrinfo (line 120) | struct addrinfo
    type addrinfo (line 127) | struct addrinfo
  class DnsRoutine (line 132) | class DnsRoutine
    method create (line 136) | static void create(DnsOutput *out, int error, struct addrinfo *ai)
  type sockaddr_un (line 156) | struct sockaddr_un
  type addrinfo (line 160) | struct addrinfo
  type sockaddr_un (line 160) | struct sockaddr_un
  type addrinfo (line 162) | struct addrinfo
  type sockaddr_un (line 165) | struct sockaddr_un
  type sockaddr (line 171) | struct sockaddr
  type sockaddr_un (line 172) | struct sockaddr_un
  type addrinfo (line 192) | struct addrinfo
  type DnsContext (line 213) | struct DnsContext
    type addrinfo (line 217) | struct addrinfo
  function __default_family (line 220) | static int __default_family()
  function __readaddrinfo_line (line 252) | static int __readaddrinfo_line(char *p, const char *name, const char *port,
  function __readaddrinfo (line 295) | static int __readaddrinfo(const char *path,
  function ThreadDnsTask (line 336) | static ThreadDnsTask *__create_thread_dns_task(const std::string& host,
  function __get_cache_host (line 351) | static std::string __get_cache_host(const std::string& hostname,
  function __get_guard_name (line 368) | static std::string __get_guard_name(const std::string& cache_host,
  type addrinfo (line 414) | struct addrinfo
  type addrinfo (line 415) | struct addrinfo
  type in6_addr (line 443) | struct in6_addr
  type addrinfo (line 471) | struct addrinfo
  type addrinfo (line 476) | struct addrinfo
  type DnsContext (line 527) | struct DnsContext
    type addrinfo (line 217) | struct addrinfo
  type DnsContext (line 527) | struct DnsContext
    type addrinfo (line 217) | struct addrinfo
  function SubTask (line 575) | SubTask *WFResolverTask::done()
  type addrinfo (line 611) | struct addrinfo
  type addrinfo (line 641) | struct addrinfo
  type DnsContext (line 663) | struct DnsContext
    type addrinfo (line 217) | struct addrinfo
  type DnsContext (line 663) | struct DnsContext
    type addrinfo (line 217) | struct addrinfo
  type DnsContext (line 679) | struct DnsContext
    type addrinfo (line 217) | struct addrinfo
  type DnsContext (line 679) | struct DnsContext
    type addrinfo (line 217) | struct addrinfo
  type DnsContext (line 680) | struct DnsContext
    type addrinfo (line 217) | struct addrinfo
  type addrinfo (line 684) | struct addrinfo
  type addrinfo (line 685) | struct addrinfo
  function WFRouterTask (line 749) | WFRouterTask *WFDnsResolver::create_router_task(const struct WFNSParams ...

FILE: src/nameservice/WFDnsResolver.h
  function class (line 27) | class WFResolverTask : public WFRouterTask
  type WFNSParams (line 48) | struct WFNSParams
  type WFNSParams (line 78) | struct WFNSParams
  type EndpointParams (line 81) | struct EndpointParams
  function class (line 91) | class WFDnsResolver : public WFNSPolicy

FILE: src/nameservice/WFNameService.cc
  type WFNSPolicyEntry (line 27) | struct WFNSPolicyEntry
    type rb_node (line 29) | struct rb_node
  type rb_node (line 36) | struct rb_node
  type rb_node (line 37) | struct rb_node
  type WFNSPolicyEntry (line 38) | struct WFNSPolicyEntry
    type rb_node (line 29) | struct rb_node
  type WFNSPolicyEntry (line 58) | struct WFNSPolicyEntry
    type rb_node (line 29) | struct rb_node
  type WFNSPolicyEntry (line 60) | struct WFNSPolicyEntry
    type rb_node (line 29) | struct rb_node
  type WFNSPolicyEntry (line 77) | struct WFNSPolicyEntry
    type rb_node (line 29) | struct rb_node
  type rb_node (line 79) | struct rb_node
  type WFNSPolicyEntry (line 80) | struct WFNSPolicyEntry
    type rb_node (line 29) | struct rb_node
  function WFNSPolicy (line 98) | WFNSPolicy *WFNameService::get_policy(const char *name)
  function WFNSPolicy (line 116) | WFNSPolicy *WFNameService::del_policy(const char *name)
  type WFNSPolicyEntry (line 136) | struct WFNSPolicyEntry
    type rb_node (line 29) | struct rb_node

FILE: src/nameservice/WFNameService.h
  function class (line 33) | class WFRouterTask : public WFGenericTask
  function class (line 65) | class WFNSTracing
  type WFNSParams (line 79) | struct WFNSParams
  function class (line 93) | class WFNSPolicy
  function class (line 118) | class WFNameService

FILE: src/nameservice/WFServiceGovernance.cc
  type AddressParams (line 62) | struct AddressParams
  type AddressParams (line 69) | struct AddressParams
  class WFSGResolverTask (line 103) | class WFSGResolverTask : public WFResolverTask
    method WFSGResolverTask (line 106) | WFSGResolverTask(const struct WFNSParams *params,
  function copy_host_port (line 121) | static void copy_host_port(ParsedURI& uri, const EndpointAddress *addr)
  function WFRouterTask (line 175) | WFRouterTask *WFServiceGovernance::create_router_task(const struct WFNSP...
  type TracingData (line 183) | struct TracingData
  type TracingData (line 183) | struct TracingData
  type TracingData (line 202) | struct TracingData
  type TracingData (line 202) | struct TracingData
  type TracingData (line 258) | struct TracingData
  type TracingData (line 258) | struct TracingData
  type TracingData (line 277) | struct TracingData
  type TracingData (line 277) | struct TracingData
  type list_head (line 291) | struct list_head
  type EndpointAddress::address_entry (line 292) | struct EndpointAddress::address_entry
  type list_head (line 327) | struct list_head
  type EndpointAddress::address_entry (line 328) | struct EndpointAddress::address_entry
  function EndpointAddress (line 336) | EndpointAddress *WFServiceGovernance::first_strategy(const ParsedURI& uri,
  function EndpointAddress (line 343) | EndpointAddress *WFServiceGovernance::another_strategy(const ParsedURI& ...

FILE: src/nameservice/WFServiceGovernance.h
  type AddressParams (line 32) | struct AddressParams
  type AddressParams (line 49) | struct AddressParams
  function class (line 60) | class PolicyAddrParams
  function class (line 74) | class EndpointAddress
  function class (line 96) | class WFServiceGovernance : public WFNSPolicy

FILE: src/protocol/ConsulDataTypes.h
  function namespace (line 28) | namespace protocol
  type ConsulService (line 304) | struct ConsulService
  type ConsulServiceInstance (line 322) | struct ConsulServiceInstance
  type ConsulServiceTags (line 345) | struct ConsulServiceTags

FILE: src/protocol/DnsMessage.cc
  type protocol (line 28) | namespace protocol
    function __append_uint8 (line 31) | static inline void __append_uint8(std::string& s, uint8_t tmp)
    function __append_uint16 (line 36) | static inline void __append_uint16(std::string& s, uint16_t tmp)
    function __append_uint32 (line 42) | static inline void __append_uint32(std::string& s, uint32_t tmp)
    function __append_name (line 48) | static inline int __append_name(std::string& s, const char *p)
    function __append_record_list (line 82) | static inline int __append_record_list(std::string& s, int *count,
    function DnsMessage (line 199) | DnsMessage& DnsMessage::operator = (DnsMessage&& msg)
    type list_head (line 220) | struct list_head
    type list_head (line 240) | struct list_head
    type list_head (line 252) | struct list_head
    type list_head (line 264) | struct list_head
    type list_head (line 276) | struct list_head
    type list_head (line 288) | struct list_head
    type list_head (line 302) | struct list_head
    type list_head (line 316) | struct list_head
    type list_head (line 329) | struct list_head
    type list_head (line 341) | struct list_head
    type dns_header (line 352) | struct dns_header
    type dns_header (line 392) | struct dns_header
    type iovec (line 414) | struct iovec
    type iovec (line 416) | struct iovec

FILE: src/protocol/DnsMessage.h
  function namespace (line 32) | namespace protocol

FILE: src/protocol/DnsUtil.cc
  type protocol (line 27) | namespace protocol
    type addrinfo (line 32) | struct addrinfo
    type addrinfo (line 37) | struct addrinfo
    type addrinfo (line 38) | struct addrinfo
    type dns_record (line 39) | struct dns_record
    type addrinfo (line 40) | struct addrinfo
    type sockaddr_in (line 85) | struct sockaddr_in
    type sockaddr_in6 (line 90) | struct sockaddr_in6
    type addrinfo (line 93) | struct addrinfo
    type addrinfo (line 93) | struct addrinfo
    type sockaddr (line 103) | struct sockaddr
    type sockaddr_in (line 108) | struct sockaddr_in
    type sockaddr_in (line 108) | struct sockaddr_in
    type in_addr (line 110) | struct in_addr
    type sockaddr_in6 (line 114) | struct sockaddr_in6
    type sockaddr_in6 (line 114) | struct sockaddr_in6
    type in6_addr (line 116) | struct in6_addr
    type addrinfo (line 134) | struct addrinfo
    type addrinfo (line 136) | struct addrinfo

FILE: src/protocol/DnsUtil.h
  function namespace (line 30) | namespace protocol

FILE: src/protocol/HttpMessage.cc
  type protocol (line 26) | namespace protocol
    type HttpMessageBlock (line 29) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type HttpMessageBlock (line 38) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type HttpMessageBlock (line 39) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type HttpMessageBlock (line 39) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type HttpMessageBlock (line 56) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type HttpMessageBlock (line 57) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type HttpMessageBlock (line 57) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type HttpMessageBlock (line 74) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type list_head (line 75) | struct list_head
    type HttpMessageBlock (line 94) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type list_head (line 95) | struct list_head
    type HttpMessageBlock (line 116) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type list_head (line 117) | struct list_head
    type list_head (line 129) | struct list_head
    type list_head (line 129) | struct list_head
    type HttpMessageBlock (line 131) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type HttpMessageBlock (line 132) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type HttpMessageBlock (line 132) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type HttpMessageBlock (line 133) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type iovec (line 159) | struct iovec
    type HttpMessageHeader (line 163) | struct HttpMessageHeader
    type HttpMessageBlock (line 164) | struct HttpMessageBlock
      type list_head (line 31) | struct list_head
    type list_head (line 165) | struct list_head
    function HttpMessage (line 285) | HttpMessage& HttpMessage::operator = (HttpMessage&& msg)
    type iovec (line 449) | struct iovec
    function HttpMessageChunk (line 613) | HttpMessageChunk& HttpMessageChunk::operator = (HttpMessageChunk&& msg)

FILE: src/protocol/HttpMessage.h
  function namespace (line 35) | namespace protocol
  function class (line 254) | class HttpRequest : public HttpMessage
  function class (line 329) | class HttpResponse : public HttpMessage
  function class (line 408) | class HttpMessageChunk : public ProtocolMessage

FILE: src/protocol/HttpUtil.cc
  type protocol (line 27) | namespace protocol
    type HttpMessageHeader (line 33) | struct HttpMessageHeader
    type HttpMessageHeader (line 395) | struct HttpMessageHeader
    type HttpMessageHeader (line 409) | struct HttpMessageHeader
    type HttpMessageHeader (line 425) | struct HttpMessageHeader

FILE: src/protocol/HttpUtil.h
  function namespace (line 118) | namespace protocol

FILE: src/protocol/KafkaDataTypes.cc
  type protocol (line 26) | namespace protocol
    function compare_member (line 55) | static bool compare_member(const kafka_member_t *m1, const kafka_membe...
    type list_head (line 155) | struct list_head
    type list_head (line 253) | struct list_head
    type list_head (line 282) | struct list_head
    type list_head (line 331) | struct list_head
    type list_head (line 350) | struct list_head
    function KafkaCgroup (line 389) | KafkaCgroup& KafkaCgroup::operator= (KafkaCgroup&& move)
    function KafkaCgroup (line 418) | KafkaCgroup& KafkaCgroup::operator= (const KafkaCgroup& copy)
    type list_head (line 461) | struct list_head
    type list_head (line 487) | struct list_head
    function KafkaToppar (line 524) | KafkaToppar *KafkaCgroup::get_assigned_toppar_next()
    type list_head (line 572) | struct list_head
    type list_head (line 588) | struct list_head
    type list_head (line 589) | struct list_head
    function KafkaToppar (line 637) | KafkaToppar *get_toppar(const char *topic, int partition,
    function KafkaMeta (line 653) | const KafkaMeta *get_meta(const char *topic, KafkaMetaList *meta_list)

FILE: src/protocol/KafkaDataTypes.h
  function namespace (line 37) | namespace protocol

FILE: src/protocol/KafkaMessage.cc
  type protocol (line 41) | namespace protocol
    function htonll (line 52) | static uint64_t htonll(uint64_t x)
    function append_bool (line 61) | static size_t append_bool(std::string& buf, bool val)
    function append_i8 (line 72) | static size_t append_i8(std::string& buf, int8_t val)
    function append_i8 (line 78) | static size_t append_i8(void **buf, int8_t val)
    function append_i16 (line 85) | static size_t append_i16(std::string& buf, int16_t val)
    function append_i32 (line 93) | static size_t append_i32(std::string& buf, int32_t val)
    function append_i32 (line 101) | static size_t append_i32(void **buf, int32_t val)
    function append_i64 (line 110) | static size_t append_i64(std::string& buf, int64_t val)
    function append_i64 (line 118) | static size_t append_i64(void **buf, int64_t val)
    function append_string (line 127) | static size_t append_string(std::string& buf, const char *str, size_t ...
    function append_string (line 134) | static size_t append_string(std::string& buf, const char *str)
    function append_string_raw (line 142) | static size_t append_string_raw(std::string& buf, const char *str, siz...
    function append_nullable_string (line 148) | static size_t append_nullable_string(std::string& buf, const char *str...
    function append_string_raw (line 156) | static size_t append_string_raw(void **buf, const char *str, size_t len)
    function append_string_raw (line 163) | static size_t append_string_raw(void **buf, const std::string& str)
    function append_bytes (line 168) | static size_t append_bytes(std::string& buf, const char *str, size_t len)
    function append_bytes (line 175) | static size_t append_bytes(std::string& buf, const std::string& str)
    function append_bytes (line 180) | static size_t append_bytes(void **buf, const char *str, size_t len)
    function append_nullable_bytes (line 191) | static size_t append_nullable_bytes(void **buf, const char *str, size_...
    function append_varint_u64 (line 199) | static size_t append_varint_u64(std::string& buf, uint64_t num)
    function append_varint_i64 (line 213) | static inline size_t append_varint_i64(std::string& buf, int64_t num)
    function append_varint_i32 (line 218) | static inline size_t append_varint_i32(std::string& buf, int32_t num)
    function append_compact_string (line 223) | static size_t append_compact_string(std::string& buf, const char *str)
    function parse_i8 (line 234) | static inline int parse_i8(void **buf, size_t *size, int8_t *val)
    function parse_i16 (line 248) | static inline int parse_i16(void **buf, size_t *size, int16_t *val)
    function parse_i32 (line 262) | static inline int parse_i32(void **buf, size_t *size, int32_t *val)
    function parse_i64 (line 276) | static inline int parse_i64(void **buf, size_t *size, int64_t *val)
    function parse_varint_i64 (line 301) | static int parse_varint_i64(void **buf, size_t *size, int64_t *val)
    function parse_varint_i32 (line 312) | static int parse_varint_i32(void **buf, size_t *size, int32_t *val)
    function compress_buf (line 329) | static int compress_buf(KafkaBlock *block, int compress_type, void *env)
    function gzip_decompress (line 446) | static int gzip_decompress(void *compressed, size_t n, KafkaBlock *block)
    function kafka_snappy_java_uncompress (line 531) | static int kafka_snappy_java_uncompress(const char *inbuf, size_t inle...
    function snappy_decompress (line 606) | static int snappy_decompress(void *buf, size_t n, KafkaBlock *block)
    function lz4_decompress (line 640) | static int lz4_decompress(void *buf, size_t n, KafkaBlock *block)
    function zstd_decompress (line 740) | static int zstd_decompress(void *buf, size_t n, KafkaBlock *block)
    function uncompress_buf (line 783) | static int uncompress_buf(void *buf, size_t size, KafkaBlock *block,
    function append_message_set (line 806) | static int append_message_set(KafkaBlock *block,
    function append_batch_record (line 861) | static int append_batch_record(KafkaBlock *block,
    function append_record (line 953) | static int append_record(KafkaBlock *block,
    function parse_string (line 985) | static int parse_string(void **buf, size_t *size, std::string& str)
    function parse_string (line 1018) | static int parse_string(void **buf, size_t *size, char **str)
    function parse_bytes (line 1064) | static int parse_bytes(void **buf, size_t *size, std::string& str)
    function parse_bytes (line 1095) | static int parse_bytes(void **buf, size_t *size,
    function parse_varint_u64 (line 1128) | static int parse_varint_u64(void **buf, size_t *size, uint64_t *val)
    type list_head (line 1156) | struct list_head
    type list_head (line 1235) | struct list_head
    type list_head (line 1245) | struct list_head
    function parse_varint_bytes (line 1273) | static int parse_varint_bytes(void **buf, size_t *size,
    type KafkaBatchRecordHeader (line 1304) | struct KafkaBatchRecordHeader
    type list_head (line 1387) | struct list_head
    type list_head (line 1509) | struct list_head
    function KafkaMessage (line 1609) | KafkaMessage& KafkaMessage::operator= (KafkaMessage &&msg)
    type iovec (line 1647) | struct iovec
    function kafka_api_get_max_ver (line 1657) | static int kafka_api_get_max_ver(int api_type)
    function kafka_get_api_version (line 1696) | static int kafka_get_api_version(const kafka_api_t *api, const KafkaCo...
    type iovec (line 1759) | struct iovec
    function kafka_compress_prepare (line 1799) | static int kafka_compress_prepare(int compress_type, void **env,
    function kafka_compress_finish (line 1891) | static int kafka_compress_finish(int compress_type, void *env,
    type iovec (line 2062) | struct iovec
    type timespec (line 2115) | struct timespec
    type iovec (line 2322) | struct iovec
    type iovec (line 2393) | struct iovec
    type iovec (line 2426) | struct iovec
    function kafka_cgroup_gen_metadata (line 2441) | static std::string kafka_cgroup_gen_metadata(KafkaMetaList& meta_list)
    type iovec (line 2467) | struct iovec
    type list_head (line 2487) | struct list_head
    type list_head (line 2516) | struct list_head
    type iovec (line 2537) | struct iovec
    type iovec (line 2567) | struct iovec
    type iovec (line 2579) | struct iovec
    type list_head (line 2587) | struct list_head
    type iovec (line 2613) | struct iovec
    type iovec (line 2641) | struct iovec
    type iovec (line 2692) | struct iovec
    type iovec (line 2709) | struct iovec
    type iovec (line 2714) | struct iovec
    type iovec (line 2725) | struct iovec
    function kafka_meta_parse_broker (line 2788) | static int kafka_meta_parse_broker(void **buf, size_t *size,
    function kafka_broker_get_leader (line 2833) | static bool kafka_broker_get_leader(int leader_id, KafkaBrokerList *br...
    function kafka_meta_parse_partition (line 2873) | static int kafka_meta_parse_partition(void **buf, size_t *size,
    function KafkaMeta (line 2961) | static KafkaMeta *find_meta_by_name(const std::string& topic, KafkaMet...
    function kafka_meta_parse_topic (line 2976) | static int kafka_meta_parse_topic(void **buf, size_t *size,
    function KafkaToppar (line 3037) | KafkaToppar *KafkaMessage::find_toppar_by_name(const std::string& topi...
    function KafkaToppar (line 3054) | KafkaToppar *KafkaMessage::find_toppar_by_name(const std::string& topi...
    type list_head (line 3109) | struct list_head
    function kafka_meta_find_or_add_topic (line 3309) | static bool kafka_meta_find_or_add_topic(const std::string& topic_name,
    function kafka_cgroup_parse_member (line 3337) | static int kafka_cgroup_parse_member(void **buf, size_t *size,
    type list_head (line 3442) | struct list_head
    function kafka_api_version_cmp (line 3591) | static bool kafka_api_version_cmp(const kafka_api_version_t& api_ver1,
    type iovec (line 3712) | struct iovec

FILE: src/protocol/KafkaMessage.h
  function namespace (line 32) | namespace protocol

FILE: src/protocol/KafkaResult.cc
  type protocol (line 21) | namespace protocol
    function KafkaResult (line 35) | KafkaResult& KafkaResult::operator= (KafkaResult&& move)

FILE: src/protocol/KafkaResult.h
  function namespace (line 28) | namespace protocol

FILE: src/protocol/MySQLMessage.cc
  type protocol (line 35) | namespace protocol
    function MySQLMessage (line 69) | MySQLMessage& MySQLMessage::operator= (MySQLMessage&& move)
    type iovec (line 135) | struct iovec
    type iovec (line 203) | struct iovec
    function __native_password_encrypt (line 298) | static std::string __native_password_encrypt(const std::string& password,
    function __caching_sha2_password_encrypt (line 316) | static std::string __caching_sha2_password_encrypt(const std::string& ...
    type iovec (line 334) | struct iovec
    type iovec (line 373) | struct iovec
    type iovec (line 503) | struct iovec
    type iovec (line 544) | struct iovec
    type iovec (line 579) | struct iovec

FILE: src/protocol/MySQLMessage.h
  function class (line 36) | class MySQLMessage : public ProtocolMessage
  function class (line 70) | class MySQLRequest : public MySQLMessage

FILE: src/protocol/MySQLResult.cc
  type protocol (line 25) | namespace protocol
    type __mysql_result_set (line 92) | struct __mysql_result_set
    type __mysql_result_set (line 140) | struct __mysql_result_set
    type __mysql_result_set (line 157) | struct __mysql_result_set
    type __mysql_result_set (line 319) | struct __mysql_result_set

FILE: src/protocol/MySQLResult.h
  type __mysql_result_set (line 164) | struct __mysql_result_set

FILE: src/protocol/MySQLUtil.cc
  type protocol (line 22) | namespace protocol

FILE: src/protocol/MySQLUtil.h
  function namespace (line 24) | namespace protocol

FILE: src/protocol/PackageWrapper.cc
  type protocol (line 22) | namespace protocol
    type iovec (line 25) | struct iovec

FILE: src/protocol/PackageWrapper.h
  function namespace (line 24) | namespace protocol

FILE: src/protocol/ProtocolMessage.h
  function namespace (line 32) | namespace protocol
  function class (line 137) | class ProtocolWrapper : public ProtocolMessage

FILE: src/protocol/RedisMessage.cc
  type protocol (line 27) | namespace protocol
    function RedisValue (line 34) | RedisValue& RedisValue::operator= (const RedisValue& copy)
    function RedisValue (line 68) | RedisValue& RedisValue::operator= (RedisValue&& move)
    function RedisMessage (line 467) | RedisMessage& RedisMessage::operator= (RedisMessage &&move)
    type iovec (line 535) | struct iovec
    type iovec (line 632) | struct iovec

FILE: src/protocol/RedisMessage.h
  function namespace (line 34) | namespace protocol

FILE: src/protocol/SSLWrapper.cc
  type protocol (line 25) | namespace protocol
    type iovec (line 28) | struct iovec
    function __ssl_handshake (line 61) | static int __ssl_handshake(const void *buf, size_t *size, SSL *ssl,
    type iovec (line 125) | struct iovec
    type iovec (line 128) | struct iovec

FILE: src/protocol/SSLWrapper.h
  function namespace (line 25) | namespace protocol

FILE: src/protocol/TLVMessage.cc
  type protocol (line 26) | namespace protocol
    type iovec (line 29) | struct iovec

FILE: src/protocol/TLVMessage.h
  function namespace (line 27) | namespace protocol

FILE: src/protocol/dns_parser.c
  type __dns_record_entry (line 32) | struct __dns_record_entry
  function __dns_parser_uint8 (line 39) | static inline uint8_t __dns_parser_uint8(const char *ptr)
  function __dns_parser_uint16 (line 44) | static inline uint16_t __dns_parser_uint16(const char *ptr)
  function __dns_parser_uint32 (line 51) | static inline uint32_t __dns_parser_uint32(const char *ptr)
  function __dns_parser_parse_host (line 67) | static int __dns_parser_parse_host(char *phost, dns_parser_t *parser)
  function __dns_parser_free_record (line 137) | static void __dns_parser_free_record(struct __dns_record_entry *r)
  function __dns_parser_free_record_list (line 168) | static void __dns_parser_free_record_list(struct list_head *head)
  function __dns_parser_parse_a (line 190) | static int __dns_parser_parse_a(struct __dns_record_entry **r,
  function __dns_parser_parse_aaaa (line 224) | static int __dns_parser_parse_aaaa(struct __dns_record_entry **r,
  function __dns_parser_parse_names (line 252) | static int __dns_parser_parse_names(struct __dns_record_entry **r,
  function __dns_parser_parse_soa (line 320) | static int __dns_parser_parse_soa(struct __dns_record_entry **r,
  function __dns_parser_parse_srv (line 395) | static int __dns_parser_parse_srv(struct __dns_record_entry **r,
  function __dns_parser_parse_mx (line 452) | static int __dns_parser_parse_mx(struct __dns_record_entry **r,
  function __dns_parser_parse_others (line 501) | static int __dns_parser_parse_others(struct __dns_record_entry **r,
  function __dns_parser_parse_record (line 546) | static int __dns_parser_parse_record(int idx, dns_parser_t *parser)
  function __dns_parser_parse_question (line 665) | static int __dns_parser_parse_question(dns_parser_t *parser)
  function dns_parser_init (line 707) | void dns_parser_init(dns_parser_t *parser)
  function dns_parser_set_question (line 723) | int dns_parser_set_question(const char *name,
  function dns_parser_set_question_name (line 741) | int dns_parser_set_question_name(const char *name, dns_parser_t *parser)
  function dns_parser_set_id (line 764) | void dns_parser_set_id(uint16_t id, dns_parser_t *parser)
  function dns_parser_parse_all (line 769) | int dns_parser_parse_all(dns_parser_t *parser)
  function dns_parser_append_message (line 804) | int dns_parser_append_message(const void *buf,
  function dns_parser_deinit (line 868) | void dns_parser_deinit(dns_parser_t *parser)
  function dns_record_cursor_next (line 878) | int dns_record_cursor_next(struct dns_record **record,
  function dns_record_cursor_find_cname (line 894) | int dns_record_cursor_find_cname(const char *name,
  function dns_add_raw_record (line 920) | int dns_add_raw_record(const char *name, uint16_t type, uint16_t rclass,
  function dns_add_str_record (line 949) | int dns_add_str_record(const char *name, uint16_t type, uint16_t rclass,
  function dns_add_soa_record (line 959) | int dns_add_soa_record(const char *name, uint16_t rclass, uint32_t ttl,
  function dns_add_srv_record (line 1011) | int dns_add_srv_record(const char *name, uint16_t rclass, uint32_t ttl,
  function dns_add_mx_record (line 1057) | int dns_add_mx_record(const char *name, uint16_t rclass, uint32_t ttl,

FILE: src/protocol/dns_parser.h
  type dns_header (line 32) | struct dns_header
  type dns_question (line 63) | struct dns_question
  type dns_record_soa (line 70) | struct dns_record_soa
  type dns_record_srv (line 81) | struct dns_record_srv
  type dns_record_mx (line 89) | struct dns_record_mx
  type dns_record (line 95) | struct dns_record
  type dns_parser_t (line 105) | typedef struct __dns_parser
  type dns_record_cursor_t (line 121) | typedef struct __dns_record_cursor
  type dns_record (line 148) | struct dns_record
  type list_head (line 157) | struct list_head
  type list_head (line 161) | struct list_head
  type list_head (line 167) | struct list_head
  type list_head (line 172) | struct list_head
  type list_head (line 176) | struct list_head
  function dns_answer_cursor_init (line 187) | static inline void dns_answer_cursor_init(dns_record_cursor_t *cursor,
  function dns_authority_cursor_init (line 194) | static inline void dns_authority_cursor_init(dns_record_cursor_t *cursor,
  function dns_additional_cursor_init (line 201) | static inline void dns_additional_cursor_init(dns_record_cursor_t *cursor,
  function dns_record_cursor_deinit (line 208) | static inline void dns_record_cursor_deinit(dns_record_cursor_t *cursor)

FILE: src/protocol/http_parser.c
  type __header_line (line 48) | struct __header_line
  function __add_message_header (line 56) | static int __add_message_header(const char *name, size_t name_len,
  function __set_message_header (line 82) | static int __set_message_header(const char *name, size_t name_len,
  function __match_request_line (line 122) | static int __match_request_line(const char *method, size_t method_len,
  function __match_status_line (line 160) | static int __match_status_line(const char *version, size_t version_len,
  function __check_message_header (line 204) | static void __check_message_header(const char *name, size_t name_len,
  function __parse_start_line (line 261) | static int __parse_start_line(const char *ptr, size_t len,
  function __parse_header_name (line 340) | static int __parse_header_name(const char *ptr, size_t len,
  function __parse_header_value (line 378) | static int __parse_header_value(const char *ptr, size_t len,
  function __parse_message_header (line 452) | static int __parse_message_header(const void *message, size_t size,
  function __parse_chunk_data (line 480) | static int __parse_chunk_data(const char *ptr, size_t len,
  function __parse_trailer_part (line 527) | static int __parse_trailer_part(const char *ptr, size_t len,
  function __parse_chunk (line 557) | static int __parse_chunk(const void *message, size_t size,
  function http_parser_init (line 581) | void http_parser_init(int is_resp, http_parser_t *parser)
  function http_parser_append_message (line 606) | int http_parser_append_message(const void *buf, size_t *n,
  function http_parser_header_complete (line 681) | int http_parser_header_complete(const http_parser_t *parser)
  function http_parser_get_body (line 686) | int http_parser_get_body(const void **body, size_t *size,
  function http_parser_set_method (line 700) | int http_parser_set_method(const char *method, http_parser_t *parser)
  function http_parser_set_uri (line 713) | int http_parser_set_uri(const char *uri, http_parser_t *parser)
  function http_parser_set_version (line 726) | int http_parser_set_version(const char *version, http_parser_t *parser)
  function http_parser_set_code (line 739) | int http_parser_set_code(const char *code, http_parser_t *parser)
  function http_parser_set_phrase (line 752) | int http_parser_set_phrase(const char *phrase, http_parser_t *parser)
  function http_parser_add_header (line 765) | int http_parser_add_header(const void *name, size_t name_len,
  function http_parser_set_header (line 782) | int http_parser_set_header(const void *name, size_t name_len,
  function http_parser_deinit (line 799) | void http_parser_deinit(http_parser_t *parser)
  function http_header_cursor_next (line 822) | int http_header_cursor_next(const void **name, size_t *name_len,
  function http_header_cursor_find (line 842) | int http_header_cursor_find(const void *name, size_t name_len,
  function http_header_cursor_erase (line 866) | int http_header_cursor_erase(http_header_cursor_t *cursor)

FILE: src/protocol/http_parser.h
  type http_parser_t (line 27) | typedef struct __http_parser
  type http_header_cursor_t (line 55) | typedef struct __http_header_cursor
  function http_parser_chunked (line 122) | static inline int http_parser_chunked(const http_parser_t *parser)
  function http_parser_keep_alive (line 127) | static inline int http_parser_keep_alive(const http_parser_t *parser)
  function http_parser_has_connection (line 132) | static inline int http_parser_has_connection(const http_parser_t *parser)
  function http_parser_has_content_length (line 137) | static inline int http_parser_has_content_length(const http_parser_t *pa...
  function http_parser_has_keep_alive (line 142) | static inline int http_parser_has_keep_alive(const http_parser_t *parser)
  function http_parser_close_message (line 147) | static inline void http_parser_close_message(http_parser_t *parser)
  function http_header_cursor_init (line 152) | static inline void http_header_cursor_init(http_header_cursor_t *cursor,
  function http_header_cursor_rewind (line 159) | static inline void http_header_cursor_rewind(http_header_cursor_t *cursor)
  function http_header_cursor_deinit (line 164) | static inline void http_header_cursor_deinit(http_header_cursor_t *cursor)

FILE: src/protocol/kafka_parser.c
  type kafka_feature_map (line 76) | struct kafka_feature_map {
  function kafka_get_legacy_api_version (line 181) | static int kafka_get_legacy_api_version(const char *broker_version,
  function kafka_api_version_is_queryable (line 228) | int kafka_api_version_is_queryable(const char *broker_version,
  function kafka_api_version_key_cmp (line 235) | static int kafka_api_version_key_cmp(const void *_a, const void *_b)
  function kafka_api_version_check (line 248) | static int kafka_api_version_check(const kafka_api_version_t *apis,
  function kafka_get_features (line 262) | unsigned kafka_get_features(kafka_api_version_t *api, size_t api_cnt)
  function kafka_broker_get_api_version (line 285) | int kafka_broker_get_api_version(const kafka_api_t *api, int api_key,
  function kafka_parser_init (line 309) | void kafka_parser_init(kafka_parser_t *parser)
  function kafka_parser_deinit (line 318) | void kafka_parser_deinit(kafka_parser_t *parser)
  function kafka_config_init (line 323) | void kafka_config_init(kafka_config_t *conf)
  function kafka_config_deinit (line 356) | void kafka_config_deinit(kafka_config_t *conf)
  function kafka_partition_init (line 366) | void kafka_partition_init(kafka_partition_t *partition)
  function kafka_partition_deinit (line 377) | void kafka_partition_deinit(kafka_partition_t *partition)
  function kafka_api_init (line 384) | void kafka_api_init(kafka_api_t *api)
  function kafka_api_deinit (line 391) | void kafka_api_deinit(kafka_api_t *api)
  function kafka_broker_init (line 396) | void kafka_broker_init(kafka_broker_t *broker)
  function kafka_broker_deinit (line 406) | void kafka_broker_deinit(kafka_broker_t *broker)
  function kafka_meta_init (line 412) | void kafka_meta_init(kafka_meta_t *meta)
  function kafka_meta_deinit (line 422) | void kafka_meta_deinit(kafka_meta_t *meta)
  function kafka_topic_partition_init (line 437) | void kafka_topic_partition_init(kafka_topic_partition_t *toppar)
  function kafka_topic_partition_deinit (line 453) | void kafka_topic_partition_deinit(kafka_topic_partition_t *toppar)
  function kafka_record_header_init (line 459) | void kafka_record_header_init(kafka_record_header_t *header)
  function kafka_record_header_deinit (line 469) | void kafka_record_header_deinit(kafka_record_header_t *header)
  function kafka_record_init (line 478) | void kafka_record_init(kafka_record_t *record)
  function kafka_record_deinit (line 493) | void kafka_record_deinit(kafka_record_t *record)
  function kafka_member_init (line 513) | void kafka_member_init(kafka_member_t *member)
  function kafka_member_deinit (line 522) | void kafka_member_deinit(kafka_member_t *member)
  function kafka_cgroup_init (line 531) | void kafka_cgroup_init(kafka_cgroup_t *cgroup)
  function kafka_cgroup_deinit (line 548) | void kafka_cgroup_deinit(kafka_cgroup_t *cgroup)
  function kafka_block_init (line 567) | void kafka_block_init(kafka_block_t *block)
  function kafka_block_deinit (line 574) | void kafka_block_deinit(kafka_block_t *block)
  function kafka_parser_append_message (line 580) | int kafka_parser_append_message(const void *buf, size_t *size,
  function kafka_topic_partition_set_tp (line 632) | int kafka_topic_partition_set_tp(const char *topic_name, int partition,
  function kafka_record_set_key (line 646) | int kafka_record_set_key(const void *key, size_t key_len,
  function kafka_record_set_value (line 661) | int kafka_record_set_value(const void *val, size_t val_len,
  function kafka_record_header_set_kv (line 676) | int kafka_record_header_set_kv(const void *key, size_t key_len,
  function kafka_meta_set_topic (line 699) | int kafka_meta_set_topic(const char *topic, kafka_meta_t *meta)
  function kafka_cgroup_set_group (line 711) | int kafka_cgroup_set_group(const char *group, kafka_cgroup_t *cgroup)
  function kafka_sasl_plain_recv (line 723) | static int kafka_sasl_plain_recv(const char *buf, size_t len, void *conf...
  function kafka_sasl_plain_client_new (line 728) | static int kafka_sasl_plain_client_new(void *p, kafka_sasl_t *sasl)
  function scram_get_attr (line 755) | static int scram_get_attr(const struct iovec *inbuf, char attr,
  type iovec (line 788) | struct iovec
  function scram_base64_decode (line 813) | static int scram_base64_decode(const struct iovec *in, struct iovec *out)
  function scram_hi (line 847) | static int scram_hi(const EVP_MD *evp, int itcnt, const struct iovec *in,
  function scram_hmac (line 893) | static int scram_hmac(const EVP_MD *evp, const struct iovec *key,
  function scram_h (line 910) | static void scram_h(kafka_scram_t *scram, const struct iovec *str,
  function scram_build_client_final_message_wo_proof (line 918) | static void scram_build_client_final_message_wo_proof(
  function scram_build_client_final_message (line 934) | static int scram_build_client_final_message(kafka_scram_t *scram, int it...
  function scram_handle_server_first_message (line 1030) | static int scram_handle_server_first_message(const char *buf, size_t len,
  function scram_handle_server_final_message (line 1081) | static int scram_handle_server_final_message(const char *buf, size_t len,
  function kafka_sasl_scram_recv (line 1104) | static int kafka_sasl_scram_recv(const char *buf, size_t len, void *p, v...
  function jitter (line 1129) | static int jitter(int low, int high)
  function scram_generate_nonce (line 1134) | static int scram_generate_nonce(struct iovec *iov)
  function kafka_sasl_scram_client_new (line 1151) | static int kafka_sasl_scram_client_new(void *p, kafka_sasl_t *sasl)
  function kafka_sasl_set_mechanisms (line 1181) | int kafka_sasl_set_mechanisms(kafka_config_t *conf)
  function kafka_sasl_init (line 1199) | void kafka_sasl_init(kafka_sasl_t *sasl)
  function kafka_sasl_deinit (line 1216) | void kafka_sasl_deinit(kafka_sasl_t *sasl)
  function kafka_sasl_set_username (line 1223) | int kafka_sasl_set_username(const char *username, kafka_config_t *conf)
  function kafka_sasl_set_password (line 1235) | int kafka_sasl_set_password(const char *password, kafka_config_t *conf)

FILE: src/protocol/kafka_parser.h
  type kafka_api_version_t (line 216) | typedef struct __kafka_api_version
  type kafka_api_t (line 223) | typedef struct __kafka_api_t
  type kafka_parser_t (line 230) | typedef struct __kafka_parser
  type __kafka_scram_state (line 240) | enum __kafka_scram_state
  type kafka_scram_t (line 248) | typedef struct __kafka_scram
  type kafka_sasl_t (line 260) | typedef struct __kafka_sasl
  type kafka_config_t (line 268) | typedef struct __kafka_config
  type kafka_broker_t (line 302) | typedef struct __kafka_broker
  type kafka_partition_t (line 312) | typedef struct __kafka_partition
  type kafka_meta_t (line 323) | typedef struct __kafka_meta
  type kafka_topic_partition_t (line 333) | typedef struct __kafka_topic_partition
  type kafka_record_header_t (line 349) | typedef struct __kafka_record_header
  type kafka_record_t (line 360) | typedef struct __kafka_record
  type kafka_member_t (line 375) | typedef struct __kafka_memeber
  type kafka_group_protocol_t (line 389) | typedef struct __kafka_group_protocol
  type kafka_cgroup_t (line 396) | typedef struct __kafka_cgroup
  type kafka_block_t (line 413) | typedef struct __kafka_block

FILE: src/protocol/mysql_byteorder.c
  function decode_length_safe (line 21) | int decode_length_safe(unsigned long long *res, const unsigned char **pos,
  function decode_string (line 72) | int decode_string(const unsigned char **str, unsigned long long *len,

FILE: src/protocol/mysql_byteorder.h
  function uint2korr (line 28) | static inline uint16_t uint2korr(const unsigned char *A)
  function uint3korr (line 35) | static inline uint32_t uint3korr(const unsigned char *A)
  function uint4korr (line 42) | static inline uint32_t uint4korr(const unsigned char *A)
  function uint8korr (line 49) | static inline uint64_t uint8korr(const unsigned char *A)
  function int2store (line 56) | static inline void int2store(unsigned char *T, uint16_t A)
  function int3store (line 61) | static inline void int3store(unsigned char *T, uint32_t A)
  function int4store (line 66) | static inline void int4store(unsigned char *T, uint32_t A)
  function int7store (line 71) | static inline void int7store(unsigned char *T, uint64_t A)
  function int8store (line 76) | static inline void int8store(unsigned char *T, uint64_t A)
  function uint2korr (line 83) | static inline uint16_t uint2korr(const unsigned char *A)
  function uint3korr (line 88) | static inline uint32_t uint3korr(const unsigned char *p)
  function uint4korr (line 95) | static inline uint32_t uint4korr(const unsigned char *A)
  function uint8korr (line 101) | static inline uint64_t uint8korr(const unsigned char *A)
  function int2store (line 109) | static inline void int2store(unsigned char *T, uint16_t A)
  function int3store (line 116) | static inline void int3store(unsigned char *p, uint32_t x)
  function int4store (line 123) | static inline void int4store(unsigned char *T, uint32_t A)
  function int7store (line 131) | static inline void int7store(unsigned char *T, uint64_t A)
  function int8store (line 142) | static inline void int8store(unsigned char *T, uint64_t A)
  function get_length_size (line 154) | static inline unsigned int get_length_size(unsigned long long num)

FILE: src/protocol/mysql_parser.c
  function mysql_parser_init (line 42) | void mysql_parser_init(mysql_parser_t *parser)
  function mysql_parser_deinit (line 52) | void mysql_parser_deinit(mysql_parser_t *parser)
  function mysql_parser_parse (line 75) | int mysql_parser_parse(const void *buf, size_t len, mysql_parser_t *parser)
  function mysql_parser_get_net_state (line 93) | void mysql_parser_get_net_state(const char **net_state_str,
  function mysql_parser_get_err_msg (line 101) | void mysql_parser_get_err_msg(const char **err_msg_str,
  function parse_base_packet (line 115) | static int parse_base_packet(const void *buf, size_t len, mysql_parser_t...
  function parse_error_packet (line 147) | static int parse_error_packet(const void *buf, size_t len, mysql_parser_...
  function parse_ok_packet (line 179) | static int parse_ok_packet(const void *buf, size_t len, mysql_parser_t *...
  function parse_eof_packet (line 257) | static int parse_eof_packet(const void *buf, size_t len, mysql_parser_t ...
  function parse_field_eof_packet (line 279) | static int parse_field_eof_packet(const void *buf, size_t len, mysql_par...
  function parse_local_inline (line 294) | static int parse_local_inline(const void *buf, size_t len, mysql_parser_...
  function parse_row_packet (line 306) | static int parse_row_packet(const void *buf, size_t len, mysql_parser_t ...
  function parse_field_count (line 349) | static int parse_field_count(const void *buf, size_t len, mysql_parser_t...
  function parse_column_def_packet (line 396) | static int parse_column_def_packet(const void *buf, size_t len, mysql_pa...

FILE: src/protocol/mysql_parser.h
  type mysql_field_t (line 37) | typedef struct __mysql_field
  type __mysql_result_set (line 61) | struct __mysql_result_set
  type mysql_result_set_cursor_t (line 80) | typedef struct __mysql_result_set_cursor
  type mysql_parser_t (line 86) | typedef struct __mysql_parser
  function mysql_parser_set_command (line 137) | static inline void mysql_parser_set_command(int cmd, mysql_parser_t *par...
  function mysql_parser_get_local_inline (line 142) | static inline void mysql_parser_get_local_inline(const char **local_inli...
  function mysql_result_set_cursor_init (line 150) | static inline void mysql_result_set_cursor_init(mysql_result_set_cursor_...
  function mysql_result_set_cursor_rewind (line 157) | static inline void mysql_result_set_cursor_rewind(mysql_result_set_curso...
  function mysql_result_set_cursor_deinit (line 162) | static inline void mysql_result_set_cursor_deinit(mysql_result_set_curso...
  function mysql_result_set_cursor_next (line 166) | static inline int mysql_result_set_cursor_next(struct __mysql_result_set...

FILE: src/protocol/mysql_stream.c
  function __mysql_stream_write_head (line 28) | static int __mysql_stream_write_head(const void *buf, size_t *n,
  function __mysql_stream_write_payload (line 67) | static int __mysql_stream_write_payload(const void *buf, size_t *n,
  function mysql_stream_init (line 89) | void mysql_stream_init(mysql_stream_t *stream)

FILE: src/protocol/mysql_stream.h
  type mysql_stream_t (line 24) | typedef struct __mysql_stream
  function mysql_stream_write (line 48) | static inline int mysql_stream_write(const void *buf, size_t *n,
  function mysql_stream_get_seq (line 54) | static inline int mysql_stream_get_seq(mysql_stream_t *stream)
  function mysql_stream_get_buf (line 59) | static inline void mysql_stream_get_buf(const void **buf, size_t *length,
  function mysql_stream_deinit (line 66) | static inline void mysql_stream_deinit(mysql_stream_t *stream)

FILE: src/protocol/redis_parser.c
  type __redis_read_record (line 44) | struct __redis_read_record
  function redis_reply_deinit (line 50) | void redis_reply_deinit(redis_reply_t *reply)
  function redis_reply_t (line 63) | static redis_reply_t **__redis_create_array(size_t size, redis_reply_t *...
  function redis_reply_set_array (line 97) | int redis_reply_set_array(size_t size, redis_reply_t *reply)
  function __redis_parse_cmd (line 111) | static int __redis_parse_cmd(const char ch, redis_parser_t *parser)
  function __redis_parse_cr (line 129) | static int __redis_parse_cr(const char ch, redis_parser_t *parser)
  function __redis_parse_lf (line 138) | static int __redis_parse_lf(const char ch, redis_parser_t *parser)
  function __redis_parse_line (line 146) | static int __redis_parse_line(redis_parser_t *parser)
  function __redis_parse_crlf (line 238) | static int __redis_parse_crlf(redis_parser_t *parser)
  function __redis_parse_nchar (line 251) | static int __redis_parse_nchar(redis_parser_t *parser)
  function __redis_parser_forward (line 267) | static int __redis_parser_forward(redis_parser_t *parser)
  function redis_parser_init (line 295) | void redis_parser_init(redis_parser_t *parser)
  function redis_parser_deinit (line 314) | void redis_parser_deinit(redis_parser_t *parser)
  function __redis_parse_done (line 330) | static int __redis_parse_done(redis_reply_t *reply, char *buf, int depth)
  function __redis_split_inline_command (line 361) | static int __redis_split_inline_command(redis_parser_t *parser)
  function redis_parser_append_message (line 420) | int redis_parser_append_message(const void *buf, size_t *size,

FILE: src/protocol/redis_parser.h
  type redis_reply_t (line 35) | typedef struct __redis_reply {
  type redis_parser_t (line 44) | typedef struct __redis_parser
  function redis_reply_init (line 79) | static inline void redis_reply_init(redis_reply_t *reply)
  function redis_reply_set_string (line 89) | static inline void redis_reply_set_string(const char *str, size_t len,
  function redis_reply_set_integer (line 97) | static inline void redis_reply_set_integer(long long intv, redis_reply_t...
  function redis_reply_set_null (line 103) | static inline void redis_reply_set_null(redis_reply_t *reply)
  function redis_reply_set_error (line 108) | static inline void redis_reply_set_error(const char *err, size_t len,
  function redis_reply_set_status (line 116) | static inline void redis_reply_set_status(const char *str, size_t len,

FILE: src/server/WFDnsServer.h
  type WFServerParams (line 31) | struct WFServerParams
  function WFDnsServer (line 42) | inline
  function CommSession (line 49) | inline

FILE: src/server/WFHttpServer.h
  type WFServerParams (line 31) | struct WFServerParams
  function WFHttpServer (line 42) | inline
  function CommSession (line 49) | inline

FILE: src/server/WFMySQLServer.cc
  function WFConnection (line 22) | WFConnection *WFMySQLServer::new_connection(int accept_fd)
  function CommSession (line 47) | CommSession *WFMySQLServer::new_session(long long seq, CommConnection *c...

FILE: src/server/WFMySQLServer.h
  type WFServerParams (line 31) | struct WFServerParams

FILE: src/server/WFRedisServer.h
  type WFServerParams (line 30) | struct WFServerParams
  function WFRedisServer (line 41) | inline

FILE: src/server/WFServer.cc
  class WFServerConnection (line 39) | class WFServerConnection : public WFConnection
    method WFServerConnection (line 42) | WFServerConnection(std::atomic<size_t> *conn_count)
  function SSL_CTX (line 71) | SSL_CTX *WFServerBase::new_ssl_ctx(const char *cert_file, const char *ke...
  type sockaddr (line 91) | struct sockaddr
  type sockaddr (line 136) | struct sockaddr
  function WFConnection (line 178) | WFConnection *WFServerBase::new_connection(int accept_fd)
  type sockaddr (line 204) | struct sockaddr
  type addrinfo (line 227) | struct addrinfo
  type addrinfo (line 232) | struct addrinfo
  type sockaddr_storage (line 257) | struct sockaddr_storage
  type sockaddr (line 260) | struct sockaddr
  type sockaddr (line 264) | struct sockaddr

FILE: src/server/WFServer.h
  type WFServerParams (line 34) | struct WFServerParams
  type WFServerParams (line 45) | struct WFServerParams
  function class (line 56) | class WFServerBase : protected CommService
  type WFServerParams (line 209) | struct WFServerParams

FILE: src/util/EncodeStream.cc
  type EncodeBuf (line 28) | struct EncodeBuf
    type list_head (line 30) | struct list_head
  type list_head (line 37) | struct list_head
  type EncodeBuf (line 38) | struct EncodeBuf
    type list_head (line 30) | struct list_head
  type EncodeBuf (line 51) | struct EncodeBuf
    type list_head (line 30) | struct list_head
  type EncodeBuf (line 57) | struct EncodeBuf
    type list_head (line 30) | struct list_head
  type EncodeBuf (line 59) | struct EncodeBuf
    type list_head (line 30) | struct list_head
  type EncodeBuf (line 61) | struct EncodeBuf
    type list_head (line 30) | struct list_head
  type EncodeBuf (line 111) | struct EncodeBuf
    type list_head (line 30) | struct list_head
  type EncodeBuf (line 118) | struct EncodeBuf
    type list_head (line 30) | struct list_head
  type EncodeBuf (line 120) | struct EncodeBuf
    type list_head (line 30) | struct list_head
  type EncodeBuf (line 122) | struct EncodeBuf
    type list_head (line 30) | struct list_head

FILE: src/util/EncodeStream.h
  function class (line 35) | class EncodeStream
  function reset (line 52) | void reset(struct iovec *vectors, int max)
  function append_nocopy (line 63) | void append_nocopy(const char *data)
  function append_nocopy (line 68) | void append_nocopy(const std::string& data)
  function append_copy (line 75) | void append_copy(const char *data)
  function append_copy (line 80) | void append_copy(const std::string& data)
  type iovec (line 86) | struct iovec
  type iovec (line 100) | struct iovec
  type list_head (line 106) | struct list_head

FILE: src/util/LRUCache.h
  type list_head (line 48) | struct list_head
  type rb_node (line 49) | struct rb_node
  type list_head (line 78) | struct list_head
  function set_max_size (line 95) | void set_max_size(size_t max_size)
  function prune (line 101) | void prune()
  function release (line 116) | void release(const Handle *handle)
  function Handle (line 123) | const Handle *get(const KEY& key)
  function Handle (line 152) | const Handle *put(const KEY& key, VALUE value)
  function del (line 204) | void del(const KEY& key)
  function unref (line 225) | void unref(Handle *e)
  function erase_node (line 238) | void erase_node(Handle *e)
  type list_head (line 250) | struct list_head
  type list_head (line 251) | struct list_head
  type rb_root (line 252) | struct rb_root

FILE: src/util/StringUtil.cc
  function __hex_to_int (line 26) | static int __hex_to_int(const char s[2])
  function __int_to_hex (line 43) | static inline char __int_to_hex(int n)
  function __url_decode (line 48) | static size_t __url_decode(char *str)

FILE: src/util/StringUtil.h
  function class (line 31) | class StringUtil

FILE: src/util/URIParser.cc
  function ParsedURI (line 151) | ParsedURI& ParsedURI::operator= (ParsedURI&& uri)

FILE: src/util/URIParser.h
  function class (line 39) | class ParsedURI
  function deinit (line 88) | void deinit()
  function class (line 103) | class URIParser

FILE: src/util/crc32c.c
  function crc32c_t (line 74) | crc32c_t
  function crc32c_t (line 80) | crc32c_t
  function crc32c_t (line 99) | crc32c_t
  function crc32c_t (line 105) | crc32c_t

FILE: src/util/crc32c.h
  type crc32c_t (line 22) | typedef unsigned int crc32c_t;

FILE: src/util/json_parser.c
  type __json_object (line 29) | struct __json_object
  type __json_array (line 35) | struct __json_array
  type __json_value (line 41) | struct __json_value
  type __json_member (line 53) | struct __json_member
  type __json_element (line 60) | struct __json_element
  type json_member_t (line 66) | typedef struct __json_member json_member_t;
  type json_element_t (line 67) | typedef struct __json_element json_element_t;
  function __json_isspace (line 75) | static int __json_isspace(char c)
  function __json_isdigit (line 80) | static int __json_isdigit(char c)
  function __json_string_length (line 97) | static int __json_string_length(const char *cursor, size_t *escape, size...
  function __parse_json_hex4 (line 126) | static int __parse_json_hex4(const char *cursor, const char **end,
  function __parse_json_unicode (line 155) | static int __parse_json_unicode(const char *cursor, const char **end,
  function __parse_json_string (line 218) | static int __parse_json_string(const char *cursor, const char **end,
  function __parse_json_number (line 372) | static int __parse_json_number(const char *cursor, const char **end,
  function __parse_json_member (line 541) | static int __parse_json_member(const char *cursor, const char **end,
  function __parse_json_members (line 578) | static int __parse_json_members(const char *cursor, const char **end,
  function __destroy_json_members (line 638) | static void __destroy_json_members(json_object_t *obj)
  function __parse_json_object (line 651) | static int __parse_json_object(const char *cursor, const char **end,
  function __parse_json_elements (line 671) | static int __parse_json_elements(const char *cursor, const char **end,
  function __destroy_json_elements (line 721) | static void __destroy_json_elements(json_array_t *arr)
  function __parse_json_array (line 734) | static int __parse_json_array(const char *cursor, const char **end,
  function __parse_json_value (line 754) | static int __parse_json_value(const char *cursor, const char **end,
  function __destroy_json_value (line 859) | static void __destroy_json_value(json_value_t *val)
  function json_value_t (line 877) | json_value_t *json_value_parse(const char *cursor)
  function __move_json_value (line 903) | static void __move_json_value(json_value_t *src, json_value_t *dest)
  function __set_json_value (line 931) | static int __set_json_value(int type, va_list ap, json_value_t *val)
  function json_value_t (line 974) | json_value_t *json_value_create(int type, ...)
  function __copy_json_members (line 996) | static int __copy_json_members(const json_object_t *src, json_object_t *...
  function __copy_json_elements (line 1027) | static int __copy_json_elements(const json_array_t *src, json_array_t *d...
  function __copy_json_value (line 1055) | static int __copy_json_value(const json_value_t *src, json_value_t *dest)
  function json_value_t (line 1104) | json_value_t *json_value_copy(const json_value_t *val)
  function json_value_destroy (line 1119) | void json_value_destroy(json_value_t *val)
  function json_value_type (line 1125) | int json_value_type(const json_value_t *val)
  function json_value_number (line 1138) | double json_value_number(const json_value_t *val)
  function json_object_t (line 1146) | json_object_t *json_value_object(const json_value_t *val)
  function json_array_t (line 1154) | json_array_t *json_value_array(const json_value_t *val)
  function json_value_t (line 1162) | const json_value_t *json_object_find(const char *name,
  function json_object_size (line 1178) | size_t json_object_size(const json_object_t *obj)
  type list_head (line 1186) | struct list_head
  function json_value_t (line 1199) | const json_value_t *json_object_next_value(const json_value_t *val,
  type list_head (line 1218) | struct list_head
  function json_value_t (line 1231) | const json_value_t *json_object_prev_value(const json_value_t *val,
  function json_value_t (line 1253) | static const json_value_t *__json_object_insert(const char *name,
  function json_value_t (line 1278) | const json_value_t *json_object_append(json_object_t *obj,
  function json_value_t (line 1291) | const json_value_t *json_object_insert_after(const json_value_t *val,
  function json_value_t (line 1310) | const json_value_t *json_object_insert_before(const json_value_t *val,
  function json_value_t (line 1329) | json_value_t *json_object_remove(const json_value_t *val,
  function json_array_size (line 1346) | size_t json_array_size(const json_array_t *arr)
  function json_value_t (line 1351) | const json_value_t *json_array_next_value(const json_value_t *val,
  function json_value_t (line 1367) | const json_value_t *json_array_prev_value(const json_value_t *val,
  function json_value_t (line 1383) | static const json_value_t *__json_array_insert(int type, va_list ap,
  function json_value_t (line 1404) | const json_value_t *json_array_append(json_array_t *arr,
  function json_value_t (line 1416) | const json_value_t *json_array_insert_after(const json_value_t *val,
  function json_value_t (line 1434) | const json_value_t *json_array_insert_before(const json_value_t *val,
  function json_value_t (line 1452) | json_value_t *json_array_remove(const json_value_t *val,

FILE: src/util/json_parser.h
  type json_value_t (line 32) | typedef struct __json_value json_value_t;
  type json_object_t (line 33) | typedef struct __json_object json_object_t;
  type json_array_t (line 34) | typedef struct __json_array json_array_t;

FILE: test/algo_unittest.cc
  function __arr_init (line 25) | static void __arr_init(int *arr, int n)
  function __arr_check (line 32) | static void __arr_check(int *arr, int n)
  function TEST (line 38) | TEST(algo_unittest, sort)
  function TEST (line 68) | TEST(algo_unittest, parallel_sort)

FILE: test/dns_unittest.cc
  function TEST (line 26) | TEST(dns_unittest, WFDnsTaskCreate1)
  function TEST (line 33) | TEST(dns_unittest, WFDnsTaskCreate2)
  function TEST (line 46) | TEST(dns_unittest, WFDnsTask)
  function TEST (line 76) | TEST(dns_unittest, WFDnsClientInit1)
  function TEST (line 83) | TEST(dns_unittest, WFDnsClientInit2)
  function TEST (line 91) | TEST(dns_unittest, WFDnsClient)
  function main (line 123) | int main(int argc, char *argv[])

FILE: test/facilities_unittest.cc
  function TEST (line 29) | TEST(facilities_unittest, usleep)
  function TEST (line 37) | TEST(facilities_unittest, async_usleep)
  function TEST (line 45) | TEST(facilities_unittest, request)
  function TEST (line 66) | TEST(facilities_unittest, async_request)
  function TEST (line 87) | TEST(facilities_unittest, fileIO)
  function f (line 102) | static inline void f(int i, WFFacilities::WaitGroup *wg)
  function TEST (line 107) | TEST(facilities_unittest, WaitGroup)
  function main (line 126) | int main(int argc, char* argv[])

FILE: test/graph_unittest.cc
  function SubTask (line 25) | static SubTask *create_task(int& target)
  function TEST (line 34) | TEST(graph_unittest, WFGraphTask1)
  function TEST (line 60) | TEST(graph_unittest, WFGraphTask2)

FILE: test/http_unittest.cc
  function __http_process (line 30) | static void __http_process(WFHttpTask *task)
  function TEST (line 39) | TEST(http_unittest, WFHttpTask1)
  function TEST (line 73) | TEST(http_unittest, WFHttpTask2)
  function TEST (line 107) | TEST(http_unittest, WFHttpTask3)
  function main (line 217) | int main(int argc, char* argv[])

FILE: test/memory_unittest.cc
  function TEST (line 23) | TEST(memory_unittest, dismiss)

FILE: test/mysql_unittest.cc
  function __mysql_process (line 28) | static void __mysql_process(WFMySQLTask *task)
  function test_client (line 36) | static void test_client(const char *url, const char *sql, std::mutex& mu...
  function TEST (line 51) | TEST(mysql_unittest, WFMySQLTask1)
  function main (line 72) | int main(int argc, char* argv[])

FILE: test/redis_unittest.cc
  function __redis_process (line 32) | static void __redis_process(WFRedisTask *task)
  function test_client (line 85) | static void test_client(const char *url, std::mutex& mutex, std::conditi...
  function TEST (line 139) | TEST(redis_unittest, WFRedisTask1)

FILE: test/resource_unittest.cc
  function TEST (line 28) | TEST(resource_unittest, resource_pool)

FILE: test/task_unittest.cc
  function TEST (line 34) | TEST(task_unittest, WFTimerTask)
  function TEST (line 59) | TEST(task_unittest, WFCounterTask1)
  function TEST (line 95) | TEST(task_unittest, WFCounterTask2)
  function TEST (line 128) | TEST(task_unittest, WFGoTask)
  function TEST (line 158) | TEST(task_unittest, WFThreadTask)
  function TEST (line 198) | TEST(task_unittest, WFFileIOTask)
  function TEST (line 260) | TEST(task_unittest, WFFilePathIOTask)

FILE: test/upstream_unittest.cc
  function __http_process (line 31) | static void __http_process(WFHttpTask *task, const char *name)
  function register_upstream_hosts (line 48) | void register_upstream_hosts()
  function basic_callback (line 110) | void basic_callback(WFHttpTask *task, std::string& message)
  function TEST (line 126) | TEST(upstream_unittest, BasicPolicy)
  function TEST (line 168) | TEST(upstream_unittest, EnableAndDisable)
  function TEST (line 194) | TEST(upstream_unittest, AddAndRemove)
  function TEST (line 258) | TEST(upstream_unittest, FuseAndRecover)
  function TEST (line 320) | TEST(upstream_unittest, TryAnother)
  function TEST (line 359) | TEST(upstream_unittest, Tracing)
  function TEST (line 394) | TEST(upstream_unittest, RoundRobin)
  function main (line 412) | int main(int argc, char* argv[])

FILE: test/uriparser_unittest.cc
  function TEST (line 22) | TEST(uriparser_unittest, parse)

FILE: tutorial/tutorial-00-helloworld.cc
  function main (line 4) | int main()

FILE: tutorial/tutorial-01-wget.cc
  function wget_callback (line 15) | void wget_callback(WFHttpTask *task)
  function sig_handler (line 84) | void sig_handler(int signo)
  function main (line 89) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-02-redis_cli.cc
  type tutorial_task_data (line 13) | struct tutorial_task_data
  function redis_callback (line 19) | void redis_callback(WFRedisTask *task)
  function sig_handler (line 90) | void sig_handler(int signo)
  function main (line 95) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-03-wget_to_redis.cc
  type tutorial_series_context (line 18) | struct tutorial_series_context
  function redis_callback (line 26) | void redis_callback(WFRedisTask *task)
  function http_callback (line 52) | void http_callback(WFHttpTask *task)
  function main (line 90) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-04-http_echo_server.cc
  function process (line 16) | void process(WFHttpTask *server_task)
  function sig_handler (line 79) | void sig_handler(int signo)
  function main (line 84) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-05-http_proxy.cc
  type tutorial_series_context (line 11) | struct tutorial_series_context
  function reply_callback (line 18) | void reply_callback(WFHttpTask *proxy_task)
  function http_callback (line 34) | void http_callback(WFHttpTask *task)
  function process (line 82) | void process(WFHttpTask *proxy_task)
  function sig_handler (line 118) | void sig_handler(int signo)
  function main (line 123) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-06-parallel_wget.cc
  type tutorial_series_context (line 16) | struct tutorial_series_context
  function callback (line 24) | void callback(const ParallelWork *pwork)
  function main (line 48) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-07-sort_task.cc
  function callback (line 12) | void callback(WFSortTask<int> *task)
  function main (line 46) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-08-matrix_multiply.cc
  type algorithm (line 9) | namespace algorithm
    type MMInput (line 14) | struct MMInput
    type MMOutput (line 20) | struct MMOutput
    function is_valid_matrix (line 27) | bool is_valid_matrix(const Matrix& matrix, size_t& m, size_t& n)
    function matrix_multiply (line 45) | void matrix_multiply(const MMInput *in, MMOutput *out)
  function print_matrix (line 87) | void print_matrix(const Matrix& matrix, size_t m, size_t n)
  function callback (line 98) | void callback(MMTask *task)
  function main (line 118) | int main()

FILE: tutorial/tutorial-09-http_file_server.cc
  function pread_callback (line 17) | void pread_callback(WFFileIOTask *task)
  function process (line 33) | void process(WFHttpTask *server_task, const char *root)
  function sig_handler (line 76) | void sig_handler(int signo)
  function main (line 81) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-10-user_defined_protocol/client-uds.cc
  class MyFactory (line 16) | class MyFactory : public WFTaskFactory
    method WFTutorialTask (line 19) | static WFTutorialTask *create_tutorial_task(const struct sockaddr *addr,
  function main (line 33) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-10-user_defined_protocol/client.cc
  class MyFactory (line 14) | class MyFactory : public WFTaskFactory
    method WFTutorialTask (line 17) | static WFTutorialTask *create_tutorial_task(const std::string& host,
  function main (line 31) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-10-user_defined_protocol/message.cc
  type protocol (line 9) | namespace protocol
    type iovec (line 12) | struct iovec
    function TutorialMessage (line 108) | TutorialMessage& TutorialMessage::operator = (TutorialMessage&& msg)

FILE: tutorial/tutorial-10-user_defined_protocol/message.h
  function namespace (line 7) | namespace protocol

FILE: tutorial/tutorial-10-user_defined_protocol/server-uds.cc
  function process (line 20) | void process(WFTutorialTask *task)
  function sig_handler (line 37) | void sig_handler(int signo)
  function main (line 42) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-10-user_defined_protocol/server.cc
  function process (line 18) | void process(WFTutorialTask *task)
  function sig_handler (line 35) | void sig_handler(int signo)
  function main (line 40) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-11-graph_task.cc
  function go_func (line 11) | void go_func(const size_t *size1, const size_t *size2)
  function http_callback (line 16) | void http_callback(WFHttpTask *task)
  function main (line 30) | int main()

FILE: tutorial/tutorial-12-mysql_cli.cc
  function get_next_cmd (line 21) | void get_next_cmd(WFMySQLTask *task)
  function mysql_callback (line 56) | void mysql_callback(WFMySQLTask *task)
  function sighandler (line 207) | static void sighandler(int signo)
  function main (line 212) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-13-kafka_cli.cc
  function kafka_callback (line 23) | void kafka_callback(WFKafkaTask *task)
  function sig_handler (line 162) | void sig_handler(int signo) { }
  function main (line 164) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-14-consul_cli.cc
  function print_discover_result (line 23) | void print_discover_result(std::vector<struct protocol::ConsulServiceIns...
  function print_list_service_result (line 87) | void print_list_service_result(
  function consul_callback (line 106) | void consul_callback(WFConsulTask *task)
  function sig_handler (line 157) | void sig_handler(int signo) { }
  function main (line 159) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-15-name_service.cc
  class MyNSPolicy (line 22) | class MyNSPolicy : public WFNSPolicy
    type WFNSParams (line 25) | struct WFNSParams
    method MyNSPolicy (line 36) | MyNSPolicy(const char *naming_file) : path(naming_file) { }
  function WFRouterTask (line 94) | WFRouterTask *MyNSPolicy::create_router_task(const struct WFNSParams *pa...
  function main (line 121) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-16-graceful_restart/bootstrap.c
  function sig_handler (line 15) | void sig_handler(int signo)
  function main (line 23) | int main(int argc, const char *argv[])

FILE: tutorial/tutorial-16-graceful_restart/server.cc
  function sig_handler (line 10) | void sig_handler(int signo)
  function main (line 15) | int main(int argc, const char *argv[])

FILE: tutorial/tutorial-17-dns_cli.cc
  function show_result (line 23) | void show_result(protocol::DnsResultCursor& cursor)
  function dns_callback (line 82) | void dns_callback(WFDnsTask *task)
  function main (line 132) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-18-redis_subscriber.cc
  function extract (line 11) | void extract(WFRedisSubscribeTask *task)
  function main (line 38) | int main(int argc, char *argv[])

FILE: tutorial/tutorial-19-dns_server.cc
  function process (line 10) | void process(WFDnsTask *task)
  function sig_handler (line 80) | void sig_handler(int signo)
  function main (line 85) | int main(int argc, char *argv[])
Condensed preview — 307 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,000K chars).
[
  {
    "path": ".editorconfig",
    "chars": 206,
    "preview": "# top-most EditorConfig file\nroot = true\n \n# all files\n[*]\nindent_style = tab\nindent_size = 4\n[src/kernel/list.h]\nindent"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1764,
    "preview": "name: ci build\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n\n  ubuntu-cmake:\n  "
  },
  {
    "path": ".github/workflows/xmake.yml",
    "chars": 709,
    "preview": "name: xmake build\n\non:\n  workflow_dispatch:\n\njobs:\n  build:\n    name: build-linux\n    runs-on: ubuntu-latest\n\n    steps:"
  },
  {
    "path": ".gitignore",
    "chars": 432,
    "preview": "# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic"
  },
  {
    "path": "BUILD",
    "chars": 8087,
    "preview": "config_setting(\n\tname = 'linux',\n\tconstraint_values = [\n\t\t\"@platforms//os:linux\",\n\t],\n\tvisibility = ['//visibility:publi"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 3498,
    "preview": "cmake_minimum_required(VERSION 3.10)\n\nset(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING \"build type\")\nset(CMAKE_SKIP_RPAT"
  },
  {
    "path": "CMakeLists_Headers.txt",
    "chars": 2880,
    "preview": "cmake_minimum_required(VERSION 3.10)\n\nset(COMMON_KERNEL_HEADERS\n\tsrc/kernel/CommRequest.h\n\tsrc/kernel/CommScheduler.h\n\ts"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3240,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "GNUmakefile",
    "chars": 1558,
    "preview": "ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))\nALL_TARGETS := all base check install preinstall "
  },
  {
    "path": "LICENSE",
    "chars": 11362,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "LICENSE_GPLV2",
    "chars": 18092,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
  },
  {
    "path": "README.md",
    "chars": 10855,
    "preview": "[简体中文版(推荐)](README_cn.md)\n\n## Sogou C++ Workflow\n\n[![License](https://img.shields.io/badge/License-Apache%202.0-green.sv"
  },
  {
    "path": "README_cn.md",
    "chars": 6917,
    "preview": "[English version](README.md)\n\n## Sogou C++ Workflow\n[![License](https://img.shields.io/badge/License-Apache%202.0-green."
  },
  {
    "path": "WORKSPACE",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "benchmark/CMakeLists.txt",
    "chars": 1199,
    "preview": "cmake_minimum_required(VERSION 3.10)\n\nset(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING \"build type\")\n\nproject(benchmark\n"
  },
  {
    "path": "benchmark/GNUmakefile",
    "chars": 722,
    "preview": "ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))\nALL_TARGETS := all clean\nMAKE_FILE := Makefile\n\nD"
  },
  {
    "path": "benchmark/README.md",
    "chars": 4286,
    "preview": "# 性能测试\n\nSogou C++ Workflow是一款性能优异的网络框架,本文介绍我们进行的性能测试,\n包括方案、代码、结果,以及与其他同类产品的对比。\n\n更多场景下的实验正在进行中,本文将持续更新。\n\n## HTTP Server\n\n"
  },
  {
    "path": "benchmark/benchmark-01-http_server.cc",
    "chars": 1142,
    "preview": "#include <csignal>\n\n#include <workflow/WFHttpServer.h>\n#include <workflow/WFGlobal.h>\n#include <workflow/WFFacilities.h>"
  },
  {
    "path": "benchmark/benchmark-02-http_server_long_req.cc",
    "chars": 1450,
    "preview": "#include <csignal>\n#include <cstring>\n\n#include <workflow/WFHttpServer.h>\n#include <workflow/WFGlobal.h>\n#include <workf"
  },
  {
    "path": "benchmark/util/args.h",
    "chars": 1899,
    "preview": "#ifndef _BENCHMARK_ARGS_H_\n#define _BENCHMARK_ARGS_H_\n\n#include <algorithm>\n#include <numeric>\n#include <string>\n\nnamesp"
  },
  {
    "path": "benchmark/util/content.h",
    "chars": 399,
    "preview": "#ifndef _BENCHMARK_CONTENT_H_\n#define _BENCHMARK_CONTENT_H_\n\n#include <random>\n#include <string>\n\nstatic inline std::str"
  },
  {
    "path": "benchmark/util/date.h",
    "chars": 302,
    "preview": "#ifndef _BENCHMARK_DATE_H_\n#define _BENCHMARK_DATE_H_\n\n#include <ctime>\n\nstatic inline void date(char * buf, size_t n)\n{"
  },
  {
    "path": "benchmark/xmake.lua",
    "chars": 559,
    "preview": "set_group(\"benchmark\")\nset_default(false)\n\nadd_deps(\"workflow\")\n\nif not is_plat(\"macosx\") then\n    add_ldflags(\"-lrt\")\ne"
  },
  {
    "path": "docs/about-conditional.md",
    "chars": 3377,
    "preview": "# 条件任务与观察者模式\n\n有的时候,我们需要让任务在某个条件下才被执行。条件任务(WFConditional)就是用于解决这种问题。  \n条件任务是一种任务包装器,可以包装任何的任务并取代原任务。通过对条件任务发送信号来触发被包装任务的执"
  },
  {
    "path": "docs/about-config.md",
    "chars": 2757,
    "preview": "# 关于全局配置\n\n全局配置用于配置全局默认参数,以适应的实际业务需求,提升程序性能。\n全局配置的修改必须在使用框架任何调用之前,否则修改可能无法生效。\n另外,一些全局配置选项,可以被upstream配置覆盖。这部分请参考upstream相"
  },
  {
    "path": "docs/about-connection-context.md",
    "chars": 3532,
    "preview": "# 关于连接上下文\n\n连接上下文是使用本框架编程的一个高级课题。使用上会有一些复杂性。  \n从之前的示例里可以看出,无论是client还是server任务,我们并没有手段指定使用的具体连接。  \n但是有一些业务场景,特别是server端,可"
  },
  {
    "path": "docs/about-counter.md",
    "chars": 5182,
    "preview": "# 关于计数器\n\n计数器是我们框架中一种非常重要的基础任务,计数器本质上是一个不占线程的信号量。  \n计数器主要用于工作流的控制,包括匿名计数器和命名计数器两种,可以实现非常复杂的业务逻辑。  \n\n# 计数器的创建\n\n由于计数器也是一种任务"
  },
  {
    "path": "docs/about-dns.md",
    "chars": 3086,
    "preview": "# 关于DNS\n当使用域名请求网络时,首先需要通过域名解析获取服务器地址,再使用网络地址进行后续的请求。Workflow已经实现了完备的域名解析和缓存系统,通常来说用户无需知晓内部机制即可流畅地发起网络任务。\n\n## DNS相关配置\nWor"
  },
  {
    "path": "docs/about-error.md",
    "chars": 2228,
    "preview": "# 关于错误处理\n\n任何软件系统里,错误处理都是一个重要而复杂的问题。在我们框架内部,错误处理可以说是无处不在并且极其繁琐的。  \n而在我们暴露给用户的接口里,我们尽可能地让事情变简单,但用户还是不可避免地需要了解一些错误信息。\n\n### "
  },
  {
    "path": "docs/about-exit.md",
    "chars": 3072,
    "preview": "# 关于程序退出\n\n由于我们的大多数调用都是非阻塞的,所以在之前的示例里我们都需要用一些机制来防止main函数提前退出。  \n例如wget示例中等待用户的Ctrl-C,或者像parallel_wget在所有抓取结束之后唤醒主线程。  \n而在"
  },
  {
    "path": "docs/about-go-task.md",
    "chars": 4501,
    "preview": "# 关于go task\n\n我们提供了另一种更简单的使用计算任务的方法,模仿go语言实现的go task。  \n使用go task来实计算任务无需定义输入与输出,所有数据通过函数参数传递。\n\n# 创建go task\n~~~cpp\nclass "
  },
  {
    "path": "docs/about-module.md",
    "chars": 2602,
    "preview": "# 关于模块任务\n\n我们的任务流是以task为元素。但很多情况下,用户需要模块级的封装,比如几个task完成一个特定的功能。  \n用原有的方法,就不得不让最后一个task的callback来衔接下一个任务,或者填写server任务的resp"
  },
  {
    "path": "docs/about-resource-pool.md",
    "chars": 3701,
    "preview": "# 资源池\n\n在我们用workflow写异步程序时经常会遇到这样一些场景:\n* 任务运行时需要先从某个池子里获得一个资源。任务运行结束,则会把资源放回池子,让下一个需要资源的任务运行。\n* 网络通信时需要对某一个或一些通信目标做总的并发度限"
  },
  {
    "path": "docs/about-selector.md",
    "chars": 2826,
    "preview": "# 关于Selector任务\n\n我们业务中经常有一些需求,从几个异步分支中选择第一个成功完成的结果进行处理,丢弃其它结果。  \nSelector任务就是为了上述这种多选一场景而设计的。  \n\n# Selector解决的问题\n常见的多选一场景"
  },
  {
    "path": "docs/about-service-governance.md",
    "chars": 6903,
    "preview": "# 关于服务治理\n\n我们拥有一套完整的机制,来管理我们所依赖的服务。这套机制包括以下的几个功能:\n* 用户级DNS。\n* 服务地址的选取\n  * 包括多种选取机制,如权重随机,一致性哈希,用户指定选取方式等。\n* 服务的熔断与恢复。\n* 负"
  },
  {
    "path": "docs/about-timeout.md",
    "chars": 4963,
    "preview": "# 关于超时\n\n为了让所有通信任务可以在用户的预期下精确运行,我们提供了大量的超时配置功能,并且确保这些超时的准确性。  \n这些超时配置里,有些是全局的,比如连接超时,但你又可以通过upstream功能,给某个域名配置自己的连接超时。  \n"
  },
  {
    "path": "docs/about-timer.md",
    "chars": 2956,
    "preview": "# 关于定时器\n\n定时器的作用是不占线程的等待一个确定时间,同样通过callback来通知定时器到期。\n\n# 定时器的创建\n\nWFTaskFactory类里包括四个定时相关的接口:\n~~~cpp\nusing timer_callback_t"
  },
  {
    "path": "docs/about-tlv-message.md",
    "chars": 3763,
    "preview": "# 关于TLV(Type-Length-Value)格式的消息\nTLV消息是一种由类型,长度,内容组成的消息。由于其结构简单通用,而且方便嵌套和扩展,特别适用于定义通信消息。  \n为方便用户实现自定义协议,我们内置了TLV消息的支持。  \n"
  },
  {
    "path": "docs/about-upstream.md",
    "chars": 14133,
    "preview": "# 关于Upstream\n\n在nginx里,Upstream代表了反向代理的负载均衡配置。在这里,我们扩充Upstream的含义,让其具备以下几个特点:\n1. 每一个Upstream都是一个独立的反向代理\n2. 访问一个Upstream等价"
  },
  {
    "path": "docs/benchmark.md",
    "chars": 4013,
    "preview": "# 性能测试\n\nSogou C++ Workflow是一款性能优异的网络框架,本文介绍我们进行的性能测试,\n包括方案、代码、结果,以及与其他同类产品的对比。\n\n更多场景下的实验正在进行中,本文将持续更新。\n\n## HTTP Server\n\n"
  },
  {
    "path": "docs/bugs.md",
    "chars": 1403,
    "preview": "# 已知BUG列表\n\n### OpenSSL 1.1.1及以下,出现网络任务状态为WFT_STATE_SYS_ERROR,错误为0。\n这是OpenSSL 1.1.1及以下的bug,在SSL_get_error()为SSL_ERROR_SYS"
  },
  {
    "path": "docs/en/CONTRIBUTING.md",
    "chars": 4119,
    "preview": "# Contribution Guide\n\nSogou C++ Workflow is community-driven and welcomes any contributor. \n\nThis document outlines some"
  },
  {
    "path": "docs/en/about-config.md",
    "chars": 4450,
    "preview": "# About global configuration\n\nGlobal configuration is used to configure default global parameters to meet the actual bus"
  },
  {
    "path": "docs/en/about-connection-context.md",
    "chars": 6874,
    "preview": "# About connection context\n\nConnection context is an advanced programming topic in this framework.  \nFrom the previous e"
  },
  {
    "path": "docs/en/about-counter.md",
    "chars": 8963,
    "preview": "# About counter\n\nCounters are very important basic tasks in our framework. A counter is essentially a semaphore that doe"
  },
  {
    "path": "docs/en/about-dns.md",
    "chars": 7772,
    "preview": "# About DNS\nWhen using a domain name to request the network, you first need to obtain the server address through domain "
  },
  {
    "path": "docs/en/about-error.md",
    "chars": 4334,
    "preview": "# About error handling\n\nError handling is an important and complex problem in any software system. Within our framework,"
  },
  {
    "path": "docs/en/about-exit.md",
    "chars": 6222,
    "preview": "# About exit\n\nAs most of our calls are non-blocking, we need some mechanisms to prevent the main function from exiting e"
  },
  {
    "path": "docs/en/about-go-task.md",
    "chars": 3236,
    "preview": "# About go task\n\nWe provide a simpler way to use computing task, which is inspired by the golang, and we name it 'go tas"
  },
  {
    "path": "docs/en/about-module.md",
    "chars": 3483,
    "preview": "# About Module Task\n\nOur **series** has tasks as elements. But in many cases, users need module-level encapsulation, suc"
  },
  {
    "path": "docs/en/about-resource-pool.md",
    "chars": 3616,
    "preview": "# Conditional task and resource pool\nWhen we use workflow to write asynchronous programs, we often encounter such scenar"
  },
  {
    "path": "docs/en/about-service-governance.md",
    "chars": 10569,
    "preview": "# About service governance\n\nWe have a complete mechanism to manage the services we depend on. This mechanism includes th"
  },
  {
    "path": "docs/en/about-timeout.md",
    "chars": 9806,
    "preview": "# About timeout\n\nIn order to make all communication tasks run as accurately as expected by users, the framework provides"
  },
  {
    "path": "docs/en/about-timer.md",
    "chars": 4389,
    "preview": "# About timer\n\n Timers are used to specify a certain waiting time without occupying a thread. The expiration of a timer "
  },
  {
    "path": "docs/en/about-tlv-message.md",
    "chars": 4921,
    "preview": "# About TLV (Type-Length-Value) format message\nA TLV message is a message consisting of type, length, and value. Because"
  },
  {
    "path": "docs/en/about-upstream.md",
    "chars": 23656,
    "preview": "# About Upstream\n\nIn nginx, Upstream represents the load balancing configuration of the reverse proxy. Here, we expand t"
  },
  {
    "path": "docs/en/tutorial-01-wget.md",
    "chars": 5057,
    "preview": "# Creating your first task: wget\n\n# Sample code\n\n[tutorial-01-wget.cc](/tutorial/tutorial-01-wget.cc)\n\n# About wget\n\nwge"
  },
  {
    "path": "docs/en/tutorial-02-redis_cli.md",
    "chars": 6634,
    "preview": "# Implementing Redis set and get: redis\\_cli\n\n# Sample code\n\n[tutorial-02-redis\\_cli.cc](/tutorial/tutorial-02-redis_cli"
  },
  {
    "path": "docs/en/tutorial-03-wget_to_redis.md",
    "chars": 3826,
    "preview": "# More features about series: wget\\_to\\_redis\n\n# Sample code\n\n[tutorial-03-wget\\_to\\_redis.cc](/tutorial/tutorial-03-wge"
  },
  {
    "path": "docs/en/tutorial-04-http_echo_server.md",
    "chars": 7944,
    "preview": "# First server: http\\_echo\\_server\n\n# Sample code\n\n[tutorial-04-http\\_echo\\_server.cc](/tutorial/tutorial-04-http_echo_s"
  },
  {
    "path": "docs/en/tutorial-05-http_proxy.md",
    "chars": 10787,
    "preview": "# Asynchronous server: http\\_proxy\n\n# Sample code\n\n[tutorial-05-http\\_proxy.cc](/tutorial/tutorial-05-http_proxy.cc)\n\n# "
  },
  {
    "path": "docs/en/tutorial-06-parallel_wget.md",
    "chars": 5521,
    "preview": "# A simple parallel wget: parallel\\_wget\n\n# Sample code\n\n[tutorial-06-parallel\\_wget.cc](/tutorial/tutorial-06-parallel_"
  },
  {
    "path": "docs/en/tutorial-07-sort_task.md",
    "chars": 7109,
    "preview": "# Using the built-in algorithm factory: sort\\_task\n\n# Sample code\n\n[tutorial-07-sort\\_task.cc](/tutorial/tutorial-07-sor"
  },
  {
    "path": "docs/en/tutorial-08-matrix_multiply.md",
    "chars": 7748,
    "preview": "# User-defined computing tasks: matrix\\_multiply\n\n# Sample code\n\n[tutorial-08-matrix\\_multiply.cc](/tutorial/tutorial-08"
  },
  {
    "path": "docs/en/tutorial-09-http_file_server.md",
    "chars": 8618,
    "preview": "# Http server with file IO: http\\_file\\_server\n\n# Sample code\n\n[tutorial-09-http\\_file\\_server.cc](/tutorial/tutorial-09"
  },
  {
    "path": "docs/en/tutorial-10-user_defined_protocol.md",
    "chars": 13911,
    "preview": "# A simple user-defined protocol: client/server \n\n# Sample codes\n\n[message.h](/tutorial/tutorial-10-user_defined_protoco"
  },
  {
    "path": "docs/en/tutorial-11-graph_task.md",
    "chars": 3439,
    "preview": "# Direct Acyclic Graph (DAG):graph_task\n# Sample code\n\n[tutorial-11-graph_task.cc](/tutorial/tutorial-11-graph_task.cc)\n"
  },
  {
    "path": "docs/en/tutorial-12-mysql_cli.md",
    "chars": 17365,
    "preview": "# Asynchronous MySQL client: mysql\\_cli\n\n# Sample code\n\n[tutorial-12-mysql\\_cli.cc](/tutorial/tutorial-12-mysql_cli.cc)\n"
  },
  {
    "path": "docs/en/tutorial-13-kafka_cli.md",
    "chars": 12594,
    "preview": "# Asynchronous Kafka Client: kafka_cli\n\n# Sample Codes\n\n[tutorial-13-kafka_cli.cc](/tutorial/tutorial-13-kafka_cli.cc)\n\n"
  },
  {
    "path": "docs/en/xmake.md",
    "chars": 1379,
    "preview": "# xmake compiling\n\n```\n// compile workflow library\nxmake\n\n// compile test\nxmake -g test\n// run test\nxmake run -g test\n\n/"
  },
  {
    "path": "docs/tutorial-01-wget.md",
    "chars": 3251,
    "preview": "# 创建第一个任务:wget\n# 示例代码\n\n[tutorial-01-wget.cc](/tutorial/tutorial-01-wget.cc)\n\n# 关于wget\n程序从stdin读取http/https URL,抓取网页并把内容打"
  },
  {
    "path": "docs/tutorial-02-redis_cli.md",
    "chars": 4156,
    "preview": "# 实现一次redis写入与读出:redis_cli\n# 示例代码\n\n[tutorial-02-redis_cli.cc](/tutorial/tutorial-02-redis_cli.cc)\n\n# 关于redis_cli\n\n程序从命令行"
  },
  {
    "path": "docs/tutorial-03-wget_to_redis.md",
    "chars": 2119,
    "preview": "# 任务序列的更多功能:wget_to_redis\n# 示例代码\n\n[tutorial-03-wget_to_redis.cc](/tutorial/tutorial-03-wget_to_redis.cc)\n\n# 关于wget_to_re"
  },
  {
    "path": "docs/tutorial-04-http_echo_server.md",
    "chars": 5380,
    "preview": "# 第一个server:http_echo_server\n# 示例代码\n\n[tutorial-04-http_echo_server.cc](/tutorial/tutorial-04-http_echo_server.cc)\n\n# 关于h"
  },
  {
    "path": "docs/tutorial-05-http_proxy.md",
    "chars": 6358,
    "preview": "# 异步server的示例:http_proxy\n# 示例代码\n\n[tutorial-05-http_proxy.cc](/tutorial/tutorial-05-http_proxy.cc)\n\n# 关于http_proxy\n\n这是一个h"
  },
  {
    "path": "docs/tutorial-06-parallel_wget.md",
    "chars": 3516,
    "preview": "# 一个简单的并行抓取:parallel_wget\n# 示例代码\n\n[tutorial-06-parallel_wget.cc](/tutorial/tutorial-06-parallel_wget.cc)\n\n# 关于parallel_w"
  },
  {
    "path": "docs/tutorial-07-sort_task.md",
    "chars": 3668,
    "preview": "# 使用内置算法工厂:sort_task\n# 示例代码\n\n[tutorial-07-sort_task.cc](/tutorial/tutorial-07-sort_task.cc)\n\n# 关于sort_task\n\n程序从命令行读入数字n,"
  },
  {
    "path": "docs/tutorial-08-matrix_multiply.md",
    "chars": 6640,
    "preview": "# 自定义计算任务:matrix_multiply\n# 示例代码\n\n[tutorial-08-matrix_multiply.cc](/tutorial/tutorial-08-matrix_multiply.cc)\n\n# 关于matrix"
  },
  {
    "path": "docs/tutorial-09-http_file_server.md",
    "chars": 6058,
    "preview": "# 异步IO的http server:http_file_server\n# 示例代码\n\n[tutorial-09-http_file_server.cc](/tutorial/tutorial-09-http_file_server.cc)"
  },
  {
    "path": "docs/tutorial-10-user_defined_protocol.md",
    "chars": 8424,
    "preview": "# 简单的用户自定义协议client/server\n# 示例代码\n\n[message.h](/tutorial/tutorial-10-user_defined_protocol/message.h)  \n[message.cc](/tut"
  },
  {
    "path": "docs/tutorial-11-graph_task.md",
    "chars": 2554,
    "preview": "# 有向无环图(DAG)的使用:graph_task\n# 示例代码\n\n[tutorial-11-graph_task.cc](/tutorial/tutorial-11-graph_task.cc)\n\n# 关于graph_task\n\ngra"
  },
  {
    "path": "docs/tutorial-12-mysql_cli.md",
    "chars": 11280,
    "preview": "# 异步MySQL客户端:mysql_cli\n# 示例代码\n\n[tutorial-12-mysql_cli.cc](/tutorial/tutorial-12-mysql_cli.cc)\n\n# 关于mysql_cli\n\n教程中的mysql_"
  },
  {
    "path": "docs/tutorial-13-kafka_cli.md",
    "chars": 8502,
    "preview": "# 异步Kafka客户端:kafka_cli\n# 示例代码\n\n[tutorial-13-kafka_cli.cc](/tutorial/tutorial-13-kafka_cli.cc)\n\n# 编译\n\n由于支持Kafka的多种压缩方式,因此"
  },
  {
    "path": "docs/tutorial-15-name_service.md",
    "chars": 2819,
    "preview": "# 自定义命名服务策略:name_service\n# 示例代码\n[tutorial-15-name_service.cc](/tutorial/tutorial-15-name_service.cc)\n\n# 关于name_service\n本"
  },
  {
    "path": "docs/tutorial-17-dns_cli.md",
    "chars": 5944,
    "preview": "# 使用workflow请求DNS\n作为一款优秀的异步编程框架,workflow帮助用户处理了大量的细节,其中就包括域名解析,因此在大部分情况下,用户无需关心如何请求DNS服务。正如workflow中的其他模块一样,DNS解析模块设计的同样"
  },
  {
    "path": "docs/tutorial-18-redis_subscriber.md",
    "chars": 2842,
    "preview": "# Redis订阅模式\n\n## 示例代码\n[tutorial-18-redis_subscriber.cc](/tutorial/tutorial-18-redis_subscriber.cc)\n\n## 创建订阅客户端和任务\n在Workfl"
  },
  {
    "path": "docs/tutorial-19-dns_server.md",
    "chars": 2389,
    "preview": "# 使用workflow实现DNS服务器\n前述文档已经讲解了使用workflow实现服务器的方法,workflow框架贴心地为用户处理了底层逻辑和各种细节,因此本文档主要介绍如何组装DNS消息。\n\n[tutorial-19-dns_serv"
  },
  {
    "path": "docs/xmake.md",
    "chars": 1167,
    "preview": "# 编译\n\n```\n// 编译workflow库\nxmake\n\n// 编译test\nxmake -g test\n// 运行test文件\nxmake run -g test\n\n// 编译tutorial\nxmake -g tutorial\n\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "chars": 4933,
    "preview": "cmake_minimum_required(VERSION 3.10)\n\nif(ANDROID)\n\tinclude_directories(${OPENSSL_INCLUDE_DIR})\n\tlink_directories(${OPENS"
  },
  {
    "path": "src/client/CMakeLists.txt",
    "chars": 501,
    "preview": "cmake_minimum_required(VERSION 3.10)\nproject(client)\n\nset(SRC\n\tWFDnsClient.cc\n\tWFHttpChunkedClient.cc\n)\n\nif (NOT REDIS S"
  },
  {
    "path": "src/client/WFConsulClient.cc",
    "chars": 28432,
    "preview": "/*\n  Copyright (c) 2022 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFConsulClient.h",
    "chars": 4582,
    "preview": "/*\n  Copyright (c) 2022 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFDnsClient.cc",
    "chars": 6486,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFDnsClient.h",
    "chars": 1164,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFHttpChunkedClient.cc",
    "chars": 2062,
    "preview": "/*\n  Copyright (c) 2025 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFHttpChunkedClient.h",
    "chars": 4193,
    "preview": "/*\n  Copyright (c) 2025 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFKafkaClient.cc",
    "chars": 42742,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFKafkaClient.h",
    "chars": 4846,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFMySQLConnection.cc",
    "chars": 1294,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFMySQLConnection.h",
    "chars": 2511,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFRedisSubscriber.cc",
    "chars": 3151,
    "preview": "/*\n  Copyright (c) 2024 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/WFRedisSubscriber.h",
    "chars": 5857,
    "preview": "/*\n  Copyright (c) 2024 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/client/xmake.lua",
    "chars": 609,
    "preview": "target(\"client\")\n    set_kind(\"object\")\n    add_files(\"*.cc\")\n    remove_files(\"WFKafkaClient.cc\")\n    if not has_config"
  },
  {
    "path": "src/factory/CMakeLists.txt",
    "chars": 509,
    "preview": "cmake_minimum_required(VERSION 3.10)\nproject(factory)\n\nset(SRC\n\tWFGraphTask.cc\n\tDnsTaskImpl.cc\n\tWFTaskFactory.cc\n\tWorkfl"
  },
  {
    "path": "src/factory/DnsTaskImpl.cc",
    "chars": 6033,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/FileTaskImpl.cc",
    "chars": 9108,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/HttpTaskImpl.cc",
    "chars": 27686,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/HttpTaskImpl.inl",
    "chars": 1201,
    "preview": "/*\n  Copyright (c) 2025 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/KafkaTaskImpl.cc",
    "chars": 17978,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/KafkaTaskImpl.inl",
    "chars": 1730,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/MySQLTaskImpl.cc",
    "chars": 19895,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/RedisTaskImpl.cc",
    "chars": 10129,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/RedisTaskImpl.inl",
    "chars": 1085,
    "preview": "/*\n  Copyright (c) 2024 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFAlgoTaskFactory.h",
    "chars": 6157,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFAlgoTaskFactory.inl",
    "chars": 16614,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFConnection.h",
    "chars": 1787,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFGraphTask.cc",
    "chars": 2045,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFGraphTask.h",
    "chars": 2145,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFMessageQueue.cc",
    "chars": 2086,
    "preview": "/*\n  Copyright (c) 2022 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFMessageQueue.h",
    "chars": 1803,
    "preview": "/*\n  Copyright (c) 2022 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFOperator.h",
    "chars": 8963,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFResourcePool.cc",
    "chars": 2566,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFResourcePool.h",
    "chars": 1547,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFTask.h",
    "chars": 16435,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFTask.inl",
    "chars": 5120,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFTaskError.h",
    "chars": 2991,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFTaskFactory.cc",
    "chars": 24348,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFTaskFactory.h",
    "chars": 14996,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/WFTaskFactory.inl",
    "chars": 23016,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/Workflow.cc",
    "chars": 4909,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/Workflow.h",
    "chars": 7402,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/factory/xmake.lua",
    "chars": 517,
    "preview": "target(\"factory\")\n    add_files(\"*.cc\")\n    set_kind(\"object\")\n    if not has_config(\"mysql\") then\n        remove_files("
  },
  {
    "path": "src/kernel/CMakeLists.txt",
    "chars": 499,
    "preview": "cmake_minimum_required(VERSION 3.10)\nproject(kernel)\n\nif (CMAKE_SYSTEM_NAME STREQUAL \"Linux\" OR CMAKE_SYSTEM_NAME STREQU"
  },
  {
    "path": "src/kernel/CommRequest.cc",
    "chars": 1087,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/CommRequest.h",
    "chars": 1833,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/CommScheduler.cc",
    "chars": 8772,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/CommScheduler.h",
    "chars": 4487,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/Communicator.cc",
    "chars": 46613,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/Communicator.h",
    "chars": 9586,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/ExecRequest.h",
    "chars": 1406,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/Executor.cc",
    "chars": 3414,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/Executor.h",
    "chars": 1655,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/IORequest.h",
    "chars": 1195,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/IOService_linux.cc",
    "chars": 8790,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/IOService_linux.h",
    "chars": 2184,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/IOService_thread.cc",
    "chars": 6205,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/IOService_thread.h",
    "chars": 2691,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/SleepRequest.h",
    "chars": 1325,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/SubTask.cc",
    "chars": 1276,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/SubTask.h",
    "chars": 1453,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/list.h",
    "chars": 8463,
    "preview": "#ifndef _LINUX_LIST_H\n#define _LINUX_LIST_H\n\n/*\n * Circular doubly linked list implementation.\n *\n * Some of the interna"
  },
  {
    "path": "src/kernel/mpoller.c",
    "chars": 2608,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/mpoller.h",
    "chars": 2456,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/msgqueue.c",
    "chars": 4497,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/msgqueue.h",
    "chars": 1498,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/poller.c",
    "chars": 33865,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/poller.h",
    "chars": 2998,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/rbtree.c",
    "chars": 8931,
    "preview": "/*\n  Red Black Trees\n  (C) 1999  Andrea Arcangeli <andrea@suse.de>\n  (C) 2002  David Woodhouse <dwmw2@infradead.org>\n  \n"
  },
  {
    "path": "src/kernel/rbtree.h",
    "chars": 4121,
    "preview": "/*\n  Red Black Trees\n  (C) 1999  Andrea Arcangeli <andrea@suse.de>\n  \n  This program is free software; you can redistrib"
  },
  {
    "path": "src/kernel/thrdpool.c",
    "chars": 6137,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/thrdpool.h",
    "chars": 1269,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/kernel/xmake.lua",
    "chars": 224,
    "preview": "target(\"kernel\")\n    set_kind(\"object\")\n    add_files(\"*.cc\")\n    add_files(\"*.c\")\n    if is_plat(\"linux\", \"android\") th"
  },
  {
    "path": "src/manager/CMakeLists.txt",
    "chars": 235,
    "preview": "cmake_minimum_required(VERSION 3.10)\nproject(manager)\n\nset(SRC\n\tDnsCache.cc\n\tRouteManager.cc\n\tWFGlobal.cc\n)\n\nif (NOT UPS"
  },
  {
    "path": "src/manager/DnsCache.cc",
    "chars": 2766,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/DnsCache.h",
    "chars": 3935,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/EndpointParams.h",
    "chars": 1316,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/RouteManager.cc",
    "chars": 12540,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/RouteManager.h",
    "chars": 2441,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/UpstreamManager.cc",
    "chars": 6500,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/UpstreamManager.h",
    "chars": 8493,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/WFFacilities.h",
    "chars": 2643,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/WFFacilities.inl",
    "chars": 6083,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/WFFuture.h",
    "chars": 4025,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/WFGlobal.cc",
    "chars": 19189,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/WFGlobal.h",
    "chars": 5250,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/manager/xmake.lua",
    "chars": 153,
    "preview": "target(\"manager\")\n    add_files(\"*.cc\")\n    set_kind(\"object\")\n    if not has_config(\"upstream\") then\n        remove_fil"
  },
  {
    "path": "src/nameservice/CMakeLists.txt",
    "chars": 258,
    "preview": "cmake_minimum_required(VERSION 3.10)\nproject(nameservice)\n\nset(SRC\n\tWFNameService.cc\n\tWFDnsResolver.cc\n)\n\nif (NOT UPSTRE"
  },
  {
    "path": "src/nameservice/UpstreamPolicies.cc",
    "chars": 18203,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/nameservice/UpstreamPolicies.h",
    "chars": 5560,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/nameservice/WFDnsResolver.cc",
    "chars": 17177,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/nameservice/WFDnsResolver.h",
    "chars": 2430,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/nameservice/WFNameService.cc",
    "chars": 3082,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/nameservice/WFNameService.h",
    "chars": 3165,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/nameservice/WFServiceGovernance.cc",
    "chars": 12180,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/nameservice/WFServiceGovernance.h",
    "chars": 5371,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/nameservice/xmake.lua",
    "chars": 185,
    "preview": "target(\"nameservice\")\n    add_files(\"*.cc\")\n    set_kind(\"object\")\n    if not has_config(\"upstream\") then\n        remove"
  },
  {
    "path": "src/protocol/CMakeLists.txt",
    "chars": 664,
    "preview": "cmake_minimum_required(VERSION 3.10)\nproject(protocol)\n\nset(SRC\n\tPackageWrapper.cc\n\tSSLWrapper.cc\n\tdns_parser.c\n\tDnsMess"
  },
  {
    "path": "src/protocol/ConsulDataTypes.h",
    "chars": 8506,
    "preview": "/*\n  Copyright (c) 2022 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/DnsMessage.cc",
    "chars": 10564,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/DnsMessage.h",
    "chars": 5769,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/DnsUtil.cc",
    "chars": 3205,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/DnsUtil.h",
    "chars": 1972,
    "preview": "/*\n  Copyright (c) 2021 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/HttpMessage.cc",
    "chars": 13201,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/HttpMessage.h",
    "chars": 9125,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/HttpUtil.cc",
    "chars": 9313,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/HttpUtil.h",
    "chars": 7666,
    "preview": "/*\n  Copyright (c) 2019 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/KafkaDataTypes.cc",
    "chars": 15625,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/KafkaDataTypes.h",
    "chars": 32553,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/KafkaMessage.cc",
    "chars": 82658,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  },
  {
    "path": "src/protocol/KafkaMessage.h",
    "chars": 6797,
    "preview": "/*\n  Copyright (c) 2020 Sogou, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use"
  }
]

// ... and 107 more files (download for full content)

About this extraction

This page contains the full source code of the sogou/workflow GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 307 files (1.7 MB), approximately 532.4k tokens, and a symbol index with 1863 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!