├── .clang-format ├── .github └── workflows │ ├── c-cpp.yml │ └── codeql.yml ├── Dockerfile ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac ├── lib ├── libdynamic │ ├── .clang-format │ ├── .travis.yml │ ├── CHANGES │ ├── LICENSE │ ├── Makefile.am │ ├── README.rst │ ├── autogen.sh │ ├── configure.ac │ ├── example │ │ ├── Makefile.am │ │ ├── async.c │ │ ├── list.c │ │ ├── mapi.c │ │ └── maps.c │ ├── libdynamic.pc.in │ ├── src │ │ ├── dynamic.h │ │ └── dynamic │ │ │ ├── buffer.c │ │ │ ├── buffer.h │ │ │ ├── core.c │ │ │ ├── core.h │ │ │ ├── hash.c │ │ │ ├── hash.h │ │ │ ├── list.c │ │ │ ├── list.h │ │ │ ├── map.c │ │ │ ├── map.h │ │ │ ├── mapi.c │ │ │ ├── mapi.h │ │ │ ├── maps.c │ │ │ ├── maps.h │ │ │ ├── pool.c │ │ │ ├── pool.h │ │ │ ├── segment.c │ │ │ ├── segment.h │ │ │ ├── string.c │ │ │ ├── string.h │ │ │ ├── utility.c │ │ │ ├── utility.h │ │ │ ├── vector.c │ │ │ └── vector.h │ └── test │ │ ├── buffer.c │ │ ├── core.c │ │ ├── coverage.sh │ │ ├── hash.c │ │ ├── list.c │ │ ├── map.c │ │ ├── mapi.c │ │ ├── maps.c │ │ ├── mock.c │ │ ├── pool.c │ │ ├── segment.c │ │ ├── string.c │ │ ├── utility.c │ │ ├── valgrind.sh │ │ └── vector.c └── libreactor │ ├── .clang-format │ ├── .travis.yml │ ├── CHANGES │ ├── Dockerfile │ ├── LICENSE │ ├── Makefile.am │ ├── README.rst │ ├── autogen.sh │ ├── configure.ac │ ├── example │ ├── Makefile.am │ ├── benchmark_http.c │ ├── httpd_lowlevel.c │ ├── notify.c │ ├── resolve_async.c │ ├── resolve_sync.c │ ├── server.c │ ├── server_single.c │ ├── stream.c │ └── timer.c │ ├── libreactor.pc.in │ ├── src │ ├── picohttpparser │ │ ├── picohttpparser.c │ │ └── picohttpparser.h │ ├── reactor.h │ └── reactor │ │ ├── http.c │ │ ├── http.h │ │ ├── net.c │ │ ├── net.h │ │ ├── notify.c │ │ ├── notify.h │ │ ├── reactor.c │ │ ├── reactor.h │ │ ├── server.c │ │ ├── server.h │ │ ├── stream.c │ │ ├── stream.h │ │ ├── timer.c │ │ └── timer.h │ └── test │ ├── coverage.sh │ ├── data │ ├── request-1-allow │ ├── request-1-deny │ ├── request-2-allow │ ├── request-2-deny │ ├── request-3-allow │ ├── request-3-deny │ ├── request-4-allow │ ├── request-4-deny │ ├── request-5-allow │ ├── request-5-deny │ ├── request-6-deny │ ├── request-7-deny │ ├── request-8-deny │ ├── request-9-deny │ ├── response-1-allow │ ├── response-1-deny │ ├── response-2-allow │ ├── response-2-deny │ ├── response-3-allow │ ├── response-3-deny │ ├── response-4-allow │ ├── response-4-deny │ └── response-5-deny │ ├── http.c │ ├── mock.c │ ├── notify.c │ ├── stream.c │ └── valgrind.sh └── src ├── connection.c ├── connection.h ├── http_client.c ├── http_client.h ├── main.c ├── net.c ├── net.h ├── pounce.c ├── pounce.h ├── stats.c ├── stats.h ├── url.c ├── url.h ├── worker.c └── worker.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Microsoft 4 | IndentWidth: 2 5 | SpaceAfterCStyleCast: true 6 | SpaceBeforeCpp11BracedList: true 7 | ColumnLimit: 120 8 | SortIncludes: false 9 | ... 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: install cmocka 17 | run: sudo apt-get install -y libcmocka-dev 18 | - name: autogen.sh 19 | run: ./autogen.sh 20 | - name: configure 21 | run: ./configure 22 | - name: make 23 | run: make 24 | - name: make check 25 | run: make check 26 | - name: make distcheck 27 | run: make distcheck 28 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '20 11 * * 2' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'cpp' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Use only 'java' to analyze code written in Java, Kotlin or both 38 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 39 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 40 | 41 | steps: 42 | - name: Checkout repository 43 | uses: actions/checkout@v3 44 | 45 | # Initializes the CodeQL tools for scanning. 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@v2 48 | with: 49 | languages: ${{ matrix.language }} 50 | # If you wish to specify custom queries, you can do so here or in a config file. 51 | # By default, queries listed here will override any specified in a config file. 52 | # Prefix the list here with "+" to use these queries and those in the config file. 53 | 54 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 55 | # queries: security-extended,security-and-quality 56 | 57 | 58 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 59 | # If this step fails, then you should remove it and run the build manually (see below) 60 | - name: Autobuild 61 | uses: github/codeql-action/autobuild@v2 62 | 63 | # ℹ️ Command-line programs to run using the OS shell. 64 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 65 | 66 | # If the Autobuild fails above, remove it and uncomment the following three lines. 67 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 68 | 69 | # - run: | 70 | # echo "Run, Build Application using script" 71 | # ./location_of_script_within_repo/buildscript.sh 72 | 73 | - name: Perform CodeQL Analysis 74 | uses: github/codeql-action/analyze@v2 75 | with: 76 | category: "/language:${{matrix.language}}" 77 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:edge 2 | RUN apk add --update alpine-sdk linux-headers 3 | 4 | RUN wget https://github.com/fredrikwidlund/pounce/releases/download/v1.0.0/pounce-1.0.0.tar.gz && \ 5 | tar fxz pounce-1.0.0.tar.gz && \ 6 | cd pounce-1.0.0 && \ 7 | ./configure && \ 8 | make && \ 9 | gcc -std=gnu11 -Wall -Wextra -Wpedantic -g -O3 -flto -march=native -I/root/project/pounce/lib/libdynamic/src -I/root/project/pounce/lib/libreactor/src -pthread -o bin/pounce src/main.o src/pounce.o src/url.o src/worker.o src/stats.o src/connection.o src/net.o src/http_client.o lib/libreactor/.libs/libreactor.a lib/libdynamic/.libs/libdynamic.a -lm -pthread -static && \ 10 | strip bin/pounce 11 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = lib/libdynamic lib/libreactor . 2 | install installdirs: SUBDIRS = 3 | 4 | AUTOMAKE_OPTIONS = subdir-objects 5 | ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 6 | AM_CFLAGS = -std=gnu11 7 | EXTRA_DIST = Dockerfile 8 | 9 | bin_PROGRAMS = \ 10 | bin/pounce 11 | 12 | POUNCE_SOURCE_FILES = \ 13 | src/main.c \ 14 | src/pounce.c \ 15 | src/url.c \ 16 | src/worker.c \ 17 | src/stats.c \ 18 | src/connection.c \ 19 | src/net.c \ 20 | src/http_client.c 21 | 22 | POUNCE_HEADER_FILES = \ 23 | src/pounce.h \ 24 | src/url.h \ 25 | src/worker.h \ 26 | src/stats.h \ 27 | src/connection.h \ 28 | src/net.h \ 29 | src/http_client.h 30 | 31 | bin_pounce_SOURCES = $(POUNCE_SOURCE_FILES) $(POUNCE_HEADER_FILES) 32 | bin_pounce_LDADD = lib/libreactor/libreactor.la lib/libdynamic/libdynamic.la -lm 33 | bin_pounce_LDFLAGS = -pthread 34 | 35 | MAINTAINERCLEANFILES = aclocal.m4 config.h.in configure Makefile.in 36 | 37 | maintainer-clean-local: 38 | rm -rf autotools m4 39 | 40 | indent: 41 | clang-format -i src/*.c 42 | 43 | docker-build: 44 | docker build -t pounce-sdk . 45 | id=$$(docker create pounce-sdk); docker cp $$id:/pounce-1.0.0/bin/pounce pounce; docker rm -v $$id 46 | 47 | .PHONY: indent 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pounce 2 | HTTP benchmark utility 3 | 4 | build 5 | ----- 6 | 7 | wget https://github.com/fredrikwidlund/pounce/releases/download/v1.0.0/pounce-1.0.0.tar.gz 8 | tar fxz pounce-1.0.0.tar.gz 9 | cd pounce-1.0.0 10 | ./configure 11 | make 12 | sudo make install 13 | 14 | run 15 | --- 16 | 17 | pounce http://127.0.0.1 18 | requests 1767450 rps, success 100.00%, latency 38.58us/90.45us/971.71us/8.29us, usage 44.67% of 111.90Ghz 19 | 20 | static binary docker build 21 | -------------------------- 22 | 23 | wget https://github.com/fredrikwidlund/pounce/releases/download/v1.0.0/pounce-1.0.0.tar.gz 24 | tar fxz pounce-1.0.0.tar.gz 25 | cd pounce-1.0.0 26 | ./configure 27 | make docker-build 28 | ./pounce 29 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p autotools m4 4 | autoreconf --force --install 5 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([pounce],[1.0.0],[fredrik.widlund@gmail.com]) 2 | AC_CONFIG_AUX_DIR(autotools) 3 | AC_CONFIG_MACRO_DIR([m4]) 4 | AC_CONFIG_SUBDIRS([lib/libdynamic lib/libreactor]) 5 | AM_INIT_AUTOMAKE([-Wall -Werror foreign no-define]) 6 | 7 | : ${CFLAGS="-Wall -Wextra -Wpedantic -g -O3 -flto -march=native"} 8 | CFLAGS="$CFLAGS -I${ac_pwd}/lib/libdynamic/src -I${ac_pwd}/lib/libreactor/src" 9 | export CFLAGS 10 | 11 | AM_PROG_AR 12 | LT_INIT 13 | AM_PROG_CC_C_O 14 | 15 | AC_PREFIX_DEFAULT(/usr) 16 | AC_CONFIG_FILES([Makefile]) 17 | AC_OUTPUT 18 | -------------------------------------------------------------------------------- /lib/libdynamic/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Microsoft 4 | IndentWidth: 2 5 | SpaceAfterCStyleCast: true 6 | SpaceBeforeCpp11BracedList: true 7 | ColumnLimit: 0 8 | SortIncludes: false 9 | ... 10 | -------------------------------------------------------------------------------- /lib/libdynamic/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: bionic 3 | language: c 4 | compiler: 5 | - gcc 6 | install: 7 | - cd ${TRAVIS_BUILD_DIR} 8 | - sudo apt-get update -qq 9 | - sudo apt-get install -y -qq valgrind 10 | - wget https://cmocka.org/files/1.1/cmocka-1.1.0.tar.xz 11 | - tar -xvf cmocka-1.1.0.tar.xz 12 | - cd cmocka-1.1.0 13 | - mkdir build 14 | - cd build 15 | - cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release .. 16 | - make 17 | - sudo make install 18 | - cd ${TRAVIS_BUILD_DIR} 19 | - wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz 20 | - tar xf lcov_1.11.orig.tar.gz 21 | - sudo make -C lcov-1.11/ install 22 | - gem install coveralls-lcov 23 | 24 | script: 25 | - cd ${TRAVIS_BUILD_DIR} 26 | - ./autogen.sh && ./configure && make check 27 | 28 | after_success: 29 | - cd ${TRAVIS_BUILD_DIR} 30 | - lcov --directory . --capture --output-file coverage.info # capture coverage info 31 | - lcov --remove coverage.info 'test/*' '/usr/*' --output-file coverage.info # filter out system and test code 32 | - lcov --list coverage.info 33 | - coveralls-lcov --repo-token ${COVERALLS_TOKEN} coverage.info 34 | -------------------------------------------------------------------------------- /lib/libdynamic/CHANGES: -------------------------------------------------------------------------------- 1 | Version 1.0 2 | =========== 3 | 4 | Released 2017-01-03 5 | 6 | * Initial release 7 | 8 | Version 1.1 9 | =========== 10 | 11 | Released 2017-12-17 12 | 13 | * New features: 14 | 15 | - List type 16 | - More uniform interfaces 17 | 18 | Version 1.2 19 | =========== 20 | 21 | Released 2019-04-19 22 | 23 | * New features: 24 | 25 | - Add maps (string map) and mapi (uint64_t) abstractions 26 | - Refactor map interface 27 | 28 | Version 1.3 29 | =========== 30 | 31 | Released 2019-09-02 32 | 33 | * New features: 34 | 35 | - Add list splicing 36 | 37 | Version 2.0 38 | =========== 39 | 40 | Released 2020-05-17 41 | 42 | * New features: 43 | 44 | - Add event handling 45 | - Add worker pools 46 | 47 | Version 2.2 48 | =========== 49 | 50 | Released 2020-12-25 51 | 52 | * New features: 53 | 54 | - Add counters 55 | - Add abort 56 | 57 | Version 2.3 58 | =========== 59 | 60 | Released 2021-01-02 61 | 62 | * New features: 63 | 64 | - Pool refactoring 65 | -------------------------------------------------------------------------------- /lib/libdynamic/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Fredrik Widlund 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/libdynamic/Makefile.am: -------------------------------------------------------------------------------- 1 | DIST_SUBDIRS = example 2 | AUTOMAKE_OPTIONS = subdir-objects 3 | ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 4 | AM_CFLAGS = -std=gnu11 -I$(srcdir)/src -fPIC 5 | AM_LDFLAGS = -static 6 | 7 | EXTRA_DIST = \ 8 | CHANGES \ 9 | LICENSE \ 10 | README.rst 11 | 12 | SOURCE_FILES = \ 13 | src/dynamic/segment.c \ 14 | src/dynamic/utility.c \ 15 | src/dynamic/hash.c \ 16 | src/dynamic/buffer.c \ 17 | src/dynamic/list.c \ 18 | src/dynamic/vector.c \ 19 | src/dynamic/string.c \ 20 | src/dynamic/map.c \ 21 | src/dynamic/maps.c \ 22 | src/dynamic/mapi.c \ 23 | src/dynamic/pool.c \ 24 | src/dynamic/core.c 25 | 26 | HEADER_FILES = \ 27 | src/dynamic/segment.h \ 28 | src/dynamic/utility.h \ 29 | src/dynamic/hash.h \ 30 | src/dynamic/buffer.h \ 31 | src/dynamic/list.h \ 32 | src/dynamic/vector.h \ 33 | src/dynamic/string.h \ 34 | src/dynamic/map.h \ 35 | src/dynamic/maps.h \ 36 | src/dynamic/mapi.h \ 37 | src/dynamic/pool.h \ 38 | src/dynamic/core.h 39 | 40 | lib_LTLIBRARIES= libdynamic.la 41 | libdynamic_la_SOURCES = $(SOURCE_FILES) $(HEADER_FILES) 42 | 43 | headerfilesdir = $(includedir)/dynamic 44 | headerfiles_HEADERS = $(HEADER_FILES) 45 | 46 | mainheaderdir = $(includedir) 47 | mainheader_HEADERS = src/dynamic.h 48 | 49 | pkgconfigdir = $(libdir)/pkgconfig 50 | pkgconfig_DATA = libdynamic.pc 51 | 52 | MAINTAINERCLEANFILES = aclocal.m4 config.h.in configure Makefile.in example/Makefile.in libdynamic-?.?.?.tar.gz 53 | maintainer-clean-local:; rm -rf autotools m4 libdynamic-?.?.? 54 | 55 | ### unit tests ### 56 | 57 | check_LIBRARIES = libdynamic_test.a 58 | libdynamic_test_a_CFLAGS = $(CHECK_CFLAGS) 59 | libdynamic_test_a_SOURCES = $(SOURCE_FILES) $(HEADER_FILES) 60 | 61 | CHECK_CFLAGS = -std=gnu11 -O0 -g -ftest-coverage -fprofile-arcs -I$(srcdir)/src -DGCOV_BUILD 62 | CHECK_LDADD = libdynamic_test.a -lcmocka 63 | CHECK_LDFLAGS_EXTRA = \ 64 | -Wl,--wrap=malloc \ 65 | -Wl,--wrap=calloc \ 66 | -Wl,--wrap=realloc \ 67 | -Wl,--wrap=aligned_alloc \ 68 | -Wl,--wrap=abort \ 69 | -Wl,--wrap=recv \ 70 | -Wl,--wrap=send \ 71 | -Wl,--wrap=pthread_create \ 72 | -Wl,--wrap=socketpair \ 73 | -Wl,--wrap=epoll_create1 \ 74 | -Wl,--wrap=epoll_ctl \ 75 | -Wl,--wrap=epoll_wait \ 76 | -pthread 77 | 78 | check_PROGRAMS = 79 | 80 | check_PROGRAMS += test/segment 81 | test_segment_CFLAGS = $(CHECK_CFLAGS) 82 | test_segment_LDADD = $(CHECK_LDADD) 83 | test_segment_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 84 | test_segment_SOURCES = test/segment.c test/mock.c 85 | 86 | check_PROGRAMS += test/utility 87 | test_utility_CFLAGS = $(CHECK_CFLAGS) 88 | test_utility_LDADD = $(CHECK_LDADD) 89 | test_utility_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 90 | test_utility_SOURCES = test/utility.c test/mock.c 91 | 92 | check_PROGRAMS += test/hash 93 | test_hash_CFLAGS = $(CHECK_CFLAGS) 94 | test_hash_LDADD = $(CHECK_LDADD) 95 | test_hash_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 96 | test_hash_SOURCES = test/hash.c test/mock.c 97 | 98 | check_PROGRAMS += test/buffer 99 | test_buffer_CFLAGS = $(CHECK_CFLAGS) 100 | test_buffer_LDADD = $(CHECK_LDADD) 101 | test_buffer_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 102 | test_buffer_SOURCES = test/buffer.c test/mock.c 103 | 104 | check_PROGRAMS += test/list 105 | test_list_CFLAGS = $(CHECK_CFLAGS) 106 | test_list_LDADD = $(CHECK_LDADD) 107 | test_list_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 108 | test_list_SOURCES = test/list.c test/mock.c 109 | 110 | check_PROGRAMS += test/vector 111 | test_vector_CFLAGS = $(CHECK_CFLAGS) 112 | test_vector_LDADD = $(CHECK_LDADD) 113 | test_vector_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 114 | test_vector_SOURCES = test/vector.c test/mock.c 115 | 116 | check_PROGRAMS += test/string 117 | test_string_CFLAGS = $(CHECK_CFLAGS) 118 | test_string_LDADD = $(CHECK_LDADD) 119 | test_string_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 120 | test_string_SOURCES = test/string.c test/mock.c 121 | 122 | check_PROGRAMS += test/maps 123 | test_maps_CFLAGS = $(CHECK_CFLAGS) 124 | test_maps_LDADD = $(CHECK_LDADD) 125 | test_maps_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 126 | test_maps_SOURCES = test/maps.c test/mock.c 127 | 128 | check_PROGRAMS += test/mapi 129 | test_mapi_CFLAGS = $(CHECK_CFLAGS) 130 | test_mapi_LDADD = $(CHECK_LDADD) 131 | test_mapi_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 132 | test_mapi_SOURCES = test/mapi.c test/mock.c 133 | 134 | check_PROGRAMS += test/map 135 | test_map_CFLAGS = $(CHECK_CFLAGS) 136 | test_map_LDADD = $(CHECK_LDADD) 137 | test_map_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 138 | test_map_SOURCES = test/map.c test/mock.c 139 | 140 | check_PROGRAMS += test/pool 141 | test_pool_CFLAGS = $(CHECK_CFLAGS) 142 | test_pool_LDADD = $(CHECK_LDADD) 143 | test_pool_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 144 | test_pool_SOURCES = test/pool.c test/mock.c 145 | 146 | check_PROGRAMS += test/core 147 | test_core_CFLAGS = $(CHECK_CFLAGS) 148 | test_core_LDADD = $(CHECK_LDADD) 149 | test_core_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 150 | test_core_SOURCES = test/core.c test/mock.c 151 | 152 | dist_noinst_SCRIPTS = test/valgrind.sh test/coverage.sh 153 | 154 | TESTS = $(check_PROGRAMS) test/coverage.sh test/valgrind.sh 155 | 156 | CLEANFILES = {.,src/dynamic,test}/*.{gcno,gcda,gcov} 157 | 158 | indent: 159 | clang-format -i src/dynamic/*.c test/*.c examples/*.c 160 | -------------------------------------------------------------------------------- /lib/libdynamic/README.rst: -------------------------------------------------------------------------------- 1 | libdynamic v2.3 2 | =============== 3 | 4 | .. image:: https://travis-ci.org/fredrikwidlund/libdynamic.svg?branch=master 5 | :target: https://travis-ci.org/fredrikwidlund/libdynamic 6 | 7 | .. image:: https://coveralls.io/repos/github/fredrikwidlund/libdynamic/badge.svg?branch=master 8 | :target: https://coveralls.io/github/fredrikwidlund/libdynamic?branch=master 9 | 10 | .. image:: https://img.shields.io/lgtm/grade/cpp/g/fredrikwidlund/libdynamic.svg?logo=lgtm&logoWidth=18) 11 | :target: https://lgtm.com/projects/g/fredrikwidlund/libdynamic/context:cpp 12 | 13 | .. image:: https://readthedocs.org/projects/libdynamic/badge/?version=latest 14 | :target: http://libdynamic.readthedocs.io/en/latest/?badge=latest 15 | :alt: Documentation Status 16 | 17 | Documentation is available at http://libdynamic.readthedocs.io/en/latest/. 18 | 19 | Description 20 | =========== 21 | 22 | libdynamic is a utility library for C that will give you dynamic data structures like buffers, lists, vectors, maps and strings. It also includes asynchronous worker pools, and a core event driven framework. It is used, for example, in conjunction with libreactor_ handling many millions of HTTP transactions daily for over 5 years. 23 | 24 | Status 25 | ====== 26 | 27 | libdynamic currently is (and has been for many years) used in high concurrency and high performance production environments in a very robust manner. 28 | 29 | Build 30 | ===== 31 | 32 | Build from the Git repository 33 | ----------------------------- 34 | 35 | Building from Git requires GNU Autotools (autoconf_, automake_, libtool_). 36 | 37 | .. code-block:: shell 38 | 39 | git clone https://github.com/fredrikwidlund/libdynamic 40 | cd libdynamic 41 | ./autogen.sh 42 | ./configure 43 | make install 44 | 45 | Build from release 46 | ------------------ 47 | 48 | .. code-block:: shell 49 | 50 | wget https://github.com/fredrikwidlund/libdynamic/releases/download/v2.3.0/libdynamic-2.3.0.tar.gz 51 | tar fxz libdynamic-2.3.0.tar.gz 52 | cd libdynamic-2.3.0 53 | ./configure 54 | make install 55 | 56 | Running unit tests 57 | ------------------ 58 | 59 | The test suite requires cmocka_ and valgrind_. 60 | 61 | .. code-block:: shell 62 | 63 | ./configure 64 | make check 65 | 66 | Versioning 67 | ========== 68 | 69 | libdynamic follows the `semantic versioning`_ scheme. 70 | 71 | Licensing 72 | ========= 73 | 74 | libdynamic is licensed under the MIT license. 75 | 76 | .. _libreactor: https://github.com/fredrikwidlund/libreactor 77 | .. _`semantic versioning`: https://semver.org/ 78 | .. _cmocka: https://cmocka.org/ 79 | .. _valgrind: http://valgrind.org/ 80 | .. _autoconf: http://www.gnu.org/software/autoconf/ 81 | .. _automake: http://www.gnu.org/software/automake/ 82 | .. _libtool: http://www.gnu.org/software/libtool/ 83 | .. _benchmarks: https://github.com/fredrikwidlund/libdynamic_benchmark 84 | -------------------------------------------------------------------------------- /lib/libdynamic/autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p autotools m4 4 | autoreconf --force --install 5 | -------------------------------------------------------------------------------- /lib/libdynamic/configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([libdynamic],[2.3.0],[fredrik.widlund@gmail.com]) 2 | AC_CONFIG_AUX_DIR(autotools) 3 | AC_CONFIG_MACRO_DIR([m4]) 4 | AM_INIT_AUTOMAKE([-Wall -Werror foreign no-define]) 5 | 6 | : ${CFLAGS="-Wall -Wextra -Wpedantic -g -O3 -march=native -flto"} 7 | 8 | AM_PROG_AR 9 | LT_INIT 10 | AM_PROG_CC_C_O 11 | 12 | AC_PREFIX_DEFAULT(/usr) 13 | AC_CONFIG_FILES([Makefile example/Makefile libdynamic.pc]) 14 | AC_OUTPUT 15 | -------------------------------------------------------------------------------- /lib/libdynamic/example/Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 2 | AM_CFLAGS = -std=gnu11 -std=gnu11 3 | LDADD = -L.. -ldynamic 4 | 5 | bin_PROGRAMS = \ 6 | maps \ 7 | mapi \ 8 | list \ 9 | async 10 | 11 | async_LDFLAGS = -pthread 12 | 13 | MAINTAINERCLEANFILES = Makefile.in 14 | -------------------------------------------------------------------------------- /lib/libdynamic/example/async.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | struct state 12 | { 13 | core core; 14 | pool pool; 15 | int timer; 16 | _Atomic size_t jobs; 17 | }; 18 | 19 | static core_status async(core_event *event) 20 | { 21 | struct state *state = event->state; 22 | 23 | if (event->type == POOL_REQUEST) 24 | { 25 | usleep(10000); 26 | state->jobs++; 27 | } 28 | return CORE_OK; 29 | } 30 | 31 | core_status timeout(core_event *event) 32 | { 33 | struct state *state = event->state; 34 | core_counters *counters; 35 | uint64_t exp; 36 | ssize_t n; 37 | int i; 38 | 39 | while (1) 40 | { 41 | n = read(state->timer, &exp, sizeof exp); 42 | if (n == -1 && errno == EAGAIN) 43 | break; 44 | if (n != sizeof exp) 45 | err(1, "read"); 46 | 47 | counters = core_get_counters(&state->core); 48 | (void) printf("[timer %lu, jobs %lu]\n", exp, state->jobs); 49 | (void) printf("[stats polls %lu, events %lu, awake %lu, total %lu, usage %f\n", 50 | counters->polls, counters->events, counters->awake, counters->awake + counters->sleep, 51 | (double) counters->awake / (double) (counters->awake + counters->sleep)); 52 | core_clear_counters(&state->core); 53 | } 54 | 55 | for (i = 0; i < 1000; i++) 56 | pool_enqueue(&state->pool, async, state); 57 | return CORE_OK; 58 | } 59 | 60 | int main() 61 | { 62 | struct state state = {0}; 63 | 64 | core_construct(&state.core); 65 | pool_construct(&state.pool, &state.core); 66 | pool_limits(&state.pool, 1, 32); 67 | 68 | state.timer = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); 69 | timerfd_settime(state.timer, 0, (struct itimerspec[]) {{{1, 0}, {1, 0}}}, NULL); 70 | core_add(&state.core, timeout, &state, state.timer, EPOLLIN | EPOLLET); 71 | 72 | core_loop(&state.core); 73 | 74 | close(state.timer); 75 | pool_destruct(&state.pool); 76 | core_destruct(&state.core); 77 | } 78 | -------------------------------------------------------------------------------- /lib/libdynamic/example/list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | static int compare(void *p1, void *p2) 7 | { 8 | return *(int *) p1 - *(int *) p2; 9 | } 10 | 11 | int main() 12 | { 13 | list list; 14 | int *p; 15 | 16 | list_construct(&list); 17 | 18 | list_push_back(&list, (int[]) {4}, sizeof(int)); 19 | list_push_back(&list, (int[]) {5}, sizeof(int)); 20 | list_push_back(&list, (int[]) {6}, sizeof(int)); 21 | list_push_front(&list, (int[]) {3}, sizeof(int)); 22 | list_push_front(&list, (int[]) {2}, sizeof(int)); 23 | list_push_front(&list, (int[]) {1}, sizeof(int)); 24 | 25 | printf("foreach\n"); 26 | list_foreach(&list, p) 27 | printf("%d\n", *p); 28 | 29 | printf("foreach reverse\n"); 30 | list_foreach_reverse(&list, p) 31 | printf("%d\n", *p); 32 | 33 | p = list_find(&list, compare, (int[]) {1}); 34 | printf("found %d\n", *p); 35 | list_insert(p, (int[]) {-2}, sizeof(int)); 36 | list_insert(p, (int[]) {0}, sizeof(int)); 37 | list_insert(list_front(&list), (int[]) {-1}, sizeof(int)); 38 | list_insert(list_end(&list), (int[]) {7}, sizeof(int)); 39 | 40 | printf("[forward]\n"); 41 | list_foreach(&list, p) 42 | printf("%d\n", *p); 43 | 44 | printf("[backward]\n"); 45 | list_foreach_reverse(&list, p) 46 | printf("%d\n", *p); 47 | 48 | list_destruct(&list, NULL); 49 | } 50 | -------------------------------------------------------------------------------- /lib/libdynamic/example/mapi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | static uint64_t nano_time(void) 8 | { 9 | struct timespec ts; 10 | 11 | (void) clock_gettime(CLOCK_MONOTONIC_RAW, &ts); 12 | return ((uint64_t) ts.tv_sec * 1000000000) + ((uint64_t) ts.tv_nsec); 13 | } 14 | 15 | static void release(mapi_entry *e) 16 | { 17 | (void) e; 18 | } 19 | 20 | int main(int argc, char **argv) 21 | { 22 | uintptr_t *keys, *values, p; 23 | mapi m; 24 | uint64_t n, i; 25 | uint64_t t1, t2; 26 | 27 | if (argc != 2) 28 | exit(1); 29 | n = strtoul(argv[1], NULL, 0); 30 | // create keys/values 31 | keys = calloc(n, sizeof keys[0]); 32 | values = calloc(n, sizeof values[0]); 33 | for (i = 0; i < n; i++) 34 | { 35 | keys[i] = i + 1; 36 | values[i] = i + 1; 37 | } 38 | 39 | // construct map 40 | mapi_construct(&m); 41 | 42 | // insert key->value mappings 43 | 44 | t1 = nano_time(); 45 | for (i = 0; i < n; i++) 46 | mapi_insert(&m, keys[i], values[i], release); 47 | t2 = nano_time(); 48 | printf("insert %lu\n", t2 - t1); 49 | 50 | // lookup key and validate value 51 | t1 = nano_time(); 52 | for (i = 0; i < n; i++) 53 | { 54 | p = mapi_at(&m, keys[i]); 55 | assert(p == values[i]); 56 | } 57 | t2 = nano_time(); 58 | printf("lookup %lu\n", t2 - t1); 59 | 60 | t1 = nano_time(); 61 | for (i = 0; i < n; i++) 62 | mapi_erase(&m, keys[i], release); 63 | t2 = nano_time(); 64 | printf("erase %lu\n", t2 - t1); 65 | 66 | // release keys/values 67 | free(keys); 68 | free(values); 69 | 70 | // destruct map 71 | mapi_destruct(&m, release); 72 | } 73 | -------------------------------------------------------------------------------- /lib/libdynamic/example/maps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | typedef struct value value; 9 | struct value 10 | { 11 | int number; 12 | }; 13 | 14 | static uint64_t nano_time(void) 15 | { 16 | struct timespec ts; 17 | 18 | (void) clock_gettime(CLOCK_MONOTONIC_RAW, &ts); 19 | return ((uint64_t) ts.tv_sec * 1000000000) + ((uint64_t) ts.tv_nsec); 20 | } 21 | 22 | static void release(maps_entry *e) 23 | { 24 | (void) e; 25 | } 26 | 27 | int main(int argc, char **argv) 28 | { 29 | char **keys, buffer[256]; 30 | value **values; 31 | maps m; 32 | int n, i, *p; 33 | uint64_t t1, t2; 34 | 35 | if (argc != 2) 36 | exit(1); 37 | n = strtoul(argv[1], NULL, 0); 38 | 39 | // create keys/values 40 | keys = calloc(n, sizeof keys[0]); 41 | values = calloc(n, sizeof values[0]); 42 | for (i = 0; i < n; i++) 43 | { 44 | snprintf(buffer, sizeof buffer, "key-%u", i); 45 | keys[i] = strdup(buffer); 46 | values[i] = malloc(sizeof(value)); 47 | values[i]->number = i; 48 | } 49 | 50 | // construct map 51 | maps_construct(&m); 52 | 53 | // insert key->value mappings 54 | 55 | t1 = nano_time(); 56 | for (i = 0; i < n; i++) 57 | maps_insert(&m, keys[i], (uintptr_t) values[i], release); 58 | t2 = nano_time(); 59 | printf("insert %lu\n", t2 - t1); 60 | 61 | // lookup key and validate value 62 | t1 = nano_time(); 63 | for (i = 0; i < n; i++) 64 | { 65 | snprintf(buffer, sizeof buffer, "key-%u", i); 66 | p = (void *) maps_at(&m, buffer); 67 | assert(p); 68 | assert(*p == i); 69 | } 70 | t2 = nano_time(); 71 | printf("lookup %lu\n", t2 - t1); 72 | 73 | t1 = nano_time(); 74 | for (i = 0; i < n; i++) 75 | maps_erase(&m, keys[i], release); 76 | t2 = nano_time(); 77 | printf("erase %lu\n", t2 - t1); 78 | 79 | // release keys/values 80 | for (i = 0; i < n; i++) 81 | { 82 | free(keys[i]); 83 | free(values[i]); 84 | } 85 | free(keys); 86 | free(values); 87 | 88 | // destruct map 89 | maps_destruct(&m, release); 90 | } 91 | -------------------------------------------------------------------------------- /lib/libdynamic/libdynamic.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=${prefix}/include 5 | 6 | Name: libdynamic 7 | Description: C library for various dynamic container types 8 | Version: @VERSION@ 9 | Libs: -L${libdir} -ldynamic 10 | Cflags: -I${includedir} -flto -fuse-linker-plugin 11 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic.h: -------------------------------------------------------------------------------- 1 | #ifndef DYNAMIC_H_INCLUDED 2 | #define DYNAMIC_H_INCLUDED 3 | 4 | #define DYNAMIC_VERSION "2.3.0" 5 | #define DYNAMIC_VERSION_MAJOR 2 6 | #define DYNAMIC_VERSION_MINOR 3 7 | #define DYNAMIC_VERSION_PATCH 0 8 | 9 | #define dynamic_likely(x) __builtin_expect(!!(x), 1) 10 | #define dynamic_unlikely(x) __builtin_expect(!!(x), 0) 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | #endif /* DYNAMIC_H_INCLUDED */ 40 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "buffer.h" 6 | 7 | static size_t buffer_roundup(size_t size) 8 | { 9 | size--; 10 | size |= size >> 1; 11 | size |= size >> 2; 12 | size |= size >> 4; 13 | size |= size >> 8; 14 | size |= size >> 16; 15 | size |= size >> 32; 16 | size++; 17 | 18 | return size; 19 | } 20 | 21 | /* constructor/destructor */ 22 | 23 | void buffer_construct(buffer *b) 24 | { 25 | *b = (buffer) {0}; 26 | } 27 | 28 | void buffer_destruct(buffer *b) 29 | { 30 | buffer_clear(b); 31 | } 32 | 33 | /* capacity */ 34 | 35 | size_t buffer_size(buffer *b) 36 | { 37 | return b->size; 38 | } 39 | 40 | size_t buffer_capacity(buffer *b) 41 | { 42 | return b->capacity; 43 | } 44 | 45 | void buffer_reserve(buffer *b, size_t capacity) 46 | { 47 | if (capacity > b->capacity) 48 | { 49 | capacity = buffer_roundup(capacity); 50 | b->data = realloc(b->data, capacity); 51 | b->capacity = capacity; 52 | } 53 | } 54 | 55 | void buffer_resize(buffer *b, size_t size) 56 | { 57 | if (size > buffer_capacity(b)) 58 | buffer_reserve(b, size); 59 | b->size = size; 60 | } 61 | 62 | void buffer_compact(buffer *b) 63 | { 64 | if (b->capacity > b->size) 65 | { 66 | b->data = realloc(b->data, b->size); 67 | b->capacity = b->size; 68 | } 69 | } 70 | 71 | /* modifiers */ 72 | 73 | void buffer_insert(buffer *b, size_t position, void *data, size_t size) 74 | { 75 | buffer_reserve(b, b->size + size); 76 | if (position < b->size) 77 | memmove((char *) b->data + position + size, (char *) b->data + position, b->size - position); 78 | memcpy((char *) b->data + position, data, size); 79 | b->size += size; 80 | } 81 | 82 | void buffer_insert_fill(buffer *b, size_t position, size_t count, void *data, size_t size) 83 | { 84 | size_t i; 85 | 86 | buffer_reserve(b, b->size + (count * size)); 87 | if (position < b->size) 88 | memmove((char *) b->data + position + (count * size), (char *) b->data + position, b->size - position); 89 | 90 | for (i = 0; i < count; i++) 91 | memcpy((char *) b->data + position + (i * size), data, size); 92 | b->size += count * size; 93 | } 94 | 95 | void buffer_erase(buffer *b, size_t position, size_t size) 96 | { 97 | memmove((char *) b->data + position, (char *) b->data + position + size, b->size - position - size); 98 | b->size -= size; 99 | } 100 | 101 | void buffer_clear(buffer *b) 102 | { 103 | free(b->data); 104 | buffer_construct(b); 105 | } 106 | 107 | /* element access */ 108 | 109 | void *buffer_data(buffer *b) 110 | { 111 | return b->data; 112 | } 113 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef DYNAMIC_BUFFER_H_INCLUDED 2 | #define DYNAMIC_BUFFER_H_INCLUDED 3 | 4 | typedef struct buffer buffer; 5 | struct buffer 6 | { 7 | void *data; 8 | size_t size; 9 | size_t capacity; 10 | }; 11 | 12 | /* constructor/destructor */ 13 | void buffer_construct(buffer *); 14 | void buffer_destruct(buffer *); 15 | 16 | /* capacity */ 17 | size_t buffer_size(buffer *); 18 | size_t buffer_capacity(buffer *); 19 | void buffer_reserve(buffer *, size_t); 20 | void buffer_resize(buffer *, size_t); 21 | void buffer_compact(buffer *); 22 | 23 | /* modifiers */ 24 | void buffer_insert(buffer *, size_t, void *, size_t); 25 | void buffer_insert_fill(buffer *, size_t, size_t, void *, size_t); 26 | void buffer_erase(buffer *, size_t, size_t); 27 | void buffer_clear(buffer *); 28 | 29 | /* element access */ 30 | void *buffer_data(buffer *); 31 | 32 | #endif /* DYNAMIC_BUFFER_H_INCLUDED */ 33 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/core.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "buffer.h" 9 | #include "vector.h" 10 | #include "core.h" 11 | #include "segment.h" 12 | #include "utility.h" 13 | 14 | static __thread core core_default = {0}; 15 | 16 | static core_status core_default_callback(core_event *); 17 | static core_handler core_handler_default = {.callback = core_default_callback}; 18 | 19 | static core_status core_default_callback(core_event *event __attribute__((unused))) 20 | { 21 | return CORE_OK; 22 | } 23 | 24 | static core *core_get(core *core) 25 | { 26 | return core ? core : &core_default; 27 | } 28 | 29 | void core_construct(core *core) 30 | { 31 | core = core_get(core); 32 | if (!core->ref) 33 | { 34 | vector_construct(&core->handlers, sizeof(core_handler)); 35 | vector_construct(&core->next, sizeof(core_handler)); 36 | core->fd = epoll_create1(EPOLL_CLOEXEC); 37 | if (core->fd == -1) 38 | core->errors++; 39 | core->active = 1; 40 | } 41 | core->ref++; 42 | } 43 | 44 | void core_destruct(core *core) 45 | { 46 | core = core_get(core); 47 | core->ref--; 48 | if (!core->ref) 49 | { 50 | if (core->fd >= 0) 51 | (void) close(core->fd); 52 | vector_destruct(&core->handlers, NULL); 53 | vector_destruct(&core->next, NULL); 54 | *core = (struct core) {0}; 55 | } 56 | } 57 | 58 | void core_abort(core *core) 59 | { 60 | core = core_get(core); 61 | core->active = 0; 62 | } 63 | 64 | core_status core_dispatch(core_handler *handler, int type, uintptr_t data) 65 | { 66 | return handler->callback((core_event[]) {{.state = handler->state, .type = type, .data = data}}); 67 | } 68 | 69 | void core_loop(core *core) 70 | { 71 | struct epoll_event events[CORE_MAX_EVENTS]; 72 | uint64_t t0, t1; 73 | int n, i; 74 | 75 | t1 = utility_tsc(); 76 | core = core_get(core); 77 | while (core->active && core->errors == 0 && (core->handlers_active || vector_size(&core->next))) 78 | { 79 | for (i = 0; (size_t) i < vector_size(&core->next); i++) 80 | (void) core_dispatch(vector_at(&core->next, i), 0, 0); 81 | vector_clear(&core->next, NULL); 82 | 83 | t0 = utility_tsc(); 84 | core->time = 0; 85 | n = core->handlers_active ? epoll_wait(core->fd, events, CORE_MAX_EVENTS, -1) : 0; 86 | core->errors += n == -1; 87 | core->counters.awake += t0 - t1; 88 | t1 = utility_tsc(); 89 | core->counters.sleep += t1 - t0; 90 | core->counters.polls++; 91 | core->counters.events += n; 92 | 93 | for (i = 0; i < n; i++) 94 | (void) core_dispatch(vector_at(&core->handlers, events[i].data.fd), 0, events[i].events); 95 | } 96 | } 97 | 98 | void core_add(core *core, core_callback *callback, void *state, int fd, int events) 99 | { 100 | core_handler *handlers; 101 | int e; 102 | 103 | core = core_get(core); 104 | while (vector_size(&core->handlers) <= (size_t) fd) 105 | vector_push_back(&core->handlers, &core_handler_default); 106 | 107 | handlers = vector_data(&core->handlers); 108 | handlers[fd] = (core_handler) {.callback = callback, .state = state}; 109 | e = epoll_ctl(core->fd, EPOLL_CTL_ADD, fd, (struct epoll_event[]) {{.events = events, .data.fd = fd}}); 110 | if (e == -1) 111 | { 112 | handlers[fd] = core_handler_default; 113 | core->errors++; 114 | } 115 | else 116 | core->handlers_active++; 117 | } 118 | 119 | void core_modify(core *core, int fd, int events) 120 | { 121 | core_handler *handlers; 122 | int e; 123 | 124 | core = core_get(core); 125 | e = epoll_ctl(core->fd, EPOLL_CTL_MOD, fd, (struct epoll_event[]) {{.events = events, .data.fd = fd}}); 126 | if (e == -1) 127 | { 128 | handlers = vector_data(&core->handlers); 129 | handlers[fd] = core_handler_default; 130 | core->errors++; 131 | } 132 | } 133 | 134 | void core_delete(core *core, int fd) 135 | { 136 | core_handler *handlers; 137 | int e; 138 | 139 | core = core_get(core); 140 | e = epoll_ctl(core->fd, EPOLL_CTL_DEL, fd, NULL); 141 | if (e == -1) 142 | core->errors++; 143 | handlers = vector_data(&core->handlers); 144 | handlers[fd] = core_handler_default; 145 | core->handlers_active--; 146 | } 147 | 148 | core_id core_next(core *core, core_callback *callback, void *state) 149 | { 150 | core_handler handler = {.callback = callback, .state = state}; 151 | 152 | core = core_get(core); 153 | vector_push_back(&core->next, &handler); 154 | return vector_size(&core->next); 155 | } 156 | 157 | void core_cancel(core *core, core_id id) 158 | { 159 | core_handler *handlers; 160 | 161 | core = core_get(core); 162 | if (id == 0) 163 | return; 164 | 165 | handlers = vector_data(&core->next); 166 | handlers[id - 1] = core_handler_default; 167 | } 168 | 169 | size_t core_errors(core *core) 170 | { 171 | core = core_get(core); 172 | return core->errors; 173 | } 174 | 175 | uint64_t core_now(core *core) 176 | { 177 | struct timespec tv; 178 | 179 | core = core_get(core); 180 | if (core->time == 0) 181 | { 182 | clock_gettime(CLOCK_REALTIME, &tv); 183 | core->time = (uint64_t) tv.tv_sec * 1000000000 + (uint64_t) tv.tv_nsec; 184 | } 185 | return core->time; 186 | } 187 | 188 | core_counters *core_get_counters(core *core) 189 | { 190 | return &core_get(core)->counters; 191 | } 192 | 193 | void core_clear_counters(core *core) 194 | { 195 | core_get(core)->counters = (core_counters) {0}; 196 | } 197 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/core.h: -------------------------------------------------------------------------------- 1 | #ifndef DYNAMIC_CORE_H_INCLUDED 2 | #define DYNAMIC_CORE_H_INCLUDED 3 | 4 | #define CORE_MAX_EVENTS 16 5 | 6 | enum core_status 7 | { 8 | CORE_OK = 0, 9 | CORE_ABORT = -1 10 | }; 11 | 12 | typedef uintptr_t core_id; 13 | typedef enum core_status core_status; 14 | typedef struct core_event core_event; 15 | typedef core_status core_callback(core_event *); 16 | typedef struct core_handler core_handler; 17 | typedef struct core_counters core_counters; 18 | typedef struct core core; 19 | 20 | struct core_event 21 | { 22 | void *state; 23 | int type; 24 | uintptr_t data; 25 | }; 26 | 27 | struct core_handler 28 | { 29 | core_callback *callback; 30 | void *state; 31 | }; 32 | 33 | struct core_counters 34 | { 35 | uint64_t awake; 36 | uint64_t sleep; 37 | uint64_t polls; 38 | uint64_t events; 39 | }; 40 | 41 | struct core 42 | { 43 | size_t ref; 44 | int fd; 45 | int active; 46 | size_t errors; 47 | vector handlers; 48 | size_t handlers_active; 49 | vector next; 50 | uint64_t time; 51 | core_counters counters; 52 | }; 53 | 54 | void core_construct(core *); 55 | void core_destruct(core *); 56 | void core_abort(core *); 57 | core_status core_dispatch(core_handler *, int, uintptr_t); 58 | void core_loop(core *); 59 | void core_add(core *, core_callback *, void *, int, int); 60 | void core_modify(core *, int, int); 61 | void core_delete(core *, int); 62 | core_id core_next(core *, core_callback *, void *); 63 | void core_cancel(core *, core_id); 64 | size_t core_errors(core *); 65 | uint64_t core_now(core *); 66 | core_counters *core_get_counters(core *); 67 | void core_clear_counters(core *); 68 | 69 | #endif /* DYNAMIC_CORE_H_INCLUDED */ 70 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define bswap32(x) __builtin_bswap32(x) 6 | #define bswap64(x) __builtin_bswap64(x) 7 | 8 | #ifdef FARMHASH_BIG_ENDIAN 9 | #define uint32_in_expected_order(x) bswap32(x) 10 | #define uint64_in_expected_order(x) bswap64(x) 11 | #else 12 | #define uint32_in_expected_order(x) (x) 13 | #define uint64_in_expected_order(x) (x) 14 | #endif 15 | 16 | // Some primes between 2^63 and 2^64 for various uses. 17 | 18 | static const uint64_t k0 = 0xc3a5c85c97cb3127ULL; 19 | static const uint64_t k1 = 0xb492b66fbe98f273ULL; 20 | static const uint64_t k2 = 0x9ae16a3b2f90404fULL; 21 | 22 | static inline uint32_t fetch32(const char *p) 23 | { 24 | uint32_t result; 25 | 26 | memcpy(&result, p, sizeof(result)); 27 | 28 | return uint32_in_expected_order(result); 29 | } 30 | 31 | static inline uint64_t fetch64(const char *p) 32 | { 33 | uint64_t result; 34 | 35 | memcpy(&result, p, sizeof(result)); 36 | 37 | return uint64_in_expected_order(result); 38 | } 39 | 40 | static inline uint64_t shift_mix(uint64_t v) 41 | { 42 | return v ^ (v >> 47); 43 | } 44 | 45 | static inline uint64_t rotate64(uint64_t v, int shift) 46 | { 47 | return ((v >> shift) | (v << (64 - shift))); 48 | } 49 | 50 | static inline uint64_t hash_len_16(uint64_t u, uint64_t v, uint64_t mul) 51 | { 52 | uint64_t a, b; 53 | 54 | a = (u ^ v) * mul; 55 | a ^= (a >> 47); 56 | b = (v ^ a) * mul; 57 | b ^= (b >> 47); 58 | b *= mul; 59 | 60 | return b; 61 | } 62 | 63 | static inline uint64_t hash_len_0_to_16(const char *s, size_t len) 64 | { 65 | if (len >= 8) 66 | { 67 | uint64_t mul = k2 + len * 2; 68 | uint64_t a = fetch64(s) + k2; 69 | uint64_t b = fetch64(s + len - 8); 70 | uint64_t c = rotate64(b, 37) * mul + a; 71 | uint64_t d = (rotate64(a, 25) + b) * mul; 72 | return hash_len_16(c, d, mul); 73 | } 74 | 75 | if (len >= 4) 76 | { 77 | uint64_t mul = k2 + len * 2; 78 | uint64_t a = fetch32(s); 79 | return hash_len_16(len + (a << 3), fetch32(s + len - 4), mul); 80 | } 81 | 82 | if (len > 0) 83 | { 84 | uint8_t a = s[0]; 85 | uint8_t b = s[len >> 1]; 86 | uint8_t c = s[len - 1]; 87 | uint32_t y = (uint32_t) a + ((uint32_t) b << 8); 88 | uint32_t z = len + ((uint32_t) c << 2); 89 | return shift_mix(y * k2 ^ z * k0) * k2; 90 | } 91 | 92 | return k2; 93 | } 94 | 95 | static inline uint64_t hash_len_17_to_32(const char *s, size_t len) 96 | { 97 | uint64_t mul = k2 + len * 2; 98 | uint64_t a = fetch64(s) * k1; 99 | uint64_t b = fetch64(s + 8); 100 | uint64_t c = fetch64(s + len - 8) * mul; 101 | uint64_t d = fetch64(s + len - 16) * k2; 102 | 103 | return hash_len_16(rotate64(a + b, 43) + rotate64(c, 30) + d, 104 | a + rotate64(b + k2, 18) + c, mul); 105 | } 106 | 107 | static inline uint64_t hash_len_33_to_64(const char *s, size_t len) 108 | { 109 | uint64_t mul = k2 + len * 2; 110 | uint64_t a = fetch64(s) * k2; 111 | uint64_t b = fetch64(s + 8); 112 | uint64_t c = fetch64(s + len - 8) * mul; 113 | uint64_t d = fetch64(s + len - 16) * k2; 114 | uint64_t y = rotate64(a + b, 43) + rotate64(c, 30) + d; 115 | uint64_t z = hash_len_16(y, a + rotate64(b + k2, 18) + c, mul); 116 | uint64_t e = fetch64(s + 16) * mul; 117 | uint64_t f = fetch64(s + 24); 118 | uint64_t g = (y + fetch64(s + len - 32)) * mul; 119 | uint64_t h = (z + fetch64(s + len - 24)) * mul; 120 | 121 | return hash_len_16(rotate64(e + f, 43) + rotate64(g, 30) + h, 122 | e + rotate64(f + a, 18) + g, mul); 123 | } 124 | 125 | #define swap(x, y) \ 126 | do \ 127 | { \ 128 | (x) = (x) ^ (y); \ 129 | (y) = (x) ^ (y); \ 130 | (x) = (x) ^ y; \ 131 | } while (0); 132 | 133 | typedef struct pair64 pair64; 134 | 135 | struct pair64 136 | { 137 | uint64_t first; 138 | uint64_t second; 139 | }; 140 | 141 | static inline pair64 weak_hash_len_32_with_seeds2(uint64_t w, uint64_t x, uint64_t y, 142 | uint64_t z, uint64_t a, uint64_t b) 143 | { 144 | uint64_t c; 145 | pair64 result; 146 | 147 | a += w; 148 | b = rotate64(b + a + z, 21); 149 | c = a; 150 | a += x; 151 | a += y; 152 | b += rotate64(a, 44); 153 | result.first = a + z; 154 | result.second = b + c; 155 | 156 | return result; 157 | } 158 | 159 | static inline pair64 weak_hash_len_32_with_seeds(const char *s, uint64_t a, uint64_t b) 160 | { 161 | return weak_hash_len_32_with_seeds2(fetch64(s), fetch64(s + 8), fetch64(s + 16), 162 | fetch64(s + 24), a, b); 163 | } 164 | 165 | static uint64_t cfarmhash(const char *s, size_t len) 166 | { 167 | uint64_t mul; 168 | const uint64_t seed = 81; 169 | 170 | if (len <= 16) 171 | return hash_len_0_to_16(s, len); 172 | 173 | if (len <= 32) 174 | return hash_len_17_to_32(s, len); 175 | 176 | if (len <= 64) 177 | return hash_len_33_to_64(s, len); 178 | 179 | uint64_t x = seed, y = seed * k1 + 113, z = shift_mix(y * k2 + 113) * k2; 180 | pair64 v = {0, 0}, w = {0, 0}; 181 | 182 | x = x * k2 + fetch64(s); 183 | 184 | const char *end = s + ((len - 1) / 64) * 64; 185 | const char *last64 = end + ((len - 1) & 63) - 63; 186 | 187 | do 188 | { 189 | x = rotate64(x + y + v.first + fetch64(s + 8), 37) * k1; 190 | y = rotate64(y + v.second + fetch64(s + 48), 42) * k1; 191 | x ^= w.second; 192 | y += v.first + fetch64(s + 40); 193 | z = rotate64(z + w.first, 33) * k1; 194 | v = weak_hash_len_32_with_seeds(s, v.second * k1, x + w.first); 195 | w = weak_hash_len_32_with_seeds(s + 32, z + w.second, y + fetch64(s + 16)); 196 | swap(z, x); 197 | s += 64; 198 | } while (s != end); 199 | 200 | mul = k1 + ((z & 0xff) << 1); 201 | s = last64; 202 | w.first += ((len - 1) & 63); 203 | v.first += w.first; 204 | w.first += v.first; 205 | x = rotate64(x + y + v.first + fetch64(s + 8), 37) * mul; 206 | y = rotate64(y + v.second + fetch64(s + 48), 42) * mul; 207 | x ^= w.second * 9; 208 | y += v.first * 9 + fetch64(s + 40); 209 | z = rotate64(z + w.first, 33) * mul; 210 | v = weak_hash_len_32_with_seeds(s, v.second * mul, x + w.first); 211 | w = weak_hash_len_32_with_seeds(s + 32, z + w.second, y + fetch64(s + 16)); 212 | swap(z, x); 213 | 214 | return hash_len_16(hash_len_16(v.first, w.first, mul) + shift_mix(y) * k0 + z, 215 | hash_len_16(v.second, w.second, mul) + x, 216 | mul); 217 | } 218 | 219 | uint64_t hash_data(void *data, size_t size) 220 | { 221 | return cfarmhash((const char *) data, size); 222 | } 223 | 224 | uint64_t hash_string(char *s) 225 | { 226 | return hash_data(s, strlen(s)); 227 | } 228 | 229 | uint64_t hash_uint64(uint64_t v) 230 | { 231 | v ^= v >> 33; 232 | v *= 0xff51afd7ed558ccdULL; 233 | v ^= v >> 33; 234 | v *= 0xc4ceb9fe1a85ec53ULL; 235 | v ^= v >> 33; 236 | return v; 237 | } 238 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/hash.h: -------------------------------------------------------------------------------- 1 | #ifndef DYNAMIC_HASH_HINCLUDED 2 | #define DYNAMIC_HASH_HINCLUDED 3 | 4 | uint64_t hash_string(char *); 5 | uint64_t hash_data(void *, size_t); 6 | uint64_t hash_uint64(uint64_t); 7 | 8 | #endif /* DYNAMIC_HASH_HINCLUDED */ 9 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "list.h" 8 | 9 | /* internals */ 10 | 11 | static list_item *list_object_item(void *object) 12 | { 13 | return (list_item *) ((uintptr_t) object - offsetof(list_item, object)); 14 | } 15 | 16 | static list_item *list_item_new(void *object, size_t size) 17 | { 18 | list_item *item; 19 | 20 | item = calloc(1, sizeof(list_item) + size); 21 | if (!item) 22 | abort(); 23 | 24 | if (object) 25 | memcpy(item->object, object, size); 26 | 27 | return item; 28 | } 29 | 30 | /* constructor/destructor */ 31 | 32 | void list_construct(list *l) 33 | { 34 | l->next = (list_item *) l; 35 | l->previous = (list_item *) l; 36 | } 37 | 38 | void list_destruct(list *l, list_release *release) 39 | { 40 | list_clear(l, release); 41 | } 42 | 43 | /* iterators */ 44 | 45 | void *list_next(void *object) 46 | { 47 | return list_object_item(object)->list.next->object; 48 | } 49 | 50 | void *list_previous(void *object) 51 | { 52 | return list_object_item(object)->list.previous->object; 53 | } 54 | 55 | /* capacity */ 56 | 57 | int list_empty(list *l) 58 | { 59 | return l->next == (list_item *) l; 60 | } 61 | 62 | /* element access */ 63 | 64 | void *list_front(list *l) 65 | { 66 | return l->next->object; 67 | } 68 | 69 | void *list_back(list *l) 70 | { 71 | return l->previous->object; 72 | } 73 | 74 | void *list_end(list *l) 75 | { 76 | return ((list_item *) l)->object; 77 | } 78 | 79 | /* modifiers */ 80 | 81 | void *list_push_front(list *l, void *object, size_t size) 82 | { 83 | return list_insert(list_front(l), object, size); 84 | } 85 | 86 | void *list_push_back(list *l, void *object, size_t size) 87 | { 88 | return list_insert(list_end(l), object, size); 89 | } 90 | 91 | void *list_insert(void *list_object, void *object, size_t size) 92 | { 93 | list_item *after, *item; 94 | 95 | after = list_object_item(list_object); 96 | 97 | item = list_item_new(object, size); 98 | item->list.previous = after->list.previous; 99 | item->list.next = after; 100 | item->list.previous->list.next = item; 101 | item->list.next->list.previous = item; 102 | 103 | return item->object; 104 | } 105 | 106 | void list_splice(void *object1, void *object2) 107 | { 108 | list_item *to, *from; 109 | 110 | if (object1 == object2) 111 | return; 112 | 113 | to = list_object_item(object1); 114 | from = list_object_item(object2); 115 | 116 | from->list.previous->list.next = from->list.next; 117 | from->list.next->list.previous = from->list.previous; 118 | 119 | from->list.previous = to->list.previous; 120 | from->list.next = to; 121 | from->list.previous->list.next = from; 122 | from->list.next->list.previous = from; 123 | } 124 | 125 | void list_erase(void *object, list_release *release) 126 | { 127 | list_item *item = list_object_item(object); 128 | 129 | item->list.previous->list.next = item->list.next; 130 | item->list.next->list.previous = item->list.previous; 131 | 132 | if (release) 133 | release(object); 134 | 135 | free(item); 136 | } 137 | 138 | void list_clear(list *l, list_release *release) 139 | { 140 | while (!list_empty(l)) 141 | list_erase(list_front(l), release); 142 | } 143 | 144 | /* operations */ 145 | 146 | void *list_find(list *l, list_compare *compare, void *object) 147 | { 148 | void *list_object; 149 | 150 | list_foreach(l, list_object) if (compare(object, list_object) == 0) return list_object; 151 | 152 | return NULL; 153 | } 154 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/list.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_H_INCLUDED 2 | #define LIST_H_INCLUDED 3 | 4 | #define list_foreach(l, o) for ((o) = list_front(l); (o) != list_end(l); (o) = list_next(o)) 5 | #define list_foreach_reverse(l, o) for ((o) = list_back(l); (o) != list_end(l); (o) = list_previous(o)) 6 | 7 | typedef void list_release(void *); 8 | typedef int list_compare(void *, void *); 9 | typedef struct list_item list_item; 10 | typedef struct list list; 11 | 12 | struct list 13 | { 14 | list_item *next; 15 | list_item *previous; 16 | }; 17 | 18 | struct list_item 19 | { 20 | list list; 21 | char object[]; 22 | }; 23 | 24 | /* constructor/destructor */ 25 | void list_construct(list *); 26 | void list_destruct(list *, list_release *); 27 | 28 | /* iterators */ 29 | void *list_next(void *); 30 | void *list_previous(void *); 31 | 32 | /* capacity */ 33 | int list_empty(list *); 34 | 35 | /* object access */ 36 | void *list_front(list *); 37 | void *list_back(list *); 38 | void *list_end(list *); 39 | 40 | /* modifiers */ 41 | void *list_push_front(list *, void *, size_t); 42 | void *list_push_back(list *, void *, size_t); 43 | void *list_insert(void *, void *, size_t); 44 | void list_splice(void *, void *); 45 | void list_erase(void *, list_release *); 46 | void list_clear(list *, list_release *); 47 | 48 | /* operations */ 49 | void *list_find(list *, list_compare *, void *); 50 | 51 | #endif /* LIST_H_INCLUDED */ 52 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/map.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "map.h" 7 | 8 | static size_t map_roundup(size_t s) 9 | { 10 | s--; 11 | s |= s >> 1; 12 | s |= s >> 2; 13 | s |= s >> 4; 14 | s |= s >> 8; 15 | s |= s >> 16; 16 | s |= s >> 32; 17 | s++; 18 | 19 | return s; 20 | } 21 | 22 | static void *map_element(map *m, size_t position) 23 | { 24 | return (char *) m->elements + (position * m->element_size); 25 | } 26 | 27 | static void map_release_all(map *m, map_equal *equal, map_release *release) 28 | { 29 | size_t i; 30 | 31 | if (release) 32 | for (i = 0; i < m->elements_capacity; i++) 33 | if (!equal(map_element(m, i), NULL)) 34 | release(map_element(m, i)); 35 | } 36 | 37 | static void map_rehash(map *m, size_t size, map_hash *hash, map_set *set, map_equal *equal) 38 | { 39 | map new; 40 | size_t i; 41 | 42 | size = map_roundup(size); 43 | new = *m; 44 | new.elements_count = 0; 45 | new.elements_capacity = size; 46 | new.elements = malloc(new.elements_capacity *new.element_size); 47 | if (!new.elements) 48 | abort(); 49 | 50 | for (i = 0; i < new.elements_capacity; i++) 51 | set(map_element(&new, i), NULL); 52 | 53 | if (m->elements) 54 | { 55 | for (i = 0; i < m->elements_capacity; i++) 56 | if (!equal(map_element(m, i), NULL)) 57 | map_insert(&new, map_element(m, i), hash, set, equal, NULL); 58 | free(m->elements); 59 | } 60 | 61 | *m = new; 62 | } 63 | 64 | /* constructor/destructor */ 65 | 66 | void map_construct(map *m, size_t element_size, map_set *set) 67 | { 68 | m->elements = NULL; 69 | m->elements_count = 0; 70 | m->elements_capacity = 0; 71 | m->element_size = element_size; 72 | map_rehash(m, MAP_ELEMENTS_CAPACITY_MIN, NULL, set, NULL); 73 | } 74 | 75 | void map_destruct(map *m, map_equal *equal, map_release *release) 76 | { 77 | map_release_all(m, equal, release); 78 | free(m->elements); 79 | } 80 | 81 | /* capacity */ 82 | 83 | size_t map_size(map *m) 84 | { 85 | return m->elements_count; 86 | } 87 | 88 | void map_reserve(map *m, size_t size, map_hash *hash, map_set *set, map_equal *equal) 89 | { 90 | size *= 2; 91 | if (size > m->elements_capacity) 92 | map_rehash(m, size, hash, set, equal); 93 | } 94 | 95 | /* element access */ 96 | 97 | void *map_at(map *m, void *element, map_hash *hash, map_equal *equal) 98 | { 99 | size_t i; 100 | void *test; 101 | 102 | i = hash(element); 103 | while (1) 104 | { 105 | i &= m->elements_capacity - 1; 106 | test = map_element(m, i); 107 | if (equal(test, NULL) || equal(test, element)) 108 | return test; 109 | i++; 110 | } 111 | } 112 | 113 | /* modifiers */ 114 | 115 | void map_insert(map *m, void *element, map_hash *hash, map_set *set, map_equal *equal, map_release *release) 116 | { 117 | void *test; 118 | 119 | map_reserve(m, m->elements_count + 1, hash, set, equal); 120 | test = map_at(m, element, hash, equal); 121 | if (equal(test, NULL)) 122 | { 123 | set(test, element); 124 | m->elements_count++; 125 | } 126 | else if (release) 127 | release(element); 128 | } 129 | 130 | void map_erase(map *m, void *element, map_hash *hash, map_set *set, map_equal *equal, map_release *release) 131 | { 132 | void *test; 133 | size_t i, j, k; 134 | 135 | i = hash(element); 136 | while (1) 137 | { 138 | i &= m->elements_capacity - 1; 139 | test = map_element(m, i); 140 | if (equal(test, NULL)) 141 | return; 142 | if (equal(test, element)) 143 | break; 144 | i++; 145 | } 146 | 147 | if (release) 148 | release(test); 149 | m->elements_count--; 150 | 151 | j = i; 152 | while (1) 153 | { 154 | j = (j + 1) & (m->elements_capacity - 1); 155 | if (equal(map_element(m, j), NULL)) 156 | break; 157 | 158 | k = hash(map_element(m, j)) & (m->elements_capacity - 1); 159 | if ((i < j && (k <= i || k > j)) || 160 | (i > j && (k <= i && k > j))) 161 | { 162 | set(map_element(m, i), map_element(m, j)); 163 | i = j; 164 | } 165 | } 166 | 167 | set(map_element(m, i), NULL); 168 | } 169 | 170 | void map_clear(map *m, map_set *set, map_equal *equal, map_release *release) 171 | { 172 | map_release_all(m, equal, release); 173 | free(m->elements); 174 | m->elements = NULL; 175 | m->elements_count = 0; 176 | m->elements_capacity = 0; 177 | map_rehash(m, MAP_ELEMENTS_CAPACITY_MIN, NULL, set, NULL); 178 | } 179 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/map.h: -------------------------------------------------------------------------------- 1 | #ifndef MAP_H_INCLUDED 2 | #define MAP_H_INCLUDED 3 | 4 | #ifndef MAP_ELEMENTS_CAPACITY_MIN 5 | #define MAP_ELEMENTS_CAPACITY_MIN 16 6 | #endif /* MAP_ELEMENTS_CAPACITY_MIN */ 7 | 8 | typedef size_t map_hash(void *); 9 | typedef int map_equal(void *, void *); 10 | typedef void map_set(void *, void *); 11 | typedef void map_release(void *); 12 | typedef struct map map; 13 | 14 | struct map 15 | { 16 | void *elements; 17 | size_t elements_count; 18 | size_t elements_capacity; 19 | size_t element_size; 20 | }; 21 | 22 | /* constructor/destructor */ 23 | void map_construct(map *, size_t, map_set *); 24 | void map_destruct(map *, map_equal *, map_release *); 25 | 26 | /* capacity */ 27 | size_t map_size(map *); 28 | void map_reserve(map *, size_t, map_hash *, map_set *, map_equal *); 29 | 30 | /* element access */ 31 | void *map_at(map *, void *, map_hash *, map_equal *); 32 | 33 | /* modifiers */ 34 | void map_insert(map *, void *, map_hash *, map_set *, map_equal *, map_release *); 35 | void map_erase(map *, void *, map_hash *, map_set *, map_equal *, map_release *); 36 | void map_clear(map *, map_set *, map_equal *, map_release *); 37 | 38 | #endif /* MAP_H_INCLUDED */ 39 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/mapi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "hash.h" 7 | #include "map.h" 8 | #include "mapi.h" 9 | 10 | static void set(void *p1, void *p2) 11 | { 12 | mapi_entry *a = p1, *b = p2; 13 | 14 | *a = b ? *b : (mapi_entry) {0}; 15 | } 16 | 17 | static int equal(void *p1, void *p2) 18 | { 19 | mapi_entry *a = p1, *b = p2; 20 | 21 | return b ? a->key == b->key : a->key == 0; 22 | } 23 | 24 | static size_t hash(void *p) 25 | { 26 | mapi_entry *a = p; 27 | 28 | return hash_uint64(a->key); 29 | } 30 | 31 | /* constructor/destructor */ 32 | 33 | void mapi_construct(mapi *mapi) 34 | { 35 | map_construct(&mapi->map, sizeof(mapi_entry), set); 36 | } 37 | 38 | void mapi_destruct(mapi *mapi, mapi_release *release) 39 | { 40 | map_destruct(&mapi->map, equal, (map_release *) release); 41 | } 42 | 43 | /* capacity */ 44 | 45 | size_t mapi_size(mapi *mapi) 46 | { 47 | return map_size(&mapi->map); 48 | } 49 | 50 | void mapi_reserve(mapi *mapi, size_t size) 51 | { 52 | map_reserve(&mapi->map, size, hash, set, equal); 53 | } 54 | 55 | /* element access */ 56 | 57 | uintptr_t mapi_at(mapi *mapi, uintptr_t key) 58 | { 59 | return ((mapi_entry *) map_at(&mapi->map, (mapi_entry[]) {{.key = key}}, hash, equal))->value; 60 | } 61 | 62 | /* modifiers */ 63 | 64 | void mapi_insert(mapi *mapi, uintptr_t key, uintptr_t value, mapi_release *release) 65 | { 66 | map_insert(&mapi->map, (mapi_entry[]) {{.key = key, .value = value}}, hash, set, equal, (map_release *) release); 67 | } 68 | 69 | void mapi_erase(mapi *mapi, uintptr_t key, mapi_release *release) 70 | { 71 | map_erase(&mapi->map, (mapi_entry[]) {{.key = key}}, hash, set, equal, (map_release *) release); 72 | } 73 | 74 | void mapi_clear(mapi *mapi, mapi_release *release) 75 | { 76 | map_clear(&mapi->map, set, equal, (map_release *) release); 77 | } 78 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/mapi.h: -------------------------------------------------------------------------------- 1 | #ifndef MAPI_H_INCLUDED 2 | #define MAPI_H_INCLUDED 3 | 4 | #define mapi_foreach(m, e) \ 5 | for ((e) = (m)->map.elements; (e) != ((mapi_entry *) (m)->map.elements) + (m)->map.elements_capacity; (e) ++) \ 6 | if ((e)->key) 7 | 8 | typedef struct mapi_entry mapi_entry; 9 | typedef struct mapi mapi; 10 | typedef void mapi_release(mapi_entry *); 11 | 12 | struct mapi_entry 13 | { 14 | uintptr_t key; 15 | uintptr_t value; 16 | }; 17 | 18 | struct mapi 19 | { 20 | map map; 21 | }; 22 | 23 | /* constructor/destructor */ 24 | void mapi_construct(mapi *); 25 | void mapi_destruct(mapi *, mapi_release *); 26 | 27 | /* capacity */ 28 | size_t mapi_size(mapi *); 29 | void mapi_reserve(mapi *, size_t); 30 | 31 | /* element access */ 32 | uintptr_t mapi_at(mapi *, uintptr_t); 33 | 34 | /* modifiers */ 35 | void mapi_insert(mapi *, uintptr_t, uintptr_t, mapi_release *); 36 | void mapi_erase(mapi *, uintptr_t, mapi_release *); 37 | void mapi_clear(mapi *, mapi_release *); 38 | 39 | #endif /* MAPI_H_INCLUDED */ 40 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/maps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "hash.h" 7 | #include "map.h" 8 | #include "maps.h" 9 | 10 | static void set(void *p1, void *p2) 11 | { 12 | maps_entry *a = p1, *b = p2; 13 | 14 | *a = b ? *b : (maps_entry) {0}; 15 | } 16 | 17 | static int equal(void *p1, void *p2) 18 | { 19 | maps_entry *a = p1, *b = p2; 20 | 21 | return b ? strcmp(a->key, b->key) == 0 : a->key == NULL; 22 | } 23 | 24 | static size_t hash(void *p) 25 | { 26 | maps_entry *a = p; 27 | 28 | return hash_string(a->key); 29 | } 30 | 31 | /* constructor/destructor */ 32 | 33 | void maps_construct(maps *maps) 34 | { 35 | map_construct(&maps->map, sizeof(maps_entry), set); 36 | } 37 | 38 | void maps_destruct(maps *maps, maps_release *release) 39 | { 40 | map_destruct(&maps->map, equal, (map_release *) release); 41 | } 42 | 43 | /* capacity */ 44 | 45 | size_t maps_size(maps *maps) 46 | { 47 | return map_size(&maps->map); 48 | } 49 | 50 | void maps_reserve(maps *maps, size_t size) 51 | { 52 | map_reserve(&maps->map, size, hash, set, equal); 53 | } 54 | 55 | /* element access */ 56 | 57 | uintptr_t maps_at(maps *maps, char *key) 58 | { 59 | return ((maps_entry *) map_at(&maps->map, (maps_entry[]) {{.key = key}}, hash, equal))->value; 60 | } 61 | 62 | /* modifiers */ 63 | 64 | void maps_insert(maps *maps, char *key, uintptr_t value, maps_release *release) 65 | { 66 | map_insert(&maps->map, (maps_entry[]) {{.key = key, .value = value}}, hash, set, equal, (map_release *) release); 67 | } 68 | 69 | void maps_erase(maps *maps, char *key, maps_release *release) 70 | { 71 | map_erase(&maps->map, (maps_entry[]) {{.key = key}}, hash, set, equal, (map_release *) release); 72 | } 73 | 74 | void maps_clear(maps *maps, maps_release *release) 75 | { 76 | map_clear(&maps->map, set, equal, (map_release *) release); 77 | } 78 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/maps.h: -------------------------------------------------------------------------------- 1 | #ifndef MAPS_H_INCLUDED 2 | #define MAPS_H_INCLUDED 3 | 4 | #define maps_foreach(m, e) \ 5 | for ((e) = (m)->map.elements; (e) != ((maps_entry *) (m)->map.elements) + (m)->map.elements_capacity; (e) ++) \ 6 | if ((e)->key) 7 | 8 | typedef struct maps_entry maps_entry; 9 | typedef struct maps maps; 10 | typedef void maps_release(maps_entry *); 11 | 12 | struct maps_entry 13 | { 14 | char *key; 15 | uintptr_t value; 16 | }; 17 | 18 | struct maps 19 | { 20 | map map; 21 | }; 22 | 23 | /* constructor/destructor */ 24 | void maps_construct(maps *); 25 | void maps_destruct(maps *, maps_release *); 26 | 27 | /* capacity */ 28 | size_t maps_size(maps *); 29 | void maps_reserve(maps *, size_t); 30 | 31 | /* element access */ 32 | uintptr_t maps_at(maps *, char *); 33 | 34 | /* modifiers */ 35 | void maps_insert(maps *, char *, uintptr_t, maps_release *); 36 | void maps_erase(maps *, char *, maps_release *); 37 | void maps_clear(maps *, maps_release *); 38 | 39 | #endif /* MAPS_H_INCLUDED */ 40 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/pool.h: -------------------------------------------------------------------------------- 1 | #ifndef DYNAMIC_POOL_H_INCLUDED 2 | #define DYNAMIC_POOL_H_INCLUDED 3 | 4 | #define POOL_WORKERS_MIN 0 5 | #define POOL_WORKERS_MAX 16 6 | 7 | enum 8 | { 9 | POOL_MESSAGE_JOB, 10 | POOL_MESSAGE_WORKER 11 | }; 12 | 13 | enum 14 | { 15 | POOL_REQUEST, 16 | POOL_REPLY 17 | }; 18 | 19 | typedef struct pool_worker pool_worker; 20 | typedef struct pool_message pool_message; 21 | typedef struct pool pool; 22 | 23 | struct pool_worker 24 | { 25 | pthread_t thread; 26 | int active; 27 | int socket; 28 | }; 29 | 30 | struct pool_message 31 | { 32 | size_t type; 33 | union 34 | { 35 | core_handler *user; 36 | pool_worker *worker; 37 | }; 38 | }; 39 | 40 | struct pool 41 | { 42 | core *core; 43 | size_t ref; 44 | size_t errors; 45 | int socket_master; 46 | int socket_worker; 47 | size_t workers_min; 48 | size_t workers_max; 49 | list jobs_waiting; 50 | list jobs_queued; 51 | size_t jobs_count; 52 | list workers; 53 | size_t workers_count; 54 | int active; 55 | }; 56 | 57 | void pool_construct(pool *, core *); 58 | void pool_destruct(pool *); 59 | void pool_limits(pool *, size_t, size_t); 60 | size_t pool_errors(pool *); 61 | core_id pool_enqueue(pool *, core_callback *, void *); 62 | void pool_cancel(pool *, core_id); 63 | void pool_abort(pool *); 64 | 65 | #endif /* DYNAMIC_POOL_H_INCLUDED */ 66 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/segment.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "dynamic.h" 7 | 8 | segment segment_data(void *base, size_t size) 9 | { 10 | return (segment) {.base = base, .size = size}; 11 | } 12 | 13 | segment segment_empty(void) 14 | { 15 | return segment_data(NULL, 0); 16 | } 17 | 18 | segment segment_string(char *string) 19 | { 20 | return segment_data(string, strlen(string)); 21 | } 22 | 23 | segment segment_offset(segment s, size_t offset) 24 | { 25 | return (segment) {.base = (char *) s.base + offset, s.size - offset}; 26 | } 27 | 28 | int segment_equal(segment s1, segment s2) 29 | { 30 | return s1.size == s2.size && memcmp(s1.base, s2.base, s1.size) == 0; 31 | } 32 | 33 | int segment_equal_case(segment s1, segment s2) 34 | { 35 | return s1.size == s2.size && strncasecmp(s1.base, s2.base, s1.size) == 0; 36 | } 37 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/segment.h: -------------------------------------------------------------------------------- 1 | #ifndef DYNAMIC_SEGMENT_H_INCLUDED 2 | #define DYNAMIC_SEGMENT_H_INCLUDED 3 | 4 | typedef struct segment segment; 5 | 6 | struct segment 7 | { 8 | void *base; 9 | size_t size; 10 | }; 11 | 12 | segment segment_empty(void); 13 | segment segment_data(void *, size_t); 14 | segment segment_string(char *); 15 | segment segment_offset(segment , size_t); 16 | int segment_equal(segment, segment); 17 | int segment_equal_case(segment, segment); 18 | 19 | #endif /* DYNAMIC_SEGMENT_H_INCLUDED */ 20 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "buffer.h" 7 | #include "vector.h" 8 | #include "string.h" 9 | 10 | /* constructor/destructor */ 11 | 12 | void string_construct(string *s) 13 | { 14 | buffer_construct(&s->buffer); 15 | buffer_insert(&s->buffer, 0, "", 1); 16 | } 17 | 18 | void string_destruct(string *s) 19 | { 20 | buffer_destruct(&s->buffer); 21 | } 22 | 23 | /* capacity */ 24 | 25 | size_t string_length(string *s) 26 | { 27 | return buffer_size(&s->buffer) - 1; 28 | } 29 | 30 | size_t string_capacity(string *s) 31 | { 32 | return buffer_capacity(&s->buffer) - 1; 33 | } 34 | 35 | void string_reserve(string *s, size_t size) 36 | { 37 | buffer_reserve(&s->buffer, size + 1); 38 | } 39 | 40 | int string_empty(string *s) 41 | { 42 | return string_length(s) == 0; 43 | } 44 | 45 | void string_shrink_to_fit(string *s) 46 | { 47 | buffer_compact(&s->buffer); 48 | } 49 | 50 | /* modifiers */ 51 | 52 | void string_insert(string *s, size_t pos, char *data) 53 | { 54 | string_insert_buffer(s, pos, data, strlen(data)); 55 | } 56 | 57 | void string_insert_buffer(string *s, size_t pos, char *data, size_t size) 58 | { 59 | buffer_insert(&s->buffer, pos, data, size); 60 | } 61 | 62 | void string_prepend(string *s, char *data) 63 | { 64 | buffer_insert(&s->buffer, 0, data, strlen(data)); 65 | } 66 | 67 | void string_append(string *s, char *data) 68 | { 69 | buffer_insert(&s->buffer, string_length(s), data, strlen(data)); 70 | } 71 | 72 | void string_erase(string *s, size_t pos, size_t size) 73 | { 74 | buffer_erase(&s->buffer, pos, size); 75 | } 76 | 77 | void string_replace(string *s, size_t pos, size_t size, char *data) 78 | { 79 | string_erase(s, pos, size); 80 | string_insert(s, pos, data); 81 | } 82 | 83 | void string_replace_all(string *s, char *find, char *sub) 84 | { 85 | ssize_t i; 86 | 87 | for (i = string_find(s, find, 0); i >= 0; i = string_find(s, find, i + strlen(sub))) 88 | string_replace(s, i, strlen(find), sub); 89 | } 90 | 91 | void string_clear(string *s) 92 | { 93 | buffer_clear(&s->buffer); 94 | string_construct(s); 95 | } 96 | 97 | void string_release(void *object) 98 | { 99 | string_destruct((string *) object); 100 | } 101 | 102 | /* string operations */ 103 | 104 | char *string_data(string *s) 105 | { 106 | return buffer_data(&s->buffer); 107 | } 108 | 109 | ssize_t string_find(string *s, char *data, size_t pos) 110 | { 111 | char *p; 112 | 113 | p = strstr(string_data(s) + pos, data); 114 | return p ? p - string_data(s) : -1; 115 | } 116 | 117 | int string_compare(string *s1, string *s2) 118 | { 119 | return strcmp(string_data(s1), string_data(s2)); 120 | } 121 | 122 | void string_split(string *s, char *delim, vector *v) 123 | { 124 | string copy, token; 125 | char *cp, *cp_saved; 126 | 127 | vector_construct(v, sizeof(string)); 128 | string_construct(©); 129 | string_append(©, string_data(s)); 130 | for (cp = strtok_r(string_data(©), delim, &cp_saved); cp; cp = strtok_r(NULL, delim, &cp_saved)) 131 | { 132 | string_construct(&token); 133 | string_append(&token, cp); 134 | vector_push_back(v, &token); 135 | } 136 | string_destruct(©); 137 | } 138 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H_INCLUDED 2 | #define STRING_H_INCLUDED 3 | 4 | typedef struct string string; 5 | 6 | struct string 7 | { 8 | buffer buffer; 9 | }; 10 | 11 | /* constructor/destructor */ 12 | void string_construct(string *); 13 | void string_destruct(string *); 14 | 15 | /* capacity */ 16 | size_t string_length(string *); 17 | size_t string_capacity(string *); 18 | void string_reserve(string *, size_t); 19 | int string_empty(string *); 20 | void string_shrink_to_fit(string *); 21 | 22 | /* modifiers */ 23 | void string_insert(string *, size_t, char *); 24 | void string_insert_buffer(string *, size_t, char *, size_t); 25 | void string_prepend(string *, char *); 26 | void string_append(string *, char *); 27 | void string_erase(string *, size_t, size_t); 28 | void string_replace(string *, size_t, size_t, char *); 29 | void string_replace_all(string *, char *, char *); 30 | void string_clear(string *); 31 | void string_release(void *); 32 | 33 | /* string operations */ 34 | char *string_data(string *); 35 | ssize_t string_find(string *, char *, size_t); 36 | int string_compare(string *, string *); 37 | void string_split(string *, char *, vector *); 38 | 39 | #endif /* STRING_H_INLCUDED */ 40 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/utility.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "dynamic.h" 6 | 7 | size_t utility_u32_len(uint32_t n) 8 | { 9 | static const uint32_t pow_10[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; 10 | uint32_t t; 11 | 12 | t = (32 - __builtin_clz(n | 1)) * 1233 >> 12; 13 | return t - (n < pow_10[t]) + 1; 14 | } 15 | 16 | void utility_u32_sprint(uint32_t n, char *string) 17 | { 18 | static const char digits[] = 19 | "0001020304050607080910111213141516171819" 20 | "2021222324252627282930313233343536373839" 21 | "4041424344454647484950515253545556575859" 22 | "6061626364656667686970717273747576777879" 23 | "8081828384858687888990919293949596979899"; 24 | size_t i; 25 | 26 | while (n >= 100) 27 | { 28 | i = (n % 100) << 1; 29 | n /= 100; 30 | *--string = digits[i + 1]; 31 | *--string = digits[i]; 32 | } 33 | 34 | if (n < 10) 35 | { 36 | *--string = n + '0'; 37 | } 38 | else 39 | { 40 | i = n << 1; 41 | *--string = digits[i + 1]; 42 | *--string = digits[i]; 43 | } 44 | } 45 | 46 | void utility_u32_toa(uint32_t n, char *string) 47 | { 48 | size_t length; 49 | 50 | length = utility_u32_len(n); 51 | string += length; 52 | *string = 0; 53 | utility_u32_sprint(n, string); 54 | } 55 | 56 | segment utility_u32_segment(uint32_t n) 57 | { 58 | static __thread char string[16]; 59 | size_t length; 60 | char *s = string; 61 | 62 | length = utility_u32_len(n); 63 | s += length; 64 | *s = 0; 65 | utility_u32_sprint(n, s); 66 | 67 | return (segment) {.base = string, .size = length}; 68 | } 69 | 70 | uint64_t utility_tsc(void) 71 | { 72 | #if defined(__x86_64__) || defined(__amd64__) 73 | uint32_t lo, hi; 74 | __asm__ volatile("RDTSC" 75 | : "=a"(lo), "=d"(hi)); 76 | return (((uint64_t) hi) << 32) | lo; 77 | #else 78 | return 0; 79 | #endif 80 | } 81 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/utility.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_H_INCLUDED 2 | #define UTILITY_H_INCLUDED 3 | 4 | size_t utility_u32_len(uint32_t); 5 | void utility_u32_sprint(uint32_t, char *); 6 | void utility_u32_toa(uint32_t, char *); 7 | segment utility_u32_segment(uint32_t); 8 | 9 | uint64_t utility_tsc(void); 10 | 11 | #endif /* UTILITY_H_INCLUDED */ 12 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/vector.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "buffer.h" 7 | #include "vector.h" 8 | 9 | /* constructor/destructor */ 10 | 11 | void vector_construct(vector *v, size_t object_size) 12 | { 13 | buffer_construct(&v->buffer); 14 | v->object_size = object_size; 15 | } 16 | 17 | void vector_destruct(vector *v, vector_release *release) 18 | { 19 | vector_clear(v, release); 20 | } 21 | 22 | /* capacity */ 23 | 24 | size_t vector_size(vector *v) 25 | { 26 | return buffer_size(&v->buffer) / v->object_size; 27 | } 28 | 29 | size_t vector_capacity(vector *v) 30 | { 31 | return buffer_capacity(&v->buffer) / v->object_size; 32 | } 33 | 34 | int vector_empty(vector *v) 35 | { 36 | return vector_size(v) == 0; 37 | } 38 | 39 | void vector_reserve(vector *v, size_t capacity) 40 | { 41 | buffer_reserve(&v->buffer, capacity * v->object_size); 42 | } 43 | 44 | void vector_shrink_to_fit(vector *v) 45 | { 46 | buffer_compact(&v->buffer); 47 | } 48 | 49 | /* element access */ 50 | 51 | void *vector_at(vector *v, size_t position) 52 | { 53 | return (char *) buffer_data(&v->buffer) + (position * v->object_size); 54 | } 55 | 56 | void *vector_front(vector *v) 57 | { 58 | return vector_data(v); 59 | } 60 | 61 | void *vector_back(vector *v) 62 | { 63 | return (char *) buffer_data(&v->buffer) + buffer_size(&v->buffer) - v->object_size; 64 | } 65 | 66 | void *vector_data(vector *v) 67 | { 68 | return buffer_data(&v->buffer); 69 | } 70 | 71 | /* modifiers */ 72 | 73 | void vector_insert(vector *v, size_t position, void *object) 74 | { 75 | buffer_insert(&v->buffer, position * v->object_size, object, v->object_size); 76 | } 77 | 78 | void vector_insert_range(vector *v, size_t position, void *first, void *last) 79 | { 80 | buffer_insert(&v->buffer, position * v->object_size, first, (char *) last - (char *) first); 81 | } 82 | 83 | void vector_insert_fill(vector *v, size_t position, size_t count, void *object) 84 | { 85 | buffer_insert_fill(&v->buffer, position * v->object_size, count, object, v->object_size); 86 | } 87 | 88 | void vector_erase(vector *v, size_t position, vector_release *release) 89 | { 90 | if (release) 91 | release(vector_at(v, position)); 92 | 93 | buffer_erase(&v->buffer, position * v->object_size, v->object_size); 94 | } 95 | 96 | void vector_erase_range(vector *v, size_t first, size_t last, vector_release *release) 97 | { 98 | size_t i; 99 | 100 | if (release) 101 | for (i = first; i < last; i++) 102 | release(vector_at(v, i)); 103 | 104 | buffer_erase(&v->buffer, first * v->object_size, (last - first) * v->object_size); 105 | } 106 | 107 | void vector_clear(vector *v, vector_release *release) 108 | { 109 | vector_erase_range(v, 0, vector_size(v), release); 110 | buffer_clear(&v->buffer); 111 | } 112 | 113 | void vector_push_back(vector *v, void *object) 114 | { 115 | buffer_insert(&v->buffer, buffer_size(&v->buffer), object, v->object_size); 116 | } 117 | 118 | void vector_pop_back(vector *v, vector_release *release) 119 | { 120 | vector_erase(v, vector_size(v) - 1, release); 121 | } 122 | -------------------------------------------------------------------------------- /lib/libdynamic/src/dynamic/vector.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_H_INCLUDED 2 | #define VECTOR_H_INCLUDED 3 | 4 | typedef struct vector vector; 5 | typedef void vector_release(void *); 6 | 7 | struct vector 8 | { 9 | buffer buffer; 10 | size_t object_size; 11 | }; 12 | 13 | /* constructor/destructor */ 14 | void vector_construct(vector *, size_t); 15 | void vector_destruct(vector *, vector_release); 16 | 17 | /* capacity */ 18 | size_t vector_size(vector *); 19 | size_t vector_capacity(vector *); 20 | int vector_empty(vector *); 21 | void vector_reserve(vector *, size_t); 22 | void vector_shrink_to_fit(vector *); 23 | 24 | /* element access */ 25 | void *vector_at(vector *, size_t); 26 | void *vector_front(vector *); 27 | void *vector_back(vector *); 28 | void *vector_data(vector *); 29 | 30 | /* modifiers */ 31 | void vector_insert(vector *, size_t, void *); 32 | void vector_insert_range(vector *, size_t, void *, void *); 33 | void vector_insert_fill(vector *, size_t, size_t, void *); 34 | void vector_erase(vector *, size_t, vector_release *); 35 | void vector_erase_range(vector *, size_t, size_t, vector_release *); 36 | void vector_push_back(vector *, void *); 37 | void vector_pop_back(vector *, vector_release *); 38 | void vector_clear(vector *, vector_release *); 39 | 40 | #endif /* VECTOR_H_INCLUDED */ 41 | -------------------------------------------------------------------------------- /lib/libdynamic/test/buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../src/dynamic/buffer.h" 11 | 12 | extern int debug_out_of_memory; 13 | extern int debug_abort; 14 | 15 | void core(__attribute__((unused)) void **state) 16 | { 17 | buffer b; 18 | 19 | buffer_construct(&b); 20 | assert_int_equal(buffer_size(&b), 0); 21 | assert_int_equal(buffer_capacity(&b), 0); 22 | 23 | buffer_resize(&b, 100); 24 | assert_int_equal(buffer_size(&b), 100); 25 | buffer_resize(&b, 0); 26 | assert_int_equal(buffer_size(&b), 0); 27 | 28 | buffer_reserve(&b, 0); 29 | buffer_reserve(&b, 1024); 30 | assert_int_equal(buffer_capacity(&b), 1024); 31 | 32 | buffer_compact(&b); 33 | buffer_compact(&b); 34 | assert_int_equal(buffer_capacity(&b), 0); 35 | 36 | buffer_insert(&b, 0, "last", 4); 37 | buffer_insert(&b, 0, "first", 5); 38 | buffer_insert(&b, buffer_size(&b), "", 1); 39 | assert_string_equal(buffer_data(&b), "firstlast"); 40 | 41 | buffer_erase(&b, 5, 4); 42 | assert_string_equal(buffer_data(&b), "first"); 43 | 44 | buffer_insert_fill(&b, 0, 5, "x", 1); 45 | assert_string_equal(buffer_data(&b), "xxxxxfirst"); 46 | buffer_insert_fill(&b, buffer_size(&b), 5, "x", 1); 47 | assert_string_equal(buffer_data(&b), "xxxxxfirst"); 48 | 49 | buffer_destruct(&b); 50 | } 51 | 52 | int main() 53 | { 54 | const struct CMUnitTest tests[] = { 55 | cmocka_unit_test(core) 56 | }; 57 | 58 | return cmocka_run_group_tests(tests, NULL, NULL); 59 | } 60 | -------------------------------------------------------------------------------- /lib/libdynamic/test/core.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "dynamic.h" 18 | 19 | extern int debug_epoll_create1; 20 | extern int debug_epoll_ctl; 21 | extern int debug_epoll_wait; 22 | 23 | static core_status next(core_event *event) 24 | { 25 | int *n = event->state; 26 | 27 | (*n)++; 28 | return CORE_OK; 29 | } 30 | 31 | static core_status in(core_event *event) 32 | { 33 | int *fd = event->state; 34 | 35 | core_delete(NULL, *fd); 36 | return CORE_ABORT; 37 | } 38 | 39 | static void basic(__attribute__((unused)) void **state) 40 | { 41 | core c = {0}; 42 | int n, id, e, p[2]; 43 | 44 | /* construct and destruct local instance */ 45 | core_construct(&c); 46 | core_loop(&c); 47 | core_destruct(&c); 48 | 49 | /* construct and destruct */ 50 | core_construct(NULL); 51 | core_construct(NULL); 52 | core_destruct(NULL); 53 | core_destruct(NULL); 54 | 55 | /* run empty core */ 56 | core_construct(NULL); 57 | core_loop(NULL); 58 | core_destruct(NULL); 59 | 60 | /* add, modify and remove fd */ 61 | core_construct(NULL); 62 | core_add(NULL, NULL, NULL, 0, 0); 63 | core_modify(NULL, 0, EPOLLIN); 64 | core_delete(NULL, 0); 65 | core_loop(NULL); 66 | core_destruct(NULL); 67 | 68 | /* poll event */ 69 | e = pipe(p); 70 | assert_int_equal(e, 0); 71 | e = write(p[1], ".", 1); 72 | assert_int_equal(e, 1); 73 | core_construct(NULL); 74 | core_add(NULL, in, &p[0], p[0], EPOLLIN); 75 | core_loop(NULL); 76 | core_destruct(NULL); 77 | 78 | /* next, cancel */ 79 | n = 0; 80 | core_construct(NULL); 81 | core_next(NULL, next, &n); 82 | id = core_next(NULL, next, &n); 83 | core_cancel(NULL, id); 84 | core_cancel(NULL, 0); 85 | core_loop(NULL); 86 | core_destruct(NULL); 87 | assert_int_equal(n, 1); 88 | 89 | /* time and counters */ 90 | core_construct(NULL); 91 | core_now(NULL); 92 | core_now(NULL); 93 | core_get_counters(NULL); 94 | core_clear_counters(NULL); 95 | core_destruct(NULL); 96 | 97 | /* abort */ 98 | core_construct(NULL); 99 | core_abort(NULL); 100 | core_loop(NULL); 101 | core_destruct(NULL); 102 | } 103 | 104 | static void error(__attribute__((unused)) void **state) 105 | { 106 | /* epoll_create1 error */ 107 | debug_epoll_create1 = 1; 108 | core_construct(NULL); 109 | assert_int_equal(core_errors(NULL), 1); 110 | core_destruct(NULL); 111 | assert_int_equal(core_errors(NULL), 0); 112 | debug_epoll_create1 = 0; 113 | 114 | /* epoll_ctl error */ 115 | debug_epoll_ctl = 1; 116 | core_construct(NULL); 117 | core_add(NULL, NULL, NULL, 0, 0); 118 | core_modify(NULL, 0, 0); 119 | core_delete(NULL, 0); 120 | assert_int_equal(core_errors(NULL), 3); 121 | core_destruct(NULL); 122 | debug_epoll_ctl = 0; 123 | 124 | /* epoll_wait */ 125 | debug_epoll_wait = 1; 126 | core_construct(NULL); 127 | core_add(NULL, NULL, NULL, 0, 0); 128 | core_loop(NULL); 129 | assert_int_equal(core_errors(NULL), 1); 130 | core_destruct(NULL); 131 | debug_epoll_wait = 0; 132 | } 133 | 134 | int main() 135 | { 136 | int e; 137 | 138 | const struct CMUnitTest tests[] = 139 | { 140 | cmocka_unit_test(basic), 141 | cmocka_unit_test(error)}; 142 | 143 | e = cmocka_run_group_tests(tests, NULL, NULL); 144 | (void) close(0); 145 | (void) close(1); 146 | (void) close(2); 147 | return e; 148 | } 149 | -------------------------------------------------------------------------------- /lib/libdynamic/test/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #for file in hash buffer list vector string maps mapi map pool 4 | for file in buffer core hash list map mapi maps pool string vector segment utility 5 | do 6 | echo [$file] 7 | test=`gcov -b src/dynamic/libdynamic_test_a-$file | grep -A4 File.*$file` 8 | echo "$test" 9 | echo "$test" | grep '% of' | grep '100.00%' >/dev/null || exit 1 10 | echo "$test" | grep '% of' | grep -v '100.00%' >/dev/null && exit 1 11 | done 12 | exit 0 13 | -------------------------------------------------------------------------------- /lib/libdynamic/test/hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../src/dynamic/hash.h" 11 | 12 | void core(__attribute__((unused)) void **state) 13 | { 14 | int i; 15 | struct 16 | { 17 | char *in; 18 | uint64_t out; 19 | } strings[] = 20 | { 21 | {"", 0x9ae16a3b2f90404f}, 22 | {"1", 0x811f023a122d0be1}, 23 | {"1234", 0xc3fa0b46e8adcae}, 24 | {"12345678", 0x2f99d2664a0fb6ea}, 25 | {"1234567890123456", 0xef3d9afd22778424}, 26 | {"12345678901234567890123456789012", 0xf8317d59683e31b1}, 27 | {"1234567890123456789012345678901234567890123456789012345678901234", 0x78a95a0d9788b255}, 28 | {"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012" 29 | "3456789012345678", 30 | 0x6085a2475352e7f9}, 31 | {"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012" 32 | "34567890123456789", 33 | 0xed4501398023b759}, 34 | {NULL, 0}}; 35 | struct 36 | { 37 | uint64_t in; 38 | uint64_t out; 39 | } integers[] = 40 | { 41 | {1ULL, 12994781566227106604ULL}, 42 | {12994781566227106604ULL, 9038243705893100514ULL}, 43 | {0, 0}}; 44 | 45 | for (i = 0; strings[i].in; i++) 46 | assert_true(hash_string(strings[i].in) == strings[i].out); 47 | 48 | for (i = 0; integers[i].in; i++) 49 | assert_true(hash_uint64(integers[i].in) == integers[i].out); 50 | } 51 | 52 | int main() 53 | { 54 | const struct CMUnitTest tests[] = { 55 | cmocka_unit_test(core)}; 56 | 57 | return cmocka_run_group_tests(tests, NULL, NULL); 58 | } 59 | -------------------------------------------------------------------------------- /lib/libdynamic/test/list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../src/dynamic/list.h" 11 | 12 | extern int debug_out_of_memory; 13 | extern int debug_abort; 14 | 15 | static void release(void *object) 16 | { 17 | free(*(char **) object); 18 | } 19 | 20 | static int compare(void *a, void *b) 21 | { 22 | char *sa = *(char **) a, *sb = *(char **) b; 23 | 24 | return strcmp(sa, sb); 25 | } 26 | 27 | void core(__attribute__((unused)) void **state) 28 | { 29 | list l; 30 | char *a[] = {"a", "list", "of", "string", "pointers"}, **s; 31 | size_t i; 32 | 33 | list_construct(&l); 34 | for (i = 0; i < sizeof a / sizeof *a; i++) 35 | list_push_back(&l, &a[i], sizeof(char *)); 36 | 37 | s = list_front(&l); 38 | assert_string_equal(*s, "a"); 39 | 40 | list_push_front(&l, (char *[]) {"test"}, sizeof(char *)); 41 | s = list_front(&l); 42 | assert_string_equal(*s, "test"); 43 | list_erase(s, NULL); 44 | 45 | i = 0; 46 | list_foreach(&l, s) 47 | assert_string_equal(*s, a[i++]); 48 | 49 | i = sizeof a / sizeof *a; 50 | list_foreach_reverse(&l, s) 51 | assert_string_equal(*s, a[--i]); 52 | 53 | s = list_find(&l, compare, (char *[]) {"pointers"}); 54 | assert_string_equal(*s, "pointers"); 55 | 56 | s = list_find(&l, compare, (char *[]) {"foo"}); 57 | assert_true(!s); 58 | 59 | list_destruct(&l, NULL); 60 | } 61 | 62 | void object_release(__attribute__((unused)) void **state) 63 | { 64 | list l; 65 | char *s; 66 | 67 | list_construct(&l); 68 | 69 | s = strdup("1"); 70 | list_push_back(&l, &s, sizeof s); 71 | s = strdup("2"); 72 | list_push_back(&l, &s, sizeof s); 73 | s = strdup("3"); 74 | list_push_back(&l, &s, sizeof s); 75 | 76 | list_destruct(&l, release); 77 | } 78 | 79 | void alloc(__attribute__((unused)) void **state) 80 | { 81 | list l; 82 | 83 | list_construct(&l); 84 | debug_out_of_memory = 1; 85 | debug_abort = 1; 86 | expect_assert_failure(list_insert(list_front(&l), (int[]) {1}, sizeof(int))); 87 | debug_abort = 0; 88 | debug_out_of_memory = 0; 89 | list_destruct(&l, NULL); 90 | } 91 | 92 | void unit(__attribute__((unused)) void **state) 93 | { 94 | list l, l2; 95 | int *p; 96 | 97 | list_construct(&l); 98 | 99 | list_insert(list_front(&l), (int[]) {1}, sizeof(int)); 100 | assert_int_equal(*(int *) list_front(&l), 1); 101 | list_clear(&l, NULL); 102 | 103 | p = list_insert(list_front(&l), NULL, sizeof(int)); 104 | *p = 42; 105 | assert_int_equal(*(int *) list_front(&l), 42); 106 | list_clear(&l, NULL); 107 | 108 | list_insert(list_previous(list_front(&l)), (int[]) {1}, sizeof(int)); 109 | assert_int_equal(*(int *) list_front(&l), 1); 110 | list_erase(list_back(&l), NULL); 111 | 112 | list_push_front(&l, (int[]) {1}, sizeof(int)); 113 | assert_int_equal(*(int *) list_front(&l), 1); 114 | list_erase(list_front(&l), NULL); 115 | 116 | list_push_back(&l, (int[]) {1}, sizeof(int)); 117 | assert_int_equal(*(int *) list_front(&l), 1); 118 | list_clear(&l, NULL); 119 | 120 | list_push_back(&l, (int[]) {1}, sizeof(int)); 121 | list_push_back(&l, (int[]) {2}, sizeof(int)); 122 | list_push_back(&l, (int[]) {3}, sizeof(int)); 123 | p = list_next(list_front(&l)); 124 | assert_int_equal(*p, 2); 125 | list_erase(p, NULL); 126 | p = list_next(list_front(&l)); 127 | assert_int_equal(*p, 3); 128 | 129 | list_clear(&l, NULL); 130 | list_construct(&l2); 131 | list_push_back(&l, (int[]) {1}, sizeof(int)); 132 | list_push_back(&l, (int[]) {2}, sizeof(int)); 133 | list_push_back(&l, (int[]) {3}, sizeof(int)); 134 | assert_true(list_empty(&l2)); 135 | list_splice(list_front(&l2), list_next(list_front(&l))); 136 | assert_int_equal(*(int *) list_front(&l2), 2); 137 | assert_int_equal(*(int *) list_next(list_front(&l)), 3); 138 | list_destruct(&l2, NULL); 139 | 140 | list_destruct(&l, NULL); 141 | } 142 | 143 | void edge(__attribute__((unused)) void **state) 144 | { 145 | list l1; 146 | int *i; 147 | 148 | list_construct(&l1); 149 | 150 | i = list_push_back(&l1, (int[]) {1}, sizeof(int)); 151 | list_splice(i, i); 152 | list_destruct(&l1, NULL); 153 | } 154 | 155 | int main() 156 | { 157 | const struct CMUnitTest tests[] = { 158 | cmocka_unit_test(core), 159 | cmocka_unit_test(object_release), 160 | cmocka_unit_test(alloc), 161 | cmocka_unit_test(unit), 162 | cmocka_unit_test(edge)}; 163 | 164 | return cmocka_run_group_tests(tests, NULL, NULL); 165 | } 166 | -------------------------------------------------------------------------------- /lib/libdynamic/test/map.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../src/dynamic/hash.h" 11 | #include "../src/dynamic/map.h" 12 | 13 | extern int debug_out_of_memory; 14 | extern int debug_abort; 15 | 16 | static void set(void *p1, void *p2) 17 | { 18 | uint32_t *a = p1, *b = p2; 19 | 20 | *a = b ? *b : 0; 21 | } 22 | 23 | static int equal(void *p1, void *p2) 24 | { 25 | uint32_t *a = p1, *b = p2; 26 | 27 | return b ? *a == *b : *a == 0; 28 | } 29 | 30 | static size_t hash(void *p) 31 | { 32 | return *(uint32_t *) p; 33 | } 34 | 35 | void erase(__attribute__((unused)) void **state) 36 | { 37 | map m; 38 | uint32_t last = MAP_ELEMENTS_CAPACITY_MIN - 1, coll1 = 1 << 16, coll2 = 1 << 17; 39 | 40 | map_construct(&m, sizeof(uint32_t), set); 41 | 42 | /* erase non-existing object */ 43 | map_erase(&m, (uint32_t[]) {42}, hash, set, equal, NULL); 44 | assert_int_equal(map_size(&m), 0); 45 | 46 | /* erase object with duplicate */ 47 | map_insert(&m, (uint32_t[]) {1}, hash, set, equal, NULL); 48 | map_insert(&m, (uint32_t[]) {1 + coll1}, hash, set, equal, NULL); 49 | map_erase(&m, (uint32_t[]) {1}, hash, set, equal, NULL); 50 | assert_int_equal(map_size(&m), 1); 51 | 52 | /* erase object with empty succ */ 53 | map_clear(&m, set, NULL, NULL); 54 | map_insert(&m, (uint32_t[]) {1}, hash, set, equal, NULL); 55 | map_erase(&m, (uint32_t[]) {5}, hash, set, equal, NULL); 56 | assert_false(equal(map_at(&m, (uint32_t[]) {1}, hash, equal), NULL)); 57 | 58 | /* erase when w wraps */ 59 | map_clear(&m, set, NULL, NULL); 60 | map_insert(&m, (uint32_t[]) {last + coll1}, hash, set, equal, NULL); 61 | map_insert(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 62 | map_insert(&m, (uint32_t[]) {last + coll2}, hash, set, equal, NULL); 63 | map_erase(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 64 | assert_false(equal(map_at(&m, (uint32_t[]) {last + coll1}, hash, equal), NULL)); 65 | assert_false(equal(map_at(&m, (uint32_t[]) {last + coll2}, hash, equal), NULL)); 66 | 67 | /* erase when i wraps */ 68 | map_clear(&m, set, NULL, NULL); 69 | map_insert(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 70 | map_erase(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 71 | assert_true(equal(map_at(&m, (uint32_t[]) {last}, hash, equal), NULL)); 72 | 73 | /* erase when i wraps and w < i */ 74 | map_clear(&m, set, NULL, NULL); 75 | map_insert(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 76 | map_insert(&m, (uint32_t[]) {coll1}, hash, set, equal, NULL); 77 | map_insert(&m, (uint32_t[]) {last + coll1}, hash, set, equal, NULL); 78 | map_erase(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 79 | assert_false(equal(map_at(&m, (uint32_t[]) {coll1}, hash, equal), NULL)); 80 | assert_false(equal(map_at(&m, (uint32_t[]) {last + coll1}, hash, equal), NULL)); 81 | assert_true(equal(map_at(&m, (uint32_t[]) {last}, hash, equal), NULL)); 82 | 83 | /* erase when i wraps and w > o */ 84 | map_clear(&m, set, NULL, NULL); 85 | map_insert(&m, (uint32_t[]) {last - 1}, hash, set, equal, NULL); 86 | map_insert(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 87 | map_insert(&m, (uint32_t[]) {last + coll1}, hash, set, equal, NULL); 88 | map_erase(&m, (uint32_t[]) {last - 1}, hash, set, equal, NULL); 89 | assert_false(equal(map_at(&m, (uint32_t[]) {last}, hash, equal), NULL)); 90 | assert_false(equal(map_at(&m, (uint32_t[]) {last + coll1}, hash, equal), NULL)); 91 | assert_true(equal(map_at(&m, (uint32_t[]) {last - 1}, hash, equal), NULL)); 92 | 93 | /* erase when j wraps */ 94 | map_clear(&m, set, NULL, NULL); 95 | map_insert(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 96 | map_insert(&m, (uint32_t[]) {coll1}, hash, set, equal, NULL); 97 | map_insert(&m, (uint32_t[]) {coll2}, hash, set, equal, NULL); 98 | map_erase(&m, (uint32_t[]) {last}, hash, set, equal, NULL); 99 | assert_false(equal(map_at(&m, (uint32_t[]) {coll1}, hash, equal), NULL)); 100 | assert_false(equal(map_at(&m, (uint32_t[]) {coll2}, hash, equal), NULL)); 101 | assert_true(equal(map_at(&m, (uint32_t[]) {last}, hash, equal), NULL)); 102 | 103 | map_destruct(&m, NULL, NULL); 104 | } 105 | 106 | int main() 107 | { 108 | const struct CMUnitTest tests[] = 109 | { 110 | cmocka_unit_test(erase)}; 111 | 112 | return cmocka_run_group_tests(tests, NULL, NULL); 113 | } 114 | -------------------------------------------------------------------------------- /lib/libdynamic/test/mapi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../src/dynamic/map.h" 11 | #include "../src/dynamic/mapi.h" 12 | 13 | void core(__attribute__((unused)) void **state) 14 | { 15 | mapi mapi; 16 | 17 | mapi_construct(&mapi); 18 | 19 | mapi_insert(&mapi, 1, 42, NULL); 20 | mapi_insert(&mapi, 1, 42, NULL); 21 | assert_true(mapi_at(&mapi, 1) == 42); 22 | assert_true(mapi_size(&mapi) == 1); 23 | mapi_erase(&mapi, 2, NULL); 24 | assert_true(mapi_size(&mapi) == 1); 25 | mapi_erase(&mapi, 1, NULL); 26 | assert_true(mapi_size(&mapi) == 0); 27 | 28 | mapi_reserve(&mapi, 32); 29 | assert_true(mapi.map.elements_capacity == 64); 30 | mapi_clear(&mapi, NULL); 31 | mapi_destruct(&mapi, NULL); 32 | } 33 | 34 | int main() 35 | { 36 | const struct CMUnitTest tests[] = 37 | { 38 | cmocka_unit_test(core)}; 39 | 40 | return cmocka_run_group_tests(tests, NULL, NULL); 41 | } 42 | -------------------------------------------------------------------------------- /lib/libdynamic/test/maps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../src/dynamic/map.h" 11 | #include "../src/dynamic/maps.h" 12 | 13 | extern int debug_out_of_memory; 14 | extern int debug_abort; 15 | 16 | void release(maps_entry *e) 17 | { 18 | free(e->key); 19 | } 20 | 21 | void core(__attribute__((unused)) void **state) 22 | { 23 | maps maps; 24 | int i; 25 | char key[16]; 26 | 27 | maps_construct(&maps); 28 | 29 | maps_insert(&maps, strdup("test"), 42, release); 30 | maps_insert(&maps, strdup("test"), 42, release); 31 | assert_true(maps_at(&maps, "test") == 42); 32 | assert_true(maps_size(&maps) == 1); 33 | maps_erase(&maps, "test2", release); 34 | assert_true(maps_size(&maps) == 1); 35 | maps_erase(&maps, "test", release); 36 | assert_true(maps_size(&maps) == 0); 37 | 38 | maps_reserve(&maps, 32); 39 | assert_true(maps.map.elements_capacity == 64); 40 | 41 | for (i = 0; i < 100; i++) 42 | { 43 | snprintf(key, sizeof key, "test%d", i); 44 | maps_insert(&maps, strdup(key), i, release); 45 | } 46 | maps_clear(&maps, release); 47 | maps_destruct(&maps, release); 48 | } 49 | 50 | void memory(__attribute__((unused)) void **state) 51 | { 52 | maps maps; 53 | 54 | debug_out_of_memory = 1; 55 | debug_abort = 1; 56 | expect_assert_failure(maps_construct(&maps)); 57 | debug_abort = 0; 58 | debug_out_of_memory = 0; 59 | } 60 | 61 | int main() 62 | { 63 | const struct CMUnitTest tests[] = 64 | { 65 | cmocka_unit_test(core), 66 | cmocka_unit_test(memory), 67 | }; 68 | 69 | return cmocka_run_group_tests(tests, NULL, NULL); 70 | } 71 | -------------------------------------------------------------------------------- /lib/libdynamic/test/mock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | int debug_out_of_memory = 0; 9 | int debug_abort = 0; 10 | int debug_recv = 0; 11 | int debug_send = 0; 12 | int debug_pthread_create = 0; 13 | int debug_socketpair = 0; 14 | int debug_epoll_create1 = 0; 15 | int debug_epoll_ctl = 0; 16 | int debug_epoll_wait = 0; 17 | 18 | void *__real_malloc(size_t); 19 | void *__wrap_malloc(size_t size) 20 | { 21 | return debug_out_of_memory ? NULL : __real_malloc(size); 22 | } 23 | 24 | void *__real_calloc(size_t, size_t); 25 | void *__wrap_calloc(size_t n, size_t size) 26 | { 27 | return debug_out_of_memory ? NULL : __real_calloc(n, size); 28 | } 29 | 30 | void *__real_realloc(void *, size_t); 31 | void *__wrap_realloc(void *p, size_t size) 32 | { 33 | return debug_out_of_memory ? NULL : __real_realloc(p, size); 34 | } 35 | 36 | void *__real_aligned_alloc(size_t, size_t); 37 | void *__wrap_aligned_alloc(size_t align, size_t size) 38 | { 39 | return debug_out_of_memory ? NULL : __real_aligned_alloc(align, size); 40 | } 41 | 42 | void __real_abort(void); 43 | void __wrap_abort(void) 44 | { 45 | if (debug_abort) 46 | mock_assert(0, "", "", 0); 47 | else 48 | __real_abort(); 49 | } 50 | 51 | ssize_t __real_recv(int, void *, size_t, int); 52 | ssize_t __wrap_recv(int socket, void *buffer, size_t length, int flags) 53 | { 54 | return debug_recv ? -1 : __real_recv(socket, buffer, length, flags); 55 | } 56 | 57 | ssize_t __real_send(int, const void *, size_t, int); 58 | ssize_t __wrap_send(int socket, const void *buffer, size_t length, int flags) 59 | { 60 | return debug_send ? -1 : __real_send(socket, buffer, length, flags); 61 | } 62 | 63 | int __real_pthread_create(pthread_t *, const pthread_attr_t *, void *(*) (void *), void *); 64 | int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) 65 | { 66 | return debug_pthread_create ? -1 : __real_pthread_create(thread, attr, start_routine, arg); 67 | } 68 | 69 | int __real_socketpair(int, int, int, int[2]); 70 | int __wrap_socketpair(int domain, int type, int protocol, int socket_vector[2]) 71 | { 72 | return debug_socketpair ? -1 : __real_socketpair(domain, type, protocol, socket_vector); 73 | } 74 | 75 | int __real_epoll_create1(int); 76 | int __wrap_epoll_create1(int flags) 77 | { 78 | return debug_epoll_create1 ? -1 : __real_epoll_create1(flags); 79 | } 80 | 81 | int __real_epoll_ctl(int, int, int, struct epoll_event *); 82 | int __wrap_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 83 | { 84 | return debug_epoll_ctl ? -1 : __real_epoll_ctl(epfd, op, fd, event); 85 | } 86 | 87 | int __real_epoll_wait(int, struct epoll_event *, int, int); 88 | int __wrap_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) 89 | { 90 | return debug_epoll_wait ? -1 : __real_epoll_wait(epfd, events, maxevents, timeout); 91 | } 92 | -------------------------------------------------------------------------------- /lib/libdynamic/test/pool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "dynamic.h" 16 | 17 | extern int debug_abort; 18 | extern int debug_recv; 19 | extern int debug_send; 20 | extern int debug_socketpair; 21 | 22 | static core_status job(core_event *event) 23 | { 24 | _Atomic int *state = event->state; 25 | 26 | if (event->type == POOL_REQUEST) 27 | (*state)++; 28 | return CORE_OK; 29 | } 30 | 31 | static core_status job_failsend(core_event *event) 32 | { 33 | _Atomic int *state = event->state; 34 | 35 | if (event->type == POOL_REQUEST) 36 | { 37 | debug_send = 1; 38 | (*state)++; 39 | } 40 | return CORE_OK; 41 | } 42 | 43 | static core_status cancel(core_event *event) 44 | { 45 | (void) event; 46 | core_abort(NULL); 47 | return CORE_OK; 48 | } 49 | 50 | static core_status broken_pipe(core_event *event) 51 | { 52 | pool *pool = event->state; 53 | 54 | close(pool->socket_worker); 55 | core_abort(NULL); 56 | return CORE_OK; 57 | } 58 | 59 | void basic(__attribute__((unused)) void **ununsed) 60 | { 61 | pool p = {0}; 62 | int i, state = 0; 63 | 64 | // create pool 65 | core_construct(NULL); 66 | 67 | // abort on non active pool 68 | pool_construct(&p, NULL); 69 | pool_abort(&p); 70 | pool_destruct(&p); 71 | 72 | // create default pool 73 | pool_construct(NULL, NULL); 74 | assert_int_equal(pool_errors(NULL), 0); 75 | pool_destruct(NULL); 76 | 77 | // create local pool 78 | pool_construct(&p, NULL); 79 | 80 | // reduce workers 81 | pool_limits(&p, 32, 32); 82 | pool_enqueue(&p, job, &state); 83 | pool_limits(&p, 1, 16); 84 | pool_enqueue(&p, job, &state); 85 | 86 | // process jobs 87 | for (i = 0; i < 100; i++) 88 | pool_enqueue(&p, job, &state); 89 | core_loop(NULL); 90 | assert_int_equal(state, 102); 91 | 92 | // create jobs with running core loop 93 | for (i = 0; i < 100; i++) 94 | pool_enqueue(&p, job, &state); 95 | assert_int_equal(pool_errors(&p), 0); 96 | 97 | // destroy pool 98 | pool_destruct(&p); 99 | 100 | // run core loop with pool removed 101 | core_loop(NULL); 102 | core_destruct(NULL); 103 | 104 | // abort 105 | signal(SIGTERM, SIG_IGN); 106 | core_construct(NULL); 107 | pool_construct(&p, NULL); 108 | pool_enqueue(&p, job, &state); 109 | core_loop(NULL); 110 | pool_abort(&p); 111 | pool_destruct(&p); 112 | core_destruct(NULL); 113 | } 114 | 115 | void fails(__attribute__((unused)) void **unused) 116 | { 117 | pool p = {0}; 118 | core_id id; 119 | int state = 0; 120 | 121 | core_construct(NULL); 122 | 123 | // send in thread 124 | pool_construct(&p, NULL); 125 | pool_enqueue(&p, job_failsend, &state); 126 | usleep(100000); 127 | pool_destruct(&p); 128 | debug_send = 0; 129 | assert_int_equal(state, 1); 130 | 131 | // recv in thread 132 | state = 0; 133 | debug_recv = 1; 134 | pool_construct(&p, NULL); 135 | pool_enqueue(&p, job, &state); 136 | usleep(100000); 137 | pool_destruct(&p); 138 | debug_recv = 0; 139 | assert_int_equal(state, 0); 140 | 141 | // send in main job 142 | pool_construct(&p, NULL); 143 | errno = 0; 144 | debug_send = 1; 145 | pool_enqueue(&p, job, &state); 146 | debug_send = 0; 147 | assert_int_equal(pool_errors(&p), 1); 148 | pool_destruct(&p); 149 | 150 | // send in main reduce 151 | pool_construct(&p, NULL); 152 | pool_limits(&p, 2, 2); 153 | pool_enqueue(&p, job, &state); 154 | pool_limits(&p, 0, 1); 155 | errno = 0; 156 | debug_send = 1; 157 | pool_enqueue(&p, job, &state); 158 | debug_send = 0; 159 | assert_int_equal(pool_errors(&p), 2); 160 | pool_destruct(&p); 161 | 162 | // cancel 163 | pool_construct(&p, NULL); 164 | id = pool_enqueue(&p, job, &state); 165 | pool_cancel(&p, id); 166 | core_loop(NULL); 167 | pool_destruct(&p); 168 | 169 | // receive in main 170 | pool_construct(&p, NULL); 171 | pool_enqueue(&p, job, &state); 172 | core_next(NULL, cancel, NULL); 173 | usleep(100000); 174 | debug_recv = 1; 175 | errno = 0; 176 | core_loop(NULL); 177 | debug_recv = 0; 178 | pool_destruct(&p); 179 | core_destruct(NULL); 180 | 181 | // receive events 182 | core_construct(NULL); 183 | pool_construct(&p, NULL); 184 | pool_enqueue(&p, job, &state); 185 | core_next(NULL, broken_pipe, &p); 186 | core_loop(NULL); 187 | pool_destruct(&p); 188 | core_destruct(NULL); 189 | assert_int_equal(pool_errors(&p), 1); 190 | 191 | // multiple refs 192 | pool_construct(&p, NULL); 193 | pool_construct(&p, NULL); 194 | pool_destruct(&p); 195 | pool_destruct(&p); 196 | 197 | // socketpair 198 | core_construct(NULL); 199 | debug_socketpair = 1; 200 | pool_construct(&p, NULL); 201 | pool_destruct(&p); 202 | debug_socketpair = 0; 203 | assert_int_equal(pool_errors(&p), 1); 204 | core_destruct(NULL); 205 | } 206 | 207 | int main() 208 | { 209 | const struct CMUnitTest tests[] = 210 | { 211 | cmocka_unit_test(basic), 212 | cmocka_unit_test(fails), 213 | }; 214 | 215 | return cmocka_run_group_tests(tests, NULL, NULL); 216 | } 217 | -------------------------------------------------------------------------------- /lib/libdynamic/test/segment.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "dynamic.h" 11 | 12 | void basic(__attribute__((unused)) void **state) 13 | { 14 | segment s1, s2; 15 | 16 | s1 = segment_string("test"); 17 | s2 = segment_data("atest", 5); 18 | 19 | assert_false(segment_equal(s1, segment_string("nope"))); 20 | assert_true(segment_equal(s1, segment_offset(s2, 1))); 21 | assert_false(segment_equal(s1, segment_empty())); 22 | assert_true(segment_equal_case(s1, segment_string("TeSt"))); 23 | assert_false(segment_equal_case(s1, segment_string("TeStA"))); 24 | assert_false(segment_equal_case(s1, segment_string("TeSA"))); 25 | } 26 | 27 | int main() 28 | { 29 | const struct CMUnitTest tests[] = { 30 | cmocka_unit_test(basic)}; 31 | 32 | return cmocka_run_group_tests(tests, NULL, NULL); 33 | } 34 | -------------------------------------------------------------------------------- /lib/libdynamic/test/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../src/dynamic/buffer.h" 11 | #include "../src/dynamic/vector.h" 12 | #include "../src/dynamic/string.h" 13 | 14 | void core(__attribute__((unused)) void **state) 15 | { 16 | string s, s2; 17 | vector v; 18 | ssize_t i; 19 | 20 | string_construct(&s); 21 | assert_int_equal(string_length(&s), 0); 22 | assert_int_equal(string_capacity(&s), 0); 23 | assert_true(string_empty(&s)); 24 | 25 | string_reserve(&s, 1023); 26 | assert_int_equal(string_capacity(&s), 1023); 27 | string_shrink_to_fit(&s); 28 | assert_int_equal(string_capacity(&s), 0); 29 | 30 | string_insert(&s, 0, "insert"); 31 | string_prepend(&s, "prepend"); 32 | string_append(&s, "append"); 33 | assert_string_equal(string_data(&s), "prependinsertappend"); 34 | 35 | i = string_find(&s, "insert", 0); 36 | assert_true(i >= 0); 37 | string_erase(&s, i, 6); 38 | assert_string_equal(string_data(&s), "prependappend"); 39 | string_replace_all(&s, "pend", "-"); 40 | 41 | string_construct(&s2); 42 | string_append(&s2, "pre-ap-"); 43 | assert_true(string_compare(&s, &s2) == 0); 44 | string_destruct(&s2); 45 | string_clear(&s); 46 | assert_true(string_empty(&s)); 47 | string_destruct(&s); 48 | 49 | string_construct(&s); 50 | string_append(&s, " some space delimited string "); 51 | string_split(&s, " ", &v); 52 | assert_int_equal(vector_size(&v), 4); 53 | assert_string_equal(string_data(vector_at(&v, 0)), "some"); 54 | assert_string_equal(string_data(vector_at(&v, 1)), "space"); 55 | assert_string_equal(string_data(vector_at(&v, 2)), "delimited"); 56 | assert_string_equal(string_data(vector_at(&v, 3)), "string"); 57 | vector_destruct(&v, string_release); 58 | string_destruct(&s); 59 | } 60 | 61 | int main() 62 | { 63 | const struct CMUnitTest tests[] = { 64 | cmocka_unit_test(core)}; 65 | 66 | return cmocka_run_group_tests(tests, NULL, NULL); 67 | } 68 | -------------------------------------------------------------------------------- /lib/libdynamic/test/utility.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "dynamic.h" 11 | 12 | void basic(__attribute__((unused)) void **state) 13 | { 14 | char s[16]; 15 | 16 | (void) utility_tsc(); 17 | 18 | assert_true(segment_equal(segment_string("1000"), utility_u32_segment(1000))); 19 | assert_true(segment_equal(segment_string("999"), utility_u32_segment(999))); 20 | assert_true(segment_equal(segment_string("101"), utility_u32_segment(101))); 21 | 22 | utility_u32_toa(100, s); 23 | assert_string_equal(s, "100"); 24 | } 25 | 26 | int main() 27 | { 28 | const struct CMUnitTest tests[] = { 29 | cmocka_unit_test(basic)}; 30 | 31 | return cmocka_run_group_tests(tests, NULL, NULL); 32 | } 33 | -------------------------------------------------------------------------------- /lib/libdynamic/test/valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if command -v valgrind; then 4 | for file in segment utility hash buffer list vector string maps mapi map pool 5 | do 6 | echo [$file] 7 | if ! valgrind --error-exitcode=1 --read-var-info=yes --leak-check=full --show-leak-kinds=all test/$file; then 8 | exit 1 9 | fi 10 | done 11 | fi 12 | -------------------------------------------------------------------------------- /lib/libdynamic/test/vector.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../src/dynamic/buffer.h" 11 | #include "../src/dynamic/vector.h" 12 | 13 | static void release(void *object) 14 | { 15 | free(*(char **) object); 16 | } 17 | 18 | void core(__attribute__((unused)) void **state) 19 | { 20 | vector v; 21 | char *a[] = {"a", "list", "of", "string", "pointers"}, *s; 22 | size_t a_len, i; 23 | 24 | vector_construct(&v, sizeof(char *)); 25 | assert_true(vector_empty(&v)); 26 | assert_int_equal(vector_size(&v), 0); 27 | assert_int_equal(vector_capacity(&v), 0); 28 | 29 | vector_reserve(&v, 1024); 30 | assert_int_equal(vector_capacity(&v), 1024); 31 | vector_shrink_to_fit(&v); 32 | assert_int_equal(vector_capacity(&v), 0); 33 | 34 | vector_insert_fill(&v, 0, 5, (char *[]) {"foo"}); 35 | for (i = 0; i < 5; i++) 36 | assert_string_equal("foo", *(char **) vector_at(&v, i)); 37 | vector_erase_range(&v, 0, 5, NULL); 38 | 39 | a_len = sizeof a / sizeof a[0]; 40 | vector_insert_range(&v, 0, &a[0], &a[a_len]); 41 | for (i = 0; i < a_len; i++) 42 | assert_string_equal(a[i], *(char **) vector_at(&v, i)); 43 | 44 | assert_string_equal(a[0], *(char **) vector_front(&v)); 45 | assert_string_equal(a[a_len - 1], *(char **) vector_back(&v)); 46 | vector_erase(&v, 0, NULL); 47 | assert_string_equal(a[1], *(char **) vector_front(&v)); 48 | 49 | vector_push_back(&v, (char *[]) {"pushed"}); 50 | assert_string_equal("pushed", *(char **) vector_back(&v)); 51 | vector_pop_back(&v, NULL); 52 | assert_string_equal(a[a_len - 1], *(char **) vector_back(&v)); 53 | vector_clear(&v, NULL); 54 | 55 | for (i = 0; i < a_len; i++) 56 | { 57 | s = strdup(a[i]); 58 | vector_insert(&v, i, &s); 59 | } 60 | for (i = 0; i < a_len; i++) 61 | assert_string_equal(a[i], *(char **) vector_at(&v, i)); 62 | vector_erase(&v, 0, release); 63 | assert_string_equal(a[1], *(char **) vector_front(&v)); 64 | 65 | vector_destruct(&v, release); 66 | } 67 | 68 | int main() 69 | { 70 | const struct CMUnitTest tests[] = { 71 | cmocka_unit_test(core)}; 72 | 73 | return cmocka_run_group_tests(tests, NULL, NULL); 74 | } 75 | -------------------------------------------------------------------------------- /lib/libreactor/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Microsoft 4 | IndentWidth: 2 5 | SpaceAfterCStyleCast: true 6 | SpaceBeforeCpp11BracedList: true 7 | ColumnLimit: 0 8 | SortIncludes: false 9 | ... 10 | -------------------------------------------------------------------------------- /lib/libreactor/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: bionic 3 | language: c 4 | compiler: 5 | - gcc 6 | install: 7 | - cd ${TRAVIS_BUILD_DIR} 8 | - sudo apt-get update -qq 9 | - sudo apt-get install software-properties-common 10 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 11 | - sudo apt-get update 12 | - sudo apt-get install gcc-6 13 | - sudo apt-get install libjansson-dev 14 | - sudo apt-get install -y -qq valgrind 15 | - export CC=gcc-6 16 | - export NM=gcc-nm-6 17 | - export RANLIB=gcc-ranlib-6 18 | - wget https://cmocka.org/files/1.1/cmocka-1.1.0.tar.xz 19 | - tar -xvf cmocka-1.1.0.tar.xz 20 | - cd cmocka-1.1.0 21 | - mkdir build 22 | - cd build 23 | - cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release .. 24 | - make 25 | - sudo make install 26 | - cd ${TRAVIS_BUILD_DIR} 27 | - wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz 28 | - tar xf lcov_1.11.orig.tar.gz 29 | - sudo make -C lcov-1.11/ install 30 | - gem install coveralls-lcov 31 | - cd ${TRAVIS_BUILD_DIR} 32 | - wget https://github.com/fredrikwidlund/libdynamic/releases/download/v1.3.0/libdynamic-1.3.0.tar.gz 33 | - tar -xvf libdynamic-1.3.0.tar.gz 34 | - cd libdynamic-1.3.0 35 | - ./configure 36 | - sudo make install 37 | 38 | branches: 39 | only: 40 | - master 41 | 42 | script: 43 | - cd ${TRAVIS_BUILD_DIR} 44 | - export CC=gcc-6 45 | - export NM=gcc-nm-6 46 | - export AR=gcc-ar-6 47 | - export RANLIB=gcc-ranlib-6 48 | - ./autogen.sh && ./configure && make check 49 | 50 | after_success: 51 | - cd ${TRAVIS_BUILD_DIR} 52 | - lcov --directory . --capture --output-file coverage.info # capture coverage info 53 | - lcov --remove coverage.info 'src/picohttpparser/*' 'test/*' '/usr/*' --output-file coverage.info # filter out system and test code 54 | - lcov --list coverage.info 55 | - coveralls-lcov --repo-token ${COVERALLS_TOKEN} coverage.info 56 | -------------------------------------------------------------------------------- /lib/libreactor/CHANGES: -------------------------------------------------------------------------------- 1 | Version 1.0 2 | =========== 3 | 4 | Released 2019-11-11 5 | 6 | * Initial release 7 | 8 | Version 2.0 9 | =========== 10 | 11 | Released 2021-01-30 12 | 13 | * In progress 14 | -------------------------------------------------------------------------------- /lib/libreactor/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:edge 2 | RUN apk add --update alpine-sdk linux-headers 3 | 4 | RUN wget https://github.com/fredrikwidlund/libdynamic/releases/download/v2.3.0/libdynamic-2.3.0.tar.gz && \ 5 | tar fvxz libdynamic-2.3.0.tar.gz && \ 6 | (cd libdynamic-2.3.0/ && ./configure && make install) 7 | 8 | RUN wget https://github.com/fredrikwidlund/libreactor/releases/download/v2.0.0-alpha/libreactor-2.0.0.tar.gz && \ 9 | tar fvxz libreactor-2.0.0.tar.gz && \ 10 | (cd libreactor-2.0.0; ./configure && make install) 11 | -------------------------------------------------------------------------------- /lib/libreactor/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Fredrik Widlund 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/libreactor/Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = subdir-objects 2 | ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 3 | AM_CFLAGS = -std=gnu11 -I$(srcdir)/src -fPIC 4 | AM_LDFLAGS = -static 5 | 6 | DIST_SUBDIRS = example 7 | EXTRA_DIST = test/data 8 | 9 | SOURCE_FILES = \ 10 | src/picohttpparser/picohttpparser.c \ 11 | src/reactor/reactor.c \ 12 | src/reactor/timer.c \ 13 | src/reactor/stream.c \ 14 | src/reactor/notify.c \ 15 | src/reactor/http.c \ 16 | src/reactor/server.c \ 17 | src/reactor/net.c 18 | 19 | HEADER_FILES = \ 20 | src/picohttpparser/picohttpparser.h \ 21 | src/reactor/reactor.h \ 22 | src/reactor/timer.h \ 23 | src/reactor/stream.h \ 24 | src/reactor/notify.h \ 25 | src/reactor/http.h \ 26 | src/reactor/server.h \ 27 | src/reactor/net.h 28 | 29 | lib_LTLIBRARIES = libreactor.la 30 | libreactor_la_SOURCES = $(SOURCE_FILES) $(HEADER_FILES) 31 | 32 | headerfilesdir = $(includedir)/reactor 33 | headerfiles_HEADERS = $(HEADER_FILES) 34 | 35 | mainheaderdir = $(includedir) 36 | mainheader_HEADERS = src/reactor.h 37 | 38 | pkgconfigdir = $(libdir)/pkgconfig 39 | pkgconfig_DATA = libreactor.pc 40 | 41 | MAINTAINERCLEANFILES = aclocal.m4 config.h.in configure Makefile.in libreactor-?.?.?.tar.gz 42 | 43 | maintainer-clean-local: 44 | rm -rf autotools m4 libreactor-?.?.? 45 | 46 | CLEANFILES = {.,test/,src/reactor/,src/picohttpparser}/*.{gcno,gcda,gcov} 47 | 48 | ### unit tests ### 49 | 50 | check_LIBRARIES = libreactor_test.a 51 | libreactor_test_a_CFLAGS = $(CHECK_CFLAGS) 52 | libreactor_test_a_SOURCES = $(SOURCE_FILES) $(HEADER_FILES) 53 | 54 | CHECK_CFLAGS = -std=gnu2x -O0 -g -ftest-coverage -fprofile-arcs -I$(srcdir)/src -DGCOV_BUILD 55 | CHECK_LDADD = -L. libreactor_test.a -lcmocka -ldynamic 56 | CHECK_LDFLAGS_EXTRA = -pthread \ 57 | -Wl,--wrap=abort \ 58 | -Wl,--wrap=read \ 59 | -Wl,--wrap=inotify_init1 60 | 61 | check_PROGRAMS = 62 | 63 | check_PROGRAMS += test/notify 64 | test_notify_CFLAGS = $(CHECK_CFLAGS) 65 | test_notify_LDADD = $(CHECK_LDADD) 66 | test_notify_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 67 | test_notify_SOURCES = test/notify.c test/mock.c 68 | 69 | check_PROGRAMS += test/stream 70 | test_stream_CFLAGS = $(CHECK_CFLAGS) 71 | test_stream_LDADD = $(CHECK_LDADD) 72 | test_stream_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 73 | test_stream_SOURCES = test/stream.c test/mock.c 74 | 75 | check_PROGRAMS += test/http 76 | test_http_CFLAGS = $(CHECK_CFLAGS) -DSRCDIR=\"$(srcdir)/\" 77 | test_http_LDADD = $(CHECK_LDADD) 78 | test_http_LDFLAGS = $(CHECK_LDFLAGS_EXTRA) 79 | test_http_SOURCES = test/http.c test/mock.c 80 | 81 | dist_noinst_SCRIPTS = test/valgrind.sh test/coverage.sh Dockerfile 82 | 83 | TESTS = $(check_PROGRAMS) test/valgrind.sh test/coverage.sh 84 | 85 | hello: 86 | docker build -t reactor-sdk . 87 | docker run --rm -v ${PWD}:/work reactor-sdk \ 88 | gcc -Wall -Wpedantic -march=native -flto -O3 -static /work/example/server.c -o /work/hello -lreactor -ldynamic && \ 89 | strip hello 90 | 91 | indent: 92 | clang-format -i src/reactor/*.c test/*.c example/*.c 93 | -------------------------------------------------------------------------------- /lib/libreactor/README.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | libreactor v2.0.0-alpha 3 | ======================= 4 | 5 | .. image:: https://travis-ci.org/fredrikwidlund/libreactor.svg?branch=master 6 | :target: https://travis-ci.org/fredrikwidlund/libreactor 7 | 8 | .. image:: https://coveralls.io/repos/github/fredrikwidlund/libreactor/badge.svg?branch=master 9 | :target: https://coveralls.io/github/fredrikwidlund/libreactor?branch=master 10 | 11 | ---------- 12 | Try it out 13 | ---------- 14 | 15 | The following will build a portable static web server ``hello`` (~60kB) 16 | 17 | .. code-block:: shell 18 | 19 | git clone -b release-2.0 https://github.com/fredrikwidlund/libreactor.git 20 | cd libreactor/ 21 | ./autogen.sh 22 | ./configure 23 | make hello 24 | 25 | Run it 26 | 27 | .. code-block:: shell 28 | 29 | ./hello 30 | 31 | In another shell try 32 | 33 | .. code-block:: shell 34 | 35 | wrk http://localhost 36 | 37 | .. _libdynamic: https://github.com/fredrikwidlund/libdynamic 38 | -------------------------------------------------------------------------------- /lib/libreactor/autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p autotools m4 4 | autoreconf --force --install 5 | -------------------------------------------------------------------------------- /lib/libreactor/configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([libreactor],[2.0.0],[fredrik.widlund@gmail.com]) 2 | AC_CONFIG_AUX_DIR(autotools) 3 | AC_CONFIG_MACRO_DIR([m4]) 4 | AM_INIT_AUTOMAKE([-Wall -Werror foreign no-define]) 5 | 6 | : ${CFLAGS="-g -O3 -Wall -Wextra -Wpedantic -flto -march=native"} 7 | 8 | AM_PROG_AR 9 | LT_INIT 10 | AM_PROG_CC_C_O 11 | 12 | AC_PREFIX_DEFAULT(/usr) 13 | AC_CONFIG_FILES([Makefile example/Makefile libreactor.pc]) 14 | AC_OUTPUT 15 | -------------------------------------------------------------------------------- /lib/libreactor/example/Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 2 | AM_CFLAGS = -std=gnu11 -I../src 3 | LDADD = -L.. -lreactor -ldynamic 4 | 5 | bin_PROGRAMS = \ 6 | timer \ 7 | stream \ 8 | notify \ 9 | httpd_lowlevel \ 10 | server \ 11 | server_single \ 12 | benchmark_http \ 13 | resolve_sync \ 14 | resolve_async 15 | 16 | resolve_async_LDFLAGS = -pthread 17 | 18 | MAINTAINERCLEANFILES = Makefile.in 19 | -------------------------------------------------------------------------------- /lib/libreactor/example/benchmark_http.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | char r[] = 11 | "GET /path HTTP/1.0\r\n" 12 | "Host: test.com\r\n" 13 | "User-Agent: benchmark\r\n" 14 | "Accept: */*\r\n" 15 | "\r\n"; 16 | 17 | int main() 18 | { 19 | char input[1024]; 20 | char output[1024]; 21 | http_request request; 22 | http_response response; 23 | uint64_t t1, t2; 24 | size_t i, n, iterations = 100000000, len; 25 | 26 | strcpy(input, r); 27 | len = strlen(input); 28 | 29 | t1 = utility_tsc(); 30 | for (i = 0; i < iterations; i++) 31 | { 32 | n = http_request_read(&request, (segment) {input, len}); 33 | assert(n == len); 34 | } 35 | t2 = utility_tsc(); 36 | (void) fprintf(stderr, "< %f\n", (double) (t2 - t1) / iterations); 37 | 38 | t1 = utility_tsc(); 39 | for (i = 0; i < iterations; i++) 40 | { 41 | http_response_ok(&response, segment_string("text/plain"), segment_string("Hello, World!")); 42 | n = http_response_size(&response); 43 | assert(n < sizeof output); 44 | http_response_write(&response, (segment) {output, n}); 45 | } 46 | t2 = utility_tsc(); 47 | (void) fprintf(stderr, "> %f\n", (double) (t2 - t1) / iterations); 48 | } 49 | -------------------------------------------------------------------------------- /lib/libreactor/example/httpd_lowlevel.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | static void plaintext(stream *client) 17 | { 18 | http_response response; 19 | http_response_ok(&response, segment_string("text/plain"), segment_string("Hello, World!")); 20 | http_response_write(&response, stream_allocate(client, http_response_size(&response))); 21 | } 22 | 23 | static void not_found(stream *client) 24 | { 25 | http_response response; 26 | http_response_not_found(&response); 27 | http_response_write(&response, stream_allocate(client, http_response_size(&response))); 28 | } 29 | 30 | static core_status client_event(core_event *event) 31 | { 32 | stream *client = (stream *) event->state; 33 | http_request request; 34 | segment s; 35 | size_t offset = 0; 36 | ssize_t n; 37 | 38 | switch (event->type) 39 | { 40 | case STREAM_READ: 41 | s = stream_read(client); 42 | do 43 | { 44 | n = http_request_read(&request, segment_offset(s, offset)); 45 | if (n <= 0) 46 | break; 47 | if (segment_equal(request.method, segment_string("GET")) && segment_equal(request.target, segment_string("/plaintext"))) 48 | plaintext(client); 49 | else 50 | not_found(client); 51 | offset += n; 52 | } while (offset < s.size); 53 | 54 | if (n == -1) 55 | { 56 | stream_destruct(client); 57 | free(client); 58 | return CORE_ABORT; 59 | } 60 | 61 | stream_flush(client); 62 | stream_consume(client, offset); 63 | return CORE_OK; 64 | case STREAM_FLUSH: 65 | return CORE_OK; 66 | default: 67 | stream_destruct(client); 68 | free(client); 69 | return CORE_ABORT; 70 | } 71 | } 72 | 73 | static core_status server_event(core_event *event) 74 | { 75 | stream *client; 76 | int *s = (int *) event->state, c; 77 | 78 | if (event->data != EPOLLIN) 79 | err(1, "server"); 80 | c = accept4(*s, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC); 81 | if (c == -1) 82 | err(1, "accept4"); 83 | 84 | client = malloc(sizeof *client); 85 | stream_construct(client, client_event, client); 86 | stream_open(client, c); 87 | return CORE_OK; 88 | } 89 | 90 | int main() 91 | { 92 | int s, e; 93 | 94 | reactor_construct(); 95 | 96 | s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); 97 | if (s == -1) 98 | err(1, "socket"); 99 | (void) setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (int[]) {1}, sizeof(int)); 100 | (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (int[]) {1}, sizeof(int)); 101 | 102 | e = bind(s, (struct sockaddr *) (struct sockaddr_in[]) {{.sin_family = AF_INET, .sin_port = htons(8080)}}, 103 | sizeof(struct sockaddr_in)); 104 | if (e == -1) 105 | err(1, "bind"); 106 | 107 | (void) listen(s, INT_MAX); 108 | core_add(NULL, server_event, &s, s, EPOLLIN); 109 | 110 | reactor_loop(); 111 | reactor_destruct(); 112 | } 113 | -------------------------------------------------------------------------------- /lib/libreactor/example/notify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | static core_status callback(core_event *event) 10 | { 11 | notify *notify = event->state; 12 | struct inotify_event *e = (struct inotify_event *) event->data; 13 | 14 | switch (event->type) 15 | { 16 | case NOTIFY_EVENT: 17 | (void) fprintf(stdout, "%04x %s\n", e->mask, e->len ? e->name : ""); 18 | return CORE_OK; 19 | default: 20 | notify_destruct(notify); 21 | return CORE_ABORT; 22 | } 23 | } 24 | 25 | int main(int argc, char **argv) 26 | { 27 | extern char *__progname; 28 | notify notify; 29 | 30 | if (argc != 2) 31 | { 32 | (void) fprintf(stderr, "usage: %s PATH\n", __progname); 33 | exit(1); 34 | } 35 | 36 | reactor_construct(); 37 | notify_construct(¬ify, callback, ¬ify); 38 | 39 | notify_watch(¬ify, argv[1], IN_ALL_EVENTS); 40 | reactor_loop(); 41 | 42 | notify_destruct(¬ify); 43 | reactor_destruct(); 44 | } 45 | -------------------------------------------------------------------------------- /lib/libreactor/example/resolve_async.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "reactor.h" 7 | 8 | static core_status resolve_handler(core_event *event) 9 | { 10 | net_resolve_job *job = (net_resolve_job *) event->data; 11 | char ni_host[NI_MAXHOST], ni_serv[NI_MAXSERV]; 12 | int e; 13 | 14 | if (job->addrinfo) 15 | { 16 | e = getnameinfo(job->addrinfo->ai_addr, job->addrinfo->ai_addrlen, ni_host, NI_MAXHOST, ni_serv, NI_MAXSERV, 17 | NI_NUMERICHOST | NI_NUMERICSERV); 18 | if (e != 0) 19 | err(1, "getnameinfo"); 20 | (void) fprintf(stdout, "%s:%s (family %d, type %d, flags 0x%04x) resolved to node %s service %s\n", 21 | job->host, job->serv, job->family, job->type, job->flags, ni_host, ni_serv); 22 | } 23 | else 24 | (void) fprintf(stdout, "%s:%s (family %d, type %d, flags 0x%04x) failed to resolv\n", 25 | job->host, job->serv, job->family, job->type, job->flags); 26 | 27 | freeaddrinfo(job->addrinfo); 28 | return CORE_OK; 29 | } 30 | 31 | int main() 32 | { 33 | reactor_construct(); 34 | net_resolve_async(NULL, resolve_handler, NULL, "127.0.0.1", "80", 0, 0, AI_NUMERICHOST | AI_NUMERICSERV); 35 | net_resolve_async(NULL, resolve_handler, NULL, "localhost", "http", 0, 0, AI_NUMERICHOST | AI_NUMERICSERV); 36 | net_resolve_async(NULL, resolve_handler, NULL, "localhost", "http", 0, 0, 0); 37 | net_resolve_async(NULL, resolve_handler, NULL, "localhost", "http", AF_INET, 0, 0); 38 | net_resolve_async(NULL, resolve_handler, NULL, "ipv6.google.com", "https", 0, 0, 0); 39 | net_resolve_async(NULL, resolve_handler, NULL, "does.not.exist", "https", 0, 0, 0); 40 | net_resolve_async(NULL, resolve_handler, NULL, "does.not.exist", "xxx", -1, -1, -1); 41 | reactor_loop(); 42 | reactor_destruct(); 43 | } 44 | -------------------------------------------------------------------------------- /lib/libreactor/example/resolve_sync.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "reactor.h" 7 | 8 | static void resolve(char *ai_host, char *ai_serv, int family, int type, int flags) 9 | { 10 | struct addrinfo *ai; 11 | char ni_host[NI_MAXHOST], ni_serv[NI_MAXSERV]; 12 | int e; 13 | 14 | net_resolve(ai_host, ai_serv, family, type, flags, &ai); 15 | if (ai) 16 | { 17 | e = getnameinfo(ai->ai_addr, ai->ai_addrlen, ni_host, NI_MAXHOST, ni_serv, NI_MAXSERV, 18 | NI_NUMERICHOST | NI_NUMERICSERV); 19 | if (e != 0) 20 | err(1, "getnameinfo"); 21 | (void) fprintf(stdout, "%s:%s (family %d, type %d, flags 0x%04x) resolved to node %s service %s\n", 22 | ai_host, ai_serv, family, type, flags, ni_host, ni_serv); 23 | } 24 | else 25 | (void) fprintf(stdout, "%s:%s (family %d, type %d, flags 0x%04x) failed to resolv\n", 26 | ai_host, ai_serv, family, type, flags); 27 | freeaddrinfo(ai); 28 | } 29 | 30 | int main() 31 | { 32 | resolve("127.0.0.1", "80", 0, 0, AI_NUMERICHOST | AI_NUMERICSERV); 33 | resolve("localhost", "http", 0, 0, AI_NUMERICHOST | AI_NUMERICSERV); 34 | resolve("localhost", "http", 0, 0, 0); 35 | resolve("localhost", "http", AF_INET, 0, 0); 36 | resolve("ipv6.google.com", "https", 0, 0, 0); 37 | resolve("does.not.exist", "https", 0, 0, 0); 38 | resolve("does.not.exist", "xxx", -1, -1, -1); 39 | } 40 | -------------------------------------------------------------------------------- /lib/libreactor/example/server.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "reactor.h" 9 | 10 | typedef struct hello hello; 11 | struct hello 12 | { 13 | int instance; 14 | server server; 15 | timer timer; 16 | size_t requests; 17 | }; 18 | 19 | static core_status request(core_event *event) 20 | { 21 | hello *hello = event->state; 22 | 23 | if (event->type != SERVER_REQUEST) 24 | err(1, "server"); 25 | 26 | server_ok((server_context *) event->data, segment_string("text/plain"), segment_string("Hello, World!")); 27 | hello->requests++; 28 | return CORE_OK; 29 | } 30 | 31 | static core_status timeout(core_event *event) 32 | { 33 | hello *hello = event->state; 34 | core_counters *counters; 35 | 36 | if (event->type != TIMER_ALARM) 37 | err(1, "timer"); 38 | 39 | counters = core_get_counters(NULL); 40 | (void) fprintf(stderr, "[hello %d/%d] requests %lu, usage %.02f%%, frequency %.02fGHz\n", hello->instance, sched_getcpu(), 41 | hello->requests, 100. * (double) counters->awake / (double) (counters->awake + counters->sleep), 42 | (double) (counters->awake + counters->sleep) / 1000000000.0); 43 | core_clear_counters(NULL); 44 | hello->requests = 0; 45 | return CORE_OK; 46 | } 47 | 48 | int main() 49 | { 50 | hello hello = {0}; 51 | struct addrinfo *ai; 52 | int i, n = get_nprocs(), fd[n]; 53 | 54 | net_resolve(NULL, "80", AF_INET, SOCK_STREAM, AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE, &ai); 55 | 56 | for (i = 0; i < n; i++) 57 | fd[i] = net_server(ai, 0); 58 | net_server_filter(fd[0], n); 59 | 60 | hello.instance = reactor_clone(n); 61 | reactor_affinity(hello.instance); 62 | reactor_construct(); 63 | 64 | server_construct(&hello.server, request, &hello); 65 | server_open(&hello.server, fd[hello.instance]); 66 | timer_construct(&hello.timer, timeout, &hello); 67 | timer_set(&hello.timer, 1000000000, 1000000000); 68 | 69 | reactor_loop(); 70 | 71 | timer_destruct(&hello.timer); 72 | server_destruct(&hello.server); 73 | reactor_destruct(); 74 | freeaddrinfo(ai); 75 | } 76 | -------------------------------------------------------------------------------- /lib/libreactor/example/server_single.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "reactor.h" 5 | 6 | typedef struct hello hello; 7 | struct hello 8 | { 9 | server server; 10 | timer timer; 11 | size_t requests; 12 | }; 13 | 14 | static core_status request(core_event *event) 15 | { 16 | hello *hello = event->state; 17 | 18 | if (event->type != SERVER_REQUEST) 19 | err(1, "server"); 20 | 21 | server_ok((server_context *) event->data, segment_string("text/plain"), segment_string("Hello, World!")); 22 | hello->requests++; 23 | return CORE_OK; 24 | } 25 | 26 | static core_status timeout(core_event *event) 27 | { 28 | hello *hello = event->state; 29 | core_counters *counters; 30 | 31 | if (event->type != TIMER_ALARM) 32 | err(1, "timer"); 33 | 34 | counters = core_get_counters(NULL); 35 | (void) fprintf(stderr, "[hello] requests %lu, usage %.02f%%, frequency %.02fGHz\n", 36 | hello->requests, 100. * (double) counters->awake / (double) (counters->awake + counters->sleep), 37 | (double) (counters->awake + counters->sleep) / 1000000000.0); 38 | core_clear_counters(NULL); 39 | hello->requests = 0; 40 | return CORE_OK; 41 | } 42 | 43 | int main() 44 | { 45 | hello hello = {0}; 46 | struct addrinfo *ai; 47 | 48 | net_resolve("127.0.0.1", "80", AF_INET, SOCK_STREAM, AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE, &ai); 49 | reactor_construct(); 50 | 51 | server_construct(&hello.server, request, &hello); 52 | server_open(&hello.server, net_server(ai, 0)); 53 | 54 | timer_construct(&hello.timer, timeout, &hello); 55 | timer_set(&hello.timer, 1000000000, 1000000000); 56 | 57 | reactor_loop(); 58 | 59 | timer_destruct(&hello.timer); 60 | server_destruct(&hello.server); 61 | reactor_destruct(); 62 | freeaddrinfo(ai); 63 | } 64 | -------------------------------------------------------------------------------- /lib/libreactor/example/stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | void process(stream *stream) 10 | { 11 | segment line; 12 | 13 | while (1) 14 | { 15 | line = stream_read_line(stream); 16 | if (!line.size) 17 | break; 18 | (void) printf("%.*s", (int) line.size, (char *) line.base); 19 | stream_consume(stream, line.size); 20 | } 21 | } 22 | 23 | static core_status callback(core_event *event) 24 | { 25 | switch (event->type) 26 | { 27 | case STREAM_READ: 28 | process(event->state); 29 | return CORE_OK; 30 | default: 31 | process(event->state); 32 | stream_destruct(event->state); 33 | return CORE_ABORT; 34 | } 35 | } 36 | 37 | int main() 38 | { 39 | stream stream; 40 | 41 | (void) fcntl(0, F_SETFL, O_NONBLOCK); 42 | 43 | reactor_construct(); 44 | stream_construct(&stream, callback, &stream); 45 | stream_open(&stream, 0); 46 | 47 | reactor_loop(); 48 | 49 | stream_destruct(&stream); 50 | reactor_destruct(); 51 | fflush(stdout); 52 | } 53 | -------------------------------------------------------------------------------- /lib/libreactor/example/timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | struct state 10 | { 11 | timer timer; 12 | int count; 13 | uint64_t tsc; 14 | }; 15 | 16 | static core_status callback(core_event *event) 17 | { 18 | struct state *state = event->state; 19 | int64_t t; 20 | 21 | switch (event->type) 22 | { 23 | case TIMER_ALARM: 24 | if (state->tsc == 0) 25 | state->tsc = utility_tsc(); 26 | else 27 | { 28 | t = utility_tsc(); 29 | fprintf(stderr, "%.02fGhz\n", (double) (t - state->tsc) / 1000000000.); 30 | state->tsc = t; 31 | state->count--; 32 | } 33 | 34 | if (state->count) 35 | return CORE_OK; 36 | /* fall through */ 37 | default: 38 | timer_destruct(&state->timer); 39 | return CORE_ABORT; 40 | } 41 | } 42 | 43 | int main() 44 | { 45 | struct state state = {.count = 10}; 46 | 47 | reactor_construct(); 48 | timer_construct(&state.timer, callback, &state); 49 | timer_set(&state.timer, 0, 1000000000); 50 | 51 | reactor_loop(); 52 | 53 | timer_destruct(&state.timer); 54 | reactor_destruct(); 55 | } 56 | -------------------------------------------------------------------------------- /lib/libreactor/libreactor.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=${prefix}/include 5 | 6 | Name: libreactor 7 | Description: Extendable event driven high performance C-abstractions 8 | Version: @VERSION@ 9 | Libs: -L${libdir} -lreactor 10 | Cflags: -I${includedir} -flto 11 | -------------------------------------------------------------------------------- /lib/libreactor/src/picohttpparser/picohttpparser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, 3 | * Shigeo Mitsunari 4 | * 5 | * The software is licensed under either the MIT License (below) or the Perl 6 | * license. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef picohttpparser_h 28 | #define picohttpparser_h 29 | 30 | #include 31 | 32 | #ifdef _MSC_VER 33 | #define ssize_t intptr_t 34 | #endif 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | /* contains name and value of a header (name == NULL if is a continuing line 41 | * of a multiline header */ 42 | struct phr_header { 43 | const char *name; 44 | size_t name_len; 45 | const char *value; 46 | size_t value_len; 47 | }; 48 | 49 | /* returns number of bytes consumed if successful, -2 if request is partial, 50 | * -1 if failed */ 51 | int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, 52 | int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); 53 | 54 | /* ditto */ 55 | int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, 56 | struct phr_header *headers, size_t *num_headers, size_t last_len); 57 | 58 | /* ditto */ 59 | int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); 60 | 61 | /* should be zero-filled before start */ 62 | struct phr_chunked_decoder { 63 | size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ 64 | char consume_trailer; /* if trailing headers should be consumed */ 65 | char _hex_count; 66 | char _state; 67 | }; 68 | 69 | /* the function rewrites the buffer given as (buf, bufsz) removing the chunked- 70 | * encoding headers. When the function returns without an error, bufsz is 71 | * updated to the length of the decoded data available. Applications should 72 | * repeatedly call the function while it returns -2 (incomplete) every time 73 | * supplying newly arrived data. If the end of the chunked-encoded data is 74 | * found, the function returns a non-negative number indicating the number of 75 | * octets left undecoded at the tail of the supplied buffer. Returns -1 on 76 | * error. 77 | */ 78 | ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz); 79 | 80 | /* returns if the chunked decoder is in middle of chunked data */ 81 | int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_H_INCLUDED 2 | #define REACTOR_H_INCLUDED 3 | 4 | #define REACTOR_VERSION "2.0.0-alpha" 5 | #define REACTOR_VERSION_MAJOR 2 6 | #define REACTOR_VERSION_MINOR 0 7 | #define REACTOR_VERSION_PATCH 0 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "reactor/reactor.h" 19 | #include "reactor/timer.h" 20 | #include "reactor/notify.h" 21 | #include "reactor/stream.h" 22 | #include "reactor/http.h" 23 | #include "reactor/server.h" 24 | #include "reactor/net.h" 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif /* REACTOR_H_INCLUDED */ 31 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/http.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_HTTP_H_INCLUDED 2 | #define REACTOR_HTTP_H_INCLUDED 3 | 4 | #define HTTP_MAX_HEADERS 16 5 | 6 | typedef struct http_header http_header; 7 | typedef struct http_headers http_headers; 8 | typedef struct http_request http_request; 9 | typedef struct http_response http_response; 10 | 11 | struct http_header 12 | { 13 | segment name; 14 | segment value; 15 | }; 16 | 17 | struct http_headers 18 | { 19 | size_t count; 20 | http_header header[HTTP_MAX_HEADERS]; 21 | }; 22 | 23 | struct http_request 24 | { 25 | segment method; 26 | segment target; 27 | int version; 28 | segment body; 29 | http_headers headers; 30 | }; 31 | 32 | struct http_response 33 | { 34 | int version; 35 | int code; 36 | segment reason; 37 | segment body; 38 | http_headers headers; 39 | }; 40 | 41 | void http_headers_construct(http_headers *); 42 | size_t http_headers_count(http_headers *); 43 | void http_headers_add(http_headers *, segment, segment); 44 | segment http_headers_lookup(http_headers *, segment); 45 | 46 | ssize_t http_request_read(http_request *, segment); 47 | 48 | ssize_t http_response_read(http_response *, segment); 49 | size_t http_response_size(http_response *); 50 | void http_response_write(http_response *, segment); 51 | void http_response_construct(http_response *, int, int, segment, segment, segment); 52 | void http_response_not_found(http_response *); 53 | void http_response_ok(http_response *, segment, segment); 54 | 55 | #endif /* REACTOR_HTTP_H_INCLUDED */ 56 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/net.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "net.h" 14 | 15 | static core_status net_resolve_handler(core_event *event) 16 | { 17 | net_resolve_job *job = event->state; 18 | 19 | switch (event->type) 20 | { 21 | case POOL_REQUEST: 22 | net_resolve(job->host, job->serv, job->family, job->type, job->flags, &job->addrinfo); 23 | break; 24 | case POOL_REPLY: 25 | (void) core_dispatch(&job->user, 0, (uintptr_t) job); 26 | free(job->host); 27 | free(job->serv); 28 | free(job); 29 | break; 30 | } 31 | 32 | return CORE_OK; 33 | } 34 | 35 | core_id net_resolve_async(pool *pool, core_callback *callback, void *state, char *host, char *serv, int family, int type, 36 | int flags) 37 | { 38 | net_resolve_job *job; 39 | 40 | job = malloc(sizeof *job); 41 | *job = (net_resolve_job) { 42 | .pool = pool, 43 | .user = {.callback = callback, .state = state}, 44 | .host = strdup(host), 45 | .serv = strdup(serv), 46 | .family = family, 47 | .type = type, 48 | .flags = flags}; 49 | job->id = pool_enqueue(pool, net_resolve_handler, job); 50 | return (core_id) job; 51 | } 52 | 53 | void net_resolve(char *host, char *serv, int family, int type, int flags, struct addrinfo **addrinfo) 54 | { 55 | struct addrinfo hints = {.ai_family = family, .ai_socktype = type, .ai_flags = flags}; 56 | int e; 57 | 58 | *addrinfo = NULL; 59 | e = getaddrinfo(host, serv, &hints, addrinfo); 60 | if (e != 0 && *addrinfo) 61 | { 62 | freeaddrinfo(*addrinfo); 63 | *addrinfo = NULL; 64 | } 65 | } 66 | 67 | int net_server(struct addrinfo *addrinfo, int flags) 68 | { 69 | int fd, e = 0; 70 | 71 | if (!addrinfo) 72 | return -1; 73 | 74 | flags = flags ? flags : NET_SERVER_DEFAULT; 75 | fd = socket(addrinfo->ai_family, addrinfo->ai_socktype | (flags & NET_FLAG_NONBLOCK ? SOCK_NONBLOCK : 0), 76 | addrinfo->ai_protocol); 77 | if (fd == -1) 78 | return -1; 79 | 80 | if (e == 0 && flags & NET_FLAG_REUSE) 81 | e = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (int[]) {1}, sizeof(int)); 82 | if (e == 0 && flags & NET_FLAG_REUSE) 83 | e = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (int[]) {1}, sizeof(int)); 84 | if (e == 0 && flags & NET_FLAG_NODELAY) 85 | e = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (int[]) {1}, sizeof(int)); 86 | if (e == 0 && flags & NET_FLAG_QUICKACK) 87 | e = setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, (int[]) {1}, sizeof(int)); 88 | if (e == 0) 89 | e = bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen); 90 | if (e == 0 && addrinfo->ai_socktype == SOCK_STREAM) 91 | e = listen(fd, -1); 92 | if (e == 0) 93 | return fd; 94 | 95 | (void) close(fd); 96 | return -1; 97 | } 98 | 99 | int net_server_filter(int fd, int mod) 100 | { 101 | struct sock_filter filter[] = 102 | { 103 | {BPF_LD | BPF_W | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_CPU}, // A = #cpu 104 | {BPF_ALU | BPF_MOD | BPF_K, 0, 0, mod}, // A = A % group_size 105 | {BPF_RET | BPF_A, 0, 0, 0} // return A 106 | }; 107 | struct sock_fprog prog = {.len = 3, .filter = filter}; 108 | return setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &prog, sizeof(prog)); 109 | } 110 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/net.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_NET_H_INCLUDED 2 | #define REACTOR_NET_H_INCLUDED 3 | 4 | #define NET_SERVER_DEFAULT (NET_FLAG_NONBLOCK | NET_FLAG_REUSE | NET_FLAG_NODELAY) 5 | 6 | enum 7 | { 8 | NET_FLAG_NONE = 1 << 0, 9 | NET_FLAG_NONBLOCK = 1 << 1, 10 | NET_FLAG_REUSE = 1 << 2, 11 | NET_FLAG_NODELAY = 1 << 3, 12 | NET_FLAG_QUICKACK = 1 << 4 13 | }; 14 | 15 | typedef struct net_resolve_job net_resolve_job; 16 | struct net_resolve_job 17 | { 18 | pool *pool; 19 | core_id id; 20 | core_handler user; 21 | char *host; 22 | char *serv; 23 | int family; 24 | int type; 25 | int flags; 26 | struct addrinfo *addrinfo; 27 | }; 28 | 29 | void net_resolve(char *, char *, int, int, int, struct addrinfo **); 30 | core_id net_resolve_async(pool *, core_callback *, void *, char *, char *, int, int, int); 31 | int net_server(struct addrinfo *, int); 32 | int net_server_filter(int, int); 33 | 34 | #endif /* REACTOR_NET_H_INCLUDED */ 35 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/notify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "notify.h" 12 | 13 | static core_status notify_next(core_event *event) 14 | { 15 | notify *notify = event->state; 16 | 17 | notify->next = 0; 18 | return core_dispatch(¬ify->user, NOTIFY_ERROR, 0); 19 | } 20 | 21 | static void notify_abort(notify *notify) 22 | { 23 | notify->error = 1; 24 | notify->next = core_next(NULL, notify_next, notify); 25 | } 26 | 27 | static core_status notify_event(core_event *event) 28 | { 29 | notify *notify = event->state; 30 | struct inotify_event *inotify_event; 31 | ssize_t n; 32 | char *p, buf[4096] __attribute__((aligned(__alignof__(struct inotify_event)))); 33 | core_status e; 34 | 35 | n = read(notify->fd, buf, sizeof buf); 36 | if (n == -1) 37 | return core_dispatch(¬ify->user, NOTIFY_ERROR, 0); 38 | 39 | for (p = buf; p < buf + n; p += sizeof(struct inotify_event) + inotify_event->len) 40 | { 41 | inotify_event = (struct inotify_event *) p; 42 | e = core_dispatch(¬ify->user, NOTIFY_EVENT, (uintptr_t) inotify_event); 43 | if (e != CORE_OK) 44 | return e; 45 | } 46 | 47 | return CORE_OK; 48 | } 49 | 50 | void notify_construct(notify *notify, core_callback *callback, void *state) 51 | { 52 | *notify = (struct notify) {.user = {.callback = callback, .state = state}, .fd = -1}; 53 | } 54 | 55 | void notify_watch(notify *notify, char *path, uint32_t mask) 56 | { 57 | int fd, e; 58 | 59 | if (notify->fd != -1) 60 | { 61 | notify_abort(notify); 62 | return; 63 | } 64 | 65 | fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); 66 | if (fd == -1) 67 | { 68 | notify_abort(notify); 69 | return; 70 | } 71 | 72 | e = inotify_add_watch(fd, path, mask); 73 | if (e == -1) 74 | { 75 | (void) close(fd); 76 | notify_abort(notify); 77 | return; 78 | } 79 | 80 | notify->fd = fd; 81 | core_add(NULL, notify_event, notify, notify->fd, EPOLLIN); 82 | } 83 | 84 | void notify_destruct(notify *notify) 85 | { 86 | if (notify->fd >= 0) 87 | { 88 | core_delete(NULL, notify->fd); 89 | (void) close(notify->fd); 90 | notify->fd = -1; 91 | } 92 | 93 | if (notify->next) 94 | { 95 | core_cancel(NULL, notify->next); 96 | notify->next = 0; 97 | } 98 | } 99 | 100 | int notify_valid(notify *notify) 101 | { 102 | return notify->error == 0; 103 | } 104 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/notify.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_NOFITY_H_INCLUDED 2 | #define REACTOR_NOTIFY_H_INCLUDED 3 | 4 | enum 5 | { 6 | NOTIFY_ERROR, 7 | NOTIFY_EVENT 8 | }; 9 | 10 | typedef struct notify notify; 11 | struct notify 12 | { 13 | core_handler user; 14 | int fd; 15 | int next; 16 | int error; 17 | }; 18 | 19 | void notify_construct(notify *, core_callback *, void *); 20 | void notify_destruct(notify *); 21 | void notify_watch(notify *, char *, uint32_t); 22 | int notify_valid(notify *); 23 | 24 | #endif /* REACTOR_NOTIFY_H_INCLUDED */ 25 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/reactor.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "reactor.h" 13 | 14 | struct reactor 15 | { 16 | size_t ref; 17 | }; 18 | 19 | static __thread struct reactor reactor = {0}; 20 | 21 | static void reactor_abort(__attribute__((unused)) int arg) 22 | { 23 | pool_abort(NULL); 24 | core_abort(NULL); 25 | } 26 | 27 | void reactor_construct(void) 28 | { 29 | if (!reactor.ref) 30 | { 31 | signal(SIGTERM, reactor_abort); 32 | signal(SIGINT, reactor_abort); 33 | signal(SIGCHLD, SIG_IGN); 34 | signal(SIGPIPE, SIG_IGN); 35 | core_construct(NULL); 36 | pool_construct(NULL, NULL); 37 | } 38 | reactor.ref++; 39 | } 40 | 41 | void reactor_destruct(void) 42 | { 43 | reactor.ref--; 44 | if (!reactor.ref) 45 | { 46 | pool_destruct(NULL); 47 | core_destruct(NULL); 48 | } 49 | } 50 | 51 | void reactor_loop(void) 52 | { 53 | core_loop(NULL); 54 | } 55 | 56 | int reactor_clone(int n) 57 | { 58 | int i; 59 | pid_t cpid; 60 | 61 | for (i = 0; i < n; i++) 62 | { 63 | cpid = fork(); 64 | if (!cpid) 65 | return i; 66 | } 67 | 68 | wait(NULL); 69 | exit(0); 70 | } 71 | 72 | int reactor_affinity(int cpu) 73 | { 74 | cpu_set_t set; 75 | 76 | CPU_ZERO(&set); 77 | CPU_SET(cpu, &set); 78 | return sched_setaffinity(0, sizeof set, &set); 79 | } 80 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/reactor.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_REACTOR_H_INCLUDED 2 | #define REACTOR_REACTOR_H_INCLUDED 3 | 4 | void reactor_construct(void); 5 | void reactor_destruct(void); 6 | void reactor_loop(void); 7 | int reactor_clone(int); 8 | int reactor_affinity(int); 9 | 10 | #endif /* REACTOR_REACTOR_H_INCLUDED */ 11 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/server.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | static core_status server_fatal_next(core_event *event) 16 | { 17 | server *server = event->state; 18 | 19 | server->next = 0; 20 | return core_dispatch(&server->user, SERVER_ERROR, 0); 21 | } 22 | 23 | static void server_fatal(server *server) 24 | { 25 | if (server->next == 0) 26 | server->next = core_next(NULL, server_fatal_next, server); 27 | } 28 | 29 | static core_status server_session_read(server_session *session) 30 | { 31 | server_context context; 32 | segment data; 33 | size_t offset; 34 | ssize_t n; 35 | core_status e; 36 | 37 | context.session = session; 38 | data = stream_read(&session->stream); 39 | for (offset = 0; offset < data.size; offset += n) 40 | { 41 | n = http_request_read(&context.request, segment_offset(data, offset)); 42 | if (dynamic_unlikely(n == 0)) 43 | break; 44 | 45 | if (dynamic_unlikely(n == -1)) 46 | { 47 | stream_destruct(&session->stream); 48 | list_erase(session, NULL); 49 | return CORE_ABORT; 50 | } 51 | 52 | e = core_dispatch(&session->server->user, SERVER_REQUEST, (uintptr_t) &context); 53 | if (dynamic_unlikely(e != CORE_OK)) 54 | return e; 55 | } 56 | 57 | stream_flush(&session->stream); 58 | stream_consume(&session->stream, offset); 59 | return CORE_OK; 60 | } 61 | 62 | static core_status server_session_handler(core_event *event) 63 | { 64 | server_session *session = (server_session *) event->state; 65 | 66 | if (dynamic_likely(event->type == STREAM_READ)) 67 | return server_session_read(session); 68 | 69 | if (event->type == STREAM_FLUSH) 70 | return CORE_OK; 71 | 72 | stream_destruct(&session->stream); 73 | list_erase(session, NULL); 74 | return CORE_ABORT; 75 | } 76 | 77 | static core_status server_fd_handler(core_event *event) 78 | { 79 | server *server = event->state; 80 | server_session *session; 81 | int fd; 82 | 83 | if (event->data != EPOLLIN) 84 | { 85 | server_fatal(server); 86 | return CORE_ABORT; 87 | } 88 | 89 | while (1) 90 | { 91 | fd = accept4(server->fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC); 92 | if (fd == -1) 93 | break; 94 | 95 | session = list_push_back(&server->sessions, NULL, sizeof *session); 96 | stream_construct(&session->stream, server_session_handler, session); 97 | session->server = server; 98 | stream_open(&session->stream, fd); 99 | } 100 | 101 | return CORE_OK; 102 | } 103 | 104 | void server_construct(server *server, core_callback *callback, void *state) 105 | { 106 | *server = (struct server) {.user = {.callback = callback, .state = state}, .fd = -1}; 107 | list_construct(&server->sessions); 108 | } 109 | 110 | void server_open(server *server, int fd) 111 | { 112 | if (fd == -1) 113 | { 114 | server_fatal(server); 115 | return; 116 | } 117 | 118 | server->fd = fd; 119 | (void) fcntl(server->fd, F_SETFL, O_NONBLOCK); 120 | core_add(NULL, server_fd_handler, server, server->fd, EPOLLIN | EPOLLET); 121 | } 122 | 123 | void server_close(server *server) 124 | { 125 | server_session *session; 126 | 127 | list_foreach(&server->sessions, session) 128 | stream_destruct(&session->stream); 129 | if (server->fd >= 0) 130 | { 131 | core_delete(NULL, server->fd); 132 | (void) close(server->fd); 133 | server->fd = -1; 134 | } 135 | } 136 | 137 | void server_destruct(server *server) 138 | { 139 | server_close(server); 140 | list_destruct(&server->sessions, NULL); 141 | } 142 | 143 | void server_ok(server_context *context, segment type, segment data) 144 | { 145 | http_response response; 146 | 147 | http_response_ok(&response, type, data); 148 | http_response_write(&response, stream_allocate(&context->session->stream, http_response_size(&response))); 149 | } 150 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/server.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_SERVER_H_INCLUDED 2 | #define REACTOR_SERVER_H_INCLUDED 3 | 4 | enum 5 | { 6 | SERVER_ERROR, 7 | SERVER_REQUEST 8 | }; 9 | 10 | typedef struct server server; 11 | typedef struct server_context server_context; 12 | typedef struct server_session server_session; 13 | 14 | struct server 15 | { 16 | core_handler user; 17 | int fd; 18 | int next; 19 | list sessions; 20 | }; 21 | 22 | struct server_context 23 | { 24 | server_session *session; 25 | http_request request; 26 | }; 27 | 28 | struct server_session 29 | { 30 | stream stream; 31 | server *server; 32 | }; 33 | 34 | void server_construct(server *, core_callback *, void *); 35 | void server_open(server *, int); 36 | void server_close(server *); 37 | void server_destruct(server *); 38 | void server_ok(server_context *, segment, segment); 39 | 40 | #endif /* REACTOR_SERVER_H_INCLUDED */ 41 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "stream.h" 13 | 14 | static core_status stream_next(core_event *event) 15 | { 16 | stream *stream = event->state; 17 | 18 | stream->next = 0; 19 | return core_dispatch(&stream->user, STREAM_ERROR, 0); 20 | } 21 | 22 | static void stream_abort(stream *stream) 23 | { 24 | stream_close(stream); 25 | stream->next = core_next(NULL, stream_next, stream); 26 | } 27 | 28 | static void stream_send(stream *stream) 29 | { 30 | buffer *b = &stream->output; 31 | size_t offset = 0, size = buffer_size(b); 32 | ssize_t n; 33 | 34 | while (size - offset) 35 | { 36 | if (stream_is_socket(stream)) 37 | n = send(stream->fd, (char *) buffer_data(b) + offset, size - offset, 0); 38 | else 39 | n = write(stream->fd, (char *) buffer_data(b) + offset, size - offset); 40 | if (n == -1) 41 | break; 42 | offset += n; 43 | } 44 | buffer_erase(b, 0, offset); 45 | } 46 | 47 | static size_t stream_receive(stream *stream) 48 | { 49 | buffer *b = &stream->input; 50 | size_t offset = buffer_size(b), size = 0; 51 | ssize_t n; 52 | 53 | do 54 | { 55 | buffer_reserve(b, offset + size + STREAM_BLOCK_SIZE); 56 | if (stream_is_socket(stream)) 57 | n = recv(stream->fd, (char *) buffer_data(b) + offset + size, STREAM_BLOCK_SIZE, 0); 58 | else 59 | n = read(stream->fd, (char *) buffer_data(b) + offset + size, STREAM_BLOCK_SIZE); 60 | size += n > 0 ? n : 0; 61 | } while (n == STREAM_BLOCK_SIZE); 62 | 63 | if (n == 0) 64 | stream->flags &= ~STREAM_OPEN; 65 | b->size += size; 66 | return size; 67 | } 68 | 69 | static core_status stream_callback(core_event *event) 70 | { 71 | stream *stream = event->state; 72 | core_status e; 73 | size_t size; 74 | 75 | if (dynamic_unlikely(event->data == EPOLLOUT)) 76 | { 77 | stream_flush(stream); 78 | return buffer_size(&stream->output) ? CORE_OK : core_dispatch(&stream->user, STREAM_FLUSH, 0); 79 | } 80 | 81 | if (dynamic_likely(event->data & EPOLLIN)) 82 | { 83 | size = stream_receive(stream); 84 | if (dynamic_likely(size)) 85 | { 86 | e = core_dispatch(&stream->user, STREAM_READ, 0); 87 | if (dynamic_unlikely(e != CORE_OK)) 88 | return e; 89 | } 90 | if (dynamic_likely(stream_is_open(stream) && event->data == EPOLLIN)) 91 | return CORE_OK; 92 | } 93 | 94 | return core_dispatch(&stream->user, STREAM_CLOSE, 0); 95 | } 96 | 97 | static void stream_events(stream *stream, int events) 98 | { 99 | if (stream->events == events) 100 | ; 101 | else if (!stream->events) 102 | core_add(NULL, stream_callback, stream, stream->fd, events); 103 | else if (!events) 104 | core_delete(NULL, stream->fd); 105 | else 106 | core_modify(NULL, stream->fd, events); 107 | stream->events = events; 108 | } 109 | 110 | void stream_construct(stream *stream, core_callback *callback, void *state) 111 | { 112 | *stream = (struct stream) {.user = {.callback = callback, .state = state}, .fd = -1}; 113 | buffer_construct(&stream->input); 114 | buffer_construct(&stream->output); 115 | } 116 | 117 | void stream_destruct(stream *stream) 118 | { 119 | stream_close(stream); 120 | if (stream->next) 121 | { 122 | core_cancel(NULL, stream->next); 123 | stream->next = 0; 124 | } 125 | buffer_destruct(&stream->input); 126 | buffer_destruct(&stream->output); 127 | } 128 | 129 | void stream_open(stream *stream, int fd) 130 | { 131 | struct stat st; 132 | int e; 133 | 134 | e = fstat(fd, &st); 135 | if (e == -1 || stream_is_open(stream)) 136 | { 137 | stream_abort(stream); 138 | return; 139 | } 140 | 141 | stream->fd = fd; 142 | stream->flags |= STREAM_OPEN | (S_ISSOCK(st.st_mode) ? STREAM_SOCKET : 0); 143 | stream_events(stream, EPOLLIN | EPOLLET); 144 | } 145 | 146 | void stream_close(stream *stream) 147 | { 148 | if (stream->fd >= 0) 149 | { 150 | stream_events(stream, 0); 151 | (void) close(stream->fd); 152 | stream->fd = -1; 153 | stream->flags &= ~STREAM_OPEN; 154 | } 155 | } 156 | 157 | segment stream_read(stream *stream) 158 | { 159 | return segment_data(buffer_data(&stream->input), buffer_size(&stream->input)); 160 | } 161 | 162 | segment stream_read_line(stream *stream) 163 | { 164 | segment input; 165 | char *delim; 166 | 167 | input = stream_read(stream); 168 | delim = memchr(input.base, '\n', input.size); 169 | return delim ? segment_data(input.base, delim - (char *) input.base + 1) : segment_empty(); 170 | } 171 | 172 | void stream_write(stream *stream, segment output) 173 | { 174 | buffer_insert(&stream->output, buffer_size(&stream->output), output.base, output.size); 175 | } 176 | 177 | segment stream_allocate(stream *stream, size_t size) 178 | { 179 | buffer_resize(&stream->output, buffer_size(&stream->output) + size); 180 | return segment_data((char *) buffer_data(&stream->output) + buffer_size(&stream->output) - size, size); 181 | } 182 | 183 | void stream_consume(stream *stream, size_t size) 184 | { 185 | buffer_erase(&stream->input, 0, size); 186 | } 187 | 188 | void stream_notify(stream *stream) 189 | { 190 | if (!stream_is_open(stream)) 191 | return; 192 | 193 | stream_events(stream, EPOLLIN | EPOLLOUT | EPOLLET); 194 | } 195 | 196 | void stream_flush(stream *stream) 197 | { 198 | if (!stream_is_open(stream)) 199 | return; 200 | 201 | stream_send(stream); 202 | stream_events(stream, buffer_size(&stream->output) ? EPOLLIN | EPOLLOUT | EPOLLET : EPOLLIN | EPOLLET); 203 | } 204 | 205 | int stream_is_open(stream *stream) 206 | { 207 | return stream->flags & STREAM_OPEN; 208 | } 209 | 210 | int stream_is_socket(stream *stream) 211 | { 212 | return stream->flags & STREAM_SOCKET; 213 | } 214 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/stream.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_STREAM_H_INCLUDED 2 | #define REACTOR_STREAM_H_INCLUDED 3 | 4 | #define STREAM_BLOCK_SIZE 65536 5 | 6 | enum 7 | { 8 | STREAM_OPEN = 0x01, 9 | STREAM_SOCKET = 0x02 10 | }; 11 | 12 | enum 13 | { 14 | STREAM_ERROR, 15 | STREAM_READ, 16 | STREAM_FLUSH, 17 | STREAM_CLOSE 18 | }; 19 | 20 | typedef struct stream stream; 21 | struct stream 22 | { 23 | core_handler user; 24 | int flags; 25 | int fd; 26 | int events; 27 | int next; 28 | buffer input; 29 | buffer output; 30 | }; 31 | 32 | void stream_construct(stream *, core_callback *, void *); 33 | void stream_destruct(stream *); 34 | void stream_open(stream *, int); 35 | void stream_close(stream *); 36 | segment stream_read(stream *); 37 | segment stream_read_line(stream *); 38 | void stream_write(stream *, segment); 39 | segment stream_allocate(stream *, size_t); 40 | void stream_consume(stream *, size_t); 41 | void stream_notify(stream *); 42 | void stream_flush(stream *); 43 | int stream_is_open(stream *); 44 | int stream_is_socket(stream *); 45 | 46 | #endif /* REACTOR_STREAM_H_INCLUDED */ 47 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "timer.h" 11 | 12 | static core_status timer_next(core_event *event) 13 | { 14 | timer *timer = event->state; 15 | 16 | timer->next = 0; 17 | return core_dispatch(&timer->user, TIMER_ERROR, 0); 18 | } 19 | 20 | static void timer_abort(timer *timer) 21 | { 22 | timer_clear(timer); 23 | timer->next = core_next(NULL, timer_next, timer); 24 | } 25 | 26 | static core_status timer_callback(core_event *event) 27 | { 28 | timer *timer = event->state; 29 | uint64_t expirations; 30 | ssize_t n; 31 | core_status e; 32 | 33 | if (event->data != EPOLLIN) 34 | return core_dispatch(&timer->user, TIMER_ERROR, 0); 35 | 36 | while (1) 37 | { 38 | n = read(timer->fd, &expirations, sizeof expirations); 39 | if (n == -1 && errno == EAGAIN) 40 | return CORE_OK; 41 | 42 | if (n != sizeof expirations) 43 | return core_dispatch(&timer->user, TIMER_ERROR, 0); 44 | 45 | e = core_dispatch(&timer->user, TIMER_ALARM, expirations); 46 | if (e != CORE_OK) 47 | return e; 48 | } 49 | } 50 | 51 | void timer_construct(timer *timer, core_callback *callback, void *state) 52 | { 53 | *timer = (struct timer) {.user = {.callback = callback, .state = state}, .fd = -1}; 54 | } 55 | 56 | void timer_destruct(timer *timer) 57 | { 58 | timer_clear(timer); 59 | if (timer->next) 60 | { 61 | core_cancel(NULL, timer->next); 62 | timer->next = 0; 63 | } 64 | } 65 | 66 | void timer_set(timer *timer, uint64_t t0, uint64_t ti) 67 | { 68 | int e; 69 | 70 | if (timer->fd == -1) 71 | { 72 | timer->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); 73 | if (timer->fd == -1) 74 | { 75 | timer_abort(timer); 76 | return; 77 | } 78 | core_add(NULL, timer_callback, timer, timer->fd, EPOLLIN | EPOLLET); 79 | } 80 | 81 | if (t0 == 0) 82 | t0 = 1; 83 | 84 | e = timerfd_settime(timer->fd, 0, (struct itimerspec[]) {{.it_interval = {.tv_sec = ti / 1000000000, .tv_nsec = ti % 1000000000}, .it_value = {.tv_sec = t0 / 1000000000, .tv_nsec = t0 % 1000000000}}}, NULL); 85 | if (e == -1) 86 | timer_abort(timer); 87 | } 88 | 89 | void timer_clear(timer *timer) 90 | { 91 | if (timer->fd >= 0) 92 | { 93 | core_delete(NULL, timer->fd); 94 | (void) close(timer->fd); 95 | timer->fd = -1; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/libreactor/src/reactor/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef REACTOR_TIMER_H_INCLUDED 2 | #define REACTOR_TIMER_H_INCLUDED 3 | 4 | enum 5 | { 6 | TIMER_ERROR, 7 | TIMER_ALARM 8 | }; 9 | 10 | typedef struct timer timer; 11 | struct timer 12 | { 13 | core_handler user; 14 | int fd; 15 | int next; 16 | }; 17 | 18 | void timer_construct(timer *, core_callback *, void *); 19 | void timer_destruct(timer *); 20 | void timer_set(timer *, uint64_t, uint64_t); 21 | void timer_clear(timer *); 22 | 23 | #endif /* REACTOR_TIMER_H_INCLUDED */ 24 | -------------------------------------------------------------------------------- /lib/libreactor/test/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | for file in notify stream 4 | do 5 | echo [$file] 6 | test=`gcov -b src/reactor/libreactor_test_a-$file | grep -A4 File.*$file` 7 | echo "$test" 8 | echo "$test" | grep '% of' | grep '100.00%' >/dev/null || exit 1 9 | echo "$test" | grep '% of' | grep -v '100.00%' >/dev/null && exit 1 10 | done 11 | exit 0 12 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-1-allow: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.0 2 | 3 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-1-deny: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host: localhost:5000 3 | User-Agent: curl/7.58.0 4 | Accept: */* 5 | Transfer-Encoding: chunked 6 | Content-Type: application/x-www-form-urlencoded 7 | 8 | 0 -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-2-allow: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.0 2 | 3 | GET / HTTP/1.0 4 | 5 | GET / HTTP/1.0 6 | 7 | GET / HTTP/1.0 8 | 9 | GET / HTTP/1.0 10 | 11 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-2-deny: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host: localhost:5000 3 | User-Agent: curl/7.58.0 4 | Accept: */* 5 | Transfer-Encoding: chunked 6 | Content-Type: application/x-www-form-urlencoded 7 | 8 | 2 9 | xx 10 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-3-allow: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host: localhost:5000 3 | User-Agent: curl/7.58.0 4 | Accept: */* 5 | Content-Length: 4 6 | Content-Type: application/x-www-form-urlencoded 7 | 8 | test -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-3-deny: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host: localhost:5000 3 | User-Agent: curl/7.58.0 4 | Accept: */* 5 | Transfer-Encoding: chunked 6 | Content-Type: application/x-www-form-urlencoded 7 | 8 | 2 9 | test 10 | 0 11 | 12 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-4-deny: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host: localhost:5000 3 | User-Agent: curl/7.58.0 4 | Accept: */* 5 | Transfer-Encoding: chunked 6 | Content-Type: application/x-www-form-urlencoded 7 | 8 | ffffffff 9 | test 10 | 0 11 | 12 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-5-allow: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host: localhost:5000 3 | User-Agent: curl/7.58.0 4 | Accept: */* 5 | Transfer-Encoding: chunked 6 | Content-Type: application/x-www-form-urlencoded 7 | 8 | 0 9 | 10 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-5-deny: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-6-deny: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.0 2 | 3 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-7-deny: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host: localhost:5000 3 | User-Agent: curl/7.58.0 4 | Accept: */* 5 | Content-Length: 4 6 | Content-Type: application/x-www-form-urlencoded 7 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-8-deny: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host: localhost:5000 3 | User-Agent: curl/7.58.0 4 | Accept: */* 5 | Content-Length: 400000 6 | Content-Type: application/x-www-form-urlencoded 7 | 8 | test -------------------------------------------------------------------------------- /lib/libreactor/test/data/request-9-deny: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host: localhost:5000 3 | User-Agent: curl/7.58.0 4 | Accept: */* 5 | Transfer-Encoding: something 6 | Content-Type: application/x-www-form-urlencoded 7 | 8 | 0 9 | 10 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/response-1-allow: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: nginx/1.12.2 3 | Date: Tue, 30 Jan 2018 10:42:40 GMT 4 | Content-Type: text/html 5 | Content-Length: 612 6 | Last-Modified: Wed, 22 Nov 2017 19:48:42 GMT 7 | Connection: close 8 | ETag: "5a15d49a-264" 9 | Accept-Ranges: bytes 10 | 11 | 12 | 13 | 14 | Welcome to nginx! 15 | 22 | 23 | 24 |

