diff --git a/.circleci/config.yml b/.circleci/config.yml index ea4d74a365..6bddcc95ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,6 +13,7 @@ jobs: - run: ./ci/setup_ci_environment.sh - run: ./ci/setup_cmake.sh - run: ./ci/install_protobuf.sh + - run: ./ci/install_libevent.sh - run: ./ci/do_ci.sh cmake.test - run: ./ci/do_ci.sh cmake.exporter.otprotocol.test - store_artifacts: @@ -42,9 +43,10 @@ jobs: steps: - checkout - run: ./ci/setup_ci_environment.sh + - run: ./ci/setup_cmake.sh - run: ./ci/install_bazelisk.sh - run: ./ci/install_gcc48.sh - - run: CC=/usr/bin/gcc-4.8 ./ci/do_ci.sh bazel.legacy.test + - run: CC=/usr/bin/gcc-4.8 CXX=/usr/bin/g++-4.8 ./ci/do_ci.sh bazel.legacy.test bazel_test: resource_class: xlarge @@ -53,6 +55,7 @@ jobs: steps: - checkout - run: ./ci/setup_ci_environment.sh + - run: ./ci/setup_cmake.sh - run: ./ci/install_bazelisk.sh - run: ./ci/do_ci.sh bazel.test @@ -63,6 +66,7 @@ jobs: steps: - checkout - run: ./ci/setup_ci_environment.sh + - run: ./ci/setup_cmake.sh - run: ./ci/install_bazelisk.sh - run: ./ci/do_ci.sh bazel.noexcept @@ -73,6 +77,7 @@ jobs: steps: - checkout - run: ./ci/setup_ci_environment.sh + - run: ./ci/setup_cmake.sh - run: ./ci/install_bazelisk.sh - run: ./ci/do_ci.sh bazel.asan @@ -83,6 +88,7 @@ jobs: steps: - checkout - run: ./ci/setup_ci_environment.sh + - run: ./ci/setup_cmake.sh - run: ./ci/install_bazelisk.sh - run: ./ci/do_ci.sh bazel.tsan @@ -93,6 +99,7 @@ jobs: steps: - checkout - run: ./ci/setup_ci_environment.sh + - run: ./ci/setup_cmake.sh - run: ./ci/install_bazelisk.sh - run: env BENCHMARK_DIR=/benchmark ./ci/do_ci.sh benchmark - store_artifacts: @@ -114,6 +121,7 @@ jobs: xcode: "11.0.0" steps: - checkout + - run: ./ci/install_osx_cmake.sh - run: ./ci/install_osx_bazelisk.sh - run: ./ci/do_ci.sh bazel.test @@ -126,6 +134,7 @@ jobs: - run: command: ./ci/install_windows_protobuf.ps1 no_output_timeout: 15m + - run: ./ci/install_windows_libevent.ps1 - run: ./ci/do_ci.ps1 cmake.test - run: ./ci/do_ci.sh cmake.exporter.otprotocol.test @@ -133,6 +142,7 @@ jobs: executor: win/vs2019 steps: - checkout + - run: ./ci/setup_windows_cmake.ps1 - run: ./ci/install_windows_bazelisk.ps1 - run: ./ci/do_ci.ps1 bazel.build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5d36c4a61..f1e6dc3a7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,12 @@ jobs: run: | sudo ./ci/setup_cmake.sh sudo ./ci/setup_ci_environment.sh + sudo ./ci/install_protobuf.sh + sudo ./ci/install_libevent.sh - name: run tests - run: ./ci/do_ci.sh cmake.test + run: | + ./ci/do_ci.sh cmake.test + ./ci/do_ci.sh cmake.exporter.otprotocol.test cmake_test_cxx20: name: CMake C++20 test @@ -28,6 +32,7 @@ jobs: run: | sudo ./ci/setup_ci_environment.sh sudo ./ci/setup_cmake.sh + sudo ./ci/install_libevent.sh - name: run tests run: ./ci/do_ci.sh cmake.c++20.test @@ -50,6 +55,7 @@ jobs: - uses: actions/checkout@v2 - name: setup run: | + sudo ./ci/setup_cmake.sh sudo ./ci/setup_ci_environment.sh sudo ./ci/install_bazelisk.sh sudo ./ci/install_gcc48.sh @@ -143,6 +149,8 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v2 + - name: setup + run: ./ci/install_osx_cmake.sh - name: run tests run: ./ci/do_ci.sh bazel.test @@ -153,9 +161,10 @@ jobs: - uses: actions/checkout@v2 - name: setup run: | - ./ci/setup_windows_cmake.ps1 ./ci/setup_windows_ci_environment.ps1 ./ci/install_windows_protobuf.ps1 + - name: setup libevent + run: ./ci/install_windows_libevent.ps1 - name: run cmake test run: ./ci/do_ci.ps1 cmake.test - name: run otprotocol test @@ -166,8 +175,6 @@ jobs: runs-on: windows-2019 steps: - uses: actions/checkout@v2 - - name: setup - run: ./ci/install_windows_bazelisk.ps1 - name: run tests run: ./ci/do_ci.ps1 bazel.build @@ -177,9 +184,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: setup - run: | - ./ci/setup_windows_cmake.ps1 - ./ci/setup_windows_ci_environment.ps1 + run: ./ci/setup_windows_ci_environment.ps1 - name: run tests run: ./ci/do_ci.ps1 cmake.test_example_plugin @@ -192,6 +197,7 @@ jobs: run: | sudo ./ci/setup_cmake.sh sudo ./ci/setup_ci_environment.sh + sudo ./ci/install_libevent.sh - name: run tests and generate report run: ./ci/do_ci.sh code.coverage - name: upload report diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fbfc1c059..995f5c559b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,14 +10,18 @@ if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 11) endif() +option(WITH_SDK "Whether to build the SDK or just the API" ON) option(WITH_OTPROTOCOL "Whether to include the OpenTelemetry Protocol in the SDK" OFF) +option(WITH_LIBEVENT "Build SDK with libevent support" ON) set(WITH_PROTOBUF OFF) if(WITH_OTPROTOCOL) set(WITH_PROTOBUF ON) endif() +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") + include(CTest) find_package(Threads) @@ -29,7 +33,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus") endif() -if(WITH_PROTOBUF) +if(WITH_SDK AND WITH_PROTOBUF) set(protobuf_MODULE_COMPATIBLE ON) find_package(Protobuf CONFIG NAMES protobuf) # Older versions of protobuf don't use cmake config files. @@ -38,21 +42,28 @@ if(WITH_PROTOBUF) endif() endif() -if(WITH_OTPROTOCOL) +if(WITH_SDK AND WITH_OTPROTOCOL) include(third_party/opentelemetry-proto/Protobuf.cmake) endif() +if(WITH_SDK AND WITH_LIBEVENT) + find_package(Libevent REQUIRED) + include_directories(SYSTEM ${LIBEVENT_INCLUDE_DIRS}) +endif() + if(BUILD_TESTING) find_package(GTest REQUIRED) find_package(benchmark REQUIRED) include_directories(SYSTEM ${GTEST_INCLUDE_DIRS}) endif() +include_directories(.) include_directories(api/include) add_subdirectory(api) -include_directories(sdk/include) -include_directories(sdk) -add_subdirectory(sdk) -include_directories(.) -add_subdirectory(exporters) +if(WITH_SDK) + include_directories(sdk/include) + include_directories(sdk) + add_subdirectory(sdk) + add_subdirectory(exporters) +endif() add_subdirectory(examples) diff --git a/WORKSPACE b/WORKSPACE index 1dd2ea37fc..8e0d1dc65b 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -37,6 +37,36 @@ new_local_repository( path = "third_party/opentelemetry-proto", ) +http_archive( + name = "bazel_skylib", + sha256 = "64ad2728ccdd2044216e4cec7815918b7bb3bb28c95b7e9d951f9d4eccb07625", + strip_prefix = "bazel-skylib-1.0.2", + urls = [ + "https://github.com/bazelbuild/bazel-skylib/archive/1.0.2.zip", + ], +) + +http_archive( + name = "rules_foreign_cc", + strip_prefix = "rules_foreign_cc-456425521973736ef346d93d3d6ba07d807047df", + url = "https://github.com/bazelbuild/rules_foreign_cc/archive/456425521973736ef346d93d3d6ba07d807047df.zip", +) + +load("@rules_foreign_cc//:workspace_definitions.bzl", "rules_foreign_cc_dependencies") + +rules_foreign_cc_dependencies([ +]) + +http_archive( + name = "com_github_libevent_libevent", + build_file = "//bazel:libevent.BUILD", + sha256 = "70158101eab7ed44fd9cc34e7f247b3cae91a8e4490745d9d6eb7edc184e4d96", + strip_prefix = "libevent-release-2.1.8-stable", + urls = [ + "https://github.com/libevent/libevent/archive/release-2.1.8-stable.zip", + ], +) + # GoogleTest framework. # Only needed for tests, not to build the OpenTelemetry library. http_archive( diff --git a/bazel/libevent.BUILD b/bazel/libevent.BUILD new file mode 100644 index 0000000000..e27b3d9335 --- /dev/null +++ b/bazel/libevent.BUILD @@ -0,0 +1,33 @@ +load("@rules_foreign_cc//tools/build_defs:cmake.bzl", "cmake_external") + +filegroup( + name = "srcs", + srcs = glob(["**"]), +) + +cmake_external( + name = "libevent", + cache_entries = { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_POSITION_INDEPENDENT_CODE": "on", + "BUILD_SHARED_LIBS": "off", + "BUILD_STATIC_LIBS": "on", + "EVENT__DISABLE_OPENSSL": "on", + "EVENT__DISABLE_REGRESS": "on", + "EVENT__DISABLE_TESTS": "on", + }, + generate_crosstool_file = select({ + "@io_opentelemetry_cpp//bazel:windows": True, + "//conditions:default": None, + }), + lib_source = ":srcs", + make_commands = select({ + "@io_opentelemetry_cpp//bazel:windows": ["MSBuild.exe INSTALL.vcxproj"], + "//conditions:default": None, + }), + static_libraries = select({ + "@io_opentelemetry_cpp//bazel:windows": ["event.lib"], + "//conditions:default": ["libevent.a"], + }), + visibility = ["//visibility:public"], +) diff --git a/bazel/opentelemetry_proto.BUILD b/bazel/opentelemetry_proto.BUILD index ddb9915bf2..d96eefe8e3 100644 --- a/bazel/opentelemetry_proto.BUILD +++ b/bazel/opentelemetry_proto.BUILD @@ -19,7 +19,7 @@ load("@rules_proto//proto:defs.bzl", "proto_library") proto_library( name = "common_proto", srcs = [ - "opentelemetry/proto/common/v1/common.proto", + "opentelemetry/proto/common/v1/common.proto", ], ) @@ -31,10 +31,10 @@ cc_proto_library( proto_library( name = "resource_proto", srcs = [ - "opentelemetry/proto/resource/v1/resource.proto", + "opentelemetry/proto/resource/v1/resource.proto", ], deps = [ - ":common_proto", + ":common_proto", ], ) @@ -46,11 +46,11 @@ cc_proto_library( proto_library( name = "trace_proto", srcs = [ - "opentelemetry/proto/trace/v1/trace.proto", + "opentelemetry/proto/trace/v1/trace.proto", ], deps = [ - ":common_proto", - ":resource_proto", + ":common_proto", + ":resource_proto", ], ) diff --git a/ci/Dockerfile b/ci/Dockerfile index a1f43af928..888eeb8de3 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -6,6 +6,7 @@ ADD setup_ci_environment.sh /setup-ci ADD setup_cmake.sh /setup-ci ADD install_gcc48.sh /setup-ci ADD install_bazelisk.sh /setup-ci +ADD install_libevent.sh /setup-ci ADD install_protobuf.sh /setup-ci ADD install_format_tools.sh /setup-ci @@ -13,5 +14,6 @@ RUN /setup-ci/setup_ci_environment.sh \ && /setup-ci/setup_cmake.sh \ && /setup-ci/install_gcc48.sh \ && /setup-ci/install_bazelisk.sh \ + && /setup-ci/install_libevent.sh \ && /setup-ci/install_protobuf.sh \ && /setup-ci/install_format_tools.sh diff --git a/ci/do_ci.ps1 b/ci/do_ci.ps1 index 367d76e080..f892eef725 100644 --- a/ci/do_ci.ps1 +++ b/ci/do_ci.ps1 @@ -73,7 +73,8 @@ switch ($action) { cd "$BUILD_DIR" cmake $SRC_DIR ` -DVCPKG_TARGET_TRIPLET=x64-windows ` - "-DCMAKE_TOOLCHAIN_FILE=$VCPKG_DIR\scripts\buildsystems\vcpkg.cmake" + "-DCMAKE_TOOLCHAIN_FILE=$VCPKG_DIR\scripts\buildsystems\vcpkg.cmake" ` + -DWITH_SDK=OFF $exit = $LASTEXITCODE if ($exit -ne 0) { exit $exit @@ -89,7 +90,8 @@ switch ($action) { cd "$BUILD_DIR" cmake $SRC_DIR ` -DVCPKG_TARGET_TRIPLET=x64-windows ` - "-DCMAKE_TOOLCHAIN_FILE=$VCPKG_DIR\scripts\buildsystems\vcpkg.cmake" + "-DCMAKE_TOOLCHAIN_FILE=$VCPKG_DIR\scripts\buildsystems\vcpkg.cmake" ` + -DWITH_SDK=OFF $exit = $LASTEXITCODE if ($exit -ne 0) { exit $exit diff --git a/ci/do_ci.sh b/ci/do_ci.sh index b6d23f763f..a53d13c9e4 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -61,6 +61,7 @@ EOF -DCMAKE_CXX_FLAGS="-Werror" \ -DCMAKE_EXE_LINKER_FLAGS="$LINKER_FLAGS" \ -DCMAKE_SHARED_LINKER_FLAGS="$LINKER_FLAGS" \ + -DWITH_SDK=OFF \ "${SRC_DIR}" make example_plugin cp examples/plugin/plugin/libexample_plugin.so ${PLUGIN_DIR} @@ -70,6 +71,7 @@ EOF rm -rf * cmake -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="-Werror" \ + -DWITH_SDK=OFF \ "${SRC_DIR}" make load_plugin_example examples/plugin/load/load_plugin_example ${PLUGIN_DIR}/libexample_plugin.so /dev/null diff --git a/ci/install_libevent.sh b/ci/install_libevent.sh new file mode 100755 index 0000000000..da40ec61c8 --- /dev/null +++ b/ci/install_libevent.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +apt-get install --no-install-recommends --no-install-suggests -y \ + libevent-dev + diff --git a/ci/install_osx_cmake.sh b/ci/install_osx_cmake.sh new file mode 100755 index 0000000000..5bbe1eda01 --- /dev/null +++ b/ci/install_osx_cmake.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +brew install cmake diff --git a/ci/install_windows_libevent.ps1 b/ci/install_windows_libevent.ps1 new file mode 100644 index 0000000000..292c0dd15d --- /dev/null +++ b/ci/install_windows_libevent.ps1 @@ -0,0 +1,5 @@ +$ErrorActionPreference = "Stop" +trap { $host.SetShouldExit(1) } + +cd vcpkg +./vcpkg install libevent:x64-windows diff --git a/cmake/Modules/FindLibevent.cmake b/cmake/Modules/FindLibevent.cmake new file mode 100644 index 0000000000..1f2e7ff2ea --- /dev/null +++ b/cmake/Modules/FindLibevent.cmake @@ -0,0 +1,48 @@ +# find LibEvent +# an event notification library (http://libevent.org/) +# +# Usage: +# LIBEVENT_INCLUDE_DIRS, where to find LibEvent headers +# LIBEVENT_LIBRARIES, LibEvent libraries +# Libevent_FOUND, If false, do not try to use libevent +# +# Taken from https://github.com/apache/thrift/blob/7edc8faefd391ce11eca3023a35cc54bcb2eb1af/build/cmake/FindLibevent.cmake +# with modification. + +set(LIBEVENT_ROOT CACHE PATH "Root directory of libevent installation") +set(LibEvent_EXTRA_PREFIXES /usr/local /opt/local "$ENV{HOME}" ${LIBEVENT_ROOT}) +foreach(prefix ${LibEvent_EXTRA_PREFIXES}) + list(APPEND LibEvent_INCLUDE_PATHS "${prefix}/include") + list(APPEND LibEvent_LIBRARIES_PATHS "${prefix}/lib") +endforeach() + +# Looking for "event.h" will find the Platform SDK include dir on windows +# so we also look for a peer header like evhttp.h to get the right path +find_path(LIBEVENT_INCLUDE_DIRS evhttp.h event.h PATHS ${LibEvent_INCLUDE_PATHS}) + +# "lib" prefix is needed on Windows in some cases +# newer versions of libevent use three libraries +find_library(LIBEVENT_LIBRARIES NAMES event_core PATHS ${LibEvent_LIBRARIES_PATHS}) + +if (LIBEVENT_LIBRARIES AND LIBEVENT_INCLUDE_DIRS) + set(Libevent_FOUND TRUE) + set(LIBEVENT_LIBRARIES ${LIBEVENT_LIBRARIES}) +else () + set(Libevent_FOUND FALSE) +endif () + +if (Libevent_FOUND) + if (NOT Libevent_FIND_QUIETLY) + message(STATUS "Found libevent: ${LIBEVENT_LIBRARIES}") + endif () +else () + if (LibEvent_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find libevent.") + endif () + message(STATUS "libevent NOT found.") +endif () + +mark_as_advanced( + LIBEVENT_LIBRARIES + LIBEVENT_INCLUDE_DIRS + ) diff --git a/cmake/TBD b/cmake/TBD deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index dcb04ccf4e..462e46fcad 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,2 +1,5 @@ -add_subdirectory(plugin) -add_subdirectory(simple) +if(WITH_SDK) + add_subdirectory(simple) +else() + add_subdirectory(plugin) +endif() diff --git a/sdk/BUILD b/sdk/BUILD index cc62431b53..e380eb7661 100644 --- a/sdk/BUILD +++ b/sdk/BUILD @@ -4,4 +4,7 @@ cc_library( name = "headers", hdrs = glob(["include/**/*.h"]), strip_include_prefix = "include", + deps = [ + "//api", + ], ) diff --git a/sdk/include/opentelemetry/sdk/common/file_descriptor.h b/sdk/include/opentelemetry/sdk/common/file_descriptor.h new file mode 100644 index 0000000000..08edc008a5 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/common/file_descriptor.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +#ifdef _WIN32 +using FileDescriptor = intptr_t; +#else +using FileDescriptor = int; +#endif +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/event/dispatcher.h b/sdk/include/opentelemetry/sdk/event/dispatcher.h new file mode 100644 index 0000000000..a271a8785d --- /dev/null +++ b/sdk/include/opentelemetry/sdk/event/dispatcher.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include "opentelemetry/sdk/common/file_descriptor.h" +#include "opentelemetry/sdk/event/file_event.h" +#include "opentelemetry/sdk/event/timer.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +/** + * Interface for managing asynchronous events. + * + * IoDispatcher enables an efficient design where SDKs use non-blocking sockets and a single + * background thread manages events by making successive calls to the OS's IO multiplexing polling + * function. + */ +class Dispatcher +{ +public: + virtual ~Dispatcher() = default; + + /** + * Create a timer event. + * @callback the callback to call when the event triggers + * @return a handle for the timer + */ + virtual std::unique_ptr CreateTimer(TimerCallback callback) noexcept = 0; + + /** + * Stop the event loop. + */ + virtual void Exit() noexcept = 0; + + /** + * Run the event loop until all events have been processed or Exit is called. + */ + virtual void Run() noexcept = 0; +}; +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/event/file_event.h b/sdk/include/opentelemetry/sdk/event/file_event.h new file mode 100644 index 0000000000..b69c1772f5 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/event/file_event.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +/** + * Available events that can be monitored on a file descriptor. + */ +struct FileReadyType +{ + static const uint32_t kRead = 1; + static const uint32_t kWrite = (1 << 1); + static const uint32_t kClosed = (1 << 2); +}; + +using FileReadyCallback = std::function; + +/** + * Manages an event set on a file descriptor. + * + * Note: Must be freed before the dipatcher is destructed + */ +class FileEvent +{ +public: + virtual ~FileEvent() = default; +}; +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/event/io_dispatcher.h b/sdk/include/opentelemetry/sdk/event/io_dispatcher.h new file mode 100644 index 0000000000..3ccb73c580 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/event/io_dispatcher.h @@ -0,0 +1,26 @@ +#pragma once + +#include "opentelemetry/sdk/event/dispatcher.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +class IoDispatcher : public Dispatcher +{ +public: + /** + * Create an event that triggers when a file descriptor becomes readable or writable. + * @param file_descriptor the file descriptor to monitor + * @param callback the callback to call when event triggers + * @param events the events to monitor + * @return a handle for the event + */ + virtual std::unique_ptr CreateFileEvent(FileDescriptor file_descriptor, + FileReadyCallback callback, + uint32_t events) noexcept = 0; +}; +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/event/timer.h b/sdk/include/opentelemetry/sdk/event/timer.h new file mode 100644 index 0000000000..3f09331381 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/event/timer.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +using TimerCallback = std::function; + +/** + * Manages a timer event. + * + * Note: Must be freed before the dipatcher is destructed + */ +class Timer +{ +public: + virtual ~Timer() = default; + + /** + * Enable a pending timeout. If a timeout is already pending, it will be reset to the new + * timeout. + */ + virtual void EnableTimer(std::chrono::microseconds timeout) noexcept = 0; + + template + void EnableTimer(std::chrono::duration timeout) noexcept + { + this->EnableTimer(std::chrono::duration_cast(timeout)); + } + + /** + * Disable a pending timeout. + * + * If no timeout is active, it does nothing. + */ + virtual void DisableTimer() noexcept = 0; +}; +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/CMakeLists.txt b/sdk/src/CMakeLists.txt index 8c8b199946..c96a933b88 100644 --- a/sdk/src/CMakeLists.txt +++ b/sdk/src/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(common) +add_subdirectory(event) add_subdirectory(trace) diff --git a/sdk/src/event/CMakeLists.txt b/sdk/src/event/CMakeLists.txt new file mode 100644 index 0000000000..b7892a965c --- /dev/null +++ b/sdk/src/event/CMakeLists.txt @@ -0,0 +1,3 @@ +if(WITH_LIBEVENT) + add_subdirectory(libevent) +endif() diff --git a/sdk/src/event/async_timer/BUILD b/sdk/src/event/async_timer/BUILD new file mode 100644 index 0000000000..47fa1d4541 --- /dev/null +++ b/sdk/src/event/async_timer/BUILD @@ -0,0 +1,25 @@ +# Copyright 2020, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "dispatcher", + srcs = glob(["**/*.cc"]), + hdrs = glob(["**/*.h"]), + include_prefix = "src/event/async_timer", + deps = [ + "//sdk:headers", + ], +) diff --git a/sdk/src/event/async_timer/dispatcher.cc b/sdk/src/event/async_timer/dispatcher.cc new file mode 100644 index 0000000000..5a03a344e9 --- /dev/null +++ b/sdk/src/event/async_timer/dispatcher.cc @@ -0,0 +1,41 @@ +#include "src/event/async_timer/dispatcher.h" + +#include + +#include "src/event/async_timer/timer.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace async_timer +{ +std::unique_ptr Dispatcher::CreateTimer(TimerCallback callback) noexcept +{ + return std::unique_ptr{new (std::nothrow) Timer{callback, *this}}; +} + +void Dispatcher::Exit() noexcept +{ + running_ = false; +} + +void Dispatcher::Run() noexcept +{ + while (running_ && !events_.empty()) + { + auto next_event = events_.begin(); + std::this_thread::sleep_until(next_event->first); + if (next_event->second.callback) + { + next_event->second.callback(); + } + next_event->second.timer->event_ = events_.end(); + events_.erase(next_event); + } +} +} // namespace async_timer +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/async_timer/dispatcher.h b/sdk/src/event/async_timer/dispatcher.h new file mode 100644 index 0000000000..0f87243e98 --- /dev/null +++ b/sdk/src/event/async_timer/dispatcher.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include "opentelemetry/sdk/event/dispatcher.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace async_timer +{ +class Timer; + +/** + * Implements the timer portion of the dispatcher interface. + * + * SDKs that don't use asynchronous networking can use this Dispatcher to avoid requiring an + * external depency on an event library such as libevent. + */ +class Dispatcher final : public event::Dispatcher +{ +public: + // event::Dispatcher + std::unique_ptr CreateTimer(TimerCallback callback) noexcept override; + + void Exit() noexcept override; + + void Run() noexcept override; + +private: + struct Event + { + TimerCallback callback; + Timer *timer; + }; + using EventIterator = std::map::const_iterator; + + friend class Timer; + bool running_{true}; + std::multimap events_; +}; +} // namespace async_timer +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/async_timer/timer.cc b/sdk/src/event/async_timer/timer.cc new file mode 100644 index 0000000000..f9b403a278 --- /dev/null +++ b/sdk/src/event/async_timer/timer.cc @@ -0,0 +1,37 @@ +#include "src/event/async_timer/timer.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace async_timer +{ +Timer::Timer(TimerCallback callback, Dispatcher &dispatcher) noexcept + : callback_(callback), dispatcher_(dispatcher), event_(dispatcher.events_.end()) +{} + +Timer::~Timer() +{ + this->DisableTimer(); +} + +void Timer::EnableTimer(std::chrono::microseconds timeout) noexcept +{ + this->DisableTimer(); + auto time_point = std::chrono::steady_clock::now() + timeout; + // Note: terminates on std::bad_alloc + event_ = dispatcher_.events_.emplace(time_point, Dispatcher::Event{callback_, this}); +} + +void Timer::DisableTimer() noexcept +{ + if (event_ != dispatcher_.events_.end()) + { + dispatcher_.events_.erase(event_); + } +} +} // namespace async_timer +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/async_timer/timer.h b/sdk/src/event/async_timer/timer.h new file mode 100644 index 0000000000..0d48ad9e90 --- /dev/null +++ b/sdk/src/event/async_timer/timer.h @@ -0,0 +1,35 @@ +#pragma once + +#include "opentelemetry/sdk/event/timer.h" +#include "opentelemetry/version.h" + +#include "src/event/async_timer/dispatcher.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace async_timer +{ +class Timer final : public event::Timer +{ +public: + Timer(TimerCallback callback, Dispatcher &dispatcher) noexcept; + + ~Timer(); + + void EnableTimer(std::chrono::microseconds timeout) noexcept override; + + void DisableTimer() noexcept override; + +private: + friend class Dispatcher; + TimerCallback callback_; + Dispatcher &dispatcher_; + Dispatcher::EventIterator event_; +}; +} // namespace async_timer +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/libevent/BUILD b/sdk/src/event/libevent/BUILD new file mode 100644 index 0000000000..c6a3139fa0 --- /dev/null +++ b/sdk/src/event/libevent/BUILD @@ -0,0 +1,26 @@ +# Copyright 2020, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "io_dispatcher", + srcs = glob(["**/*.cc"]), + hdrs = glob(["**/*.h"]), + include_prefix = "src/event/libevent", + deps = [ + "//sdk:headers", + "@com_github_libevent_libevent//:libevent", + ], +) diff --git a/sdk/src/event/libevent/CMakeLists.txt b/sdk/src/event/libevent/CMakeLists.txt new file mode 100644 index 0000000000..2ec6925b91 --- /dev/null +++ b/sdk/src/event/libevent/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(opentelemetry_event_libevent io_dispatcher.cc event.cc + event_base.cc file_event.cc timer.cc) +target_link_libraries(opentelemetry_event_libevent ${LIBEVENT_LIBRARIES}) diff --git a/sdk/src/event/libevent/event.cc b/sdk/src/event/libevent/event.cc new file mode 100644 index 0000000000..d7fb399b3d --- /dev/null +++ b/sdk/src/event/libevent/event.cc @@ -0,0 +1,73 @@ +#include "src/event/libevent/event.h" + +#include + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace libevent +{ +static void ToTimeval(std::chrono::microseconds timeout, timeval &tv) noexcept +{ + auto num_microseconds = timeout.count(); + const size_t microseconds_in_second = 1000000; + tv.tv_sec = static_cast(num_microseconds / microseconds_in_second); + tv.tv_usec = static_cast(num_microseconds % microseconds_in_second); +} + +Event::Event(EventBase &event_base, + FileDescriptor file_descriptor, + Callback callback, + short what, + void *context) noexcept +{ + event_ = ::event_new(event_base.libevent_handle(), file_descriptor, what, callback, context); + if (event_ == nullptr) + { + std::cerr << "event_new failed\n"; + std::terminate(); + } +} + +Event::~Event() noexcept +{ + ::event_free(event_); +} + +void Event::Add(std::chrono::microseconds timeout) noexcept +{ + int rcode; + if (timeout.count() == 0) + { + rcode = ::event_add(event_, nullptr); + } + else + { + timeval tv; + ToTimeval(timeout, tv); + rcode = ::event_add(event_, &tv); + } + if (rcode == -1) + { + std::cerr << "event_add failed\n"; + std::terminate(); + } +} + +void Event::Delete() noexcept +{ + auto rcode = ::event_del(event_); + if (rcode == -1) + { + std::cerr << "event_del failed\n"; + std::terminate(); + } +} +} // namespace libevent +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/libevent/event.h b/sdk/src/event/libevent/event.h new file mode 100644 index 0000000000..baf314a46c --- /dev/null +++ b/sdk/src/event/libevent/event.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "src/event/libevent/event_base.h" + +#include "opentelemetry/sdk/common/file_descriptor.h" +#include "opentelemetry/version.h" + +struct event; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace libevent +{ +class Event +{ +public: + using Callback = void (*)(FileDescriptor, short what, void *context); + + Event(EventBase &event_base, + FileDescriptor file_descriptor, + Callback callback, + short what, + void *context) noexcept; + + ~Event() noexcept; + + virtual void Add(std::chrono::microseconds timeout = std::chrono::microseconds{0}) noexcept; + + virtual void Delete() noexcept; + +private: + ::event *event_; +}; +} // namespace libevent +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/libevent/event_base.cc b/sdk/src/event/libevent/event_base.cc new file mode 100644 index 0000000000..0c4425629a --- /dev/null +++ b/sdk/src/event/libevent/event_base.cc @@ -0,0 +1,64 @@ +#include "src/event/libevent/event_base.h" + +#include +#include + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace libevent +{ +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +EventBase::EventBase() noexcept +{ + event_base_ = ::event_base_new(); + if (event_base_ == nullptr) + { + std::cerr << "event_base_new failed\n"; + std::terminate(); + } +} + +//------------------------------------------------------------------------------ +// destructor +//------------------------------------------------------------------------------ +EventBase::~EventBase() +{ + ::event_base_free(event_base_); +} + +//------------------------------------------------------------------------------ +// Dispatch +//------------------------------------------------------------------------------ +void EventBase::Dispatch() const noexcept +{ + auto rcode = ::event_base_dispatch(event_base_); + if (rcode == -1) + { + std::cerr << "event_base_dispatch failed\n"; + std::terminate(); + } +} + +//------------------------------------------------------------------------------ +// LoopBreak +//------------------------------------------------------------------------------ +void EventBase::LoopBreak() const noexcept +{ + auto rcode = ::event_base_loopbreak(event_base_); + if (rcode == -1) + { + std::cerr << "event_base_loopbreak failed\n"; + std::terminate(); + } +} +} // namespace libevent +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/libevent/event_base.h b/sdk/src/event/libevent/event_base.h new file mode 100644 index 0000000000..67cedf43fd --- /dev/null +++ b/sdk/src/event/libevent/event_base.h @@ -0,0 +1,53 @@ +#pragma once + +#include + +struct event_base; + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace libevent +{ +/** + * Wrapper for libevent's event_base struct. + */ +class EventBase +{ +public: + EventBase() noexcept; + + EventBase(EventBase &&other) = delete; + EventBase(const EventBase &) = delete; + + ~EventBase(); + + EventBase &operator=(EventBase &&other) noexcept = delete; + EventBase &operator=(const EventBase &) = delete; + + /** + * @return the underlying event_base. + */ + event_base *libevent_handle() const noexcept { return event_base_; } + + /** + * Run the dispatch event loop. + */ + void Dispatch() const noexcept; + + /** + * Break out of the dispatch event loop. + */ + void LoopBreak() const noexcept; + +private: + event_base *event_base_; +}; +} // namespace libevent +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/libevent/file_event.cc b/sdk/src/event/libevent/file_event.cc new file mode 100644 index 0000000000..9f5937caff --- /dev/null +++ b/sdk/src/event/libevent/file_event.cc @@ -0,0 +1,69 @@ +#include "src/event/libevent/file_event.h" + +#include + +#include "event2/event.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace libevent +{ +static short ToLibeventWhat(uint32_t events) noexcept +{ + short result = 0; + if (events & FileReadyType::kRead) + { + result |= EV_READ; + } + if (events & FileReadyType::kWrite) + { + result |= EV_WRITE; + } + if (events & FileReadyType::kClosed) + { + result |= EV_CLOSED; + } + assert(events != 0); + return result; +} + +static uint32_t FromLibeventWhat(short what) noexcept +{ + uint32_t result = 0; + if (what & EV_READ) + { + result |= FileReadyType::kRead; + } + if (what & EV_WRITE) + { + result |= FileReadyType::kWrite; + } + if (what & EV_CLOSED) + { + result |= FileReadyType::kClosed; + } + return result; +} + +FileEvent::FileEvent(EventBase &event_base, + FileDescriptor file_descriptor, + uint32_t events, + Callback callback) noexcept + : event_{event_base, file_descriptor, OnFileEvent, ToLibeventWhat(events), this}, + callback_{std::move(callback)} +{ + assert(callback_); + event_.Add(); +} + +void FileEvent::OnFileEvent(FileDescriptor /*file_descriptor*/, short what, void *context) noexcept +{ + static_cast(context)->callback_(FromLibeventWhat(what)); +} +} // namespace libevent +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/libevent/file_event.h b/sdk/src/event/libevent/file_event.h new file mode 100644 index 0000000000..eca4f10150 --- /dev/null +++ b/sdk/src/event/libevent/file_event.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "src/event/libevent/event.h" + +#include "opentelemetry/sdk/event/file_event.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace libevent +{ +class FileEvent final : public event::FileEvent +{ +public: + using Callback = std::function; + + FileEvent(EventBase &event_base, + FileDescriptor file_descriptor, + uint32_t events, + Callback callback) noexcept; + +private: + Event event_; + Callback callback_; + + static void OnFileEvent(FileDescriptor file_descriptor, short what, void *context) noexcept; +}; +} // namespace libevent +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/libevent/io_dispatcher.cc b/sdk/src/event/libevent/io_dispatcher.cc new file mode 100644 index 0000000000..1a3baa3db3 --- /dev/null +++ b/sdk/src/event/libevent/io_dispatcher.cc @@ -0,0 +1,39 @@ +#include "src/event/libevent/io_dispatcher.h" +#include "src/event/libevent/file_event.h" +#include "src/event/libevent/timer.h" + +#include "event2/event.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace libevent +{ +std::unique_ptr IoDispatcher::CreateFileEvent(FileDescriptor file_descriptor, + FileReadyCallback callback, + uint32_t events) noexcept +{ + return std::unique_ptr{ + new (std::nothrow) FileEvent{event_base_, file_descriptor, events, callback}}; +} + +std::unique_ptr IoDispatcher::CreateTimer(TimerCallback callback) noexcept +{ + return std::unique_ptr{new (std::nothrow) Timer{event_base_, callback}}; +} + +void IoDispatcher::Exit() noexcept +{ + event_base_.LoopBreak(); +} + +void IoDispatcher::Run() noexcept +{ + event_base_.Dispatch(); +} +} // namespace libevent +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/libevent/io_dispatcher.h b/sdk/src/event/libevent/io_dispatcher.h new file mode 100644 index 0000000000..c09877aa88 --- /dev/null +++ b/sdk/src/event/libevent/io_dispatcher.h @@ -0,0 +1,40 @@ +#pragma once + +#include "opentelemetry/sdk/event/io_dispatcher.h" +#include "opentelemetry/version.h" + +#include "src/event/libevent/event_base.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace libevent +{ +/** + * Manages asynchronous events on a file descriptor. + * + * Built on top of libevent https://libevent.org/ + */ +class IoDispatcher final : public event::IoDispatcher +{ +public: + // Dispatcher + std::unique_ptr CreateFileEvent(FileDescriptor file_descriptor, + FileReadyCallback callback, + uint32_t events) noexcept override; + + std::unique_ptr CreateTimer(TimerCallback callback) noexcept override; + + void Exit() noexcept override; + + void Run() noexcept override; + +private: + EventBase event_base_; +}; +} // namespace libevent +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/libevent/timer.cc b/sdk/src/event/libevent/timer.cc new file mode 100644 index 0000000000..e8df443635 --- /dev/null +++ b/sdk/src/event/libevent/timer.cc @@ -0,0 +1,35 @@ +#include "src/event/libevent/timer.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace libevent +{ +Timer::Timer(EventBase &event_base, Callback callback) noexcept + : event_{event_base, -1, OnTimeout, 0, this}, callback_{std::move(callback)} +{ + assert(callback_); +} + +void Timer::EnableTimer(std::chrono::microseconds timeout) noexcept +{ + event_.Add(timeout); +} + +void Timer::DisableTimer() noexcept +{ + event_.Delete(); +} + +void Timer::OnTimeout(FileDescriptor /*file_descriptor*/, short /*what*/, void *context) noexcept +{ + static_cast(context)->callback_(); +} +} // namespace libevent +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/event/libevent/timer.h b/sdk/src/event/libevent/timer.h new file mode 100644 index 0000000000..ed221fbe87 --- /dev/null +++ b/sdk/src/event/libevent/timer.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "src/event/libevent/event.h" + +#include "opentelemetry/sdk/event/timer.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace event +{ +namespace libevent +{ +class Timer final : public event::Timer +{ +public: + using Callback = std::function; + + Timer(EventBase &event_base, Callback callback) noexcept; + + // event::Timer + void EnableTimer(std::chrono::microseconds timeout) noexcept override; + + void DisableTimer() noexcept override; + +private: + Event event_; + Callback callback_; + + static void OnTimeout(FileDescriptor /*file_descriptor*/, short /*what*/, void *context) noexcept; +}; +} // namespace libevent +} // namespace event +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/test/CMakeLists.txt b/sdk/test/CMakeLists.txt index 8c8b199946..c96a933b88 100644 --- a/sdk/test/CMakeLists.txt +++ b/sdk/test/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(common) +add_subdirectory(event) add_subdirectory(trace) diff --git a/sdk/test/event/CMakeLists.txt b/sdk/test/event/CMakeLists.txt new file mode 100644 index 0000000000..b7892a965c --- /dev/null +++ b/sdk/test/event/CMakeLists.txt @@ -0,0 +1,3 @@ +if(WITH_LIBEVENT) + add_subdirectory(libevent) +endif() diff --git a/sdk/test/event/async_timer/BUILD b/sdk/test/event/async_timer/BUILD new file mode 100644 index 0000000000..ea7fee99e9 --- /dev/null +++ b/sdk/test/event/async_timer/BUILD @@ -0,0 +1,11 @@ +cc_test( + name = "dispatcher_test", + srcs = [ + "dispatcher_test.cc", + ], + linkstatic = 1, + deps = [ + "//sdk/src/event/async_timer:dispatcher", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/sdk/test/event/async_timer/dispatcher_test.cc b/sdk/test/event/async_timer/dispatcher_test.cc new file mode 100644 index 0000000000..afd105ddf9 --- /dev/null +++ b/sdk/test/event/async_timer/dispatcher_test.cc @@ -0,0 +1,70 @@ +#include "src/event/async_timer/dispatcher.h" + +#include + +#include +using opentelemetry::sdk::event::Timer; +using opentelemetry::sdk::event::async_timer::Dispatcher; + +TEST(DispatcherTest, Timer) +{ + Dispatcher dispatcher; + std::chrono::steady_clock::time_point t1, t2; + auto timer = dispatcher.CreateTimer([&] { t2 = std::chrono::steady_clock::now(); }); + timer->EnableTimer(std::chrono::milliseconds{100}); + t1 = std::chrono::steady_clock::now(); + dispatcher.Run(); + auto duration = t2 - t1; + EXPECT_TRUE(duration > std::chrono::milliseconds{50}); + EXPECT_TRUE(duration < std::chrono::milliseconds{200}); +} + +TEST(DispatcherTest, ResetTimer) +{ + Dispatcher dispatcher; + std::chrono::steady_clock::time_point t1, t2; + auto timer = dispatcher.CreateTimer([&] { t2 = std::chrono::steady_clock::now(); }); + timer->EnableTimer(std::chrono::milliseconds{500}); + timer->EnableTimer(std::chrono::milliseconds{100}); + t1 = std::chrono::steady_clock::now(); + dispatcher.Run(); + auto duration = t2 - t1; + EXPECT_TRUE(duration > std::chrono::milliseconds{50}); + EXPECT_TRUE(duration < std::chrono::milliseconds{300}); +} + +TEST(DispatcherTest, TimerOrder) +{ + Dispatcher dispatcher; + std::vector order; + + auto t1 = dispatcher.CreateTimer([&] { order.push_back(1); }); + t1->EnableTimer(std::chrono::milliseconds{5}); + + auto t2 = dispatcher.CreateTimer([&] { order.push_back(2); }); + t2->EnableTimer(std::chrono::milliseconds{2}); + + auto t3 = dispatcher.CreateTimer([&] { order.push_back(3); }); + t3->EnableTimer(std::chrono::milliseconds{7}); + + dispatcher.Run(); + + EXPECT_EQ(order, (std::vector{2, 1, 3})); +} + +TEST(DispatcherTest, ReuseTimer) +{ + Dispatcher dispatcher; + std::vector order; + + auto t1 = dispatcher.CreateTimer([&] { order.push_back(1); }); + t1->EnableTimer(std::chrono::milliseconds{5}); + + dispatcher.Run(); + + t1->EnableTimer(std::chrono::milliseconds{5}); + + dispatcher.Run(); + + EXPECT_EQ(order, (std::vector{1, 1})); +} diff --git a/sdk/test/event/libevent/BUILD b/sdk/test/event/libevent/BUILD new file mode 100644 index 0000000000..50b7a261a8 --- /dev/null +++ b/sdk/test/event/libevent/BUILD @@ -0,0 +1,11 @@ +cc_test( + name = "io_dispatcher_test", + srcs = [ + "io_dispatcher_test.cc", + ], + linkstatic = 1, + deps = [ + "//sdk/src/event/libevent:io_dispatcher", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/sdk/test/event/libevent/CMakeLists.txt b/sdk/test/event/libevent/CMakeLists.txt new file mode 100644 index 0000000000..7f7add8d03 --- /dev/null +++ b/sdk/test/event/libevent/CMakeLists.txt @@ -0,0 +1,6 @@ +foreach(testname io_dispatcher_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_event_libevent) + gtest_add_tests(TARGET ${testname} TEST_PREFIX event. TEST_LIST ${testname}) +endforeach() diff --git a/sdk/test/event/libevent/io_dispatcher_test.cc b/sdk/test/event/libevent/io_dispatcher_test.cc new file mode 100644 index 0000000000..f7b88c6e82 --- /dev/null +++ b/sdk/test/event/libevent/io_dispatcher_test.cc @@ -0,0 +1,32 @@ +#include "src/event/libevent/io_dispatcher.h" + +#include + +#include +using opentelemetry::sdk::event::Timer; +using opentelemetry::sdk::event::libevent::IoDispatcher; + +TEST(DispatcherTest, Timer) +{ + IoDispatcher dispatcher; + std::chrono::steady_clock::time_point t1, t2; + auto timer = dispatcher.CreateTimer([&] { t2 = std::chrono::steady_clock::now(); }); + timer->EnableTimer(std::chrono::milliseconds{100}); + t1 = std::chrono::steady_clock::now(); + dispatcher.Run(); + auto duration = t2 - t1; + EXPECT_TRUE(duration > std::chrono::milliseconds{50}); + EXPECT_TRUE(duration < std::chrono::milliseconds{200}); +} + +TEST(DispatcherTest, Exit) +{ + IoDispatcher dispatcher; + std::unique_ptr timer; + auto f = [&] { + timer->EnableTimer(std::chrono::milliseconds{1}); + dispatcher.Exit(); + }; + timer = dispatcher.CreateTimer(f); + dispatcher.Run(); +} diff --git a/tools/format.sh b/tools/format.sh index 1b150dbca3..48323e45a3 100755 --- a/tools/format.sh +++ b/tools/format.sh @@ -50,8 +50,8 @@ if [[ -z "$BUILDIFIER" ]]; then fi if which "$BUILDIFIER" >/dev/null; then echo "Running $BUILDIFIER" - "$BUILDIFIER" $($FIND -name WORKSPACE -print -o -name BUILD -print -o \ - -name '*.BUILD' -o -name '*.bzl' -print) + "$BUILDIFIER" $($FIND -name WORKSPACE -print -o -name BUILD -print -o -name '*.BUILD' -print -o \ + -name '*.bzl' -print) else echo "Can't find buildifier. It can be installed with:" echo " go get github.com/bazelbuild/buildtools/buildifier"