diff --git a/.woodpecker/linux-aarch64-gcc.yml b/.woodpecker/linux-aarch64-gcc.yml new file mode 100644 index 0000000..02f22df --- /dev/null +++ b/.woodpecker/linux-aarch64-gcc.yml @@ -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 + + diff --git a/.woodpecker/linux-arm-gcc.yml.stop b/.woodpecker/linux-arm-gcc.yml.stop new file mode 100644 index 0000000..9a167bb --- /dev/null +++ b/.woodpecker/linux-arm-gcc.yml.stop @@ -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 + + diff --git a/.woodpecker/linux-x64-gcc.yml b/.woodpecker/linux-x64-gcc.yml new file mode 100644 index 0000000..5b03d91 --- /dev/null +++ b/.woodpecker/linux-x64-gcc.yml @@ -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 + + diff --git a/.woodpecker/linux-x86-gcc.yml.stop b/.woodpecker/linux-x86-gcc.yml.stop new file mode 100644 index 0000000..80fbaf1 --- /dev/null +++ b/.woodpecker/linux-x86-gcc.yml.stop @@ -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 + + diff --git a/CMakeLists.txt b/CMakeLists.txt index cdf0287..00837d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,10 +3,13 @@ cmake_minimum_required(VERSION 3.5) set(tile_VERSION_MAJOR 0) set(tile_VERSION_MINOR 1) 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( 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) set(CMAKE_CXX_STANDARD 11) @@ -89,7 +92,7 @@ add_subdirectory("third_party/gflags") set(GFLAGS_USE_TARGET_NAMESPACE ON) set(gflags_DIR "${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags") add_subdirectory("third_party/glog") -add_subdirectory("third_party/context") +# add_subdirectory("third_party/context") set(CURL_DISABLE_TESTS ON) set(CURL_CA_PATH @@ -185,10 +188,12 @@ set(TILE_SRCS "tile/base/thread/rw_mutex.cc" "tile/base/thread/scoped_lock.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/native/acceptor.cc" "tile/io/descriptor.cc" + "tile/io/util/rate_limiter.cc" "tile/io/event_loop.cc" "tile/init.cc" "tile/init/on_init.cc" @@ -205,6 +210,9 @@ set(TILE_SRCS "tile/rpc/protocol/http/buffer_io.cc" "tile/rpc/protocol/message.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)) @@ -220,7 +228,9 @@ target_precompile_headers(tile PUBLIC json/nlohann/json.hpp) # target_sources(tile PRIVATE ${TILE_SRCS}) target_include_directories( 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_BINARY_DIR}/third_party/glog" "${CMAKE_CURRENT_SOURCE_DIR}/third_party/glog/src" @@ -232,7 +242,8 @@ target_include_directories( target_link_libraries( tile PUBLIC # -Wl,--start-group - nova_context zlib gflags::gflags glog::glog + # nova_context + zlib gflags::gflags glog::glog jsoncpp_static # -Wl,--end-group libcurl fmt) @@ -284,8 +295,9 @@ if(TILE_BUILD_TESTS) add_test(NAME ${test_name} COMMAND ${test_name}) 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(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(net_internal_http_engine_test # "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_test(base_internal_time_keeper_test # "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) if(TILE_BUILD_BENCHMARKS) @@ -374,7 +387,7 @@ if(TILE_BUILD_BENCHMARKS) target_sources(tile_bm_all PRIVATE ${benchmark_file}) 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_thread_mutex_benchmark "tile/base/thread/mutex_benchmark.cc") tile_add_bm(base_encoding_benchmark "tile/base/encoding_benchmark.cc") diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 233c808..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - build-env: - image: ubuntu:16.04 - restart: always - container_name: "env" - command: ["/bin/bash", "-c", "sleep 36000"] - volumes: - - ./:/workspace diff --git a/tile/base/casting.h b/tile/base/casting.h index 3aa4dfd..a5ee6e1 100644 --- a/tile/base/casting.h +++ b/tile/base/casting.h @@ -10,10 +10,32 @@ #include "tile/base/likely.h" namespace tile { +namespace detail { +// Has classof +template struct HasClassofImpl { + template < + typename TT, typename TBase, + typename = enable_if_t())), bool>::value>> + static std::true_type test(int); + template static std::false_type test(...); + + static constexpr bool value = decltype(test(0))::value; +}; +} // namespace detail + template struct CastingTraits { - template static bool RuntimeTypeCheck(const U &val) { + template + static auto RuntimeTypeCheck(const Base &val) + -> enable_if_t::value, bool> { return T::classof(val); } + + template + static auto RuntimeTypeCheck(const Base &val) + -> enable_if_t::value, bool> { + return dynamic_cast(&val) != nullptr; + } }; class Castable { diff --git a/tile/base/config.h.in b/tile/base/config.h.in index 9d57d67..d16f6cc 100644 --- a/tile/base/config.h.in +++ b/tile/base/config.h.in @@ -20,6 +20,7 @@ #define TILE_SYSTEM_PROCESSOR "@CMAKE_SYSTEM_PROCESSOR@" #define TILE_SYSTEM_NAME "@CMAKE_SYSTEM_NAME@" #define TILE_SYSTEM_VERSION "@CMAKE_SYSTEM_VERSION@" +#define TILE_VERSION "@PROJECT_VERSOIN@" // clang-format on diff --git a/tile/base/internal/logging.h b/tile/base/internal/logging.h index b151218..6c527d1 100644 --- a/tile/base/internal/logging.h +++ b/tile/base/internal/logging.h @@ -637,6 +637,11 @@ public: size_t message_len); }; +class FileLogSink : public LogSink { +public: + FileLogSink(const std::string &file_path_template); +}; + void AddLogSink(LogSink *dest); void RemoveLogSink(LogSink *dest); void SetStderrLogging(LogSeverity min_severity); diff --git a/tile/base/make_unique.h b/tile/base/make_unique.h index d6d270b..b701aab 100644 --- a/tile/base/make_unique.h +++ b/tile/base/make_unique.h @@ -17,6 +17,12 @@ make_unique(Args &&...args) { return std::unique_ptr(new T(std::forward(args)...)); } +template +inline enable_if_t::value, std::unique_ptr> +make_unique(T *ptr) { + return std::unique_ptr(ptr); +} + // dynamic array template inline enable_if_t::value && std::extent::value == 0, diff --git a/tile/base/memory_barrier.cc b/tile/base/memory_barrier.cc new file mode 100644 index 0000000..df619ae --- /dev/null +++ b/tile/base/memory_barrier.cc @@ -0,0 +1,45 @@ +#include "tile/base/memory_barrier.h" + +#include +#include + +#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 lock; + std::lock_guard _(*lock); + + TILE_PCHECK(mprotect(dummy_page, 1, PROT_READ | PROT_WRITE) == 0, "Cannot protect dummy page."); + *static_cast(dummy_page) = 0; + TILE_PCHECK(mprotect(dummy_page, 1, PROT_READ) == 0); + + MemoryBarrier(); +} + +}// namespace + +void +AsymmetricBarrierHeavy() +{ + HomemadeMembarrier(); +} +}// namespace internal + +}// namespace tile diff --git a/tile/base/memory_barrier.h b/tile/base/memory_barrier.h new file mode 100644 index 0000000..41176c7 --- /dev/null +++ b/tile/base/memory_barrier.h @@ -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 + +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 diff --git a/tile/base/net/endpoint_test.cc b/tile/base/net/endpoint_test.cc index 7cbcc48..0d89a96 100644 --- a/tile/base/net/endpoint_test.cc +++ b/tile/base/net/endpoint_test.cc @@ -139,7 +139,7 @@ TEST(Endpoint, ResolveDomainName) { const std::string test_domain = "www.baidu.com"; auto eps = ResolveDomain(test_domain, 80); - ASSERT_TRUE(eps); + ASSERT_TRUE(eps) << eps.error(); for (auto &ep : *eps) { std::cout << Format("Get IP From {}: {}", test_domain, ep.ToString()) << std::endl; diff --git a/tile/base/ref_ptr.h b/tile/base/ref_ptr.h index a1c3a1c..35f000b 100644 --- a/tile/base/ref_ptr.h +++ b/tile/base/ref_ptr.h @@ -3,6 +3,8 @@ #pragma once +#include "tile/base/casting.h" +#include "tile/base/down_cast.h" #include "tile/base/internal/logging.h" #include "tile/base/internal/meta.h" #include "tile/base/internal/utility.h" @@ -333,6 +335,12 @@ public: return internal::Exchange(ptr_, nullptr); } + template bool Is() const noexcept { return isa(ptr_); } + + template RefPtr As() const noexcept { + return RefPtr(ref_ptr, down_cast(ptr_)); + } + private: T *ptr_; }; diff --git a/tile/base/string.cc b/tile/base/string.cc index 84e2728..fce7e6e 100644 --- a/tile/base/string.cc +++ b/tile/base/string.cc @@ -83,35 +83,30 @@ std::vector Split(Slice s, Slice delim, bool keep_empty, 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::max(); std::vector splited; - if (s.empty()) { + if (s.empty() || max_split_parts == 1) { return splited; } auto current = s; assert(!delim.empty()); int split_count = 0; while (max_split_parts == kMaxSplit || split_count < max_split_parts) { - auto pos = current.find(delim); - if (pos != 0 || keep_empty) { - splited.push_back(current.substr(0, pos)); - ++split_count; + auto next_pos = current.find(delim); + auto part = current.substr(0, next_pos); + if (!part.empty() || keep_empty) { + splited.push_back(part); } - if (pos == Slice::npos) { - break; - } + ++split_count; - current = current.substr(pos + delim.size()); - if (current.empty()) { - if (keep_empty) { - splited.push_back(""); - } + if (next_pos == Slice::npos) { break; - } else if (split_count + 1 == max_split_parts) { - // ++split_count; - splited.push_back(current); + } else if (split_count + 1 >= max_split_parts) { + splited.push_back(current.substr(next_pos + delim.size())); break; + } else { + current = current.substr(next_pos + delim.size()); } } @@ -119,22 +114,36 @@ std::vector Split(Slice s, Slice delim, bool keep_empty, } 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 pred) { + while (!s.empty() && pred(s[0])) { s.RemovePrefix(1); } return s; } - -Slice TrimRight(Slice s, Slice cutset) { - while (!s.empty() && - cutset.find(Slice(s.data() + s.size() - 1, 1)) != Slice::npos) { +Slice TrimRight(Slice s, std::function pred) { + while (!s.empty() && pred(s[s.size() - 1])) { s.RemoveSuffix(1); } return s; } - -Slice Trim(Slice s, Slice cutset) { - return TrimRight(TrimLeft(s, cutset), cutset); +Slice Trim(Slice s, std::function pred) { + return TrimRight(TrimLeft(s, pred), pred); } template @@ -198,11 +207,23 @@ std::string ToLower(Slice s) { 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()); } -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() && std::equal(a.begin(), a.end(), b.begin(), [](char a, char b) { return ToLower(a) == ToLower(b); }); @@ -213,21 +234,27 @@ TryParseTraits::TryParse(Slice s, bool recognizes_alpbabet_symbol, bool ignore_case) { auto num_opt = tile::TryParse(s); if (num_opt) { - if (*num_opt == 0) { + return *num_opt != 0; + } else { + const std::vector true_arr{"true", "yes", "on", "y"}; + const std::vector false_arr{"false", "no", "off", "n"}; + if (s.size() > 5) { return false; - } else if (*num_opt == 1) { - return true; } - return std::nullopt; - } - - if (EqualsIgnoreCase(s, "y") || EqualsIgnoreCase(s, "yes") || - EqualsIgnoreCase(s, "true")) { - return true; - } else if (EqualsIgnoreCase(s, "n") || EqualsIgnoreCase(s, "no") || - EqualsIgnoreCase(s, "false")) { - return false; + auto InArray = [](const std::vector &arr, Slice s) { + for (auto &&e : arr) { + if (EqualsIgnoreCase(e, s)) { + return true; + } + } + return false; + }; + if (InArray(true_arr, s)) { + return true; + } else if (InArray(false_arr, s)) { + return false; + } } return std::nullopt; diff --git a/tile/base/string.h b/tile/base/string.h index 2c79a0f..46b5143 100644 --- a/tile/base/string.h +++ b/tile/base/string.h @@ -34,14 +34,19 @@ std::string Replace(Slice str, Slice from, Slice to, std::size_t count = std::numeric_limits::max()); -Slice TrimLeft(Slice s, Slice cutset = " \t"); -Slice TrimRight(Slice s, Slice cutset = " \t"); -Slice Trim(Slice s, Slice cutset = " \t"); +Slice TrimLeft(Slice s, Slice cutset); +Slice TrimRight(Slice s, Slice cutset); +Slice Trim(Slice s, Slice cutset); +Slice TrimLeft(Slice s, std::function pred = isspace); +Slice TrimRight(Slice s, std::function pred = isspace); +Slice Trim(Slice s, std::function pred = isspace); -std::vector Split(Slice s, char delim, bool keep_empty = false, - std::size_t max_split_parts = -1); -std::vector Split(Slice s, Slice delim, bool keep_empty = false, - std::size_t max_split_parts = -1); +std::vector +Split(Slice s, char delim, bool keep_empty = false, + std::size_t max_split_parts = std::numeric_limits::max()); +std::vector +Split(Slice s, Slice delim, bool keep_empty = false, + std::size_t max_split_parts = std::numeric_limits::max()); std::string Join(const std::vector &parts, Slice delim); std::string Join(const std::vector &parts, Slice delim); @@ -54,8 +59,11 @@ void ToLower(std::string *str); std::string ToUpper(Slice s); std::string ToLower(Slice s); -bool Equals(Slice a, Slice b); -bool EqualsIgnoreCase(Slice a, Slice b); +bool Equals(Slice a, Slice b, + std::size_t len = std::numeric_limits::max()); +bool EqualsIgnoreCase( + Slice a, Slice b, + std::size_t len = std::numeric_limits::max()); // TryParse template struct TryParseTraits; @@ -73,6 +81,10 @@ template struct TryParseTraits { }; // String to bool +// 1. "true" or "false" +// 2. "yes" or "no" +// 3. "on" or "off" +// 4. non-zero or zero template <> struct TryParseTraits { static std::optional TryParse(Slice s, bool recognizes_alpbabet_symbol = true, diff --git a/tile/base/string_test.cc b/tile/base/string_test.cc index 25ba71c..19cdca7 100644 --- a/tile/base/string_test.cc +++ b/tile/base/string_test.cc @@ -102,7 +102,7 @@ TEST(String, TryParseFloatingPoint) { TEST(String, TryParseBool) { ASSERT_FALSE(TryParse("")); ASSERT_FALSE(TryParse("..")); - ASSERT_FALSE(TryParse("2")); + ASSERT_TRUE(TryParse("2")); ASSERT_TRUE(TryParse("1")); ASSERT_TRUE(TryParse("0")); diff --git a/tile/base/thread/cond_var_test.cc b/tile/base/thread/cond_var_test.cc index b489c58..85ff911 100644 --- a/tile/base/thread/cond_var_test.cc +++ b/tile/base/thread/cond_var_test.cc @@ -10,26 +10,48 @@ namespace tile { class CondVarTest : public ::testing::Test { +protected: void SetUp() override { m = new Mutex(); cv = new CondVar(); - is_set = false; - is_timeout = false; worker = nullptr; + ResetEnv(); } void TearDown() override { + SetWorker(nullptr); + delete m; + delete cv; + } + + void SetWorker(std::thread *new_worker) { if (worker) { if (worker->joinable()) { worker->join(); } 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: Mutex *m; CondVar *cv; + std::atomic is_started; bool is_set; bool is_timeout; std::thread *worker; @@ -42,11 +64,13 @@ TEST_F(CondVarTest, NotifyOne_Wait) { // UniqueLock inner_locker(*m); // cv->Wait(inner_locker); ScopedLock inner_locker(*m); + SetStarted(); cv->Wait(inner_locker); is_set = true; cv->NotifyOne(); }); + WaitStarted(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); ASSERT_FALSE(is_set); @@ -62,21 +86,23 @@ TEST_F(CondVarTest, NotifyOne_Wait) { TEST_F(CondVarTest, NotifyOne_WaitFor) { // Wait - constexpr auto kWaitTimeoutMs = 100; + // constexpr auto kWaitTimeoutMs = 100; + constexpr auto kWaitTimeout = std::chrono::milliseconds(100); auto WaitFor = [&] { UniqueLock inner_locker(*m); - if (cv->WaitFor(inner_locker, std::chrono::milliseconds(kWaitTimeoutMs))) { - cv->NotifyOne(); + SetStarted(); + if (cv->WaitFor(inner_locker, kWaitTimeout)) { is_set = true; + cv->NotifyOne(); } else { is_timeout = true; } }; + { - std::thread t1(WaitFor); - - std::this_thread::sleep_for(std::chrono::milliseconds(kWaitTimeoutMs / 2)); + SetWorker(new std::thread(WaitFor)); + WaitStarted(); ASSERT_FALSE(is_set); { @@ -84,25 +110,22 @@ TEST_F(CondVarTest, NotifyOne_WaitFor) { cv->NotifyOne(); } - t1.join(); + worker->join(); + ASSERT_FALSE(is_timeout); ASSERT_TRUE(is_set); } // timeout { - std::thread t1(WaitFor); - + ResetEnv(); + SetWorker(new std::thread(WaitFor)); { UniqueLock locker(*m); ASSERT_FALSE(is_timeout); - std::this_thread::sleep_for( - std::chrono::milliseconds(kWaitTimeoutMs * 2)); - ASSERT_FALSE(is_timeout); - cv->NotifyOne(); } - - t1.join(); + worker->join(); ASSERT_TRUE(is_timeout); + ASSERT_FALSE(is_set); } } diff --git a/tile/base/thread/thread_local.h b/tile/base/thread/thread_local.h new file mode 100644 index 0000000..c36d320 --- /dev/null +++ b/tile/base/thread/thread_local.h @@ -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 + +namespace tile { +template +class ThreadLocal { +public: + ThreadLocal() : ThreadLocal([]() {}) {}; + + template, F>::value> * = nullptr> + explicit ThreadLocal(F &&creator) : creator_(std::forward(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 ptr = nullptr) noexcept { ScopedLock _(init_lock_); } + + template + 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()> creator_; +}; +}// namespace tile + +#endif// TILE_BASE_THREAD_THREAD_LOCAL_H diff --git a/tile/base/thread/thread_local/object_array.h b/tile/base/thread/thread_local/object_array.h new file mode 100644 index 0000000..e8ede26 --- /dev/null +++ b/tile/base/thread/thread_local/object_array.h @@ -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 +struct Entry { + std::aligned_storage storage; + static_assert(sizeof(T) == sizeof(storage), ""); +}; + +template +struct ObjectArrayCache { + std::size_t limit{}; + T *objects{}; +}; + +template +struct ObjectArray; + +template +class LazyInitObjectArray { + struct EntryDeleter { + void operator()(Entry *ptr) noexcept { operator delete(ptr); } + }; + + using AlignedArray = std::unique_ptr[], EntryDeleter>; + +public: + LazyInitObjectArray() = default; + + ~LazyInitObjectArray() + { + for (int i = 0; i != initialized_.size(); ++i) { + if (initialized_[i]) { reinterpret_cast(&objects_[i])->~T(); } + } + } + + LazyInitObjectArray(LazyInitObjectArray &&) = default; + LazyInitObjectArray &operator=(LazyInitObjectArray &&) = default; + + template + void InitializeAt(std::size_t index, F &&f) + { + TILE_CHECK_LT(index, initialized_.size()); + TILE_CHECK(!initialized_[index]); + + initialized_[index] = true; + std::forward(f)(&objects_[index]); + } + + void DestroyAt(std::size_t index) + { + TILE_CHECK_LT(index, initialized_.size()); + TILE_CHECK(initialized_[index]); + + initialized_[index] = false; + reinterpret_cast(&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(&objects_[index]); + } + + T *GetObjectsMaybeUninitialized() noexcept + { + static_assert(sizeof(T) == sizeof(Entry), ""); + return reinterpret_cast(objects_.get()); + } + + std::size_t size() const noexcept { return initialized_.size(); } + +private: + AlignedArray objects_; + std::vector initialized_; +}; + +template +class ObjectArrayRegistry { +public: + static ObjectArrayRegistry *Instance() + { + static NeverDestroyedSingleton instance; + return instance.get(); + } + + void Register(ObjectArray *array) + { + ScopedLock _(lock_); + TILE_DCHECK(std::find(arrays_.begin(), arrays_.end(), array) == arrays_.end()); + arrays_.push_back(array); + } + + void Deregister(ObjectArray *array) + { + ScopedLock _(lock_); + auto iter = std::find(arrays_.begin(), arrays_.end(), array); + TILE_CHECK(iter != arrays_.end()); + arrays_.erase(iter); + } + + template + 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 *> arrays_; +}; + +template +struct ObjectArray { + Mutex lock; + LazyInitObjectArray objects_; + + ObjectArray() { ObjectArrayRegistry::Instance()->Register(this); } + + ~ObjectArray() { ObjectArrayRegistry::Instance()->Deregister(this); } +}; + +}// namespace detail +}// namespace tls +}// namespace tile + +#endif// TILE_BASE_THREAD_THREAD_LOCAL_OBJECT_ARRAY_H diff --git a/tile/init.cc b/tile/init.cc index f4b225b..bcde904 100644 --- a/tile/init.cc +++ b/tile/init.cc @@ -1,6 +1,7 @@ #include "init.h" #include "tile/base/chrono.h" +#include "tile/base/config.h" #include "tile/base/internal/background_task_host.h" #include "tile/base/internal/time_keeper.h" #include "tile/base/internal/utility.h" @@ -54,14 +55,14 @@ int Start(int argc, char **argv, std::function cb, } // Init gflags - gflags::SetVersionString("0.1.0"); + gflags::SetVersionString(TILE_VERSION); gflags::ParseCommandLineFlags(&argc, &argv, true); detail::ApplyFlagOverrider(); // Init Glog 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); diff --git a/tile/init.h b/tile/init.h index 16eb335..0350f76 100644 --- a/tile/init.h +++ b/tile/init.h @@ -1,5 +1,5 @@ -#ifndef _TILE_INIT_H -#define _TILE_INIT_H +#ifndef TILE_INIT_H +#define TILE_INIT_H #pragma once @@ -17,4 +17,4 @@ void TerminateBasicRuntime(); } // namespace tile -#endif // _TILE_INIT_H +#endif // TILE_INIT_H diff --git a/tile/io/event_loop.cc b/tile/io/event_loop.cc index e5b3d09..159bb1c 100644 --- a/tile/io/event_loop.cc +++ b/tile/io/event_loop.cc @@ -24,7 +24,7 @@ void EventLoop::Barrier() {} void EventLoop::Run() {} void EventLoop::Stop() {} void EventLoop::Join() {} -EventLoop *EventLoop::Current() {} +EventLoop *EventLoop::Current() { return nullptr; } void EventLoop::WaitAndRunEvents(std::chrono::milliseconds wait_for) {} void EventLoop::RunUserTasks() {} } // namespace tile diff --git a/tile/io/util/rate_limiter.cc b/tile/io/util/rate_limiter.cc new file mode 100644 index 0000000..ff2569b --- /dev/null +++ b/tile/io/util/rate_limiter.cc @@ -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(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 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 lock(lock_); + return impl_->GetQuota(); +} +void ThreadSafeRateLimiter::ConsumeBytes(std::size_t consumed) { + std::lock_guard lock(lock_); + impl_->ConsumeBytes(consumed); +} + +LayeredRateLimiter::LayeredRateLimiter(RateLimiter *upper, + MaybeOwning 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 diff --git a/tile/io/util/rate_limiter.h b/tile/io/util/rate_limiter.h new file mode 100644 index 0000000..8ed24c6 --- /dev/null +++ b/tile/io/util/rate_limiter.h @@ -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 +#include + +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 limiter, + std::size_t burst_limit = std::numeric_limits::max()); + std::size_t GetQuota() override; + void ConsumeBytes(std::size_t consumed) override; + +private: + std::size_t burst_limit_; + std::mutex lock_; + MaybeOwning impl_; +}; + +class LayeredRateLimiter : public RateLimiter { +public: + LayeredRateLimiter(RateLimiter *upper, MaybeOwning ours); + + std::size_t GetQuota() override; + void ConsumeBytes(std::size_t consumed) override; + +private: + RateLimiter *upper_; + MaybeOwning ours_; +}; + +} // namespace tile + +#endif // TILE_IO_UTIL_RATE_LIMITER_H diff --git a/tile/io/util/rate_limiter_test.cc b/tile/io/util/rate_limiter_test.cc new file mode 100644 index 0000000..753e8f1 --- /dev/null +++ b/tile/io/util/rate_limiter_test.cc @@ -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(1000, 1)); + std::atomic total{0}; + std::vector 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(1000, 1)); + auto our_limiter = make_unique( + make_unique(1000, 100)); + LayeredRateLimiter layered_limiter(&base_limiter, std::move(our_limiter)); + std::atomic total{0}; + std::vector 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(1000, 100)); + auto our_limiter = make_unique( + make_unique(1000, 1)); + LayeredRateLimiter layered_limiter(&base_limiter, std::move(our_limiter)); + std::atomic total{0}; + std::vector 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(1000, 1)); + std::atomic total{0}; + std::vector ts; + + for (int i = 0; i != 10; ++i) { + ts.emplace_back(std::thread([&] { + auto our_limiter = make_unique(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(1000, 100)); + std::atomic total{0}; + std::vector ts; + + for (int i = 0; i != 10; ++i) { + ts.emplace_back(std::thread([&] { + auto our_limiter = make_unique( + make_unique(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 diff --git a/tile/net/internal/http_engine.cc b/tile/net/internal/http_engine.cc index 2b12a2c..4296278 100644 --- a/tile/net/internal/http_engine.cc +++ b/tile/net/internal/http_engine.cc @@ -374,7 +374,7 @@ void HttpEngine::Join() { HttpEngine::HttpEngine() { auto ret = curl_global_init(CURL_GLOBAL_DEFAULT); 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( FLAGS_tile_http_engine_workers_per_scheduling_group, i)); } diff --git a/tile/sigslot/signal.h b/tile/sigslot/signal.h deleted file mode 100644 index 9f27191..0000000 --- a/tile/sigslot/signal.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef TILE_SIGSLOT_SIGNAL_H -#define TILE_SIGSLOT_SIGNAL_H - -#pragma once -#include "tile/base/internal/meta.h" -#include -#include - -/** - * tile::sigslot::Signal 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 connections_; -}; - -template class Signal; -template class Signal { -public: - using SlotType = std::function; - - R Emit(Args... args) const; - R operator()(Args... args) const { Emit(std::forward(args)...); }; - - // function ptr - Connection Connect(R (*func)(Args...)); - - // member function - template Connection Connect(C *clz, R (C::*mem_fn)(Args...)); - - // lambda function - Connection Connect(std::function lambda_fn); - template ()(std::declval()...)), - R>::value>> - Connection Connect(Functor functor); -}; - -} // namespace sigslot -} // namespace tile - -#endif // TILE_SIGSLOT_SIGNAL_H diff --git a/tile/sigslot/sigslot.h b/tile/sigslot/sigslot.h new file mode 100644 index 0000000..912b235 --- /dev/null +++ b/tile/sigslot/sigslot.h @@ -0,0 +1,1682 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with the proviso that +// the author takes on no responsibility or liability for any use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables +// all of the thread safety support on platforms where it is +// available. +// +// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than +// gcc on a platform that supports Posix threads. (When using gcc, +// this is the default - use SIGSLOT_PURE_ISO to disable this if +// necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global. +// Otherwise, the default is single_threaded. #define this yourself to +// override the default. In pure ISO mode, anything other than +// single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it +// yourself if your build environment is less standard. This causes +// the Win32 thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this +// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++ - If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off, +// along with any code that might cause a pure ISO C++ environment to +// complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded - Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and +// slots can be safely created and destroyed from any thread, even when +// connections exist. In multi_threaded_global mode, this is achieved by a +// single global mutex (actually a critical section on Windows because they +// are faster). This option uses less OS resources, but results in more +// opportunities for contention, possibly resulting in more context switches +// than are strictly necessary. +// +// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global, +// except that each signal, and each object that inherits has_slots, all +// have their own mutex/critical section. In practice, this means that +// mutex collisions (and hence context switches) only happen if they are +// absolutely essential. However, on some platforms, creating a lot of +// mutexes can slow down the whole OS, so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// + +#ifndef __SIGSLOT_H__ +#define __SIGSLOT_H__ + +#include +#include + +#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS)) +# define _SIGSLOT_SINGLE_THREADED +#elif defined(WIN32) +# define _SIGSLOT_HAS_WIN32_THREADS +# include +#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS) +# define _SIGSLOT_HAS_POSIX_THREADS +# include +#else +# define _SIGSLOT_SINGLE_THREADED +#endif + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +# ifdef _SIGSLOT_SINGLE_THREADED +# define SIGSLOT_DEFAULT_MT_POLICY single_threaded +# else +# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +# endif +#endif + +#ifndef SIGSLOT_EMIT +# ifdef QT_VERSION +# define SIGSLOT_EMIT broadcast +# else +# define SIGSLOT_EMIT emit +# endif +#endif + +namespace sigslot { + +class single_threaded +{ +public: + single_threaded() {} + virtual ~single_threaded() {} + virtual void lock() {} + virtual void unlock() {} +}; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS +// The multi threading policies only get compiled in if they are enabled. +class multi_threaded_global +{ +public: + multi_threaded_global() + { + static bool isinitialised = false; + + if(!isinitialised) + { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + multi_threaded_global(const multi_threaded_global&) + {} + + virtual ~multi_threaded_global() + {} + + virtual void lock() + { + EnterCriticalSection(get_critsec()); + } + + virtual void unlock() + { + LeaveCriticalSection(get_critsec()); + } + +private: + CRITICAL_SECTION* get_critsec() + { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } +}; + +class multi_threaded_local +{ +public: + multi_threaded_local() + { + InitializeCriticalSection(&m_critsec); + } + + multi_threaded_local(const multi_threaded_local&) + { + InitializeCriticalSection(&m_critsec); + } + + virtual ~multi_threaded_local() + { + DeleteCriticalSection(&m_critsec); + } + + virtual void lock() + { + EnterCriticalSection(&m_critsec); + } + + virtual void unlock() + { + LeaveCriticalSection(&m_critsec); + } + +private: + CRITICAL_SECTION m_critsec; +}; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS +// The multi threading policies only get compiled in if they are enabled. +class multi_threaded_global +{ +public: + multi_threaded_global() + { + pthread_mutex_init(get_mutex(), NULL); + } + + multi_threaded_global(const multi_threaded_global&) + {} + + virtual ~multi_threaded_global() + {} + + virtual void lock() + { + pthread_mutex_lock(get_mutex()); + } + + virtual void unlock() + { + pthread_mutex_unlock(get_mutex()); + } + +private: + pthread_mutex_t* get_mutex() + { + static pthread_mutex_t g_mutex; + return &g_mutex; + } +}; + +class multi_threaded_local +{ +public: + multi_threaded_local() + { + pthread_mutex_init(&m_mutex, NULL); + } + + multi_threaded_local(const multi_threaded_local&) + { + pthread_mutex_init(&m_mutex, NULL); + } + + virtual ~multi_threaded_local() + { + pthread_mutex_destroy(&m_mutex); + } + + virtual void lock() + { + pthread_mutex_lock(&m_mutex); + } + + virtual void unlock() + { + pthread_mutex_unlock(&m_mutex); + } + +private: + pthread_mutex_t m_mutex; +}; +#endif // _SIGSLOT_HAS_POSIX_THREADS + +template +class lock_block +{ +public: + mt_policy *m_mutex; + + lock_block(mt_policy *mtx) + : m_mutex(mtx) + { + m_mutex->lock(); + } + + ~lock_block() + { + m_mutex->unlock(); + } +}; + +template +class has_slots; + +template +class _connection_base0 +{ +public: + virtual ~_connection_base0() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT() = 0; + virtual _connection_base0* clone() = 0; + virtual _connection_base0* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base1 +{ +public: + virtual ~_connection_base1() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type) = 0; + virtual _connection_base1* clone() = 0; + virtual _connection_base1* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base2 +{ +public: + virtual ~_connection_base2() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type) = 0; + virtual _connection_base2* clone() = 0; + virtual _connection_base2* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base3 +{ +public: + virtual ~_connection_base3() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type) = 0; + virtual _connection_base3* clone() = 0; + virtual _connection_base3* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base4 +{ +public: + virtual ~_connection_base4() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type, arg4_type) = 0; + virtual _connection_base4* clone() = 0; + virtual _connection_base4* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base5 +{ +public: + virtual ~_connection_base5() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type) = 0; + virtual _connection_base5* clone() = 0; + virtual _connection_base5* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base6 +{ +public: + virtual ~_connection_base6() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type) = 0; + virtual _connection_base6* clone() = 0; + virtual _connection_base6* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base7 +{ +public: + virtual ~_connection_base7() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type) = 0; + virtual _connection_base7* clone() = 0; + virtual _connection_base7* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base8 +{ +public: + virtual ~_connection_base8() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type, arg8_type) = 0; + virtual _connection_base8* clone() = 0; + virtual _connection_base8* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _signal_base : public mt_policy +{ +public: + virtual void slot_disconnect(has_slots* pslot) = 0; + virtual void slot_duplicate(const has_slots* poldslot, has_slots* pnewslot) = 0; +}; + +// Implements common functionality of signalN classes. signalN classes +// derive from this class. +template +class _signal_base_middle: public _signal_base +{ +public: + + typedef T connections_list; + typedef typename connections_list::iterator iterator; + typedef typename connections_list::const_iterator const_iterator; + + _signal_base_middle() {} + + _signal_base_middle(const _signal_base_middle &s) + : _signal_base(s) + { + lock_block lock(this); + + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + ++it; + } + } + + virtual ~_signal_base_middle() {} + + void disconnect_all() + { + lock_block lock(this); + + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + bool is_empty() + { + lock_block lock(this); + + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + return it == itEnd; + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + + const_iterator itNext, it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + +protected: + + connections_list m_connected_slots; + +}; + +template +class has_slots : public mt_policy +{ +private: + typedef typename std::set<_signal_base *> sender_set; + typedef typename sender_set::const_iterator const_iterator; + +public: + has_slots() + {} + + has_slots(const has_slots& hs) + : mt_policy(hs) + { + lock_block lock(this); + const_iterator it = hs.m_senders.begin(); + const_iterator itEnd = hs.m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_duplicate(&hs, this); + m_senders.insert(*it); + ++it; + } + } + + void signal_connect(_signal_base* sender) + { + lock_block lock(this); + m_senders.insert(sender); + } + + void signal_disconnect(_signal_base* sender) + { + lock_block lock(this); + m_senders.erase(sender); + } + + virtual ~has_slots() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_senders.begin(); + const_iterator itEnd = m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_disconnect(this); + ++it; + } + + m_senders.erase(m_senders.begin(), m_senders.end()); + } + +private: + sender_set m_senders; +}; + + + +template +class _connection0 : public _connection_base0 +{ +public: + _connection0() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection0(dest_type* pobject, void (dest_type::*pmemfun)()) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection0() + {} + + virtual _connection_base0* clone() + { + return new _connection0(*this); + } + + virtual _connection_base0* duplicate(has_slots* pnewdest) + { + return new _connection0((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT() + { + (m_pobject->*m_pmemfun)(); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(); +}; + +template +class _connection1 : public _connection_base1 +{ +public: + _connection1() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection1() + {} + + virtual _connection_base1* clone() + { + return new _connection1(*this); + } + + virtual _connection_base1* duplicate(has_slots* pnewdest) + { + return new _connection1((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1) + { + (m_pobject->*m_pmemfun)(a1); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type); +}; + +template +class _connection2 : public _connection_base2 +{ +public: + _connection2() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection2() + {} + + virtual _connection_base2* clone() + { + return new _connection2(*this); + } + + virtual _connection_base2* duplicate(has_slots* pnewdest) + { + return new _connection2((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2) + { + (m_pobject->*m_pmemfun)(a1, a2); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type); +}; + +template +class _connection3 : public _connection_base3 +{ +public: + _connection3() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection3() + {} + + virtual _connection_base3* clone() + { + return new _connection3(*this); + } + + virtual _connection_base3* duplicate(has_slots* pnewdest) + { + return new _connection3((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3) + { + (m_pobject->*m_pmemfun)(a1, a2, a3); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type); +}; + +template +class _connection4 : public _connection_base4 +{ +public: + _connection4() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection4() + {} + + virtual _connection_base4* clone() + { + return new _connection4(*this); + } + + virtual _connection_base4* duplicate(has_slots* pnewdest) + { + return new _connection4((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, + arg4_type a4) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, + arg4_type); +}; + +template +class _connection5 : public _connection_base5 +{ +public: + _connection5() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection5() + {} + + virtual _connection_base5* clone() + { + return new _connection5(*this); + } + + virtual _connection_base5* duplicate(has_slots* pnewdest) + { + return new _connection5((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type); +}; + +template +class _connection6 : public _connection_base6 +{ +public: + _connection6() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection6() + {} + + virtual _connection_base6* clone() + { + return new _connection6(*this); + } + + virtual _connection_base6* duplicate(has_slots* pnewdest) + { + return new _connection6((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type); +}; + +template +class _connection7 : public _connection_base7 +{ +public: + _connection7() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection7() + {} + + virtual _connection_base7* clone() + { + return new _connection7(*this); + } + + virtual _connection_base7* duplicate(has_slots* pnewdest) + { + return new _connection7((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type); +}; + +template +class _connection8 : public _connection_base8 +{ +public: + _connection8() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection8() + {} + + virtual _connection_base8* clone() + { + return new _connection8(*this); + } + + virtual _connection_base8* duplicate(has_slots* pnewdest) + { + return new _connection8((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type); +}; + +template +class signal0 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal0 this_type; + typedef std::list<_connection_base0*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal0() + {} + + signal0(const this_type& s) + : base_type(s) + {} + + ~signal0() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)()) + { + lock_block lock(this); + + _connection0* conn = + new _connection0(pclass, pmemfun); + + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT() + { + lock_block lock(this); + + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(); + + it = itNext; + } + } + + void operator()() + { + SIGSLOT_EMIT(); + } +}; + +template +class signal1 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal1 this_type; + typedef std::list<_connection_base1*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal1() + {} + + signal1(const this_type& s) + : base_type(s) + {} + + ~signal1() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type)) + { + lock_block lock(this); + _connection1* conn = + new _connection1(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1); + + it = itNext; + } + } + + void operator()(arg1_type a1) + { + SIGSLOT_EMIT(a1); + } +}; + +template +class signal2 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal2 this_type; + typedef std::list<_connection_base2*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal2() + {} + + signal2(const this_type& s) + : base_type(s) + {} + + ~signal2() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type)) + { + lock_block lock(this); + _connection2* conn = new + _connection2(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2) + { + SIGSLOT_EMIT(a1, a2); + } +}; + +template +class signal3 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal3 this_type; + typedef std::list<_connection_base3*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal3() + {} + + signal3(const this_type& s) + : base_type(s) + {} + + ~signal3() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + lock_block lock(this); + _connection3* conn = + new _connection3(pclass, + pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3) + { + SIGSLOT_EMIT(a1, a2, a3); + } +}; + +template +class signal4 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal4 this_type; + typedef std::list<_connection_base4*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal4() + {} + + signal4(const this_type& s) + : base_type(s) + {} + + ~signal4() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + lock_block lock(this); + _connection4* + conn = new _connection4(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3, a4); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + SIGSLOT_EMIT(a1, a2, a3, a4); + } +}; + +template +class signal5 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal5 this_type; + typedef std::list<_connection_base5*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal5() + {} + + signal5(const this_type& s) + : base_type(s) + {} + + ~signal5() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + lock_block lock(this); + _connection5* conn = new _connection5(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3, a4, a5); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + SIGSLOT_EMIT(a1, a2, a3, a4, a5); + } +}; + + +template +class signal6 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal6 this_type; + typedef std::list<_connection_base6*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal6() + {} + + signal6(const this_type& s) + : base_type(s) + {} + + ~signal6() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + lock_block lock(this); + _connection6* conn = + new _connection6(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6); + } +}; + +template +class signal7 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal7 this_type; + typedef std::list<_connection_base7*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal7() + {} + + signal7(const this_type& s) + : base_type(s) + {} + + ~signal7() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type)) + { + lock_block lock(this); + _connection7* conn = + new _connection7(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6, a7); + } +}; + +template +class signal8 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal8 this_type; + typedef std::list<_connection_base8*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal8() + {} + + signal8(const this_type& s) + : base_type(s) + {} + + ~signal8() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + lock_block lock(this); + _connection8* conn = + new _connection8(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6, a7, a8); + } +}; + +namespace impl +{ + +struct empty {}; + +} // namespace impl + +// signal can be used instead of the numbered signalN classes. +// For example: +// +// sigslot::signal signal; +// +// instead of +// +// sigslot::signal2 signal; +template +struct signal; + +template<> +struct signal<>: public signal0<> +{}; + +template +struct signal: public signal1 +{}; + +template +struct signal: public signal2 +{}; + +template +struct signal: public signal3 +{}; + +template +struct signal: public signal4 +{}; + +template +struct signal: public signal5 +{}; + +template +struct signal: public signal6 +{}; + +template +struct signal: + public signal7 +{}; + +template +struct signal: public signal8 +{}; + +// Some convenience methods for signal handling. +template +struct has_signals +{ + virtual ~has_signals() {} + + // Connect a signal to a slot on the specified destination object. + template + static inline void connect(Signal &signal, Dst *dst, Sig memfun) + { + signal.connect(dst, memfun); + } + + // Connect a signal to a slot on 'this'. + template + inline void connect(Signal &signal, Sig memfun) + { + Derived* dst = static_cast(this); + connect(signal, dst, memfun); + } +}; + +}; // namespace sigslot + +#endif // __SIGSLOT_H__ \ No newline at end of file diff --git a/tile/testing/bm_main.cc b/tile/testing/bm_main.cc index be8dadd..9330183 100644 --- a/tile/testing/bm_main.cc +++ b/tile/testing/bm_main.cc @@ -11,27 +11,29 @@ namespace tile { namespace testing { namespace { -int StartBenchmark(int argc, char **argv) { - ::benchmark::Initialize(&argc, argv); - if (::benchmark::ReportUnrecognizedArguments(argc, argv)) { - return 1; - } - ::benchmark::RunSpecifiedBenchmarks(); - ::benchmark::Shutdown(); - return 0; +int +StartBenchmark(int argc, char **argv) +{ + ::benchmark::Initialize(&argc, argv); + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) { return 1; } + ::benchmark::RunSpecifiedBenchmarks(); + ::benchmark::Shutdown(); + return 0; } -} // namespace +}// namespace -int InitAndRunAllBenchmarks(int *argc, char **argv) { +int +InitAndRunAllBenchmarks(int *argc, char **argv) +{ - if (gflags::GetCommandLineFlagInfoOrDie("logtostderr").is_default) { - FLAGS_logtostderr = true; - } + // if (gflags::GetCommandLineFlagInfoOrDie("logtostderr").is_default) { + // FLAGS_logtostderr = true; + // } - return Start(*argc, argv, StartBenchmark, true); + return Start(*argc, argv, StartBenchmark, true); } -} // namespace testing -} // namespace tile +}// namespace testing +}// namespace tile TILE_BENCHMARK_MAIN diff --git a/tile/tile.h b/tile/tile.h index 37f505c..4c1dbcc 100644 --- a/tile/tile.h +++ b/tile/tile.h @@ -1,22 +1,54 @@ -#ifndef _TILE_TILE_H -#define _TILE_TILE_H +#ifndef TILE_TILE_H +#define TILE_TILE_H #pragma once // 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/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/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/random.h" +#include "tile/base/ref_ptr.h" #include "tile/base/slice.h" +#include "tile/base/status.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 #include "tile/init/override_flag.h" +#include "sigslot/signal.h" + // Tile Init +#include "mustache.hpp" #include "tile/init.h" -#endif // _TILE_TILE_H +#endif // TILE_TILE_H diff --git a/tile/util/config.cc b/tile/util/config.cc new file mode 100644 index 0000000..ddd3178 --- /dev/null +++ b/tile/util/config.cc @@ -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 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 lock(mutex_); + return Enumerate(root); +} + +#define TILE_DEFINE_CONFIG_GETTER(type, name) \ + std::optional Config::Get##name(const Slice &key) const { \ + std::string value; \ + if (GetRaw(key, &value)) { \ + auto opt = TryParseTraits::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 Config::GetString(const Slice &key) const { + std::string value; + UniqueLock 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 lock(mutex_); + SetRaw(key, value); + } + + if (events_enabled_) { + OnChanged(key, value); + } +} + +Config::~Config() {} + +} // namespace util + +} // namespace tile diff --git a/tile/util/config.h b/tile/util/config.h new file mode 100644 index 0000000..3c68673 --- /dev/null +++ b/tile/util/config.h @@ -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 + +namespace tile { +namespace util { +class Config : public RefCounted { +public: + using Keys = std::vector; + using Ptr = RefPtr; + + // events + // Key, Value + sigslot::signal2 OnChanging; + // Key, Value + sigslot::signal2 OnChanged; + // Key + sigslot::signal1 OnRemoving; + // Key + sigslot::signal1 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 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; + friend class LayeredConfig; + +private: + mutable Mutex mutex_; + bool events_enabled_{false}; +}; +} // namespace util +} // namespace tile + +#endif // TILE_UTIL_CONFIG_H diff --git a/tile/util/ini_file_config.cc b/tile/util/ini_file_config.cc new file mode 100644 index 0000000..2223820 --- /dev/null +++ b/tile/util/ini_file_config.cc @@ -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 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::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 diff --git a/tile/util/ini_file_config.h b/tile/util/ini_file_config.h new file mode 100644 index 0000000..b672719 --- /dev/null +++ b/tile/util/ini_file_config.h @@ -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 + +namespace tile { +namespace util { +class IniFileConfig : public Config { +public: + using Ptr = RefPtr; + + 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 IStringMap; + IStringMap map_; + std::string section_key_; +}; +} // namespace util +} // namespace tile + +#endif // TILE_UTIL_INI_FILE_CONFIG_H diff --git a/tile/util/ini_file_config_test.cc b/tile/util/ini_file_config_test.cc new file mode 100644 index 0000000..b3b8600 --- /dev/null +++ b/tile/util/ini_file_config_test.cc @@ -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::value, ""); + +TEST(IniFileConfig, LoadFromIStream) { + std::stringstream ss(kIniFileConfig); + Config::Ptr config = MakeRefCounted(); + ASSERT_FALSE(config->Has("a")); + ASSERT_FALSE(config->Has("sec1.a")); + ASSERT_FALSE(config->Has("sec3.a")); + if (config.Is()) { + IniFileConfig::Ptr ini = config.As(); + 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 diff --git a/tile/util/layered_config.cc b/tile/util/layered_config.cc new file mode 100644 index 0000000..4ab3851 --- /dev/null +++ b/tile/util/layered_config.cc @@ -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 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 diff --git a/tile/util/layered_config.h b/tile/util/layered_config.h new file mode 100644 index 0000000..a23b60b --- /dev/null +++ b/tile/util/layered_config.h @@ -0,0 +1,57 @@ +#ifndef TILE_UTIL_LAYERD_CONFIG_H +#define TILE_UTIL_LAYERD_CONFIG_H + +#pragma once + +#include "tile/util/config.h" +#include + +namespace tile { +namespace util { +class LayeredConfig : public Config { +public: + using Ptr = RefPtr; + 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; + ConfigList configs_; +}; +} // namespace util +} // namespace tile + +#endif // TILE_UTIL_LAYERD_CONFIG_H