Welcome to nginx!

25 |

If you see this page, the nginx web server is successfully installed and 26 | working. Further configuration is required.

27 | 28 |

For online documentation and support please refer to 29 | nginx.org.
30 | Commercial support is available at 31 | nginx.com.

32 | 33 |

Thank you for using nginx.

34 | 35 | 36 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/response-1-deny: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/response-2-allow: -------------------------------------------------------------------------------- 1 | HTTP/1.1 400 Bad Request 2 | Server: nginx/1.12.2 3 | Date: Tue, 30 Jan 2018 15:00:03 GMT 4 | Content-Length: 0 5 | Connection: close 6 | 7 | HTTP/1.1 400 Bad Request 8 | Server: nginx/1.12.2 9 | Date: Tue, 30 Jan 2018 15:00:03 GMT 10 | Content-Length: 0 11 | Connection: close 12 | 13 | HTTP/1.1 400 Bad Request 14 | Server: nginx/1.12.2 15 | Date: Tue, 30 Jan 2018 15:00:03 GMT 16 | Content-Length: 0 17 | Connection: close 18 | 19 | HTTP/1.1 400 Bad Request 20 | Server: nginx/1.12.2 21 | Date: Tue, 30 Jan 2018 15:00:03 GMT 22 | Content-Length: 0 23 | Connection: close 24 | 25 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/response-2-deny: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | 3 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/response-3-deny: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: test 3 | Date: Tue, 30 Jan 2018 15:00:03 GMT 4 | Content-Type: text/html 5 | Transfer-Encoding: chunked 6 | 7 | ffffffff 8 | test 9 | 0 10 | 11 | -------------------------------------------------------------------------------- /lib/libreactor/test/data/response-4-deny: -------------------------------------------------------------------------------- 1 | HTTP/1.0 200 OK 2 | Server: nginx/1.12.2 3 | Date: Tue, 30 Jan 2018 15:00:03 GMT 4 | Content-Type: text/html 5 | Content-Length: 173 6 | 7 | test -------------------------------------------------------------------------------- /lib/libreactor/test/data/response-5-deny: -------------------------------------------------------------------------------- 1 | Broken 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/libreactor/test/http.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include "reactor.h" 20 | 21 | static void headers(__attribute__((unused)) void **unused) 22 | { 23 | http_headers headers; 24 | int i; 25 | 26 | http_headers_construct(&headers); 27 | http_headers_add(&headers, segment_string("Name"), segment_string("Value")); 28 | assert_int_equal(http_headers_count(&headers), 1); 29 | assert_true(segment_equal(http_headers_lookup(&headers, segment_string("Name")), segment_string("Value"))); 30 | assert_true(segment_equal(http_headers_lookup(&headers, segment_string("Fail")), segment_empty())); 31 | for (i = 0; i < 128; i++) 32 | http_headers_add(&headers, segment_string("Name"), segment_string("Value")); 33 | } 34 | 35 | static segment iterate_read(char *file) 36 | { 37 | int fd; 38 | struct stat st; 39 | char *data; 40 | ssize_t n; 41 | 42 | fd = open(file, O_RDONLY); 43 | assert_true(fd >= 0); 44 | assert_int_equal(fstat(fd, &st), 0); 45 | data = malloc(st.st_size); 46 | n = read(fd, data, st.st_size); 47 | assert_int_equal(n, st.st_size); 48 | return segment_data(data, st.st_size); 49 | } 50 | 51 | static void iterate_request(char *file, int allow) 52 | { 53 | http_request request; 54 | segment s; 55 | ssize_t n; 56 | size_t total = 0; 57 | 58 | fprintf(stderr, "<%s>\n", file); 59 | s = iterate_read(file); 60 | while (1) 61 | { 62 | n = http_request_read(&request, segment_offset(s, total)); 63 | if (n <= 0) 64 | break; 65 | total += n; 66 | } 67 | if (allow) 68 | assert_int_equal(total, s.size); 69 | else 70 | assert_int_not_equal(total, s.size); 71 | free(s.base); 72 | } 73 | 74 | static void iterate_response(char *file, int allow) 75 | { 76 | http_response response; 77 | segment s; 78 | ssize_t n; 79 | size_t total = 0; 80 | 81 | fprintf(stderr, "<%s>\n", file); 82 | s = iterate_read(file); 83 | while (1) 84 | { 85 | n = http_response_read(&response, segment_offset(s, total)); 86 | if (n <= 0) 87 | break; 88 | total += n; 89 | } 90 | if (allow) 91 | assert_int_equal(total, s.size); 92 | else 93 | assert_int_not_equal(total, s.size); 94 | free(s.base); 95 | } 96 | 97 | static void iterate(__attribute__((unused)) void **unused) 98 | { 99 | char cwd[PATH_MAX]; 100 | DIR *dir; 101 | struct dirent *d; 102 | int request, allow; 103 | 104 | getcwd(cwd, sizeof cwd); 105 | assert_int_equal(chdir(SRCDIR "test/data/"), 0); 106 | dir = opendir("."); 107 | assert_true(dir != NULL); 108 | while (1) 109 | { 110 | d = readdir(dir); 111 | if (!d) 112 | break; 113 | if (d->d_type != DT_REG || strlen(d->d_name) < 5) 114 | continue; 115 | request = strncmp(d->d_name, "request", strlen("request")) == 0; 116 | allow = strcmp(d->d_name + strlen(d->d_name) - 5, "allow") == 0; 117 | if (request) 118 | iterate_request(d->d_name, allow); 119 | else 120 | iterate_response(d->d_name, allow); 121 | } 122 | (void) closedir(dir); 123 | assert_int_equal(chdir(cwd), 0); 124 | } 125 | 126 | int main() 127 | { 128 | const struct CMUnitTest tests[] = 129 | { 130 | cmocka_unit_test(headers), 131 | cmocka_unit_test(iterate)}; 132 | 133 | return cmocka_run_group_tests(tests, NULL, NULL); 134 | } 135 | -------------------------------------------------------------------------------- /lib/libreactor/test/mock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int debug_abort = 0; 8 | int debug_read = 0; 9 | int debug_inotify_init1 = 0; 10 | 11 | void __real_abort(void); 12 | void __wrap_abort(void) 13 | { 14 | if (debug_abort) 15 | mock_assert(0, "", "", 0); 16 | else 17 | __real_abort(); 18 | } 19 | 20 | ssize_t __real_read(int, void *, size_t); 21 | ssize_t __wrap_read(int fildes, void *buf, size_t nbyte) 22 | { 23 | if (debug_read) 24 | { 25 | errno = debug_read; 26 | return -1; 27 | } 28 | else 29 | return __real_read(fildes, buf, nbyte); 30 | } 31 | 32 | ssize_t __real_inotify_init1(int); 33 | ssize_t __wrap_inotify_init1(int flags) 34 | { 35 | return debug_inotify_init1 ? -1 : __real_inotify_init1(flags); 36 | } 37 | -------------------------------------------------------------------------------- /lib/libreactor/test/notify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "reactor.h" 17 | 18 | extern int debug_read; 19 | extern int debug_inotify_init1; 20 | 21 | static void touch(char *dir, char *file) 22 | { 23 | char path[PATH_MAX]; 24 | FILE *f; 25 | 26 | snprintf(path, sizeof path, "%s/%s", dir, file); 27 | f = fopen(path, "w"); 28 | fclose(f); 29 | } 30 | 31 | static void cleanup(char *dir, char *file) 32 | { 33 | char path[PATH_MAX]; 34 | 35 | snprintf(path, sizeof path, "%s/%s", dir, file ? file : ""); 36 | remove(path); 37 | } 38 | 39 | static core_status callback_abort(core_event *event) 40 | { 41 | notify *notify = event->state; 42 | 43 | notify_destruct(notify); 44 | return CORE_ABORT; 45 | } 46 | 47 | static core_status callback_ok(core_event *event) 48 | { 49 | notify *notify = event->state; 50 | 51 | notify_destruct(notify); 52 | return CORE_OK; 53 | } 54 | 55 | static void basic(__attribute__((unused)) void **state) 56 | { 57 | notify notify; 58 | char template[PATH_MAX] = "/tmp/notifyXXXXXX"; 59 | char *path = mkdtemp(template); 60 | 61 | assert_true(path); 62 | core_construct(NULL); 63 | 64 | // watch on already open notifier 65 | notify_construct(¬ify, callback_abort, ¬ify); 66 | notify_watch(¬ify, path, IN_CLOSE_WRITE); 67 | notify_watch(¬ify, path, IN_CLOSE_WRITE); 68 | assert_false(notify_valid(¬ify)); 69 | core_loop(NULL); 70 | 71 | // notify on close write 72 | notify_construct(¬ify, callback_abort, ¬ify); 73 | notify_watch(¬ify, path, IN_CLOSE_WRITE); 74 | touch(path, "test"); 75 | core_loop(NULL); 76 | 77 | // notify on close write 78 | notify_construct(¬ify, callback_ok, ¬ify); 79 | notify_watch(¬ify, path, IN_CLOSE_WRITE); 80 | touch(path, "test"); 81 | core_loop(NULL); 82 | 83 | // read error when notify 84 | assert_true(path); 85 | notify_construct(¬ify, callback_abort, ¬ify); 86 | notify_watch(¬ify, path, IN_CLOSE_WRITE); 87 | touch(path, "test"); 88 | debug_read = ENOBUFS; 89 | core_loop(NULL); 90 | debug_read = 0; 91 | 92 | // notify error 93 | notify_construct(¬ify, callback_abort, ¬ify); 94 | notify_watch(¬ify, "/doesnotexist", IN_CLOSE_WRITE); 95 | core_loop(NULL); 96 | 97 | // notify error 98 | notify_construct(¬ify, callback_abort, ¬ify); 99 | notify_watch(¬ify, "/doesnotexist", IN_CLOSE_WRITE); 100 | assert_false(notify_valid(¬ify)); 101 | notify_destruct(¬ify); 102 | core_loop(NULL); 103 | 104 | // notify init error 105 | debug_inotify_init1 = 1; 106 | notify_construct(¬ify, callback_abort, ¬ify); 107 | notify_watch(¬ify, "/", IN_CLOSE_WRITE); 108 | assert_false(notify_valid(¬ify)); 109 | notify_destruct(¬ify); 110 | core_loop(NULL); 111 | debug_inotify_init1 = 0; 112 | 113 | core_destruct(NULL); 114 | cleanup(path, "test"); 115 | cleanup(path, NULL); 116 | } 117 | 118 | int main() 119 | { 120 | const struct CMUnitTest tests[] = 121 | { 122 | cmocka_unit_test(basic)}; 123 | 124 | return cmocka_run_group_tests(tests, NULL, NULL); 125 | } 126 | -------------------------------------------------------------------------------- /lib/libreactor/test/stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include "reactor.h" 18 | 19 | static int write_count = 0; 20 | static int read_size = 0; 21 | static int error_count = 0; 22 | 23 | static core_status callback_readline(core_event *event) 24 | { 25 | stream *stream = event->state; 26 | segment s; 27 | 28 | switch (event->type) 29 | { 30 | case STREAM_FLUSH: 31 | if (write_count) 32 | { 33 | s = stream_allocate(stream, 5); 34 | memcpy(s.base, "line\n", 5); 35 | stream_flush(stream); 36 | write_count--; 37 | } 38 | else 39 | stream_destruct(stream); 40 | return CORE_OK; 41 | case STREAM_READ: 42 | s = stream_read_line(stream); 43 | assert_true(segment_equal(s, segment_string("line\n"))); 44 | stream_destruct(stream); 45 | return CORE_ABORT; 46 | default: 47 | stream_destruct(stream); 48 | return CORE_ABORT; 49 | } 50 | } 51 | 52 | static core_status callback(core_event *event) 53 | { 54 | stream *stream = event->state; 55 | char data[1048576] = {0}; 56 | segment s; 57 | 58 | switch (event->type) 59 | { 60 | case STREAM_FLUSH: 61 | if (write_count) 62 | { 63 | stream_write(stream, segment_data(data, sizeof data)); 64 | stream_flush(stream); 65 | write_count--; 66 | } 67 | else 68 | stream_destruct(stream); 69 | return CORE_OK; 70 | case STREAM_READ: 71 | s = stream_read(stream); 72 | read_size += s.size; 73 | stream_consume(stream, s.size); 74 | return CORE_OK; 75 | case STREAM_CLOSE: 76 | stream_destruct(stream); 77 | return CORE_ABORT; 78 | case STREAM_ERROR: 79 | error_count++; 80 | stream_destruct(stream); 81 | return CORE_ABORT; 82 | } 83 | 84 | return CORE_OK; 85 | } 86 | 87 | static void basic_pipe(__attribute__((unused)) void **state) 88 | { 89 | char data[1024] = {0}; 90 | int fd[2]; 91 | stream stream, out; 92 | 93 | core_construct(NULL); 94 | 95 | // pipe close 96 | pipe(fd); 97 | fcntl(fd[0], F_SETFL, O_NONBLOCK); 98 | stream_construct(&stream, callback, &stream); 99 | stream_open(&stream, fd[0]); 100 | close(fd[1]); 101 | assert_true(stream_is_open(&stream)); 102 | core_loop(NULL); 103 | assert_false(stream_is_open(&stream)); 104 | 105 | // pipe message 106 | pipe(fd); 107 | fcntl(fd[0], F_SETFL, O_NONBLOCK); 108 | stream_construct(&stream, callback, &stream); 109 | stream_open(&stream, fd[0]); 110 | stream_construct(&out, callback, &out); 111 | stream_open(&out, fd[1]); 112 | stream_write(&out, segment_data(data, sizeof data)); 113 | stream_flush(&out); 114 | stream_destruct(&out); 115 | assert_true(stream_is_open(&stream)); 116 | core_loop(NULL); 117 | assert_false(stream_is_open(&stream)); 118 | 119 | core_destruct(NULL); 120 | } 121 | 122 | static void basic_socketpair(__attribute__((unused)) void **state) 123 | { 124 | char data[1024] = {0}; 125 | int e, fd[2]; 126 | stream in, out; 127 | segment s; 128 | 129 | core_construct(NULL); 130 | 131 | // short message 132 | e = socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd); 133 | assert_true(e == 0); 134 | fcntl(fd[0], F_SETFL, O_NONBLOCK); 135 | stream_construct(&in, callback, &in); 136 | stream_open(&in, fd[0]); 137 | write(fd[1], data, sizeof data); 138 | close(fd[1]); 139 | assert_true(stream_is_open(&in)); 140 | assert_true(stream_is_socket(&in)); 141 | core_loop(NULL); 142 | assert_false(stream_is_open(&in)); 143 | 144 | // long message 145 | e = socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC, fd); 146 | assert_true(e == 0); 147 | stream_construct(&in, callback, &in); 148 | stream_construct(&out, callback, &out); 149 | stream_open(&in, fd[0]); 150 | stream_open(&out, fd[1]); 151 | write_count = 16; 152 | read_size = 0; 153 | stream_notify(&out); 154 | core_loop(NULL); 155 | assert_int_equal(write_count, 0); 156 | assert_int_equal(read_size, 16 * 1048576); 157 | 158 | // lines 159 | e = socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC, fd); 160 | assert_true(e == 0); 161 | stream_construct(&in, callback_readline, &in); 162 | stream_construct(&out, callback_readline, &out); 163 | stream_open(&in, fd[0]); 164 | s = stream_read_line(&in); 165 | assert_int_equal(s.size, 0); 166 | stream_open(&out, fd[1]); 167 | write_count = 1; 168 | stream_notify(&out); 169 | core_loop(NULL); 170 | assert_int_equal(write_count, 0); 171 | 172 | core_destruct(NULL); 173 | } 174 | 175 | static void errors(__attribute__((unused)) void **state) 176 | { 177 | stream s; 178 | 179 | core_construct(NULL); 180 | 181 | // open invalid 182 | stream_construct(&s, callback, &s); 183 | error_count = 0; 184 | stream_open(&s, -1); 185 | core_loop(NULL); 186 | assert_int_equal(error_count, 1); 187 | 188 | // open invalid and destruct 189 | stream_construct(&s, callback, &s); 190 | error_count = 0; 191 | stream_open(&s, -1); 192 | stream_destruct(&s); 193 | core_loop(NULL); 194 | assert_int_equal(error_count, 0); 195 | 196 | // write on closed 197 | stream_construct(&s, callback, &s); 198 | stream_write(&s, segment_empty()); 199 | stream_notify(&s); 200 | stream_flush(&s); 201 | stream_destruct(&s); 202 | 203 | // double open 204 | stream_construct(&s, callback, &s); 205 | stream_open(&s, 0); 206 | stream_open(&s, 0); 207 | stream_destruct(&s); 208 | 209 | core_destruct(NULL); 210 | } 211 | 212 | int main() 213 | { 214 | const struct CMUnitTest tests[] = 215 | { 216 | cmocka_unit_test(basic_pipe), 217 | cmocka_unit_test(basic_socketpair), 218 | cmocka_unit_test(errors)}; 219 | 220 | return cmocka_run_group_tests(tests, NULL, NULL); 221 | } 222 | -------------------------------------------------------------------------------- /lib/libreactor/test/valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exit 0 4 | 5 | if command -v valgrind; then 6 | for file in notify stream http 7 | do 8 | echo [$file] 9 | if ! valgrind --track-fds=yes --error-exitcode=1 --read-var-info=yes --leak-check=full --show-leak-kinds=all test/$file; then 10 | exit 1 11 | fi 12 | done 13 | fi 14 | -------------------------------------------------------------------------------- /src/connection.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "net.h" 4 | #include "worker.h" 5 | #include "connection.h" 6 | 7 | static core_status connection_request(core_event *event) 8 | { 9 | connection *connection = event->state; 10 | http_response *response; 11 | 12 | switch (event->type) 13 | { 14 | case HTTP_CLIENT_READY: 15 | while (connection->requests <= connection->responses + connection->worker->pounce->pipeline) 16 | { 17 | stream_write(&connection->client.stream, segment_string(string_data(&connection->worker->pounce->request))); 18 | connection->requests++; 19 | } 20 | connection->request_start = core_now(NULL); 21 | break; 22 | case HTTP_CLIENT_RESPONSE: 23 | response = (http_response *) event->data; 24 | connection->responses++; 25 | connection->request_stop = core_now(NULL); 26 | stats_data(&connection->worker->stats, connection->request_stop - connection->request_start, 27 | response->code >= 200 && response->code < 300); 28 | break; 29 | case HTTP_CLIENT_CLOSE: 30 | connection->requests = 0; 31 | connection->responses = 0; 32 | http_client_close(&connection->client); 33 | http_client_open(&connection->client, net_client(connection->worker->pounce->addrinfo, 0)); 34 | break; 35 | case HTTP_CLIENT_ERROR: 36 | http_client_close(&connection->client); 37 | return CORE_ABORT; 38 | } 39 | 40 | return CORE_OK; 41 | } 42 | 43 | void connection_construct(connection *connection, core_callback *callback, void *state) 44 | { 45 | (void) callback; 46 | (void) state; 47 | *connection = (struct connection) {0}; 48 | http_client_construct(&connection->client, connection_request, connection); 49 | } 50 | 51 | void connection_start(connection *connection, worker *worker) 52 | { 53 | connection->worker = worker; 54 | http_client_open(&connection->client, net_client(connection->worker->pounce->addrinfo, 0)); 55 | } 56 | 57 | void connection_destruct(connection *connection) 58 | { 59 | http_client_destruct(&connection->client); 60 | *connection = (struct connection) {0}; 61 | } 62 | -------------------------------------------------------------------------------- /src/connection.h: -------------------------------------------------------------------------------- 1 | #ifndef CONNECTION_H_INCLUDED 2 | #define CONNECTION_H_INCLUDED 3 | 4 | #include 5 | 6 | #include "worker.h" 7 | #include "http_client.h" 8 | 9 | typedef struct connection connection; 10 | struct connection 11 | { 12 | worker *worker; 13 | http_client client; 14 | uint64_t request_start; 15 | uint64_t request_stop; 16 | size_t requests; 17 | size_t responses; 18 | }; 19 | 20 | void connection_construct(connection *, core_callback *, void *); 21 | void connection_start(connection *, worker *); 22 | void connection_destruct(connection *); 23 | 24 | #endif /* CONNECTION_H_INCLUDED */ 25 | -------------------------------------------------------------------------------- /src/http_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "net.h" 5 | #include "http_client.h" 6 | 7 | static core_status http_client_read(http_client *client) 8 | { 9 | segment data; 10 | size_t offset; 11 | ssize_t n; 12 | http_response response = {0}; 13 | core_status e; 14 | int close = 0; 15 | 16 | data = stream_read(&client->stream); 17 | for (offset = 0; offset < data.size; offset += n) 18 | { 19 | n = http_response_read(&response, segment_offset(data, offset)); 20 | if (dynamic_unlikely(n == 0)) 21 | break; 22 | if (dynamic_unlikely(n == -1)) 23 | return core_dispatch(&client->user, HTTP_CLIENT_ERROR, 0); 24 | e = core_dispatch(&client->user, HTTP_CLIENT_RESPONSE, (uintptr_t) &response); 25 | if (dynamic_unlikely(e != CORE_OK)) 26 | return e; 27 | close = segment_equal_case(http_headers_lookup(&response.headers, segment_string("Connection")), 28 | segment_string("close")); 29 | } 30 | 31 | stream_consume(&client->stream, offset); 32 | if (close) 33 | return core_dispatch(&client->user, HTTP_CLIENT_CLOSE, 0); 34 | 35 | if (offset) 36 | { 37 | e = core_dispatch(&client->user, HTTP_CLIENT_READY, 0); 38 | if (e != CORE_OK) 39 | return e; 40 | stream_flush(&client->stream); 41 | } 42 | 43 | return CORE_OK; 44 | } 45 | 46 | static core_status http_client_stream(core_event *event) 47 | { 48 | http_client *client = event->state; 49 | core_status e; 50 | 51 | switch (event->type) 52 | { 53 | case STREAM_FLUSH: 54 | e = core_dispatch(&client->user, HTTP_CLIENT_READY, 0); 55 | if (e != CORE_OK) 56 | return e; 57 | stream_flush(&client->stream); 58 | return CORE_OK; 59 | case STREAM_READ: 60 | return http_client_read(client); 61 | case STREAM_CLOSE: 62 | return core_dispatch(&client->user, HTTP_CLIENT_CLOSE, 0); 63 | default: 64 | return core_dispatch(&client->user, HTTP_CLIENT_ERROR, 0); 65 | } 66 | } 67 | 68 | void http_client_construct(http_client *client, core_callback *callback, void *state) 69 | { 70 | *client = (http_client) {.user = {.callback = callback, .state = state}}; 71 | stream_construct(&client->stream, http_client_stream, client); 72 | } 73 | 74 | void http_client_open(http_client *client, int fd) 75 | { 76 | stream_open(&client->stream, fd); 77 | stream_notify(&client->stream); 78 | } 79 | 80 | void http_client_close(http_client *client) 81 | { 82 | stream_close(&client->stream); 83 | } 84 | 85 | void http_client_destruct(http_client *client) 86 | { 87 | stream_destruct(&client->stream); 88 | } 89 | -------------------------------------------------------------------------------- /src/http_client.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_CLIENT_H_INCLUDED 2 | #define HTTP_CLIENT_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | enum 8 | { 9 | HTTP_CLIENT_ERROR, 10 | HTTP_CLIENT_READY, 11 | HTTP_CLIENT_RESPONSE, 12 | HTTP_CLIENT_CLOSE 13 | }; 14 | 15 | typedef struct http_client http_client; 16 | struct http_client 17 | { 18 | core_handler user; 19 | stream stream; 20 | }; 21 | 22 | void http_client_construct(http_client *, core_callback *, void *); 23 | void http_client_destruct(http_client *); 24 | void http_client_open(http_client *, int); 25 | void http_client_close(http_client *); 26 | 27 | #endif /* HTTP_CLIENT_H_INCLUDED */ 28 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "pounce.h" 8 | 9 | int main(int argc, char **argv) 10 | { 11 | pounce pounce; 12 | 13 | reactor_construct(); 14 | pounce_construct(&pounce, NULL, NULL); 15 | pounce_configure(&pounce, argc, argv); 16 | 17 | reactor_loop(); 18 | pounce_report(&pounce); 19 | 20 | pounce_destruct(&pounce); 21 | reactor_destruct(); 22 | } 23 | -------------------------------------------------------------------------------- /src/net.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "net.h" 10 | 11 | int net_client(struct addrinfo *addrinfo, int flags) 12 | { 13 | int fd, e = 0; 14 | 15 | if (!addrinfo) 16 | return -1; 17 | 18 | flags = flags ? flags : NET_CLIENT_DEFAULT; 19 | fd = socket(addrinfo->ai_family, addrinfo->ai_socktype | (flags & NET_FLAG_NONBLOCK ? SOCK_NONBLOCK : 0), 20 | addrinfo->ai_protocol); 21 | if (fd == -1) 22 | return -1; 23 | if (e == 0 && flags & NET_FLAG_NODELAY) 24 | e = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (int[]) {1}, sizeof(int)); 25 | if (e == 0 && flags & NET_FLAG_QUICKACK) 26 | e = setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, (int[]) {1}, sizeof(int)); 27 | if (e == 0) 28 | e = connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen); 29 | if (e == 0 || (e == -1 && errno == EINPROGRESS)) 30 | return fd; 31 | 32 | (void) close(fd); 33 | return -1; 34 | } 35 | -------------------------------------------------------------------------------- /src/net.h: -------------------------------------------------------------------------------- 1 | #ifndef NET_H_INCLUDED 2 | #define NET_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define NET_CLIENT_DEFAULT (NET_FLAG_NONBLOCK | NET_FLAG_NODELAY) 9 | 10 | int net_client(struct addrinfo *, int); 11 | 12 | #endif /* NET_H_INCLUDED */ 13 | -------------------------------------------------------------------------------- /src/pounce.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "url.h" 13 | #include "pounce.h" 14 | #include "worker.h" 15 | 16 | static core_status pounce_timeout(core_event *event) 17 | { 18 | pounce *pounce = event->state; 19 | 20 | if (event->type != TIMER_ALARM) 21 | err(1, "timer"); 22 | timer_clear(&pounce->timer); 23 | 24 | pounce_stop(pounce); 25 | return CORE_ABORT; 26 | } 27 | 28 | void pounce_construct(pounce *pounce, core_callback *callback, void *state) 29 | { 30 | (void) callback; 31 | (void) state; 32 | 33 | *pounce = (struct pounce) {0}; 34 | list_construct(&pounce->workers); 35 | timer_construct(&pounce->timer, pounce_timeout, pounce); 36 | string_construct(&pounce->request); 37 | } 38 | 39 | static void pounce_usage(void) 40 | { 41 | extern char *__progname; 42 | 43 | (void) fprintf(stderr, "Usage: %s [OPTION]... URL\n", __progname); 44 | (void) fprintf(stderr, "HTTP load generator.\n"); 45 | (void) fprintf(stderr, "\n"); 46 | (void) fprintf(stderr, "Options:\n"); 47 | (void) fprintf(stderr, " -c NUMBER set number of connections (defaults to number of cpu cores * 4)\n"); 48 | (void) fprintf(stderr, " -t NUMBER set number of threads (defaults to number of cpu cores)\n"); 49 | (void) fprintf(stderr, " -d SECONDS set duration of benchmark (defaults to 10 seconds)\n"); 50 | (void) fprintf(stderr, " -H HEADER add custom header to request\n"); 51 | (void) fprintf(stderr, " -p NUMBER pipeline a number of requests (defaults to off)\n"); 52 | (void) fprintf(stderr, 53 | " -a set thread affinity (automatic when threads equal number of cpu cores)\n"); 54 | (void) fprintf(stderr, " -r enable realtime scheduler (defaults to off)\n"); 55 | (void) fprintf(stderr, " -v increase verbosity\n"); 56 | (void) fprintf(stderr, " -h display this help\n"); 57 | } 58 | 59 | void pounce_configure(pounce *pounce, int argc, char **argv) 60 | { 61 | size_t i, remaining, share; 62 | worker *worker; 63 | int c, e; 64 | char *s; 65 | 66 | while (1) 67 | { 68 | c = getopt(argc, argv, "ac:d:H:p:rt:vh"); 69 | if (c == -1) 70 | break; 71 | switch (c) 72 | { 73 | case 'a': 74 | pounce->affinity = 1; 75 | break; 76 | case 'c': 77 | pounce->connections = strtoul(optarg, NULL, 0); 78 | break; 79 | case 'd': 80 | pounce->duration = strtod(optarg, NULL); 81 | break; 82 | case 'H': 83 | string_append(&pounce->request, optarg); 84 | string_append(&pounce->request, "\r\n"); 85 | break; 86 | case 'p': 87 | pounce->pipeline = strtoul(optarg, NULL, 0); 88 | break; 89 | case 'r': 90 | pounce->realtime = 1; 91 | break; 92 | case 't': 93 | pounce->workers_count = strtoul(optarg, NULL, 0); 94 | break; 95 | case 'v': 96 | pounce->verbosity++; 97 | break; 98 | case 'h': 99 | default: 100 | pounce_usage(); 101 | return; 102 | } 103 | } 104 | 105 | argc -= optind; 106 | argv += optind; 107 | if (argc != 1) 108 | { 109 | pounce_usage(); 110 | return; 111 | } 112 | 113 | if (pounce->workers_count == 0) 114 | pounce->workers_count = get_nprocs(); 115 | if (pounce->workers_count == (size_t) get_nprocs()) 116 | pounce->affinity = 1; 117 | if (pounce->connections == 0) 118 | pounce->connections = pounce->workers_count * 4; 119 | if (pounce->duration == 0) 120 | pounce->duration = 10.0; 121 | 122 | pounce->host = url_host(argv[0]); 123 | pounce->serv = url_port(argv[0]); 124 | pounce->target = url_target(argv[0]); 125 | e = asprintf(&s, "GET %s HTTP/1.1\r\nHost: %s\r\n", pounce->target, pounce->host); 126 | if (e == -1) 127 | err(1, "asprintf"); 128 | string_prepend(&pounce->request, s); 129 | free(s); 130 | string_append(&pounce->request, "\r\n"); 131 | 132 | net_resolve(pounce->host, pounce->serv, AF_INET, SOCK_STREAM, 0, &pounce->addrinfo); 133 | if (!pounce->addrinfo) 134 | { 135 | warnx("unable to resolve url http://%s:%s", pounce->host, pounce->serv); 136 | return; 137 | } 138 | 139 | pool_limits(NULL, 0, pounce->workers_count + 8); 140 | remaining = pounce->connections; 141 | for (i = 0; i < pounce->workers_count; i++) 142 | { 143 | share = remaining / (pounce->workers_count - i); 144 | remaining -= share; 145 | worker = list_push_back(&pounce->workers, NULL, sizeof *worker); 146 | worker_construct(worker, NULL, NULL); 147 | worker_configure(worker, pounce, i, share); 148 | } 149 | 150 | if (pounce->verbosity >= 1) 151 | (void) fprintf(stderr, "[pounce] running benchmark for %.02fs on %lu threads with %lu connections\n", 152 | pounce->duration, pounce->workers_count, pounce->connections); 153 | 154 | timer_set(&pounce->timer, pounce->duration * 1000000000, 0); 155 | } 156 | 157 | void pounce_stop(pounce *pounce) 158 | { 159 | worker *worker; 160 | 161 | list_foreach(&pounce->workers, worker) worker_stop(worker); 162 | } 163 | 164 | void pounce_report(pounce *pounce) 165 | { 166 | worker *worker; 167 | char prefix[32]; 168 | stats stats; 169 | 170 | if (list_empty(&pounce->workers)) 171 | return; 172 | 173 | if (pounce->verbosity >= 1) 174 | list_foreach(&pounce->workers, worker) 175 | { 176 | (void) snprintf(prefix, sizeof prefix, "[worker %lu]", worker->instance); 177 | stats_report(&worker->stats, prefix, stderr); 178 | } 179 | 180 | stats_construct(&stats); 181 | list_foreach(&pounce->workers, worker) stats_aggregate(&stats, &worker->stats); 182 | stats_report(&stats, NULL, stdout); 183 | stats_destruct(&stats); 184 | } 185 | 186 | void pounce_destruct(pounce *pounce) 187 | { 188 | worker *worker; 189 | 190 | list_foreach(&pounce->workers, worker) worker_destruct(worker); 191 | list_destruct(&pounce->workers, NULL); 192 | free(pounce->host); 193 | free(pounce->serv); 194 | free(pounce->target); 195 | if (pounce->addrinfo) 196 | freeaddrinfo(pounce->addrinfo); 197 | string_destruct(&pounce->request); 198 | *pounce = (struct pounce) {0}; 199 | } 200 | -------------------------------------------------------------------------------- /src/pounce.h: -------------------------------------------------------------------------------- 1 | #ifndef POUNCE_H_INCLUDED 2 | #define POUNCE_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | typedef struct pounce pounce; 11 | struct pounce 12 | { 13 | char *host; 14 | char *serv; 15 | char *target; 16 | int verbosity; 17 | size_t pipeline; 18 | int affinity; 19 | int realtime; 20 | double duration; 21 | size_t connections; 22 | size_t workers_count; 23 | struct addrinfo *addrinfo; 24 | list workers; 25 | timer timer; 26 | string request; 27 | }; 28 | 29 | void pounce_construct(pounce *, core_callback *, void *); 30 | void pounce_configure(pounce *, int, char **); 31 | void pounce_stop(pounce *); 32 | void pounce_report(pounce *); 33 | void pounce_destruct(pounce *); 34 | 35 | #endif /* POUNCE_H_INCLUDED */ 36 | 37 | -------------------------------------------------------------------------------- /src/stats.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "stats.h" 8 | 9 | void stats_construct(stats *stats) 10 | { 11 | *stats = (struct stats) {.time_start = core_now(NULL)}; 12 | } 13 | 14 | void stats_data(stats *stats, uint64_t latency, int success) 15 | { 16 | stats->data_points++; 17 | 18 | if (stats->data_points == 1 || latency < stats->latency_minimum) 19 | stats->latency_minimum = latency; 20 | if (stats->data_points == 1 || latency > stats->latency_maximum) 21 | stats->latency_maximum = latency; 22 | stats->latency_sum += latency; 23 | stats->latency_sum_squared += latency * latency; 24 | if (success) 25 | stats->success++; 26 | } 27 | 28 | void stats_counters(stats *stats, uint64_t awake, uint64_t sleep) 29 | { 30 | stats->counters_awake += awake; 31 | stats->counters_sleep += sleep; 32 | } 33 | 34 | void stats_aggregate(stats *aggregate, stats *group) 35 | { 36 | if (!aggregate->data_points) 37 | { 38 | *aggregate = *group; 39 | return; 40 | } 41 | 42 | if (!group->data_points) 43 | return; 44 | 45 | if (group->time_start < aggregate->time_start) 46 | aggregate->time_start = group->time_start; 47 | if (group->time_stop > aggregate->time_stop) 48 | aggregate->time_stop = group->time_stop; 49 | if (group->latency_minimum < aggregate->latency_minimum) 50 | aggregate->latency_minimum = group->latency_minimum; 51 | if (group->latency_maximum > aggregate->latency_maximum) 52 | aggregate->latency_maximum = group->latency_maximum; 53 | 54 | aggregate->latency_sum += group->latency_sum; 55 | aggregate->latency_sum_squared += group->latency_sum_squared; 56 | aggregate->data_points += group->data_points; 57 | aggregate->success += group->success; 58 | aggregate->counters_awake += group->counters_awake; 59 | aggregate->counters_sleep += group->counters_sleep; 60 | } 61 | 62 | void stats_report(stats *stats, char *prefix, FILE *file) 63 | { 64 | double duration, average, stddev; 65 | 66 | stats->time_stop = core_now(NULL); 67 | 68 | duration = (double) (stats->time_stop - stats->time_start) / 1000000000.0; 69 | average = (double) stats->latency_sum / stats->data_points; 70 | stddev = sqrt(((double) stats->latency_sum_squared / stats->data_points) - (average * average)); 71 | 72 | (void) fprintf(file, 73 | "%s%srequests %.0f rps, success %.02f%%" 74 | ", latency %.02fus/%.02fus/%.02fus/%.02fus, usage %.02f%% of %.02fGhz\n", 75 | prefix ? prefix : "", prefix ? " " : "", (double) stats->data_points / duration, 76 | 100. * (double) stats->success / stats->data_points, (double) stats->latency_minimum / 1000., 77 | (double) average / 1000., (double) stats->latency_maximum / 1000., stddev / 1000., 78 | 100. * (double) stats->counters_awake / (double) (stats->counters_awake + stats->counters_sleep), 79 | (double) (stats->counters_awake + stats->counters_sleep) / 1000000000.0 / duration); 80 | } 81 | 82 | void stats_destruct(stats *stats) 83 | { 84 | *stats = (struct stats) {0}; 85 | } 86 | -------------------------------------------------------------------------------- /src/stats.h: -------------------------------------------------------------------------------- 1 | #ifndef STATS_H_INCLUDED 2 | #define STATS_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | typedef struct stats stats; 8 | 9 | struct stats 10 | { 11 | uint64_t time_start; 12 | uint64_t time_stop; 13 | size_t data_points; 14 | size_t success; 15 | uint64_t latency_minimum; 16 | uint64_t latency_maximum; 17 | uint64_t latency_sum; 18 | uint64_t latency_sum_squared; 19 | uint64_t counters_awake; 20 | uint64_t counters_sleep; 21 | }; 22 | 23 | void stats_construct(stats *); 24 | void stats_data(stats *, uint64_t, int); 25 | void stats_counters(stats *, uint64_t, uint64_t); 26 | void stats_aggregate(stats *, stats *); 27 | void stats_report(stats *, char *, FILE *); 28 | void stats_destruct(stats *); 29 | 30 | #endif /* STATS_H_INCLUDED */ 31 | -------------------------------------------------------------------------------- /src/url.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "url.h" 4 | 5 | char *url_host(char *arg) 6 | { 7 | char *b = arg, *e, *d; 8 | 9 | d = strstr(b, "://"); 10 | if (d) 11 | b = d + 3; 12 | e = strchr(b, ':'); 13 | if (!e) 14 | e = strchr(b, '/'); 15 | if (!e) 16 | e = b + strlen(b); 17 | return strndup(b, e - b); 18 | } 19 | 20 | char *url_port(char *arg) 21 | { 22 | char *b = arg, *e, *d; 23 | 24 | d = strstr(b, "://"); 25 | if (d) 26 | b = d + 3; 27 | d = strchr(b, ':'); 28 | if (!d) 29 | return strdup("80"); 30 | b = d + 1; 31 | e = strchr(b, '/'); 32 | if (!e) 33 | e = b + strlen(b); 34 | return strndup(b, e - b); 35 | } 36 | 37 | char *url_target(char *arg) 38 | { 39 | char *b = arg, *d; 40 | 41 | d = strstr(b, "://"); 42 | if (d) 43 | b = d + 3; 44 | b = strchr(b, '/'); 45 | return b ? strdup(b) : strdup("/"); 46 | } 47 | -------------------------------------------------------------------------------- /src/url.h: -------------------------------------------------------------------------------- 1 | #ifndef URL_H_INCLUDED 2 | #define URL_H_INCLUDED 3 | 4 | char *url_host(char *); 5 | char *url_port(char *); 6 | char *url_target(char *); 7 | 8 | #endif /* URL_H_INCLUDED */ 9 | -------------------------------------------------------------------------------- /src/worker.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "pounce.h" 14 | #include "worker.h" 15 | #include "connection.h" 16 | 17 | int realtime(void) 18 | { 19 | struct sched_param param; 20 | struct rlimit rlim; 21 | int e; 22 | 23 | e = sched_getparam(0, ¶m); 24 | if (e == -1) 25 | return -1; 26 | 27 | param.sched_priority = sched_get_priority_max(SCHED_FIFO); 28 | rlim = (struct rlimit) {.rlim_cur = param.sched_priority, .rlim_max = param.sched_priority}; 29 | e = prlimit(0, RLIMIT_RTPRIO, &rlim, NULL); 30 | if (e == -1) 31 | return -1; 32 | 33 | e = sched_setscheduler(0, SCHED_FIFO, ¶m); 34 | if (e == -1) 35 | return -1; 36 | 37 | return 0; 38 | } 39 | 40 | static void worker_main(worker *worker) 41 | { 42 | list connections; 43 | connection *connection; 44 | size_t i; 45 | core_counters *counters; 46 | 47 | worker->thread = pthread_self(); 48 | reactor_construct(); 49 | if (worker->pounce->affinity) 50 | reactor_affinity(worker->instance); 51 | if (worker->pounce->realtime) 52 | realtime(); 53 | 54 | list_construct(&connections); 55 | for (i = 0; i < worker->connections_count; i++) 56 | { 57 | connection = list_push_back(&connections, NULL, sizeof *connection); 58 | connection_construct(connection, NULL, NULL); 59 | connection_start(connection, worker); 60 | } 61 | 62 | reactor_loop(); 63 | counters = core_get_counters(NULL); 64 | stats_counters(&worker->stats, counters->awake, counters->sleep); 65 | 66 | list_foreach(&connections, connection) connection_destruct(connection); 67 | list_destruct(&connections, NULL); 68 | 69 | reactor_destruct(); 70 | worker->thread = 0; 71 | } 72 | 73 | static core_status worker_job(core_event *event) 74 | { 75 | worker *worker = event->state; 76 | 77 | switch (event->type) 78 | { 79 | case POOL_REQUEST: 80 | worker_main(worker); 81 | break; 82 | case POOL_REPLY: 83 | worker->job = 0; 84 | worker->thread = 0; 85 | break; 86 | } 87 | return CORE_OK; 88 | } 89 | 90 | void worker_construct(worker *worker, core_callback *callback, void *state) 91 | { 92 | (void) callback; 93 | (void) state; 94 | 95 | *worker = (struct worker) {0}; 96 | stats_construct(&worker->stats); 97 | } 98 | 99 | void worker_configure(worker *worker, pounce *pounce, size_t instance, size_t connections) 100 | { 101 | worker->pounce = pounce; 102 | worker->instance = instance; 103 | worker->connections_count = connections; 104 | worker->job = pool_enqueue(NULL, worker_job, worker); 105 | } 106 | 107 | void worker_stop(worker *worker) 108 | { 109 | if (worker->thread) 110 | { 111 | pthread_kill(worker->thread, SIGTERM); 112 | worker->thread = 0; 113 | } 114 | 115 | if (worker->job) 116 | { 117 | pool_cancel(NULL, worker->job); 118 | worker->job = 0; 119 | } 120 | } 121 | 122 | void worker_destruct(worker *worker) 123 | { 124 | worker_stop(worker); 125 | stats_destruct(&worker->stats); 126 | } 127 | -------------------------------------------------------------------------------- /src/worker.h: -------------------------------------------------------------------------------- 1 | #ifndef WORKER_H_INCLUDED 2 | #define WORKER_H_INCLUDED 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "stats.h" 10 | #include "pounce.h" 11 | 12 | typedef struct worker worker; 13 | struct worker 14 | { 15 | pounce *pounce; 16 | size_t instance; 17 | size_t connections_count; 18 | core_id job; 19 | stats stats; 20 | pthread_t thread; 21 | }; 22 | 23 | void worker_construct(worker *, core_callback *, void *); 24 | void worker_configure(worker *, pounce *, size_t, size_t); 25 | void worker_stop(worker *); 26 | void worker_destruct(worker *); 27 | 28 | #endif /* WORKER_H_INCLUDED */ 29 | --------------------------------------------------------------------------------