feat/support_fiber #6
37
.woodpecker/linux-aarch64-gcc.yml
Normal file
37
.woodpecker/linux-aarch64-gcc.yml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
when:
|
||||||
|
- event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
- path:
|
||||||
|
include:
|
||||||
|
- ".woodpecker/linux-aarch64-gcc.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
BUILD_TYPE:
|
||||||
|
- Debug
|
||||||
|
- Release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: linux-aarch64-gcc-build
|
||||||
|
image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=./toolchains/aarch64-linux-gnu.toolchain.cmake
|
||||||
|
- cmake --build build -j $(nproc)
|
||||||
|
|
||||||
|
- name: linux-aarch64-gcc-test
|
||||||
|
image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
commands:
|
||||||
|
- cd build
|
||||||
|
- ctest --output-on-failure -j $(nproc)
|
||||||
|
|
||||||
|
# - name: linux-aarch64-gcc-benchmark
|
||||||
|
# image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
# commands:
|
||||||
|
# - ./build/bin/tile_bm_all
|
||||||
|
|
||||||
|
|
30
.woodpecker/linux-arm-gcc.yml.stop
Normal file
30
.woodpecker/linux-arm-gcc.yml.stop
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
when:
|
||||||
|
- event: []
|
||||||
|
#- push
|
||||||
|
#- pull_request
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
BUILD_TYPE:
|
||||||
|
- Debug
|
||||||
|
- Release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: linux-arm-gcc-build
|
||||||
|
image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=./toolchains/arm-linux-gnueabihf.toolchain.cmake
|
||||||
|
- cmake --build build -j $(nproc)
|
||||||
|
|
||||||
|
- name: linux-arm-gcc-test
|
||||||
|
image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
commands:
|
||||||
|
- cd build
|
||||||
|
- ctest --output-on-failure -j $(nproc)
|
||||||
|
|
||||||
|
# - name: linux-arm-gcc-benchmark
|
||||||
|
# image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
# commands:
|
||||||
|
# - ./build/bin/tile_bm_all
|
||||||
|
|
||||||
|
|
37
.woodpecker/linux-x64-gcc.yml
Normal file
37
.woodpecker/linux-x64-gcc.yml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
when:
|
||||||
|
- event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
- path:
|
||||||
|
include:
|
||||||
|
- ".woodpecker/linux-x64-gcc.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
BUILD_TYPE:
|
||||||
|
- Debug
|
||||||
|
- Release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: linux-x64-gcc-build
|
||||||
|
image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON
|
||||||
|
- cmake --build build -j $(nproc)
|
||||||
|
|
||||||
|
- name: linux-x64-gcc-test
|
||||||
|
image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
commands:
|
||||||
|
- cd build
|
||||||
|
- ctest --output-on-failure -j $(nproc)
|
||||||
|
|
||||||
|
- name: linux-x64-gcc-benchmark
|
||||||
|
image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
commands:
|
||||||
|
- ./build/bin/tile_bm_all
|
||||||
|
|
||||||
|
|
36
.woodpecker/linux-x86-gcc.yml.stop
Normal file
36
.woodpecker/linux-x86-gcc.yml.stop
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
when:
|
||||||
|
- event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
- path:
|
||||||
|
include:
|
||||||
|
- ".woodpecker/linux-x86-gcc.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
matrix:
|
||||||
|
BUILD_TYPE:
|
||||||
|
- Debug
|
||||||
|
- Release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: linux-x86-gcc-build
|
||||||
|
image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=./toolchains/host.gcc-m32.toolchain.cmake
|
||||||
|
- cmake --build build -j $(nproc)
|
||||||
|
|
||||||
|
- name: linux-x86-gcc-test
|
||||||
|
image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
commands:
|
||||||
|
- cd build
|
||||||
|
- ctest --output-on-failure -j $(nproc)
|
||||||
|
|
||||||
|
- name: linux-x86-gcc-benchmark
|
||||||
|
image: art.uocat.com/docker/tqcq/cross:v1.0.1
|
||||||
|
commands:
|
||||||
|
- ./build/bin/tile_bm_all
|
||||||
|
|
||||||
|
|
@ -3,10 +3,13 @@ cmake_minimum_required(VERSION 3.5)
|
|||||||
set(tile_VERSION_MAJOR 0)
|
set(tile_VERSION_MAJOR 0)
|
||||||
set(tile_VERSION_MINOR 1)
|
set(tile_VERSION_MINOR 1)
|
||||||
set(tile_VERSION_PATCH 0)
|
set(tile_VERSION_PATCH 0)
|
||||||
|
set(tile_VERSION_BUILD $ENV{CI_STEP_NUMBER})
|
||||||
|
if (NOT tile_VERSION_BUILD)
|
||||||
|
set(tile_VERSION_BUILD "0")
|
||||||
|
endif()
|
||||||
project(
|
project(
|
||||||
tile
|
tile
|
||||||
VERSION ${tile_VERSION_MAJOR}.${tile_VERSION_MINOR}.${tile_VERSION_PATCH}
|
VERSION ${tile_VERSION_MAJOR}.${tile_VERSION_MINOR}.${tile_VERSION_PATCH}.${tile_VERSION_BUILD}
|
||||||
LANGUAGES C CXX)
|
LANGUAGES C CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
@ -89,7 +92,7 @@ add_subdirectory("third_party/gflags")
|
|||||||
set(GFLAGS_USE_TARGET_NAMESPACE ON)
|
set(GFLAGS_USE_TARGET_NAMESPACE ON)
|
||||||
set(gflags_DIR "${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags")
|
set(gflags_DIR "${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags")
|
||||||
add_subdirectory("third_party/glog")
|
add_subdirectory("third_party/glog")
|
||||||
add_subdirectory("third_party/context")
|
# add_subdirectory("third_party/context")
|
||||||
|
|
||||||
set(CURL_DISABLE_TESTS ON)
|
set(CURL_DISABLE_TESTS ON)
|
||||||
set(CURL_CA_PATH
|
set(CURL_CA_PATH
|
||||||
@ -185,10 +188,12 @@ set(TILE_SRCS
|
|||||||
"tile/base/thread/rw_mutex.cc"
|
"tile/base/thread/rw_mutex.cc"
|
||||||
"tile/base/thread/scoped_lock.cc"
|
"tile/base/thread/scoped_lock.cc"
|
||||||
"tile/base/thread/spinlock.cc"
|
"tile/base/thread/spinlock.cc"
|
||||||
"tile/fiber/detail/fiber.cc"
|
"tile/base/memory_barrier.cc"
|
||||||
|
# "tile/fiber/detail/fiber.cc"
|
||||||
"tile/io/detail/eintr_safe.cc"
|
"tile/io/detail/eintr_safe.cc"
|
||||||
"tile/io/native/acceptor.cc"
|
"tile/io/native/acceptor.cc"
|
||||||
"tile/io/descriptor.cc"
|
"tile/io/descriptor.cc"
|
||||||
|
"tile/io/util/rate_limiter.cc"
|
||||||
"tile/io/event_loop.cc"
|
"tile/io/event_loop.cc"
|
||||||
"tile/init.cc"
|
"tile/init.cc"
|
||||||
"tile/init/on_init.cc"
|
"tile/init/on_init.cc"
|
||||||
@ -205,6 +210,9 @@ set(TILE_SRCS
|
|||||||
"tile/rpc/protocol/http/buffer_io.cc"
|
"tile/rpc/protocol/http/buffer_io.cc"
|
||||||
"tile/rpc/protocol/message.cc"
|
"tile/rpc/protocol/message.cc"
|
||||||
# "tile/rpc/server.cc"
|
# "tile/rpc/server.cc"
|
||||||
|
"tile/util/config.cc"
|
||||||
|
"tile/util/ini_file_config.cc"
|
||||||
|
"tile/util/layered_config.cc"
|
||||||
)
|
)
|
||||||
|
|
||||||
if((NOT TILE_HAVE_GETIFADDRS) OR (NOT TILE_HAVE_FREEIFADDRS))
|
if((NOT TILE_HAVE_GETIFADDRS) OR (NOT TILE_HAVE_FREEIFADDRS))
|
||||||
@ -220,7 +228,9 @@ target_precompile_headers(tile PUBLIC json/nlohann/json.hpp)
|
|||||||
# target_sources(tile PRIVATE ${TILE_SRCS})
|
# target_sources(tile PRIVATE ${TILE_SRCS})
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
tile
|
tile
|
||||||
PUBLIC "${CMAKE_CURRENT_BINARY_DIR}"
|
PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/mustache.hpp"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/fmt/include"
|
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/fmt/include"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/third_party/glog"
|
"${CMAKE_CURRENT_BINARY_DIR}/third_party/glog"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/glog/src"
|
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/glog/src"
|
||||||
@ -232,7 +242,8 @@ target_include_directories(
|
|||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
tile
|
tile
|
||||||
PUBLIC # -Wl,--start-group
|
PUBLIC # -Wl,--start-group
|
||||||
nova_context zlib gflags::gflags glog::glog
|
# nova_context
|
||||||
|
zlib gflags::gflags glog::glog
|
||||||
jsoncpp_static
|
jsoncpp_static
|
||||||
# -Wl,--end-group
|
# -Wl,--end-group
|
||||||
libcurl fmt)
|
libcurl fmt)
|
||||||
@ -284,8 +295,9 @@ if(TILE_BUILD_TESTS)
|
|||||||
add_test(NAME ${test_name} COMMAND ${test_name})
|
add_test(NAME ${test_name} COMMAND ${test_name})
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
tile_add_test(io_util_rate_limiter_test "tile/io/util/rate_limiter_test.cc")
|
||||||
tile_add_test(base_exposed_var_test "tile/base/exposed_var_test.cc")
|
tile_add_test(base_exposed_var_test "tile/base/exposed_var_test.cc")
|
||||||
tile_add_test(fiber_detail_scheduler_test "tile/fiber/detail/scheduler_test.cc")
|
# tile_add_test(fiber_detail_scheduler_test "tile/fiber/detail/scheduler_test.cc")
|
||||||
tile_add_test(base_internal_meta_test "tile/base/internal/meta_test.cc")
|
tile_add_test(base_internal_meta_test "tile/base/internal/meta_test.cc")
|
||||||
# tile_add_test(net_internal_http_engine_test
|
# tile_add_test(net_internal_http_engine_test
|
||||||
# "tile/net/internal/http_engine_test.cc")
|
# "tile/net/internal/http_engine_test.cc")
|
||||||
@ -357,6 +369,7 @@ if(TILE_BUILD_TESTS)
|
|||||||
tile_add_custom_test("custom_http_client_test" "tests/http_client_test.cc")
|
tile_add_custom_test("custom_http_client_test" "tests/http_client_test.cc")
|
||||||
# tile_add_test(base_internal_time_keeper_test
|
# tile_add_test(base_internal_time_keeper_test
|
||||||
# "tile/base/internal/time_keeper_test.cc")
|
# "tile/base/internal/time_keeper_test.cc")
|
||||||
|
tile_add_test(tile_util_ini_file_config_test "tile/util/ini_file_config_test.cc")
|
||||||
endif(TILE_BUILD_TESTS)
|
endif(TILE_BUILD_TESTS)
|
||||||
|
|
||||||
if(TILE_BUILD_BENCHMARKS)
|
if(TILE_BUILD_BENCHMARKS)
|
||||||
@ -374,7 +387,7 @@ if(TILE_BUILD_BENCHMARKS)
|
|||||||
target_sources(tile_bm_all PRIVATE ${benchmark_file})
|
target_sources(tile_bm_all PRIVATE ${benchmark_file})
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
tile_add_bm(fiber_detail_fiber_benchmark "tile/fiber/detail/fiber_benchmark.cc")
|
# tile_add_bm(fiber_detail_fiber_benchmark "tile/fiber/detail/fiber_benchmark.cc")
|
||||||
tile_add_bm(base_casting_benchmark "tile/base/casting_benchmark.cc")
|
tile_add_bm(base_casting_benchmark "tile/base/casting_benchmark.cc")
|
||||||
tile_add_bm(base_thread_mutex_benchmark "tile/base/thread/mutex_benchmark.cc")
|
tile_add_bm(base_thread_mutex_benchmark "tile/base/thread/mutex_benchmark.cc")
|
||||||
tile_add_bm(base_encoding_benchmark "tile/base/encoding_benchmark.cc")
|
tile_add_bm(base_encoding_benchmark "tile/base/encoding_benchmark.cc")
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
services:
|
|
||||||
build-env:
|
|
||||||
image: ubuntu:16.04
|
|
||||||
restart: always
|
|
||||||
container_name: "env"
|
|
||||||
command: ["/bin/bash", "-c", "sleep 36000"]
|
|
||||||
volumes:
|
|
||||||
- ./:/workspace
|
|
@ -10,10 +10,32 @@
|
|||||||
#include "tile/base/likely.h"
|
#include "tile/base/likely.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
|
namespace detail {
|
||||||
|
// Has classof
|
||||||
|
template <typename T, typename Base> struct HasClassofImpl {
|
||||||
|
template <
|
||||||
|
typename TT, typename TBase,
|
||||||
|
typename = enable_if_t<std::is_same<
|
||||||
|
decltype(TT::classof(std::declval<const TBase &>())), bool>::value>>
|
||||||
|
static std::true_type test(int);
|
||||||
|
template <typename TT, typename TBase> static std::false_type test(...);
|
||||||
|
|
||||||
|
static constexpr bool value = decltype(test<T, Base>(0))::value;
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
template <typename T, typename = void> struct CastingTraits {
|
template <typename T, typename = void> struct CastingTraits {
|
||||||
template <typename U> static bool RuntimeTypeCheck(const U &val) {
|
template <typename Base>
|
||||||
|
static auto RuntimeTypeCheck(const Base &val)
|
||||||
|
-> enable_if_t<detail::HasClassofImpl<T, Base>::value, bool> {
|
||||||
return T::classof(val);
|
return T::classof(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Base>
|
||||||
|
static auto RuntimeTypeCheck(const Base &val)
|
||||||
|
-> enable_if_t<!detail::HasClassofImpl<T, Base>::value, bool> {
|
||||||
|
return dynamic_cast<const T *>(&val) != nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Castable {
|
class Castable {
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#define TILE_SYSTEM_PROCESSOR "@CMAKE_SYSTEM_PROCESSOR@"
|
#define TILE_SYSTEM_PROCESSOR "@CMAKE_SYSTEM_PROCESSOR@"
|
||||||
#define TILE_SYSTEM_NAME "@CMAKE_SYSTEM_NAME@"
|
#define TILE_SYSTEM_NAME "@CMAKE_SYSTEM_NAME@"
|
||||||
#define TILE_SYSTEM_VERSION "@CMAKE_SYSTEM_VERSION@"
|
#define TILE_SYSTEM_VERSION "@CMAKE_SYSTEM_VERSION@"
|
||||||
|
#define TILE_VERSION "@PROJECT_VERSOIN@"
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
@ -637,6 +637,11 @@ public:
|
|||||||
size_t message_len);
|
size_t message_len);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FileLogSink : public LogSink {
|
||||||
|
public:
|
||||||
|
FileLogSink(const std::string &file_path_template);
|
||||||
|
};
|
||||||
|
|
||||||
void AddLogSink(LogSink *dest);
|
void AddLogSink(LogSink *dest);
|
||||||
void RemoveLogSink(LogSink *dest);
|
void RemoveLogSink(LogSink *dest);
|
||||||
void SetStderrLogging(LogSeverity min_severity);
|
void SetStderrLogging(LogSeverity min_severity);
|
||||||
|
@ -17,6 +17,12 @@ make_unique(Args &&...args) {
|
|||||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
inline enable_if_t<!std::is_array<T>::value, std::unique_ptr<T>>
|
||||||
|
make_unique(T *ptr) {
|
||||||
|
return std::unique_ptr<T>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
// dynamic array
|
// dynamic array
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline enable_if_t<std::is_array<T>::value && std::extent<T>::value == 0,
|
inline enable_if_t<std::is_array<T>::value && std::extent<T>::value == 0,
|
||||||
|
45
tile/base/memory_barrier.cc
Normal file
45
tile/base/memory_barrier.cc
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include "tile/base/memory_barrier.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include "tile/base/logging.h"
|
||||||
|
#include "tile/base/never_destroyed.h"
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
namespace internal {
|
||||||
|
namespace {
|
||||||
|
void *
|
||||||
|
CreateOneByteDummyPage()
|
||||||
|
{
|
||||||
|
auto ptr = mmap(nullptr, 1, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
TILE_PCHECK(ptr, "Cannot create dummy page for asymmetric memory barrier.");
|
||||||
|
(void) mlock(ptr, 1);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HomemadeMembarrier()
|
||||||
|
{
|
||||||
|
static void *dummy_page = CreateOneByteDummyPage();
|
||||||
|
MemoryBarrier();
|
||||||
|
static NeverDestroyed<std::mutex> lock;
|
||||||
|
std::lock_guard<std::mutex> _(*lock);
|
||||||
|
|
||||||
|
TILE_PCHECK(mprotect(dummy_page, 1, PROT_READ | PROT_WRITE) == 0, "Cannot protect dummy page.");
|
||||||
|
*static_cast<char volatile *>(dummy_page) = 0;
|
||||||
|
TILE_PCHECK(mprotect(dummy_page, 1, PROT_READ) == 0);
|
||||||
|
|
||||||
|
MemoryBarrier();
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
void
|
||||||
|
AsymmetricBarrierHeavy()
|
||||||
|
{
|
||||||
|
HomemadeMembarrier();
|
||||||
|
}
|
||||||
|
}// namespace internal
|
||||||
|
|
||||||
|
}// namespace tile
|
45
tile/base/memory_barrier.h
Normal file
45
tile/base/memory_barrier.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#ifndef DOWNLINK__3PARTY_TILE_TILE_BASE_MEMORY_BARRIER_H
|
||||||
|
#define DOWNLINK__3PARTY_TILE_TILE_BASE_MEMORY_BARRIER_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
namespace internal {
|
||||||
|
inline void
|
||||||
|
CompilerBarrier()
|
||||||
|
{
|
||||||
|
std::atomic_signal_fence(std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
ReadBarrier()
|
||||||
|
{
|
||||||
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
WriteBarrier()
|
||||||
|
{
|
||||||
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
MemoryBarrier()
|
||||||
|
{
|
||||||
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
AsymmetricBarrierLight()
|
||||||
|
{
|
||||||
|
CompilerBarrier();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsymmetricBarrierHeavy();
|
||||||
|
|
||||||
|
}// namespace internal
|
||||||
|
}// namespace tile
|
||||||
|
|
||||||
|
#endif// DOWNLINK__3PARTY_TILE_TILE_BASE_MEMORY_BARRIER_H
|
@ -139,7 +139,7 @@ TEST(Endpoint, ResolveDomainName) {
|
|||||||
const std::string test_domain = "www.baidu.com";
|
const std::string test_domain = "www.baidu.com";
|
||||||
|
|
||||||
auto eps = ResolveDomain(test_domain, 80);
|
auto eps = ResolveDomain(test_domain, 80);
|
||||||
ASSERT_TRUE(eps);
|
ASSERT_TRUE(eps) << eps.error();
|
||||||
for (auto &ep : *eps) {
|
for (auto &ep : *eps) {
|
||||||
std::cout << Format("Get IP From {}: {}", test_domain, ep.ToString())
|
std::cout << Format("Get IP From {}: {}", test_domain, ep.ToString())
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "tile/base/casting.h"
|
||||||
|
#include "tile/base/down_cast.h"
|
||||||
#include "tile/base/internal/logging.h"
|
#include "tile/base/internal/logging.h"
|
||||||
#include "tile/base/internal/meta.h"
|
#include "tile/base/internal/meta.h"
|
||||||
#include "tile/base/internal/utility.h"
|
#include "tile/base/internal/utility.h"
|
||||||
@ -333,6 +335,12 @@ public:
|
|||||||
return internal::Exchange(ptr_, nullptr);
|
return internal::Exchange(ptr_, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename U> bool Is() const noexcept { return isa<U>(ptr_); }
|
||||||
|
|
||||||
|
template <typename U> RefPtr<U> As() const noexcept {
|
||||||
|
return RefPtr<U>(ref_ptr, down_cast<U>(ptr_));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T *ptr_;
|
T *ptr_;
|
||||||
};
|
};
|
||||||
|
@ -83,35 +83,30 @@ std::vector<Slice> Split(Slice s, Slice delim, bool keep_empty,
|
|||||||
|
|
||||||
TILE_CHECK_GE(max_split_parts, 0, "`max_split_parts` must be greater than 0");
|
TILE_CHECK_GE(max_split_parts, 0, "`max_split_parts` must be greater than 0");
|
||||||
|
|
||||||
constexpr std::size_t kMaxSplit = -1;
|
constexpr std::size_t kMaxSplit = std::numeric_limits<std::size_t>::max();
|
||||||
std::vector<Slice> splited;
|
std::vector<Slice> splited;
|
||||||
if (s.empty()) {
|
if (s.empty() || max_split_parts == 1) {
|
||||||
return splited;
|
return splited;
|
||||||
}
|
}
|
||||||
auto current = s;
|
auto current = s;
|
||||||
assert(!delim.empty());
|
assert(!delim.empty());
|
||||||
int split_count = 0;
|
int split_count = 0;
|
||||||
while (max_split_parts == kMaxSplit || split_count < max_split_parts) {
|
while (max_split_parts == kMaxSplit || split_count < max_split_parts) {
|
||||||
auto pos = current.find(delim);
|
auto next_pos = current.find(delim);
|
||||||
if (pos != 0 || keep_empty) {
|
auto part = current.substr(0, next_pos);
|
||||||
splited.push_back(current.substr(0, pos));
|
if (!part.empty() || keep_empty) {
|
||||||
|
splited.push_back(part);
|
||||||
|
}
|
||||||
|
|
||||||
++split_count;
|
++split_count;
|
||||||
}
|
|
||||||
|
|
||||||
if (pos == Slice::npos) {
|
if (next_pos == Slice::npos) {
|
||||||
break;
|
break;
|
||||||
}
|
} else if (split_count + 1 >= max_split_parts) {
|
||||||
|
splited.push_back(current.substr(next_pos + delim.size()));
|
||||||
current = current.substr(pos + delim.size());
|
|
||||||
if (current.empty()) {
|
|
||||||
if (keep_empty) {
|
|
||||||
splited.push_back("");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else if (split_count + 1 == max_split_parts) {
|
|
||||||
// ++split_count;
|
|
||||||
splited.push_back(current);
|
|
||||||
break;
|
break;
|
||||||
|
} else {
|
||||||
|
current = current.substr(next_pos + delim.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,22 +114,36 @@ std::vector<Slice> Split(Slice s, Slice delim, bool keep_empty,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Slice TrimLeft(Slice s, Slice cutset) {
|
Slice TrimLeft(Slice s, Slice cutset) {
|
||||||
while (!s.empty() && cutset.find(Slice(s.data(), 1)) != Slice::npos) {
|
return TrimLeft(s, [&cutset](char c) {
|
||||||
|
return cutset.find(Slice(&c, 1)) != Slice::npos;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Slice TrimRight(Slice s, Slice cutset) {
|
||||||
|
return TrimRight(s, [&cutset](char c) {
|
||||||
|
return cutset.find(Slice(&c, 1)) != Slice::npos;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Slice Trim(Slice s, Slice cutset) {
|
||||||
|
return Trim(s, [&cutset](char c) {
|
||||||
|
return cutset.find(Slice(&c, 1)) != Slice::npos;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Slice TrimLeft(Slice s, std::function<bool(char)> pred) {
|
||||||
|
while (!s.empty() && pred(s[0])) {
|
||||||
s.RemovePrefix(1);
|
s.RemovePrefix(1);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
Slice TrimRight(Slice s, std::function<bool(char)> pred) {
|
||||||
Slice TrimRight(Slice s, Slice cutset) {
|
while (!s.empty() && pred(s[s.size() - 1])) {
|
||||||
while (!s.empty() &&
|
|
||||||
cutset.find(Slice(s.data() + s.size() - 1, 1)) != Slice::npos) {
|
|
||||||
s.RemoveSuffix(1);
|
s.RemoveSuffix(1);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
Slice Trim(Slice s, std::function<bool(char)> pred) {
|
||||||
Slice Trim(Slice s, Slice cutset) {
|
return TrimRight(TrimLeft(s, pred), pred);
|
||||||
return TrimRight(TrimLeft(s, cutset), cutset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -198,11 +207,23 @@ std::string ToLower(Slice s) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Equals(Slice a, Slice b) {
|
bool Equals(Slice a, Slice b, std::size_t len) {
|
||||||
|
if (a.size() > len) {
|
||||||
|
a.RemoveSuffix(a.size() - len);
|
||||||
|
}
|
||||||
|
if (b.size() > len) {
|
||||||
|
b.RemoveSuffix(b.size() - len);
|
||||||
|
}
|
||||||
return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
|
return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EqualsIgnoreCase(Slice a, Slice b) {
|
bool EqualsIgnoreCase(Slice a, Slice b, std::size_t len) {
|
||||||
|
if (a.size() > len) {
|
||||||
|
a.RemoveSuffix(a.size() - len);
|
||||||
|
}
|
||||||
|
if (b.size() > len) {
|
||||||
|
b.RemoveSuffix(b.size() - len);
|
||||||
|
}
|
||||||
return a.size() == b.size() &&
|
return a.size() == b.size() &&
|
||||||
std::equal(a.begin(), a.end(), b.begin(),
|
std::equal(a.begin(), a.end(), b.begin(),
|
||||||
[](char a, char b) { return ToLower(a) == ToLower(b); });
|
[](char a, char b) { return ToLower(a) == ToLower(b); });
|
||||||
@ -213,21 +234,27 @@ TryParseTraits<bool>::TryParse(Slice s, bool recognizes_alpbabet_symbol,
|
|||||||
bool ignore_case) {
|
bool ignore_case) {
|
||||||
auto num_opt = tile::TryParse<int>(s);
|
auto num_opt = tile::TryParse<int>(s);
|
||||||
if (num_opt) {
|
if (num_opt) {
|
||||||
if (*num_opt == 0) {
|
return *num_opt != 0;
|
||||||
|
} else {
|
||||||
|
const std::vector<std::string> true_arr{"true", "yes", "on", "y"};
|
||||||
|
const std::vector<std::string> false_arr{"false", "no", "off", "n"};
|
||||||
|
if (s.size() > 5) {
|
||||||
return false;
|
return false;
|
||||||
} else if (*num_opt == 1) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
auto InArray = [](const std::vector<std::string> &arr, Slice s) {
|
||||||
}
|
for (auto &&e : arr) {
|
||||||
|
if (EqualsIgnoreCase(e, s)) {
|
||||||
if (EqualsIgnoreCase(s, "y") || EqualsIgnoreCase(s, "yes") ||
|
|
||||||
EqualsIgnoreCase(s, "true")) {
|
|
||||||
return true;
|
return true;
|
||||||
} else if (EqualsIgnoreCase(s, "n") || EqualsIgnoreCase(s, "no") ||
|
}
|
||||||
EqualsIgnoreCase(s, "false")) {
|
}
|
||||||
return false;
|
return false;
|
||||||
|
};
|
||||||
|
if (InArray(true_arr, s)) {
|
||||||
|
return true;
|
||||||
|
} else if (InArray(false_arr, s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -34,14 +34,19 @@ std::string
|
|||||||
Replace(Slice str, Slice from, Slice to,
|
Replace(Slice str, Slice from, Slice to,
|
||||||
std::size_t count = std::numeric_limits<std::size_t>::max());
|
std::size_t count = std::numeric_limits<std::size_t>::max());
|
||||||
|
|
||||||
Slice TrimLeft(Slice s, Slice cutset = " \t");
|
Slice TrimLeft(Slice s, Slice cutset);
|
||||||
Slice TrimRight(Slice s, Slice cutset = " \t");
|
Slice TrimRight(Slice s, Slice cutset);
|
||||||
Slice Trim(Slice s, Slice cutset = " \t");
|
Slice Trim(Slice s, Slice cutset);
|
||||||
|
Slice TrimLeft(Slice s, std::function<bool(char)> pred = isspace);
|
||||||
|
Slice TrimRight(Slice s, std::function<bool(char)> pred = isspace);
|
||||||
|
Slice Trim(Slice s, std::function<bool(char)> pred = isspace);
|
||||||
|
|
||||||
std::vector<Slice> Split(Slice s, char delim, bool keep_empty = false,
|
std::vector<Slice>
|
||||||
std::size_t max_split_parts = -1);
|
Split(Slice s, char delim, bool keep_empty = false,
|
||||||
std::vector<Slice> Split(Slice s, Slice delim, bool keep_empty = false,
|
std::size_t max_split_parts = std::numeric_limits<std::size_t>::max());
|
||||||
std::size_t max_split_parts = -1);
|
std::vector<Slice>
|
||||||
|
Split(Slice s, Slice delim, bool keep_empty = false,
|
||||||
|
std::size_t max_split_parts = std::numeric_limits<std::size_t>::max());
|
||||||
|
|
||||||
std::string Join(const std::vector<std::string> &parts, Slice delim);
|
std::string Join(const std::vector<std::string> &parts, Slice delim);
|
||||||
std::string Join(const std::vector<Slice> &parts, Slice delim);
|
std::string Join(const std::vector<Slice> &parts, Slice delim);
|
||||||
@ -54,8 +59,11 @@ void ToLower(std::string *str);
|
|||||||
std::string ToUpper(Slice s);
|
std::string ToUpper(Slice s);
|
||||||
std::string ToLower(Slice s);
|
std::string ToLower(Slice s);
|
||||||
|
|
||||||
bool Equals(Slice a, Slice b);
|
bool Equals(Slice a, Slice b,
|
||||||
bool EqualsIgnoreCase(Slice a, Slice b);
|
std::size_t len = std::numeric_limits<std::size_t>::max());
|
||||||
|
bool EqualsIgnoreCase(
|
||||||
|
Slice a, Slice b,
|
||||||
|
std::size_t len = std::numeric_limits<std::size_t>::max());
|
||||||
|
|
||||||
// TryParse
|
// TryParse
|
||||||
template <typename T, typename = void> struct TryParseTraits;
|
template <typename T, typename = void> struct TryParseTraits;
|
||||||
@ -73,6 +81,10 @@ template <typename T, typename> struct TryParseTraits {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// String to bool
|
// String to bool
|
||||||
|
// 1. "true" or "false"
|
||||||
|
// 2. "yes" or "no"
|
||||||
|
// 3. "on" or "off"
|
||||||
|
// 4. non-zero or zero
|
||||||
template <> struct TryParseTraits<bool> {
|
template <> struct TryParseTraits<bool> {
|
||||||
static std::optional<bool> TryParse(Slice s,
|
static std::optional<bool> TryParse(Slice s,
|
||||||
bool recognizes_alpbabet_symbol = true,
|
bool recognizes_alpbabet_symbol = true,
|
||||||
|
@ -102,7 +102,7 @@ TEST(String, TryParseFloatingPoint) {
|
|||||||
TEST(String, TryParseBool) {
|
TEST(String, TryParseBool) {
|
||||||
ASSERT_FALSE(TryParse<bool>(""));
|
ASSERT_FALSE(TryParse<bool>(""));
|
||||||
ASSERT_FALSE(TryParse<bool>(".."));
|
ASSERT_FALSE(TryParse<bool>(".."));
|
||||||
ASSERT_FALSE(TryParse<bool>("2"));
|
ASSERT_TRUE(TryParse<bool>("2"));
|
||||||
|
|
||||||
ASSERT_TRUE(TryParse<bool>("1"));
|
ASSERT_TRUE(TryParse<bool>("1"));
|
||||||
ASSERT_TRUE(TryParse<bool>("0"));
|
ASSERT_TRUE(TryParse<bool>("0"));
|
||||||
|
@ -10,26 +10,48 @@
|
|||||||
namespace tile {
|
namespace tile {
|
||||||
|
|
||||||
class CondVarTest : public ::testing::Test {
|
class CondVarTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
m = new Mutex();
|
m = new Mutex();
|
||||||
cv = new CondVar();
|
cv = new CondVar();
|
||||||
is_set = false;
|
|
||||||
is_timeout = false;
|
|
||||||
worker = nullptr;
|
worker = nullptr;
|
||||||
|
ResetEnv();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
|
SetWorker(nullptr);
|
||||||
|
delete m;
|
||||||
|
delete cv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetWorker(std::thread *new_worker) {
|
||||||
if (worker) {
|
if (worker) {
|
||||||
if (worker->joinable()) {
|
if (worker->joinable()) {
|
||||||
worker->join();
|
worker->join();
|
||||||
}
|
}
|
||||||
delete worker;
|
delete worker;
|
||||||
}
|
}
|
||||||
|
worker = new_worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetEnv() {
|
||||||
|
is_started = false;
|
||||||
|
is_set = false;
|
||||||
|
is_timeout = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetStarted() { is_started = true; }
|
||||||
|
|
||||||
|
void WaitStarted() {
|
||||||
|
while (!is_started) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Mutex *m;
|
Mutex *m;
|
||||||
CondVar *cv;
|
CondVar *cv;
|
||||||
|
std::atomic<bool> is_started;
|
||||||
bool is_set;
|
bool is_set;
|
||||||
bool is_timeout;
|
bool is_timeout;
|
||||||
std::thread *worker;
|
std::thread *worker;
|
||||||
@ -42,11 +64,13 @@ TEST_F(CondVarTest, NotifyOne_Wait) {
|
|||||||
// UniqueLock<Mutex> inner_locker(*m);
|
// UniqueLock<Mutex> inner_locker(*m);
|
||||||
// cv->Wait(inner_locker);
|
// cv->Wait(inner_locker);
|
||||||
ScopedLock inner_locker(*m);
|
ScopedLock inner_locker(*m);
|
||||||
|
SetStarted();
|
||||||
cv->Wait(inner_locker);
|
cv->Wait(inner_locker);
|
||||||
is_set = true;
|
is_set = true;
|
||||||
cv->NotifyOne();
|
cv->NotifyOne();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
WaitStarted();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
ASSERT_FALSE(is_set);
|
ASSERT_FALSE(is_set);
|
||||||
|
|
||||||
@ -62,21 +86,23 @@ TEST_F(CondVarTest, NotifyOne_Wait) {
|
|||||||
|
|
||||||
TEST_F(CondVarTest, NotifyOne_WaitFor) {
|
TEST_F(CondVarTest, NotifyOne_WaitFor) {
|
||||||
// Wait
|
// Wait
|
||||||
constexpr auto kWaitTimeoutMs = 100;
|
// constexpr auto kWaitTimeoutMs = 100;
|
||||||
|
constexpr auto kWaitTimeout = std::chrono::milliseconds(100);
|
||||||
auto WaitFor = [&] {
|
auto WaitFor = [&] {
|
||||||
UniqueLock<Mutex> inner_locker(*m);
|
UniqueLock<Mutex> inner_locker(*m);
|
||||||
if (cv->WaitFor(inner_locker, std::chrono::milliseconds(kWaitTimeoutMs))) {
|
SetStarted();
|
||||||
cv->NotifyOne();
|
if (cv->WaitFor(inner_locker, kWaitTimeout)) {
|
||||||
is_set = true;
|
is_set = true;
|
||||||
|
cv->NotifyOne();
|
||||||
} else {
|
} else {
|
||||||
is_timeout = true;
|
is_timeout = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
std::thread t1(WaitFor);
|
SetWorker(new std::thread(WaitFor));
|
||||||
|
WaitStarted();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(kWaitTimeoutMs / 2));
|
|
||||||
ASSERT_FALSE(is_set);
|
ASSERT_FALSE(is_set);
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -84,25 +110,22 @@ TEST_F(CondVarTest, NotifyOne_WaitFor) {
|
|||||||
cv->NotifyOne();
|
cv->NotifyOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
t1.join();
|
worker->join();
|
||||||
|
ASSERT_FALSE(is_timeout);
|
||||||
ASSERT_TRUE(is_set);
|
ASSERT_TRUE(is_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
// timeout
|
// timeout
|
||||||
{
|
{
|
||||||
std::thread t1(WaitFor);
|
ResetEnv();
|
||||||
|
SetWorker(new std::thread(WaitFor));
|
||||||
{
|
{
|
||||||
UniqueLock<Mutex> locker(*m);
|
UniqueLock<Mutex> locker(*m);
|
||||||
ASSERT_FALSE(is_timeout);
|
ASSERT_FALSE(is_timeout);
|
||||||
std::this_thread::sleep_for(
|
|
||||||
std::chrono::milliseconds(kWaitTimeoutMs * 2));
|
|
||||||
ASSERT_FALSE(is_timeout);
|
|
||||||
cv->NotifyOne();
|
|
||||||
}
|
}
|
||||||
|
worker->join();
|
||||||
t1.join();
|
|
||||||
ASSERT_TRUE(is_timeout);
|
ASSERT_TRUE(is_timeout);
|
||||||
|
ASSERT_FALSE(is_set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
54
tile/base/thread/thread_local.h
Normal file
54
tile/base/thread/thread_local.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#ifndef TILE_BASE_THREAD_THREAD_LOCAL_H
|
||||||
|
#define TILE_BASE_THREAD_THREAD_LOCAL_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tile/base/internal/meta.h"
|
||||||
|
#include "tile/base/thread/mutex.h"
|
||||||
|
#include "tile/base/thread/scoped_lock.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
template<typename T>
|
||||||
|
class ThreadLocal {
|
||||||
|
public:
|
||||||
|
ThreadLocal() : ThreadLocal([]() {}) {};
|
||||||
|
|
||||||
|
template<typename F, enable_if_t<eggs::is_invocable_r<std::unique_ptr<T>, F>::value> * = nullptr>
|
||||||
|
explicit ThreadLocal(F &&creator) : creator_(std::forward<F>(creator))
|
||||||
|
{}
|
||||||
|
|
||||||
|
T *Get() const {}
|
||||||
|
|
||||||
|
T *operator->() const { return Get(); }
|
||||||
|
|
||||||
|
T &operator*() const { return *Get(); }
|
||||||
|
|
||||||
|
T *Leak() noexcept
|
||||||
|
{
|
||||||
|
ScopedLock _(init_lock_);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset(std::unique_ptr<T> ptr = nullptr) noexcept { ScopedLock _(init_lock_); }
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void ForEach(F &&f) const
|
||||||
|
{
|
||||||
|
ScopedLock _(init_lock_);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadLocal(const ThreadLocal &) = delete;
|
||||||
|
ThreadLocal &operator=(const ThreadLocal &) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static thread_local T val_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable Mutex init_lock_;
|
||||||
|
std::function<std::unique_ptr<T>()> creator_;
|
||||||
|
};
|
||||||
|
}// namespace tile
|
||||||
|
|
||||||
|
#endif// TILE_BASE_THREAD_THREAD_LOCAL_H
|
148
tile/base/thread/thread_local/object_array.h
Normal file
148
tile/base/thread/thread_local/object_array.h
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
#ifndef TILE_BASE_THREAD_THREAD_LOCAL_OBJECT_ARRAY_H
|
||||||
|
#define TILE_BASE_THREAD_THREAD_LOCAL_OBJECT_ARRAY_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tile/base/align.h"
|
||||||
|
#include "tile/base/logging.h"
|
||||||
|
#include "tile/base/thread/mutex.h"
|
||||||
|
#include "tile/base/thread/scoped_lock.h"
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
namespace tls {
|
||||||
|
namespace detail {
|
||||||
|
template<typename T>
|
||||||
|
struct Entry {
|
||||||
|
std::aligned_storage<sizeof(T), alignof(T)> storage;
|
||||||
|
static_assert(sizeof(T) == sizeof(storage), "");
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct ObjectArrayCache {
|
||||||
|
std::size_t limit{};
|
||||||
|
T *objects{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct ObjectArray;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class LazyInitObjectArray {
|
||||||
|
struct EntryDeleter {
|
||||||
|
void operator()(Entry<T> *ptr) noexcept { operator delete(ptr); }
|
||||||
|
};
|
||||||
|
|
||||||
|
using AlignedArray = std::unique_ptr<Entry<T>[], EntryDeleter>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LazyInitObjectArray() = default;
|
||||||
|
|
||||||
|
~LazyInitObjectArray()
|
||||||
|
{
|
||||||
|
for (int i = 0; i != initialized_.size(); ++i) {
|
||||||
|
if (initialized_[i]) { reinterpret_cast<T *>(&objects_[i])->~T(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyInitObjectArray(LazyInitObjectArray &&) = default;
|
||||||
|
LazyInitObjectArray &operator=(LazyInitObjectArray &&) = default;
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void InitializeAt(std::size_t index, F &&f)
|
||||||
|
{
|
||||||
|
TILE_CHECK_LT(index, initialized_.size());
|
||||||
|
TILE_CHECK(!initialized_[index]);
|
||||||
|
|
||||||
|
initialized_[index] = true;
|
||||||
|
std::forward<F>(f)(&objects_[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroyAt(std::size_t index)
|
||||||
|
{
|
||||||
|
TILE_CHECK_LT(index, initialized_.size());
|
||||||
|
TILE_CHECK(initialized_[index]);
|
||||||
|
|
||||||
|
initialized_[index] = false;
|
||||||
|
reinterpret_cast<T *>(&objects_[index])->~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInitializedAt(std::size_t index) const noexcept
|
||||||
|
{
|
||||||
|
TILE_CHECK_LT(index, initialized_.size());
|
||||||
|
return initialized_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
T *GetAt(std::size_t index) noexcept
|
||||||
|
{
|
||||||
|
TILE_CHECK_LT(index, initialized_.size());
|
||||||
|
TILE_CHECK(initialized_[index]);
|
||||||
|
return reinterpret_cast<T *>(&objects_[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
T *GetObjectsMaybeUninitialized() noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == sizeof(Entry<T>), "");
|
||||||
|
return reinterpret_cast<T *>(objects_.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const noexcept { return initialized_.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
AlignedArray objects_;
|
||||||
|
std::vector<bool> initialized_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class ObjectArrayRegistry {
|
||||||
|
public:
|
||||||
|
static ObjectArrayRegistry *Instance()
|
||||||
|
{
|
||||||
|
static NeverDestroyedSingleton<ObjectArrayRegistry> instance;
|
||||||
|
return instance.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Register(ObjectArray<T> *array)
|
||||||
|
{
|
||||||
|
ScopedLock _(lock_);
|
||||||
|
TILE_DCHECK(std::find(arrays_.begin(), arrays_.end(), array) == arrays_.end());
|
||||||
|
arrays_.push_back(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deregister(ObjectArray<T> *array)
|
||||||
|
{
|
||||||
|
ScopedLock _(lock_);
|
||||||
|
auto iter = std::find(arrays_.begin(), arrays_.end(), array);
|
||||||
|
TILE_CHECK(iter != arrays_.end());
|
||||||
|
arrays_.erase(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void ForEachLocked(std::size_t index, F &&f)
|
||||||
|
{
|
||||||
|
ScopedLock _(lock_);
|
||||||
|
for (auto &&e : arrays_) {
|
||||||
|
ScopedLock _(e->lock);
|
||||||
|
if (index < e->objects.size()) { f(e); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mutex lock_;
|
||||||
|
std::vector<ObjectArray<T> *> arrays_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct ObjectArray {
|
||||||
|
Mutex lock;
|
||||||
|
LazyInitObjectArray<T> objects_;
|
||||||
|
|
||||||
|
ObjectArray() { ObjectArrayRegistry<T>::Instance()->Register(this); }
|
||||||
|
|
||||||
|
~ObjectArray() { ObjectArrayRegistry<T>::Instance()->Deregister(this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace detail
|
||||||
|
}// namespace tls
|
||||||
|
}// namespace tile
|
||||||
|
|
||||||
|
#endif// TILE_BASE_THREAD_THREAD_LOCAL_OBJECT_ARRAY_H
|
@ -1,6 +1,7 @@
|
|||||||
#include "init.h"
|
#include "init.h"
|
||||||
|
|
||||||
#include "tile/base/chrono.h"
|
#include "tile/base/chrono.h"
|
||||||
|
#include "tile/base/config.h"
|
||||||
#include "tile/base/internal/background_task_host.h"
|
#include "tile/base/internal/background_task_host.h"
|
||||||
#include "tile/base/internal/time_keeper.h"
|
#include "tile/base/internal/time_keeper.h"
|
||||||
#include "tile/base/internal/utility.h"
|
#include "tile/base/internal/utility.h"
|
||||||
@ -54,14 +55,14 @@ int Start(int argc, char **argv, std::function<int(int, char **)> cb,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init gflags
|
// Init gflags
|
||||||
gflags::SetVersionString("0.1.0");
|
gflags::SetVersionString(TILE_VERSION);
|
||||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
detail::ApplyFlagOverrider();
|
detail::ApplyFlagOverrider();
|
||||||
|
|
||||||
// Init Glog
|
// Init Glog
|
||||||
google::InitGoogleLogging(argv[0]);
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
|
||||||
TILE_LOG_INFO("Tile started.");
|
TILE_LOG_INFO("Tile started. version: {}", TILE_VERSION);
|
||||||
|
|
||||||
TILE_PCHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
|
TILE_PCHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#ifndef _TILE_INIT_H
|
#ifndef TILE_INIT_H
|
||||||
#define _TILE_INIT_H
|
#define TILE_INIT_H
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -17,4 +17,4 @@ void TerminateBasicRuntime();
|
|||||||
|
|
||||||
} // namespace tile
|
} // namespace tile
|
||||||
|
|
||||||
#endif // _TILE_INIT_H
|
#endif // TILE_INIT_H
|
||||||
|
@ -24,7 +24,7 @@ void EventLoop::Barrier() {}
|
|||||||
void EventLoop::Run() {}
|
void EventLoop::Run() {}
|
||||||
void EventLoop::Stop() {}
|
void EventLoop::Stop() {}
|
||||||
void EventLoop::Join() {}
|
void EventLoop::Join() {}
|
||||||
EventLoop *EventLoop::Current() {}
|
EventLoop *EventLoop::Current() { return nullptr; }
|
||||||
void EventLoop::WaitAndRunEvents(std::chrono::milliseconds wait_for) {}
|
void EventLoop::WaitAndRunEvents(std::chrono::milliseconds wait_for) {}
|
||||||
void EventLoop::RunUserTasks() {}
|
void EventLoop::RunUserTasks() {}
|
||||||
} // namespace tile
|
} // namespace tile
|
||||||
|
84
tile/io/util/rate_limiter.cc
Normal file
84
tile/io/util/rate_limiter.cc
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#include "tile/io/util/rate_limiter.h"
|
||||||
|
|
||||||
|
#include "tile/base/chrono.h"
|
||||||
|
#include "tile/base/logging.h"
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class NullLimiter : public RateLimiter {
|
||||||
|
public:
|
||||||
|
std::size_t GetQuota() override { return 0; }
|
||||||
|
void ConsumeBytes(std::size_t consumed) override {}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
RateLimiter *RateLimiter::GetDefaultRxRateLimiter() {
|
||||||
|
static NullLimiter null_limiter;
|
||||||
|
return &null_limiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
RateLimiter *RateLimiter::GetDefaultTxRateLimiter() {
|
||||||
|
static NullLimiter null_limiter;
|
||||||
|
return &null_limiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenBucketRateLimiter::TokenBucketRateLimiter(std::size_t bucket_quota,
|
||||||
|
std::size_t quota_per_tick,
|
||||||
|
std::chrono::nanoseconds tick,
|
||||||
|
bool over_consumption_allowed)
|
||||||
|
: max_quota_(bucket_quota), quota_per_tick_(quota_per_tick), tick_(tick),
|
||||||
|
over_consumption_allowed_(over_consumption_allowed) {
|
||||||
|
TILE_CHECK_GT(bucket_quota, 0);
|
||||||
|
TILE_CHECK_GT(quota_per_tick, 0);
|
||||||
|
last_refill_ = ReadSteadyClock().time_since_epoch() / tick_;
|
||||||
|
curr_quota_ = max_quota_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t TokenBucketRateLimiter::GetQuota() {
|
||||||
|
auto now = ReadSteadyClock().time_since_epoch() / tick_;
|
||||||
|
std::uint64_t last_refill = internal::Exchange(last_refill_, now);
|
||||||
|
|
||||||
|
curr_quota_ += quota_per_tick_ * (now - last_refill);
|
||||||
|
|
||||||
|
if (curr_quota_ > 0) {
|
||||||
|
curr_quota_ = std::min<std::size_t>(curr_quota_, max_quota_);
|
||||||
|
return curr_quota_;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TokenBucketRateLimiter::ConsumeBytes(std::size_t consumed) {
|
||||||
|
TILE_CHECK(over_consumption_allowed_ || consumed <= curr_quota_);
|
||||||
|
curr_quota_ -= consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadSafeRateLimiter::ThreadSafeRateLimiter(MaybeOwning<RateLimiter> limiter,
|
||||||
|
std::size_t burst_limit)
|
||||||
|
: burst_limit_(burst_limit), impl_(std::move(limiter)) {
|
||||||
|
TILE_CHECK_GT(burst_limit_, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t ThreadSafeRateLimiter::GetQuota() {
|
||||||
|
std::lock_guard<std::mutex> lock(lock_);
|
||||||
|
return impl_->GetQuota();
|
||||||
|
}
|
||||||
|
void ThreadSafeRateLimiter::ConsumeBytes(std::size_t consumed) {
|
||||||
|
std::lock_guard<std::mutex> lock(lock_);
|
||||||
|
impl_->ConsumeBytes(consumed);
|
||||||
|
}
|
||||||
|
|
||||||
|
LayeredRateLimiter::LayeredRateLimiter(RateLimiter *upper,
|
||||||
|
MaybeOwning<RateLimiter> ours)
|
||||||
|
: upper_(upper), ours_(std::move(ours)) {}
|
||||||
|
|
||||||
|
std::size_t LayeredRateLimiter::GetQuota() {
|
||||||
|
return std::min(upper_->GetQuota(), ours_->GetQuota());
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayeredRateLimiter::ConsumeBytes(std::size_t consumed) {
|
||||||
|
upper_->ConsumeBytes(consumed);
|
||||||
|
ours_->ConsumeBytes(consumed);
|
||||||
|
}
|
||||||
|
} // namespace tile
|
70
tile/io/util/rate_limiter.h
Normal file
70
tile/io/util/rate_limiter.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#ifndef TILE_IO_UTIL_RATE_LIMITER_H
|
||||||
|
#define TILE_IO_UTIL_RATE_LIMITER_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tile/base/maybe_owning.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
class RateLimiter {
|
||||||
|
public:
|
||||||
|
virtual ~RateLimiter() = default;
|
||||||
|
|
||||||
|
virtual std::size_t GetQuota() = 0;
|
||||||
|
virtual void ConsumeBytes(std::size_t consumed) = 0;
|
||||||
|
static RateLimiter *GetDefaultRxRateLimiter();
|
||||||
|
static RateLimiter *GetDefaultTxRateLimiter();
|
||||||
|
};
|
||||||
|
|
||||||
|
class TokenBucketRateLimiter : public RateLimiter {
|
||||||
|
public:
|
||||||
|
TokenBucketRateLimiter(
|
||||||
|
std::size_t bucket_quota, std::size_t quota_per_tick,
|
||||||
|
std::chrono::nanoseconds tick = std::chrono::milliseconds(1),
|
||||||
|
bool over_consumption_allowed = true);
|
||||||
|
|
||||||
|
std::size_t GetQuota() override;
|
||||||
|
void ConsumeBytes(std::size_t consumed) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t max_quota_;
|
||||||
|
std::size_t quota_per_tick_;
|
||||||
|
std::chrono::nanoseconds tick_;
|
||||||
|
bool over_consumption_allowed_;
|
||||||
|
|
||||||
|
std::uint64_t last_refill_;
|
||||||
|
std::int64_t curr_quota_{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThreadSafeRateLimiter : public RateLimiter {
|
||||||
|
public:
|
||||||
|
explicit ThreadSafeRateLimiter(
|
||||||
|
MaybeOwning<RateLimiter> limiter,
|
||||||
|
std::size_t burst_limit = std::numeric_limits<std::size_t>::max());
|
||||||
|
std::size_t GetQuota() override;
|
||||||
|
void ConsumeBytes(std::size_t consumed) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t burst_limit_;
|
||||||
|
std::mutex lock_;
|
||||||
|
MaybeOwning<RateLimiter> impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LayeredRateLimiter : public RateLimiter {
|
||||||
|
public:
|
||||||
|
LayeredRateLimiter(RateLimiter *upper, MaybeOwning<RateLimiter> ours);
|
||||||
|
|
||||||
|
std::size_t GetQuota() override;
|
||||||
|
void ConsumeBytes(std::size_t consumed) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
RateLimiter *upper_;
|
||||||
|
MaybeOwning<RateLimiter> ours_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tile
|
||||||
|
|
||||||
|
#endif // TILE_IO_UTIL_RATE_LIMITER_H
|
188
tile/io/util/rate_limiter_test.cc
Normal file
188
tile/io/util/rate_limiter_test.cc
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
#include "rate_limiter.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "tile/base/chrono.h"
|
||||||
|
#include "tile/base/make_unique.h"
|
||||||
|
#include "tile/base/random.h"
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
|
||||||
|
TEST(RateLimiter, TokenBucketRateLimiter) {
|
||||||
|
TokenBucketRateLimiter limiter(1000, 1);
|
||||||
|
std::size_t total = 0;
|
||||||
|
auto start = ReadSteadyClock();
|
||||||
|
while (ReadSteadyClock() - start < std::chrono::seconds(5)) {
|
||||||
|
auto current = limiter.GetQuota();
|
||||||
|
total += current;
|
||||||
|
limiter.ConsumeBytes(current);
|
||||||
|
|
||||||
|
// with sleep
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1) * Random(100));
|
||||||
|
}
|
||||||
|
ASSERT_NEAR(6000, total, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RateLimiter, TokenBucketRateLimiter2) {
|
||||||
|
TokenBucketRateLimiter limiter(1000, 1);
|
||||||
|
std::size_t total = 0;
|
||||||
|
auto start = ReadSteadyClock();
|
||||||
|
while (ReadSteadyClock() - start < std::chrono::seconds(5)) {
|
||||||
|
auto current = limiter.GetQuota();
|
||||||
|
total += current;
|
||||||
|
limiter.ConsumeBytes(current);
|
||||||
|
}
|
||||||
|
ASSERT_NEAR(6000, total, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RateLimiter, TokenBucketRateLimiterCapBurst) {
|
||||||
|
TokenBucketRateLimiter limiter(25, 500);
|
||||||
|
for (int i = 0; i != 10; ++i) {
|
||||||
|
ASSERT_EQ(25, limiter.GetQuota());
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RateLimiter, TokenBucketRateLimiterCapBurst2) {
|
||||||
|
TokenBucketRateLimiter limiter(1000, 500);
|
||||||
|
for (int i = 0; i != 10; ++i) {
|
||||||
|
ASSERT_EQ(1000, limiter.GetQuota());
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::milliseconds(10)); // Enough to fully fill the bucket.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RateLimiter, MultithreadedRateLimiter) {
|
||||||
|
ThreadSafeRateLimiter limiter(make_unique<TokenBucketRateLimiter>(1000, 1));
|
||||||
|
std::atomic<std::size_t> total{0};
|
||||||
|
std::vector<std::thread> ts;
|
||||||
|
|
||||||
|
for (int i = 0; i != 10; ++i) {
|
||||||
|
ts.emplace_back(std::thread([&] {
|
||||||
|
auto start = ReadSteadyClock();
|
||||||
|
while (ReadSteadyClock() - start < std::chrono::seconds(5)) {
|
||||||
|
auto current = limiter.GetQuota();
|
||||||
|
total += current;
|
||||||
|
limiter.ConsumeBytes(current);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10) * Random(10));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &&t : ts) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_NEAR(6000, total.load(), 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RateLimiter, LayeredRateLimiter) {
|
||||||
|
ThreadSafeRateLimiter base_limiter(
|
||||||
|
make_unique<TokenBucketRateLimiter>(1000, 1));
|
||||||
|
auto our_limiter = make_unique<ThreadSafeRateLimiter>(
|
||||||
|
make_unique<TokenBucketRateLimiter>(1000, 100));
|
||||||
|
LayeredRateLimiter layered_limiter(&base_limiter, std::move(our_limiter));
|
||||||
|
std::atomic<std::size_t> total{0};
|
||||||
|
std::vector<std::thread> ts;
|
||||||
|
|
||||||
|
for (int i = 0; i != 10; ++i) {
|
||||||
|
ts.emplace_back(std::thread([&] {
|
||||||
|
auto start = ReadSteadyClock();
|
||||||
|
while (ReadSteadyClock() - start < std::chrono::seconds(5)) {
|
||||||
|
auto current = layered_limiter.GetQuota();
|
||||||
|
total += current;
|
||||||
|
layered_limiter.ConsumeBytes(current);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10) * Random(10));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &&t : ts) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_NEAR(6000, total.load(), 500); // `msrl` takes effect.
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RateLimiter, LayeredRateLimiter2) {
|
||||||
|
ThreadSafeRateLimiter base_limiter(
|
||||||
|
make_unique<TokenBucketRateLimiter>(1000, 100));
|
||||||
|
auto our_limiter = make_unique<ThreadSafeRateLimiter>(
|
||||||
|
make_unique<TokenBucketRateLimiter>(1000, 1));
|
||||||
|
LayeredRateLimiter layered_limiter(&base_limiter, std::move(our_limiter));
|
||||||
|
std::atomic<std::size_t> total{0};
|
||||||
|
std::vector<std::thread> ts;
|
||||||
|
|
||||||
|
for (int i = 0; i != 10; ++i) {
|
||||||
|
ts.emplace_back(std::thread([&] {
|
||||||
|
auto start = ReadSteadyClock();
|
||||||
|
while (ReadSteadyClock() - start < std::chrono::seconds(5)) {
|
||||||
|
auto current = layered_limiter.GetQuota();
|
||||||
|
total += current;
|
||||||
|
layered_limiter.ConsumeBytes(current);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1) * Random(10));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &&t : ts) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_NEAR(6000, total.load(), 500); // `tbsrl` takes effect.
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RateLimiter, LayeredRateLimiter3) {
|
||||||
|
ThreadSafeRateLimiter base_limiter(
|
||||||
|
make_unique<TokenBucketRateLimiter>(1000, 1));
|
||||||
|
std::atomic<std::size_t> total{0};
|
||||||
|
std::vector<std::thread> ts;
|
||||||
|
|
||||||
|
for (int i = 0; i != 10; ++i) {
|
||||||
|
ts.emplace_back(std::thread([&] {
|
||||||
|
auto our_limiter = make_unique<TokenBucketRateLimiter>(1000, 100);
|
||||||
|
LayeredRateLimiter layered_limiter(&base_limiter, std::move(our_limiter));
|
||||||
|
auto start = ReadSteadyClock();
|
||||||
|
while (ReadSteadyClock() - start < std::chrono::seconds(5)) {
|
||||||
|
auto current = layered_limiter.GetQuota();
|
||||||
|
total += current;
|
||||||
|
layered_limiter.ConsumeBytes(current);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &&t : ts) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_NEAR(6000, total.load(), 500); // `msrl` takes effect.
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RateLimiter, LayeredRateLimiter4) {
|
||||||
|
ThreadSafeRateLimiter base_limiter(
|
||||||
|
make_unique<TokenBucketRateLimiter>(1000, 100));
|
||||||
|
std::atomic<std::size_t> total{0};
|
||||||
|
std::vector<std::thread> ts;
|
||||||
|
|
||||||
|
for (int i = 0; i != 10; ++i) {
|
||||||
|
ts.emplace_back(std::thread([&] {
|
||||||
|
auto our_limiter = make_unique<ThreadSafeRateLimiter>(
|
||||||
|
make_unique<TokenBucketRateLimiter>(1000, 1));
|
||||||
|
LayeredRateLimiter layered_limiter(&base_limiter, std::move(our_limiter));
|
||||||
|
auto start = ReadSteadyClock();
|
||||||
|
while (ReadSteadyClock() - start < std::chrono::seconds(5)) {
|
||||||
|
auto current = layered_limiter.GetQuota();
|
||||||
|
total += current;
|
||||||
|
layered_limiter.ConsumeBytes(current);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &&t : ts) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_NEAR(60000, total.load(), 5000); // `tbsrl` takes effect.
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tile
|
@ -374,7 +374,7 @@ void HttpEngine::Join() {
|
|||||||
HttpEngine::HttpEngine() {
|
HttpEngine::HttpEngine() {
|
||||||
auto ret = curl_global_init(CURL_GLOBAL_DEFAULT);
|
auto ret = curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||||
TILE_CHECK(!ret, "Curl Init failed {}", ret);
|
TILE_CHECK(!ret, "Curl Init failed {}", ret);
|
||||||
for (auto i = 0; i < 1; ++i) {
|
for (auto i = 0; i < 5; ++i) {
|
||||||
curl_client_groups.push_back(make_unique<CurlClientGroup>(
|
curl_client_groups.push_back(make_unique<CurlClientGroup>(
|
||||||
FLAGS_tile_http_engine_workers_per_scheduling_group, i));
|
FLAGS_tile_http_engine_workers_per_scheduling_group, i));
|
||||||
}
|
}
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
#ifndef TILE_SIGSLOT_SIGNAL_H
|
|
||||||
#define TILE_SIGSLOT_SIGNAL_H
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "tile/base/internal/meta.h"
|
|
||||||
#include <functional>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* tile::sigslot::Signal<void(int)> signal;
|
|
||||||
**/
|
|
||||||
|
|
||||||
namespace tile {
|
|
||||||
namespace sigslot {
|
|
||||||
|
|
||||||
class NonLock {
|
|
||||||
public:
|
|
||||||
void Lock() {}
|
|
||||||
void Unlock() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class GlobalLock {
|
|
||||||
void Lock();
|
|
||||||
void Unlock();
|
|
||||||
};
|
|
||||||
|
|
||||||
class ThreadedLock {
|
|
||||||
void Lock();
|
|
||||||
void Unlock();
|
|
||||||
};
|
|
||||||
|
|
||||||
class Connection {
|
|
||||||
public:
|
|
||||||
};
|
|
||||||
|
|
||||||
class Slot {
|
|
||||||
public:
|
|
||||||
Slot() = default;
|
|
||||||
virtual ~Slot() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::set<Connection> connections_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename> class Signal;
|
|
||||||
template <typename R, typename... Args> class Signal<R(Args...)> {
|
|
||||||
public:
|
|
||||||
using SlotType = std::function<R(Args...)>;
|
|
||||||
|
|
||||||
R Emit(Args... args) const;
|
|
||||||
R operator()(Args... args) const { Emit(std::forward<Args>(args)...); };
|
|
||||||
|
|
||||||
// function ptr
|
|
||||||
Connection Connect(R (*func)(Args...));
|
|
||||||
|
|
||||||
// member function
|
|
||||||
template <typename C> Connection Connect(C *clz, R (C::*mem_fn)(Args...));
|
|
||||||
|
|
||||||
// lambda function
|
|
||||||
Connection Connect(std::function<R(Args...)> lambda_fn);
|
|
||||||
template <typename Functor,
|
|
||||||
typename = enable_if_t<std::is_same<
|
|
||||||
decltype(std::declval<Functor>()(std::declval<Args>()...)),
|
|
||||||
R>::value>>
|
|
||||||
Connection Connect(Functor functor);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace sigslot
|
|
||||||
} // namespace tile
|
|
||||||
|
|
||||||
#endif // TILE_SIGSLOT_SIGNAL_H
|
|
1682
tile/sigslot/sigslot.h
Normal file
1682
tile/sigslot/sigslot.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,27 +11,29 @@ namespace tile {
|
|||||||
namespace testing {
|
namespace testing {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int StartBenchmark(int argc, char **argv) {
|
int
|
||||||
|
StartBenchmark(int argc, char **argv)
|
||||||
|
{
|
||||||
::benchmark::Initialize(&argc, argv);
|
::benchmark::Initialize(&argc, argv);
|
||||||
if (::benchmark::ReportUnrecognizedArguments(argc, argv)) {
|
if (::benchmark::ReportUnrecognizedArguments(argc, argv)) { return 1; }
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
::benchmark::RunSpecifiedBenchmarks();
|
::benchmark::RunSpecifiedBenchmarks();
|
||||||
::benchmark::Shutdown();
|
::benchmark::Shutdown();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
}// namespace
|
||||||
|
|
||||||
int InitAndRunAllBenchmarks(int *argc, char **argv) {
|
int
|
||||||
|
InitAndRunAllBenchmarks(int *argc, char **argv)
|
||||||
|
{
|
||||||
|
|
||||||
if (gflags::GetCommandLineFlagInfoOrDie("logtostderr").is_default) {
|
// if (gflags::GetCommandLineFlagInfoOrDie("logtostderr").is_default) {
|
||||||
FLAGS_logtostderr = true;
|
// FLAGS_logtostderr = true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
return Start(*argc, argv, StartBenchmark, true);
|
return Start(*argc, argv, StartBenchmark, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace testing
|
}// namespace testing
|
||||||
} // namespace tile
|
}// namespace tile
|
||||||
|
|
||||||
TILE_BENCHMARK_MAIN
|
TILE_BENCHMARK_MAIN
|
||||||
|
40
tile/tile.h
40
tile/tile.h
@ -1,22 +1,54 @@
|
|||||||
#ifndef _TILE_TILE_H
|
#ifndef TILE_TILE_H
|
||||||
#define _TILE_TILE_H
|
#define TILE_TILE_H
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// base module
|
// base module
|
||||||
#include "tile/base/defered.h"
|
#include "tile/base/align.h"
|
||||||
|
#include "tile/base/buffer.h"
|
||||||
|
#include "tile/base/casting.h"
|
||||||
|
#include "tile/base/chrono.h"
|
||||||
|
#include "tile/base/compression.h"
|
||||||
|
#include "tile/base/data.h"
|
||||||
|
#include "tile/base/deferred.h"
|
||||||
|
#include "tile/base/demangle.h"
|
||||||
|
#include "tile/base/dependency_registry.h"
|
||||||
#include "tile/base/down_cast.h"
|
#include "tile/base/down_cast.h"
|
||||||
#include "tile/base/encoding.h"
|
#include "tile/base/encoding.h"
|
||||||
|
#include "tile/base/enum.h"
|
||||||
|
#include "tile/base/erased_ptr.h"
|
||||||
|
#include "tile/base/exposed_var.h"
|
||||||
|
#include "tile/base/future.h"
|
||||||
|
#include "tile/base/handle.h"
|
||||||
|
#include "tile/base/id_alloc.h"
|
||||||
|
#include "tile/base/likely.h"
|
||||||
#include "tile/base/logging.h"
|
#include "tile/base/logging.h"
|
||||||
|
#include "tile/base/make_unique.h"
|
||||||
|
#include "tile/base/maybe_owning.h"
|
||||||
|
#include "tile/base/never_destroyed.h"
|
||||||
|
#include "tile/base/object_pool.h"
|
||||||
|
#include "tile/base/option.h"
|
||||||
#include "tile/base/optional.h"
|
#include "tile/base/optional.h"
|
||||||
#include "tile/base/random.h"
|
#include "tile/base/random.h"
|
||||||
|
#include "tile/base/ref_ptr.h"
|
||||||
#include "tile/base/slice.h"
|
#include "tile/base/slice.h"
|
||||||
|
#include "tile/base/status.h"
|
||||||
#include "tile/base/string.h"
|
#include "tile/base/string.h"
|
||||||
|
#include "tile/base/type_index.h"
|
||||||
|
#include "tile/base/variant.h"
|
||||||
|
#include "tile/base/write_mostly.h"
|
||||||
|
|
||||||
|
// util
|
||||||
|
#include "tile/util/config.h"
|
||||||
|
#include "tile/util/ini_file_config.h"
|
||||||
|
|
||||||
// init module
|
// init module
|
||||||
#include "tile/init/override_flag.h"
|
#include "tile/init/override_flag.h"
|
||||||
|
|
||||||
|
#include "sigslot/signal.h"
|
||||||
|
|
||||||
// Tile Init
|
// Tile Init
|
||||||
|
#include "mustache.hpp"
|
||||||
#include "tile/init.h"
|
#include "tile/init.h"
|
||||||
|
|
||||||
#endif // _TILE_TILE_H
|
#endif // TILE_TILE_H
|
||||||
|
113
tile/util/config.cc
Normal file
113
tile/util/config.cc
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#include "tile/util/config.h"
|
||||||
|
#include "tile/base/thread/unique_lock.h"
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
namespace util {
|
||||||
|
bool Config::Has(const Slice &key) const {
|
||||||
|
UniqueLock<tile::Mutex> lock(mutex_);
|
||||||
|
std::string value;
|
||||||
|
return GetRaw(key, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::Remove(const Slice &key) {}
|
||||||
|
|
||||||
|
void Config::EnableEvents(bool enable) { events_enabled_ = enable; }
|
||||||
|
bool Config::EventsEnabled() const { return events_enabled_; }
|
||||||
|
|
||||||
|
Config::Keys Config::keys(const Slice &root) const {
|
||||||
|
UniqueLock<tile::Mutex> lock(mutex_);
|
||||||
|
return Enumerate(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TILE_DEFINE_CONFIG_GETTER(type, name) \
|
||||||
|
std::optional<type> Config::Get##name(const Slice &key) const { \
|
||||||
|
std::string value; \
|
||||||
|
if (GetRaw(key, &value)) { \
|
||||||
|
auto opt = TryParseTraits<type>::TryParse(value); \
|
||||||
|
if (opt.has_value()) { \
|
||||||
|
return *opt; \
|
||||||
|
} else { \
|
||||||
|
return std::nullopt; \
|
||||||
|
} \
|
||||||
|
} else { \
|
||||||
|
return std::nullopt; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
type Config::Get##name(const Slice &key, type default_value) const { \
|
||||||
|
auto opt = Get##name(key); \
|
||||||
|
if (opt.has_value()) { \
|
||||||
|
return *opt; \
|
||||||
|
} else { \
|
||||||
|
return default_value; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TILE_DEFINE_CONFIG_SETTER(type, name) \
|
||||||
|
void Config::Set##name(const Slice &key, type value) { \
|
||||||
|
SetRawWithEvent(key, Format("{}", value)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Config::GetString(const Slice &key) const {
|
||||||
|
std::string value;
|
||||||
|
UniqueLock<tile::Mutex> lock(mutex_);
|
||||||
|
if (GetRaw(key, &value)) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Config::GetString(const Slice &key,
|
||||||
|
std::string default_value) const {
|
||||||
|
auto opt = GetString(key);
|
||||||
|
return opt.has_value() ? *opt : default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::SetString(const Slice &key, const std::string &value) {
|
||||||
|
SetRawWithEvent(key, value);
|
||||||
|
}
|
||||||
|
void Config::SetBool(const Slice &key, bool value) {
|
||||||
|
SetRawWithEvent(key, value ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
TILE_DEFINE_CONFIG_GETTER(int, Int)
|
||||||
|
TILE_DEFINE_CONFIG_GETTER(unsigned int, UInt)
|
||||||
|
TILE_DEFINE_CONFIG_GETTER(int16_t, Int16)
|
||||||
|
TILE_DEFINE_CONFIG_GETTER(int32_t, Int32)
|
||||||
|
TILE_DEFINE_CONFIG_GETTER(int64_t, Int64)
|
||||||
|
TILE_DEFINE_CONFIG_GETTER(uint16_t, UInt16)
|
||||||
|
TILE_DEFINE_CONFIG_GETTER(uint32_t, UInt32)
|
||||||
|
TILE_DEFINE_CONFIG_GETTER(uint64_t, UInt64)
|
||||||
|
TILE_DEFINE_CONFIG_GETTER(double, Double)
|
||||||
|
TILE_DEFINE_CONFIG_GETTER(bool, Bool)
|
||||||
|
|
||||||
|
TILE_DEFINE_CONFIG_SETTER(int, Int)
|
||||||
|
TILE_DEFINE_CONFIG_SETTER(unsigned int, UInt)
|
||||||
|
TILE_DEFINE_CONFIG_SETTER(int16_t, Int16)
|
||||||
|
TILE_DEFINE_CONFIG_SETTER(int32_t, Int32)
|
||||||
|
TILE_DEFINE_CONFIG_SETTER(int64_t, Int64)
|
||||||
|
TILE_DEFINE_CONFIG_SETTER(uint16_t, UInt16)
|
||||||
|
TILE_DEFINE_CONFIG_SETTER(uint32_t, UInt32)
|
||||||
|
TILE_DEFINE_CONFIG_SETTER(uint64_t, UInt64)
|
||||||
|
TILE_DEFINE_CONFIG_SETTER(double, Double)
|
||||||
|
|
||||||
|
void Config::SetRawWithEvent(const Slice &key, const std::string &value) {
|
||||||
|
if (events_enabled_) {
|
||||||
|
OnChanging(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
UniqueLock<tile::Mutex> lock(mutex_);
|
||||||
|
SetRaw(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events_enabled_) {
|
||||||
|
OnChanged(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::~Config() {}
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
|
||||||
|
} // namespace tile
|
128
tile/util/config.h
Normal file
128
tile/util/config.h
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#ifndef TILE_UTIL_CONFIG_H
|
||||||
|
#define TILE_UTIL_CONFIG_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tile/base/optional.h"
|
||||||
|
#include "tile/base/ref_ptr.h"
|
||||||
|
#include "tile/base/slice.h"
|
||||||
|
#include "tile/base/string.h"
|
||||||
|
#include "tile/base/thread/mutex.h"
|
||||||
|
#include "tile/sigslot/sigslot.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
namespace util {
|
||||||
|
class Config : public RefCounted<Config> {
|
||||||
|
public:
|
||||||
|
using Keys = std::vector<std::string>;
|
||||||
|
using Ptr = RefPtr<Config>;
|
||||||
|
|
||||||
|
// events
|
||||||
|
// Key, Value
|
||||||
|
sigslot::signal2<const Slice &, const Slice &> OnChanging;
|
||||||
|
// Key, Value
|
||||||
|
sigslot::signal2<const Slice &, const Slice &> OnChanged;
|
||||||
|
// Key
|
||||||
|
sigslot::signal1<const Slice &> OnRemoving;
|
||||||
|
// Key
|
||||||
|
sigslot::signal1<const Slice &> OnRemoved;
|
||||||
|
|
||||||
|
bool Has(const Slice &key) const;
|
||||||
|
void Remove(const Slice &key);
|
||||||
|
void EnableEvents(bool enable = true);
|
||||||
|
bool EventsEnabled() const;
|
||||||
|
|
||||||
|
Keys keys(const Slice &root = "") const;
|
||||||
|
|
||||||
|
#define TILE_DECLARE_CONFIG_GETTER(type, name) \
|
||||||
|
std::optional<type> Get##name(const Slice &key) const; \
|
||||||
|
type Get##name(const Slice &key, type default_value) const;
|
||||||
|
|
||||||
|
#define TILE_DECLARE_CONFIG_SETTER(type, name) \
|
||||||
|
virtual void Set##name(const Slice &key, type value);
|
||||||
|
|
||||||
|
std::string GetRawString(const Slice &key, const Slice &default_value) const;
|
||||||
|
// std::stirng GetString()
|
||||||
|
// int GetInt()
|
||||||
|
// unsigned int GetUInt()
|
||||||
|
// int16_t GetInt16()
|
||||||
|
// int32_t GetInt32()
|
||||||
|
// int64_t GetInt64()
|
||||||
|
// uint16_t GetUInt16()
|
||||||
|
// uint32_t GetUInt32()
|
||||||
|
// uint64_t GetUInt64()
|
||||||
|
// double GetDouble()
|
||||||
|
// bool GetBool()
|
||||||
|
|
||||||
|
// getters
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(std::string, String)
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(int, Int)
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(unsigned int, UInt)
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(int16_t, Int16)
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(int32_t, Int32)
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(int64_t, Int64)
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(uint16_t, UInt16)
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(uint32_t, UInt32)
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(uint64_t, UInt64)
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(double, Double)
|
||||||
|
TILE_DECLARE_CONFIG_GETTER(bool, Bool)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// setters
|
||||||
|
// SetString(const Slice &key, const std::string &value)
|
||||||
|
// SetInt(const Slice &key, int value)
|
||||||
|
// SetUInt(const Slice &key, unsigned int value)
|
||||||
|
// SetInt16(const Slice &key, int16_t value)
|
||||||
|
// SetInt32(const Slice &key, int32_t value)
|
||||||
|
// SetInt64(const Slice &key, int64_t value)
|
||||||
|
// SetUInt16(const Slice &key, uint16_t value)
|
||||||
|
// SetUInt32(const Slice &key, uint32_t value)
|
||||||
|
// SetUInt64(const Slice &key, uint64_t value)
|
||||||
|
// SetDouble(const Slice &key, double value)
|
||||||
|
// SetBool(const Slice &key, bool value)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(const std::string &, String)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(int, Int)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(unsigned int, UInt)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(int16_t, Int16)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(int32_t, Int32)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(int64_t, Int64)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(uint16_t, UInt16)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(uint32_t, UInt32)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(uint64_t, UInt64)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(double, Double)
|
||||||
|
TILE_DECLARE_CONFIG_SETTER(bool, Bool)
|
||||||
|
|
||||||
|
#undef TILE_DECLARE_CONFIG_GETTER
|
||||||
|
#undef TILE_DECLARE_CONFIG_SETTER
|
||||||
|
|
||||||
|
protected:
|
||||||
|
class ScopedLock {
|
||||||
|
public:
|
||||||
|
explicit ScopedLock(const Config &config) : config_(config) {
|
||||||
|
config_.mutex_.Lock();
|
||||||
|
}
|
||||||
|
~ScopedLock() { config_.mutex_.Unlock(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Config &config_;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual bool GetRaw(const Slice &key, std::string *value) const = 0;
|
||||||
|
virtual bool SetRaw(const Slice &key, const Slice &value) = 0;
|
||||||
|
virtual void RemoveRaw(const Slice &key) = 0;
|
||||||
|
virtual Keys Enumerate(const Slice &range) const = 0;
|
||||||
|
void SetRawWithEvent(const Slice &key, const std::string &value);
|
||||||
|
virtual ~Config();
|
||||||
|
|
||||||
|
friend class std::default_delete<Config>;
|
||||||
|
friend class LayeredConfig;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable Mutex mutex_;
|
||||||
|
bool events_enabled_{false};
|
||||||
|
};
|
||||||
|
} // namespace util
|
||||||
|
} // namespace tile
|
||||||
|
|
||||||
|
#endif // TILE_UTIL_CONFIG_H
|
145
tile/util/ini_file_config.cc
Normal file
145
tile/util/ini_file_config.cc
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#include "tile/util/ini_file_config.h"
|
||||||
|
#include "tile/base/thread/scoped_lock.h"
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
IniFileConfig::IniFileConfig() {}
|
||||||
|
// IniFileConfig::IniFileConfig(std::istream &istr) { load(istr); }
|
||||||
|
// IniFileConfig::IniFileConfig(const std::string &path) { load(path); }
|
||||||
|
IniFileConfig::~IniFileConfig() {}
|
||||||
|
|
||||||
|
bool IniFileConfig::load(std::istream &istr) {
|
||||||
|
Config::ScopedLock lock(*this);
|
||||||
|
map_.clear();
|
||||||
|
section_key_.clear();
|
||||||
|
while (!istr.eof()) {
|
||||||
|
ParseLine(istr);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool IniFileConfig::load(const std::string &path) {
|
||||||
|
std::ifstream istr(path);
|
||||||
|
if (istr.good()) {
|
||||||
|
return load(istr);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool IniFileConfig::GetRaw(const Slice &key, std::string *value) const {
|
||||||
|
auto iter = map_.find(key.ToString());
|
||||||
|
if (iter != map_.end()) {
|
||||||
|
*value = iter->second;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool IniFileConfig::SetRaw(const Slice &key, const Slice &value) {
|
||||||
|
map_[key] = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IniFileConfig::RemoveRaw(const Slice &key) {
|
||||||
|
std::string prefix = key;
|
||||||
|
if (!prefix.empty()) {
|
||||||
|
prefix.push_back('.');
|
||||||
|
}
|
||||||
|
IStringMap::iterator it = map_.begin();
|
||||||
|
IStringMap::iterator cur;
|
||||||
|
while (it != map_.end()) {
|
||||||
|
if (EqualsIgnoreCase(cur->first, key) ||
|
||||||
|
EqualsIgnoreCase(cur->first, prefix)) {
|
||||||
|
it = map_.erase(cur);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::Keys IniFileConfig::Enumerate(const Slice &key) const {
|
||||||
|
Config::Keys range;
|
||||||
|
std::set<Slice> keys;
|
||||||
|
std::string prefix = key.ToString();
|
||||||
|
if (prefix.empty()) {
|
||||||
|
prefix.push_back('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string::size_type psize = prefix.size();
|
||||||
|
for (const auto &p : map_) {
|
||||||
|
auto &key = p.first;
|
||||||
|
if (EqualsIgnoreCase(key, prefix, psize)) {
|
||||||
|
std::string::size_type end = key.find('.', psize);
|
||||||
|
std::string subkey;
|
||||||
|
if (end == std::string::npos) {
|
||||||
|
subkey = key.substr(psize);
|
||||||
|
} else {
|
||||||
|
subkey = key.substr(psize, end - psize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys.find(subkey) == keys.end()) {
|
||||||
|
keys.insert(subkey);
|
||||||
|
range.push_back(subkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IniFileConfig::ParseLine(std::istream &istr) {
|
||||||
|
static const int eof = std::char_traits<char>::eof();
|
||||||
|
auto ReadLine = [&](std::string *line) {
|
||||||
|
line->clear();
|
||||||
|
while (true) {
|
||||||
|
int c = istr.get();
|
||||||
|
if (c == eof || c == '\n') {
|
||||||
|
return c != eof;
|
||||||
|
} else {
|
||||||
|
*line += (char)c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string raw_line;
|
||||||
|
while (ReadLine(&raw_line)) {
|
||||||
|
if (raw_line.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Slice line = TrimLeft(raw_line);
|
||||||
|
if (line.empty() || line[0] == ';' || line[0] == '#') {
|
||||||
|
// skip empty line
|
||||||
|
// skip comment line ; or #
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse section
|
||||||
|
if (line[0] == '[') {
|
||||||
|
auto pos = line.find(']');
|
||||||
|
if (pos == Slice::npos) {
|
||||||
|
section_key_ = Trim(line.substr(1));
|
||||||
|
} else {
|
||||||
|
section_key_ = Trim(line.substr(1, pos - 1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto strs = Split(line, "=", true, 2);
|
||||||
|
std::string full_key = section_key_;
|
||||||
|
if (!full_key.empty()) {
|
||||||
|
full_key.push_back('.');
|
||||||
|
}
|
||||||
|
full_key.append(Trim(strs[0]));
|
||||||
|
if (strs.size() > 1) {
|
||||||
|
map_[full_key] = Trim(strs[1]);
|
||||||
|
} else {
|
||||||
|
map_[full_key] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool IniFileConfig::ICompare::operator()(const std::string &s1,
|
||||||
|
const std::string &s2) const {
|
||||||
|
auto len = std::min(s1.size(), s2.size());
|
||||||
|
return strncmp(s1.c_str(), s2.c_str(), len) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace tile
|
40
tile/util/ini_file_config.h
Normal file
40
tile/util/ini_file_config.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef TILE_UTIL_INI_FILE_CONFIG_H
|
||||||
|
#define TILE_UTIL_INI_FILE_CONFIG_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tile/util/config.h"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
namespace util {
|
||||||
|
class IniFileConfig : public Config {
|
||||||
|
public:
|
||||||
|
using Ptr = RefPtr<IniFileConfig>;
|
||||||
|
|
||||||
|
IniFileConfig();
|
||||||
|
~IniFileConfig() override;
|
||||||
|
// IniFileConfig(std::istream &istr);
|
||||||
|
// IniFileConfig(const std::string &path);
|
||||||
|
bool load(std::istream &istr);
|
||||||
|
bool load(const std::string &path);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool GetRaw(const Slice &key, std::string *value) const override;
|
||||||
|
bool SetRaw(const Slice &key, const Slice &value) override;
|
||||||
|
void RemoveRaw(const Slice &key) override;
|
||||||
|
Keys Enumerate(const Slice &range) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ParseLine(std::istream &istr);
|
||||||
|
struct ICompare {
|
||||||
|
bool operator()(const std::string &s1, const std::string &s2) const;
|
||||||
|
};
|
||||||
|
typedef std::map<std::string, std::string, ICompare> IStringMap;
|
||||||
|
IStringMap map_;
|
||||||
|
std::string section_key_;
|
||||||
|
};
|
||||||
|
} // namespace util
|
||||||
|
} // namespace tile
|
||||||
|
|
||||||
|
#endif // TILE_UTIL_INI_FILE_CONFIG_H
|
46
tile/util/ini_file_config_test.cc
Normal file
46
tile/util/ini_file_config_test.cc
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include "tile/util/ini_file_config.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
const char *kIniFileConfig = R"(
|
||||||
|
# comment 1
|
||||||
|
; comment 2
|
||||||
|
# commet 3
|
||||||
|
; commment 4
|
||||||
|
a=1
|
||||||
|
[sec1]
|
||||||
|
a=2
|
||||||
|
[sec3]
|
||||||
|
a=3
|
||||||
|
[sec2.kk]
|
||||||
|
a=4
|
||||||
|
)";
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
static_assert(!detail::HasClassofImpl<IniFileConfig, Config>::value, "");
|
||||||
|
|
||||||
|
TEST(IniFileConfig, LoadFromIStream) {
|
||||||
|
std::stringstream ss(kIniFileConfig);
|
||||||
|
Config::Ptr config = MakeRefCounted<IniFileConfig>();
|
||||||
|
ASSERT_FALSE(config->Has("a"));
|
||||||
|
ASSERT_FALSE(config->Has("sec1.a"));
|
||||||
|
ASSERT_FALSE(config->Has("sec3.a"));
|
||||||
|
if (config.Is<IniFileConfig>()) {
|
||||||
|
IniFileConfig::Ptr ini = config.As<IniFileConfig>();
|
||||||
|
ASSERT_TRUE(ini->load(ss));
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(config->Has("a"));
|
||||||
|
ASSERT_TRUE(config->Has("sec1.a"));
|
||||||
|
ASSERT_TRUE(config->Has("sec3.a"));
|
||||||
|
ASSERT_TRUE(config->Has("sec2.kk.a"));
|
||||||
|
|
||||||
|
ASSERT_TRUE(config->GetInt("a"));
|
||||||
|
ASSERT_TRUE(config->GetInt("sec1.a"));
|
||||||
|
ASSERT_EQ(1, *config->GetInt("a"));
|
||||||
|
ASSERT_EQ(2, *config->GetInt("sec1.a"));
|
||||||
|
ASSERT_EQ(3, *config->GetInt("sec3.a"));
|
||||||
|
ASSERT_EQ(4, *config->GetInt("sec2.kk.a"));
|
||||||
|
}
|
||||||
|
} // namespace util
|
||||||
|
} // namespace tile
|
129
tile/util/layered_config.cc
Normal file
129
tile/util/layered_config.cc
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#include "tile/util/layered_config.h"
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
LayeredConfig::LayeredConfig() {}
|
||||||
|
LayeredConfig::~LayeredConfig() {}
|
||||||
|
|
||||||
|
void LayeredConfig::Add(Config::Ptr cfg) { Add(cfg, highest(), false); }
|
||||||
|
|
||||||
|
void LayeredConfig::Add(Config::Ptr cfg, int priority) {
|
||||||
|
Add(cfg, priority, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayeredConfig::Add(Config::Ptr cfg, const std::string &label) {
|
||||||
|
Add(cfg, label, highest(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayeredConfig::Add(Config::Ptr cfg, const std::string &label,
|
||||||
|
int priority) {
|
||||||
|
Add(cfg, label, priority, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayeredConfig::Add(Config::Ptr cfg, const std::string &label,
|
||||||
|
bool writeable) {
|
||||||
|
Add(cfg, label, highest(), writeable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayeredConfig::Add(Config::Ptr cfg, const std::string &label, int priority,
|
||||||
|
bool writeable) {
|
||||||
|
Config::ScopedLock lock(*this);
|
||||||
|
ConfigItem item;
|
||||||
|
item.cfg = cfg;
|
||||||
|
item.priority = priority;
|
||||||
|
item.writeable = writeable;
|
||||||
|
item.label = label;
|
||||||
|
auto it = configs_.begin();
|
||||||
|
while (it != configs_.end() && it->priority < priority) {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
configs_.insert(it, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayeredConfig::Add(Config::Ptr cfg, int priority, bool writeable) {
|
||||||
|
Add(cfg, std::string(), priority, writeable);
|
||||||
|
}
|
||||||
|
void LayeredConfig::AddWriteable(Config::Ptr cfg, int priority) {
|
||||||
|
Add(cfg, priority, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::Ptr LayeredConfig::Find(const Slice &label) const {
|
||||||
|
Config::ScopedLock lock(*this);
|
||||||
|
|
||||||
|
for (const auto &conf : configs_) {
|
||||||
|
if (conf.label == label) {
|
||||||
|
return conf.cfg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayeredConfig::RemoveConfig(Config::Ptr cfg) {
|
||||||
|
Config::ScopedLock lock(*this);
|
||||||
|
for (auto it = configs_.begin(); it != configs_.end();) {
|
||||||
|
if (it->cfg == cfg) {
|
||||||
|
it = configs_.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LayeredConfig::GetRaw(const Slice &key, std::string *value) const {
|
||||||
|
for (const auto &conf : configs_) {
|
||||||
|
if (conf.cfg->GetRaw(key, value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LayeredConfig::SetRaw(const Slice &key, const Slice &value) {
|
||||||
|
for (const auto &conf : configs_) {
|
||||||
|
if (conf.writeable && conf.cfg->SetRaw(key, value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::Keys LayeredConfig::Enumerate(const Slice &key) const {
|
||||||
|
Config::Keys keys;
|
||||||
|
std::set<Slice> key_set;
|
||||||
|
for (const auto &conf : configs_) {
|
||||||
|
auto conf_keys = conf.cfg->Enumerate(key);
|
||||||
|
for (const auto &k : conf_keys) {
|
||||||
|
if (key_set.insert(k).second) {
|
||||||
|
keys.push_back(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayeredConfig::RemoveRaw(const std::string &key) {
|
||||||
|
for (auto &conf : configs_) {
|
||||||
|
if (conf.writeable) {
|
||||||
|
conf.cfg->RemoveRaw(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int LayeredConfig::lowest() const {
|
||||||
|
if (configs_.empty()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return configs_.front().priority - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int LayeredConfig::highest() const {
|
||||||
|
if (configs_.empty()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return configs_.back().priority + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace tile
|
57
tile/util/layered_config.h
Normal file
57
tile/util/layered_config.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef TILE_UTIL_LAYERD_CONFIG_H
|
||||||
|
#define TILE_UTIL_LAYERD_CONFIG_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tile/util/config.h"
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace tile {
|
||||||
|
namespace util {
|
||||||
|
class LayeredConfig : public Config {
|
||||||
|
public:
|
||||||
|
using Ptr = RefPtr<LayeredConfig>;
|
||||||
|
LayeredConfig();
|
||||||
|
LayeredConfig(const LayeredConfig &) = delete;
|
||||||
|
LayeredConfig &operator=(const LayeredConfig &) = delete;
|
||||||
|
|
||||||
|
void Add(Config::Ptr cfg);
|
||||||
|
void Add(Config::Ptr cfg, int priority);
|
||||||
|
void Add(Config::Ptr cfg, const std::string &label);
|
||||||
|
void Add(Config::Ptr cfg, const std::string &label, int priority);
|
||||||
|
void Add(Config::Ptr cfg, const std::string &label, bool writeable);
|
||||||
|
void Add(Config::Ptr cfg, const std::string &label, int priority,
|
||||||
|
bool writeable);
|
||||||
|
void Add(Config::Ptr cfg, int priority, bool writeable);
|
||||||
|
void AddWriteable(Config::Ptr cfg, int priority);
|
||||||
|
Config::Ptr Find(const Slice &label) const;
|
||||||
|
void RemoveConfig(Config::Ptr cfg);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct ConfigItem {
|
||||||
|
Config::Ptr cfg;
|
||||||
|
// ... > -2 > -1 > 0 > 1 > 2 > 3 > 4 ...
|
||||||
|
int priority;
|
||||||
|
// can remove or set new?
|
||||||
|
bool writeable;
|
||||||
|
std::string label;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GetRaw(const Slice &key, std::string *value) const override;
|
||||||
|
bool SetRaw(const Slice &key, const Slice &value) override;
|
||||||
|
Config::Keys Enumerate(const Slice &key) const override;
|
||||||
|
void RemoveRaw(const std::string &key);
|
||||||
|
|
||||||
|
int lowest() const;
|
||||||
|
int highest() const;
|
||||||
|
|
||||||
|
~LayeredConfig();
|
||||||
|
|
||||||
|
private:
|
||||||
|
using ConfigList = std::list<ConfigItem>;
|
||||||
|
ConfigList configs_;
|
||||||
|
};
|
||||||
|
} // namespace util
|
||||||
|
} // namespace tile
|
||||||
|
|
||||||
|
#endif // TILE_UTIL_LAYERD_CONFIG_H
|
Loading…
x
Reference in New Issue
Block a user