diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ac3640809..54659db9f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,34 +27,55 @@ jobs: # sysbench cannot work property on this platform. # I found that sysbench would send more request before receiving last response - # sysbench-test: - # runs-on: ubuntu-latest - - - # steps: - # - name: Checkout repository and submodules - # uses: actions/checkout@v2 - - # - name: install sysbench and mariadb-client - # shell: bash - # run: | - # curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.deb.sh -o script.deb.sh - # sudo bash script.deb.sh - # sudo apt -y install sysbench mariadb-client - - # - name: start server - # shell: bash - # run: | - # sudo bash build.sh init - # bash build.sh -DCONCURRENCY=ON -DWITH_UNIT_TESTS=OFF - # nohup ./build_debug/bin/observer -s /tmp/miniob.sock -f etc/observer.ini -P mysql -t mvcc & - # sleep 10 && echo "wake up" - # mysql --version - # mysql -S /tmp/miniob.sock -e "show tables" - - # - name: sysbench test - # shell: bash - # run: | - # cd test/sysbench - # sysbench --mysql-socket=/tmp/miniob.sock --threads=10 miniob_insert prepare - # sysbench --mysql-socket=/tmp/miniob.sock --threads=10 miniob_insert run + sysbench-test: + strategy: + matrix: + thread_model: ['one-thread-per-connection', 'java-thread-pool'] + test_case: ['miniob_insert', 'miniob_delete', 'miniob_select'] + + runs-on: ubuntu-latest + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + + - name: install sysbench and mariadb-client + shell: bash + run: | + curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.deb.sh -o script.deb.sh + sudo bash script.deb.sh + sudo apt -y install sysbench mariadb-client + + - name: start server + shell: bash + run: | + sudo bash build.sh init + bash build.sh release -DCONCURRENCY=ON -DWITH_UNIT_TESTS=OFF + nohup ./build_release/bin/observer -T ${{ matrix.thread_model }} -s /tmp/miniob.sock -f etc/observer.ini -P mysql -t mvcc & + sleep 10 && echo "wake up" + mysql --version + mysql -S /tmp/miniob.sock -e "show tables" + + - name: sysbench test + shell: bash + run: | + cd test/sysbench + sysbench --mysql-socket=/tmp/miniob.sock --threads=10 ${{ matrix.test_case }} prepare + sysbench --mysql-socket=/tmp/miniob.sock --threads=10 ${{ matrix.test_case }} run + + benchmark-test: + runs-on: ubuntu-latest + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + + - name: build observer and benchmark + shell: bash + run: | + sudo bash build.sh init + bash build.sh release -DCONCURRENCY=ON -DWITH_UNIT_TESTS=OFF -DWITH_BENCHMARK=ON + + - name: testing + shell: bash + run: | + cd build_release/bin/ + for file in `find ./ -name "*_concurrency_test" -executable`; do $file; if [ $? -ne 0 ]; then exit 1; fi; done diff --git a/CMakeLists.txt b/CMakeLists.txt index 2568a2cb7..fbbe9077f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,8 @@ OPTION(ENABLE_ASAN "Enable build with address sanitizer" ON) OPTION(ENABLE_TSAN "Build with thread sanitizer" OFF) OPTION(ENABLE_UBSAN "Build with undefined behavior sanitizer" OFF) OPTION(WITH_UNIT_TESTS "Compile miniob with unit tests" ON) +OPTION(WITH_BENCHMARK "Compile benchmark" OFF) +OPTION(ENABLE_COVERAGE "Enable unittest coverage" OFF) OPTION(CONCURRENCY "Support concurrency operations" OFF) OPTION(STATIC_STDLIB "Link std library static or dynamic, such as libgcc, libstdc++, libasan" OFF) @@ -63,6 +65,7 @@ IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND ${STATIC_STDLIB}) ENDIF() IF (ENABLE_ASAN) + MESSAGE(STATUS "Instrumenting with Address Sanitizer") SET(CMAKE_COMMON_FLAGS "${CMAKE_COMMON_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope") IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND ${STATIC_STDLIB}) ADD_LINK_OPTIONS(-static-libasan) @@ -87,18 +90,18 @@ IF (ENABLE_TSAN) ENDIF (ENABLE_TSAN) IF (ENABLE_UBSAN) - # Only success on Mac Clang - MESSAGE(STATUS "Instrumenting with Undefined Behavior Sanitizer") - SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fno-omit-frame-pointer") - SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=undefined") - SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=implicit-conversion") - SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=implicit-integer-truncation") - SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=integer") - SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=nullability") - SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=vptr") - SET(CMAKE_COMMON_FLAGS "${CMAKE_COMMON_FLAGS} ${UBSAN_FLAGS}") - SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${UBSAN_FLAGS}") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${UBSAN_FLAGS}") + # Only success on Mac Clang + MESSAGE(STATUS "Instrumenting with Undefined Behavior Sanitizer") + SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fno-omit-frame-pointer") + SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=undefined") + SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=implicit-conversion") + SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=implicit-integer-truncation") + SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=integer") + SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=nullability") + SET(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=vptr") + SET(CMAKE_COMMON_FLAGS "${CMAKE_COMMON_FLAGS} ${UBSAN_FLAGS}") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${UBSAN_FLAGS}") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${UBSAN_FLAGS}") ENDIF (ENABLE_UBSAN) IF (CMAKE_INSTALL_PREFIX) @@ -127,7 +130,9 @@ ENDIF () INCLUDE_DIRECTORIES(. ${PROJECT_SOURCE_DIR}/deps /usr/local/include) IF(WITH_UNIT_TESTS) - SET(CMAKE_COMMON_FLAGS "${CMAKE_COMMON_FLAGS} -fprofile-arcs -ftest-coverage") + IF (ENABLE_COVERAGE) + SET(CMAKE_COMMON_FLAGS "${CMAKE_COMMON_FLAGS} -fprofile-arcs -ftest-coverage") + ENDIF (ENABLE_COVERAGE) enable_testing() ENDIF(WITH_UNIT_TESTS) @@ -140,9 +145,12 @@ ADD_SUBDIRECTORY(deps) ADD_SUBDIRECTORY(src/obclient) ADD_SUBDIRECTORY(src/observer) ADD_SUBDIRECTORY(test/perf) -ADD_SUBDIRECTORY(benchmark) ADD_SUBDIRECTORY(tools) +IF (WITH_BENCHMARK) + ADD_SUBDIRECTORY(benchmark) +ENDIF(WITH_BENCHMARK) + IF(WITH_UNIT_TESTS) ADD_SUBDIRECTORY(unittest) ENDIF(WITH_UNIT_TESTS) diff --git a/benchmark/bplus_tree_concurrency_test.cpp b/benchmark/bplus_tree_concurrency_test.cpp index fc9b36c53..de878ccfe 100644 --- a/benchmark/bplus_tree_concurrency_test.cpp +++ b/benchmark/bplus_tree_concurrency_test.cpp @@ -16,7 +16,7 @@ See the Mulan PSL v2 for more details. */ #include #include "common/log/log.h" -#include "integer_generator.h" +#include "common/math/integer_generator.h" #include "storage/buffer/disk_buffer_pool.h" #include "storage/index/bplus_tree.h" diff --git a/benchmark/record_manager_concurrency_test.cpp b/benchmark/record_manager_concurrency_test.cpp index 94b4328bf..546fd00c2 100644 --- a/benchmark/record_manager_concurrency_test.cpp +++ b/benchmark/record_manager_concurrency_test.cpp @@ -18,7 +18,7 @@ See the Mulan PSL v2 for more details. */ #include #include "common/log/log.h" -#include "integer_generator.h" +#include "common/math/integer_generator.h" #include "storage/buffer/disk_buffer_pool.h" #include "storage/common/condition_filter.h" #include "storage/record/record_manager.h" @@ -87,7 +87,7 @@ class BenchmarkBase : public Fixture string log_name = this->Name() + ".log"; string record_filename = this->record_filename(); - LoggerFactory::init_default(log_name.c_str(), LOG_LEVEL_TRACE); + LoggerFactory::init_default(log_name.c_str(), LOG_LEVEL_INFO); std::call_once(init_bpm_flag, []() { BufferPoolManager::set_instance(&bpm); }); @@ -110,8 +110,8 @@ class BenchmarkBase : public Fixture LOG_WARN("failed to init record file handler. rc=%s", strrc(rc)); throw runtime_error("failed to init record file handler"); } - LOG_INFO( - "test %s setup done. threads=%d, thread index=%d", this->Name().c_str(), state.threads(), state.thread_index()); + LOG_INFO("test %s setup done. threads=%d, thread index=%d", + this->Name().c_str(), state.threads(), state.thread_index()); } virtual void TearDown(const State &state) @@ -156,9 +156,9 @@ class BenchmarkBase : public Fixture uint32_t GetRangeMax(const State &state) const { - uint32_t max = static_cast(state.range(0) * 3); + int32_t max = static_cast(state.range(0) * 3); if (max <= 0) { - max = (1 << 31); + max = INT32_MAX - 1; } return max; } @@ -261,24 +261,33 @@ class DeletionBenchmark : public BenchmarkBase void SetUp(const State &state) override { + BenchmarkBase::SetUp(state); + if (0 != state.thread_index()) { + while (!setup_done_) { + this_thread::sleep_for(chrono::milliseconds(100)); + } return; } - BenchmarkBase::SetUp(state); - uint32_t max = GetRangeMax(state); ASSERT(max > 0, "invalid argument count. %ld", state.range(0)); FillUp(0, max, rids_); + ASSERT(rids_.size() > 0, "invalid argument count. %ld", rids_.size()); + setup_done_ = true; } protected: - vector rids_; + // 从实际测试情况看,每个线程都会执行setup,但是它们操作的对象都是同一个 + // 但是每个线程set up结束后,就会执行测试了。如果不等待的话,就会导致有些 + // 线程访问的数据不是想要的结果 + volatile bool setup_done_ = false; + vector rids_; }; BENCHMARK_DEFINE_F(DeletionBenchmark, Deletion)(State &state) { - IntegerGenerator generator(0, static_cast(rids_.size())); + IntegerGenerator generator(0, static_cast(rids_.size() - 1)); Stat stat; for (auto _ : state) { diff --git a/build.sh b/build.sh index 8b4246e8b..7c23ae9bf 100755 --- a/build.sh +++ b/build.sh @@ -96,7 +96,7 @@ function do_init cd ${TOPDIR}/deps/3rd/benchmark && \ mkdir -p build && \ cd build && \ - ${CMAKE_COMMAND} .. -DBENCHMARK_ENABLE_TESTING=OFF -DBENCHMARK_INSTALL_DOCS=OFF -DBENCHMARK_ENABLE_GTEST_TESTS=OFF -DBENCHMARK_USE_BUNDLED_GTEST=OFF -DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF && \ + ${CMAKE_COMMAND} .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBENCHMARK_ENABLE_TESTING=OFF -DBENCHMARK_INSTALL_DOCS=OFF -DBENCHMARK_ENABLE_GTEST_TESTS=OFF -DBENCHMARK_USE_BUNDLED_GTEST=OFF -DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF && \ ${MAKE_COMMAND} -j4 && \ ${MAKE_COMMAND} install diff --git a/deps/common/log/log.h b/deps/common/log/log.h index 33b931c50..0c98dac2e 100644 --- a/deps/common/log/log.h +++ b/deps/common/log/log.h @@ -333,6 +333,15 @@ int Log::out(const LOG_LEVEL console_level, const LOG_LEVEL log_level, T &msg) #endif // ASSERT +#ifndef TRACE +#ifdef DEBUG +#define TRACE(format, ...) LOG_TRACE(format, ##__VA_ARGS__) +#else // DEBUG +#define TRACE(...) +#endif // DEBUG + +#endif // TRACE + #define SYS_OUTPUT_FILE_POS ", File:" << __FILE__ << ", line:" << __LINE__ << ",function:" << __FUNCTION__ #define SYS_OUTPUT_ERROR ",error:" << errno << ":" << strerror(errno) diff --git a/benchmark/integer_generator.h b/deps/common/math/integer_generator.h similarity index 70% rename from benchmark/integer_generator.h rename to deps/common/math/integer_generator.h index 26ae4904f..2ac182812 100644 --- a/benchmark/integer_generator.h +++ b/deps/common/math/integer_generator.h @@ -11,16 +11,29 @@ See the Mulan PSL v2 for more details. */ // // Created by Wangyunlai on 2023/05/04 // + +#pragma once + #include +namespace common { + class IntegerGenerator { public: IntegerGenerator(int min, int max) : distrib_(min, max) {} + IntegerGenerator(const IntegerGenerator &other) = delete; + IntegerGenerator(IntegerGenerator &&) = delete; + IntegerGenerator &operator=(const IntegerGenerator &) = delete; + int next() { return distrib_(rd_); } + int min() const { return distrib_.min(); } + int max() const { return distrib_.max(); } private: std::random_device rd_; std::uniform_int_distribution<> distrib_; -}; \ No newline at end of file +}; + +} // namespace common diff --git a/deps/common/os/process_param.h b/deps/common/os/process_param.h index 6e0b2b857..b3f0226a5 100644 --- a/deps/common/os/process_param.h +++ b/deps/common/os/process_param.h @@ -72,6 +72,15 @@ class ProcessParam } const std::string &trx_kit_name() const { return trx_kit_name_; } + void set_thread_handling_name(const char *thread_handling_name) + { + if (thread_handling_name) { + thread_handling_name_ = thread_handling_name; + } + } + + const std::string &thread_handling_name() const { return thread_handling_name_; } + void set_buffer_pool_memory_size(int bytes) { buffer_pool_memory_size_ = bytes; } int buffer_pool_memory_size() const { return buffer_pool_memory_size_; } @@ -87,6 +96,7 @@ class ProcessParam std::string unix_socket_path_; std::string protocol_; std::string trx_kit_name_; + std::string thread_handling_name_; int buffer_pool_memory_size_ = -1; }; diff --git a/deps/common/os/signal.cpp b/deps/common/os/signal.cpp index 156ac21d9..2753dd999 100644 --- a/deps/common/os/signal.cpp +++ b/deps/common/os/signal.cpp @@ -17,7 +17,7 @@ See the Mulan PSL v2 for more details. */ #include "pthread.h" namespace common { -void setSignalHandler(int sig, sighandler_t func) +void set_signal_handler(int sig, sighandler_t func) { struct sigaction newsa, oldsa; sigemptyset(&newsa.sa_mask); @@ -33,16 +33,16 @@ void setSignalHandler(int sig, sighandler_t func) /* ** Set Singal handling Fucntion */ -void setSignalHandler(sighandler_t func) +void set_signal_handler(sighandler_t func) { - setSignalHandler(SIGQUIT, func); - setSignalHandler(SIGINT, func); - setSignalHandler(SIGHUP, func); - setSignalHandler(SIGTERM, func); + set_signal_handler(SIGQUIT, func); + set_signal_handler(SIGINT, func); + set_signal_handler(SIGHUP, func); + set_signal_handler(SIGTERM, func); signal(SIGPIPE, SIG_IGN); } -void blockDefaultSignals(sigset_t *signal_set, sigset_t *old_set) +void block_default_signals(sigset_t *signal_set, sigset_t *old_set) { sigemptyset(signal_set); #ifndef DEBUG @@ -54,7 +54,7 @@ void blockDefaultSignals(sigset_t *signal_set, sigset_t *old_set) pthread_sigmask(SIG_BLOCK, signal_set, old_set); } -void unBlockDefaultSignals(sigset_t *signal_set, sigset_t *old_set) +void unblock_default_signals(sigset_t *signal_set, sigset_t *old_set) { sigemptyset(signal_set); #ifndef DEBUG @@ -65,7 +65,7 @@ void unBlockDefaultSignals(sigset_t *signal_set, sigset_t *old_set) pthread_sigmask(SIG_UNBLOCK, signal_set, old_set); } -void *waitForSignals(void *args) +void *wait_for_signals(void *args) { LOG_INFO("Start thread to wait signals."); sigset_t *signal_set = (sigset_t *)args; @@ -81,7 +81,7 @@ void *waitForSignals(void *args) return NULL; } -void startWaitForSignals(sigset_t *signal_set) +void start_wait_for_signals(sigset_t *signal_set) { pthread_t pThread; pthread_attr_t pThreadAttrs; @@ -90,6 +90,6 @@ void startWaitForSignals(sigset_t *signal_set) pthread_attr_init(&pThreadAttrs); pthread_attr_setdetachstate(&pThreadAttrs, PTHREAD_CREATE_DETACHED); - pthread_create(&pThread, &pThreadAttrs, waitForSignals, (void *)signal_set); + pthread_create(&pThread, &pThreadAttrs, wait_for_signals, (void *)signal_set); } } // namespace common diff --git a/deps/common/os/signal.h b/deps/common/os/signal.h index 63a44b1e3..ddfbf76e0 100644 --- a/deps/common/os/signal.h +++ b/deps/common/os/signal.h @@ -22,22 +22,22 @@ namespace common { /** * Now it blocks SIGINT, SIGTERM, and SIGUSR1 */ -void blockDefaultSignals(sigset_t *signal_set, sigset_t *old_set); +void block_default_signals(sigset_t *signal_set, sigset_t *old_set); //! Default function that unblocks signals. /** * It unblocks SIGINT, SIGTERM,and SIGUSR1. */ -void unBlockDefaultSignals(sigset_t *signal_set, sigset_t *old_set); +void unblock_default_signals(sigset_t *signal_set, sigset_t *old_set); -void *waitForSignals(sigset_t *signal_set); -void startWaitForSignals(sigset_t *signal_set); +void *wait_for_signals(sigset_t *signal_set); +void start_wait_for_signals(sigset_t *signal_set); // Set signal handling function /** * handler function */ typedef void (*sighandler_t)(int); -void setSignalHandler(sighandler_t func); -void setSignalHandler(int sig, sighandler_t func); +void set_signal_handler(sighandler_t func); +void set_signal_handler(int sig, sighandler_t func); } // namespace common diff --git a/deps/common/queue/queue.h b/deps/common/queue/queue.h new file mode 100644 index 000000000..3744be5f2 --- /dev/null +++ b/deps/common/queue/queue.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2023/01/11. +// + +#pragma once + +namespace common { + +/** + * @brief 任务队列 + * @defgroup Queue + */ + +/** + * @brief 任务队列接口 + * @ingroup Queue + * @tparam T 任务数据类型。 + */ +template +class Queue +{ +public: + using value_type = T; + +public: + Queue() = default; + virtual ~Queue() = default; + + /** + * @brief 在队列中放一个任务 + * + * @param value 任务数据 + * @return int 成功返回0 + */ + virtual int push(value_type &&value) = 0; + + /** + * @brief 从队列中取出一个任务 + * + * @param value 任务数据 + * @return int 成功返回0。如果队列为空,也不是成功的 + */ + virtual int pop(value_type &value) = 0; + + /** + * @brief 当前队列中任务的数量 + * + * @return int 对列中任务的数量 + */ + virtual int size() const = 0; +}; + +} // namespace common \ No newline at end of file diff --git a/deps/common/queue/simple_queue.h b/deps/common/queue/simple_queue.h new file mode 100644 index 000000000..7cb976876 --- /dev/null +++ b/deps/common/queue/simple_queue.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2023/01/11. +// + +#pragma once + +#include +#include +#include "common/queue/queue.h" + +namespace common { + +/** + * @brief 一个十分简单的线程安全的任务队列 + * @details 所有的接口都加了一个锁来保证线程安全。 + * 如果想了解更高效的队列实现,请参考 [Oceanbase](https://github.com/oceanbase/oceanbase) 中 + * deps/oblib/src/lib/queue/ 的一些队列的实现 + * @tparam T 任务数据类型。 + * @ingroup Queue + */ +template +class SimpleQueue : public Queue +{ +public: + using value_type = T; + +public: + SimpleQueue() : Queue() {} + virtual ~SimpleQueue() {} + + //! @copydoc Queue::emplace + int push(value_type &&value) override; + //! @copydoc Queue::pop + int pop(value_type &value) override; + //! @copydoc Queue::size + int size() const override; + +private: + std::mutex mutex_; + std::queue queue_; +}; + +} // namespace common + +#include "common/queue/simple_queue.ipp" \ No newline at end of file diff --git a/deps/common/seda/stage_factory.h b/deps/common/queue/simple_queue.ipp similarity index 52% rename from deps/common/seda/stage_factory.h rename to deps/common/queue/simple_queue.ipp index 4d95e9b4e..4a5a40b2f 100644 --- a/deps/common/seda/stage_factory.h +++ b/deps/common/queue/simple_queue.ipp @@ -9,17 +9,36 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. */ // -// Created by Longda on 2010 +// Created by Wangyunlai on 2023/01/11. // -#pragma once - -#include "common/seda/class_factory.h" -#include "common/seda/stage.h" namespace common { -class Stage; +template +int SimpleQueue::push(T &&value) +{ + std::lock_guard lock(mutex_); + queue_.push(std::move(value)); + return 0; +} + +template +int SimpleQueue::pop(T &value) +{ + std::lock_guard lock(mutex_); + if (queue_.empty()) { + return -1; + } + + value = std::move(queue_.front()); + queue_.pop(); + return 0; +} -typedef ClassFactory StageFactory; +template +int SimpleQueue::size() const +{ + return queue_.size(); +} -} // namespace common +} // namespace common \ No newline at end of file diff --git a/deps/common/seda/callback.cpp b/deps/common/seda/callback.cpp deleted file mode 100644 index 1f6b57780..000000000 --- a/deps/common/seda/callback.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -// Include Files -#include "common/seda/callback.h" - -#include - -#include "common/log/log.h" -#include "common/seda/stage.h" -#include "common/seda/stage_event.h" -namespace common { - -extern bool &get_event_history_flag(); - -/** - * @author Longda - * @date 3/27/07 - * - * Implementation of CompletionCallback class. - */ - -// Constructor -CompletionCallback::CompletionCallback(Stage *trgt, CallbackContext *ctx) - : target_stage_(trgt), context_(ctx), next_cb_(NULL), ev_hist_flag_(get_event_history_flag()) -{} - -// Destructor -CompletionCallback::~CompletionCallback() -{ - if (context_) { - delete context_; - } - if (next_cb_) { - delete next_cb_; - } -} - -// Push onto a callback stack -void CompletionCallback::push_callback(CompletionCallback *next) -{ - ASSERT((!next_cb_), "%s", "cannot push a callback twice"); - - next_cb_ = next; -} - -// Pop off of a callback stack -CompletionCallback *CompletionCallback::pop_callback() -{ - CompletionCallback *ret_val = next_cb_; - - next_cb_ = NULL; - return ret_val; -} - -// One event is complete -void CompletionCallback::event_done(StageEvent *ev) -{ - - if (ev_hist_flag_) { - ev->save_stage(target_stage_, StageEvent::CALLBACK_EV); - } - target_stage_->callback_event(ev, context_); -} - -// Reschedule callback on target stage thread -void CompletionCallback::event_reschedule(StageEvent *ev) { target_stage_->add_event(ev); } - -void CompletionCallback::event_timeout(StageEvent *ev) -{ - LOG_DEBUG("to call event_timeout for stage %s", target_stage_->get_name()); - if (ev_hist_flag_) { - ev->save_stage(target_stage_, StageEvent::TIMEOUT_EV); - } - target_stage_->timeout_event(ev, context_); -} - -} // namespace common \ No newline at end of file diff --git a/deps/common/seda/callback.h b/deps/common/seda/callback.h deleted file mode 100644 index 9b605af35..000000000 --- a/deps/common/seda/callback.h +++ /dev/null @@ -1,120 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -#pragma once - -// Include Files -#include "common/defs.h" -namespace common { - -class StageEvent; -class Stage; -class CallbackContext; - -/** - * A generic CompletionCallback - * A completion callback object provides a function that should be - * invoked when an event has made it successfully through the stage - * pipeline. Usually, each event will reference a completion callback, - * and before an event is deleted, the stage doing the deletion should - * invoke the "done()" event method. This method eventually invokes the - * "callback_event()" method in the stage which set the callback, providing - * a reference to the event as a parameter. - *

- * The purpose of the callback is to allow a stage to register some - * processing for an event that is delayed until after the event has - * progressed all the way through the stage pipeline. Callbacks can be - * chained. Typically, each stage in the pipeline might add a callback - * to an event's callback chain before passing the event to the next stage. - * When the "done()" method is finally invoked, the callback on top of the - * callback stack is invoked. It becomes the responsibility of this callback - * to either forward the event to another stage for more processing, or - * to eventually call done() again of the event. In this way, with each - * callback on the stack eventually invoking the next callback on the stack, - * all the callbacks are eventually executed. Each callback - * can have an optional context associated with it. This context is - * provided to the stage callback function when it is invoked. It is - * opaque to the callback object. - *

- * By default, the callback will run on the thread of the stage that created - * the callback. If the stage that is calling done() on the event wants - * to execute the callback stack in place, it can call the done_immediate() - * interface. Note that this will execute the *entire* callback stack on - * the current thread. - */ - -class CompletionCallback -{ - - // public interface operations - -public: - // Constructor - CompletionCallback(Stage *trgt, CallbackContext *ctx = NULL); - - // Destructor - virtual ~CompletionCallback(); - - // Push onto a callback stack - void push_callback(CompletionCallback *stack); - - /** - * Pop off of a callback stack - * @returns remainder of callback stack - */ - CompletionCallback *pop_callback(); - - // One event is complete - void event_done(StageEvent *ev); - - // Reschedule this event as a callback on the target stage - void event_reschedule(StageEvent *ev); - - // Complete this event if it has timed out - void event_timeout(StageEvent *ev); - -protected: - // implementation state - - Stage *target_stage_; // stage which is setting this callback - CallbackContext *context_; // argument to pass when invoking cb - CompletionCallback *next_cb_; // next event in the chain - bool ev_hist_flag_; // true if event histories are enabled -}; - -/** - * Context attached to callback - * The callback context may be optionally supplied to a callback. It - * is useful for passing extra arguments to the callback function when - * invoked. To make use of this feature, a stage should derive its own - * callback context class from this base. - */ -class CallbackContext -{ -public: - virtual ~CallbackContext() {} -}; - -class CallbackContextEvent : public CallbackContext -{ -public: - CallbackContextEvent(StageEvent *event = NULL) : ev_(event) {} - ~CallbackContextEvent() {} - StageEvent *get_event() { return ev_; } - -private: - StageEvent *ev_; -}; - -} // namespace common diff --git a/deps/common/seda/class_factory.h b/deps/common/seda/class_factory.h deleted file mode 100644 index e03d91031..000000000 --- a/deps/common/seda/class_factory.h +++ /dev/null @@ -1,141 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -#pragma once - -#include - -#include "common/defs.h" -#include "common/log/log.h" -namespace common { - -/** - * A class to construct arbitrary subclass instances - * - * This class provides a general solution to constructing instances - * of subclasses from a common generic base. The template is - * instantiated for each generic base class. Then each new subclass - * class creates a corresponding ClassFactory instance which includes - * a tag which identifies the class and a factory function which - * constructs an instance of the class when invoked. The factory - * function takes the tag and a properties object as parameters. - * - * Each class factory instance MUST be constructed before the first - * call to make_instance(). Otherwise, the entry will not be found - * on the list. To ensure this, if the class factory instance is - * provided by a library, it should be declared with static linkage - * within the library initialization routine. If the class factory - * instance is provided by the main application, it should be declared - * with static linkage in a global initialization routine. - */ - -template -class ClassFactory -{ - -public: - typedef T *(*FactoryFunc)(const std::string &); - - /** - * Constructor - * @param[in] tag Tag identifies a particular sub-class - * @param[in] func Factory function to create sub-class instance - * - * @post (tag,func) pair is entered into global factory list - */ - ClassFactory(const std::string &tag, FactoryFunc func); - - // Destructor - ~ClassFactory(); - - /** - * Construct an instance of a specified sub-class - * @param[in] tag Identifies sub-class to instantiate - * @param[in] prop Properties desired for the instance - * - * @return a reference to the desired instance - */ - static T *make_instance(const std::string &tag); - -private: - // Accessor function that gets the head of the factory list - static ClassFactory *&fact_list_head(); - - std::string identifier_; // identifier for this factory - FactoryFunc fact_func_; // factory function for this class - ClassFactory *next_; // next factory in global list -}; - -/** - * Accessor function that gets the head of the factory list - * Implementation notes: - * The head pointer in the list must be initialized to NULL before - * it is accessed from any ClassFactory constructor. We cannot - * rely on C++ constructor order to achieve this. Instead, we wrap - * the list head in an accessor function, and declare its linkage - * as static. C++ guarantees that the first time the function is - * invoked (from anywhere) the static local will be initialized. - */ -template -ClassFactory *&ClassFactory::fact_list_head() -{ - static ClassFactory *fact_list = NULL; - return fact_list; -} - -/** - * Constructor - * Implementation notes: - * constructor places current instance on the global factory list. - */ -template -ClassFactory::ClassFactory(const std::string &tag, FactoryFunc func) : identifier_(tag), fact_func_(func) -{ - next_ = fact_list_head(); - fact_list_head() = this; -} - -// Destructor -template -ClassFactory::~ClassFactory() -{} - -/** - * Construct an instance of a specified sub-class - * Implementation notes: - * scan global list to find matching tag and use the factory func to - * create an instance. - */ -template -T *ClassFactory::make_instance(const std::string &tag) -{ - T *instance = NULL; - ClassFactory *current = fact_list_head(); - - // search the global factory list for a match - while ((current != NULL) && (tag != current->identifier_)) { - current = current->next_; - } - - // if we have a match, create and return an instance - if (current != NULL) { - FactoryFunc fptr = current->fact_func_; - instance = (*fptr)(tag); - } - - ASSERT((instance != NULL), "%s%s", tag.c_str(), "instance not created"); - return instance; -} - -} // namespace common diff --git a/deps/common/seda/event_dispatcher.cpp b/deps/common/seda/event_dispatcher.cpp deleted file mode 100644 index ebe1a5386..000000000 --- a/deps/common/seda/event_dispatcher.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -// Include Files -#include "common/seda/event_dispatcher.h" -namespace common { - -// Constructor -EventDispatcher::EventDispatcher(const char *tag) : Stage(tag), event_store_(), next_stage_(NULL) -{ - LOG_TRACE("enter\n"); - - pthread_mutexattr_t attr; - - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); - pthread_mutex_init(&event_lock_, &attr); - - LOG_TRACE("exit\n"); -} - -// Destructor -EventDispatcher::~EventDispatcher() -{ - LOG_TRACE("enter\n"); - pthread_mutex_destroy(&event_lock_); - LOG_TRACE("exit\n"); -} - -/** - * Process an event - * Check if the event can be dispatched. If not, hash it and store - * it. If so, send it on to the next stage. - */ -void EventDispatcher::handle_event(StageEvent *event) -{ - LOG_TRACE("enter\n"); - - std::string hash; - DispatchContext *ctx = NULL; - status_t stat; - - pthread_mutex_lock(&event_lock_); - stat = dispatch_event(event, ctx, hash); - if (stat == SEND_EVENT) { - next_stage_->add_event(event); - } else if (stat == STORE_EVENT) { - StoredEvent se(event, ctx); - - event_store_[hash].push_back(se); - } else { - LOG_ERROR("Dispatch event failure\n"); - // in this case, dispatch_event is assumed to have disposed of event - } - pthread_mutex_unlock(&event_lock_); - - LOG_TRACE("exit\n"); -} - -// Initialize stage params and validate outputs -bool EventDispatcher::initialize() -{ - bool ret_val = true; - - if (next_stage_list_.size() != 1) { - ret_val = false; - } else { - next_stage_ = *(next_stage_list_.begin()); - } - return ret_val; -} - -/** - * Cleanup stage after disconnection - * Call done() on any events left over in the event_store_. - */ -void EventDispatcher::cleanup() -{ - pthread_mutex_lock(&event_lock_); - - // for each hash chain... - for (EventHash::iterator i = event_store_.begin(); i != event_store_.end(); i++) { - - // for each event on the chain - for (std::list::iterator j = i->second.begin(); j != i->second.end(); j++) { - j->first->done(); - } - i->second.clear(); - } - event_store_.clear(); - - pthread_mutex_unlock(&event_lock_); -} - -// Wake up a stored event -bool EventDispatcher::wakeup_event(std::string hashkey) -{ - bool sent = false; - EventHash::iterator i; - - i = event_store_.find(hashkey); - if (i != event_store_.end()) { - - // find the event and remove it from the current queue - StoredEvent target_ev = *(i->second.begin()); - i->second.pop_front(); - if (i->second.size() == 0) { - event_store_.erase(i); - } - - // try to dispatch the event again - status_t stat = dispatch_event(target_ev.first, target_ev.second, hashkey); - if (stat == SEND_EVENT) { - next_stage_->add_event(target_ev.first); - sent = true; - } else if (stat == STORE_EVENT) { - event_store_[hashkey].push_back(target_ev); - } else { - LOG_ERROR("Dispatch event failure\n"); - // in this case, dispatch_event is assumed to have disposed of event - } - } - - return sent; -} - -} // namespace common \ No newline at end of file diff --git a/deps/common/seda/event_dispatcher.h b/deps/common/seda/event_dispatcher.h deleted file mode 100644 index f9b089527..000000000 --- a/deps/common/seda/event_dispatcher.h +++ /dev/null @@ -1,160 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -#pragma once - -// Include Files -#include -#include - -// SEDA headers -#include "common/seda/stage.h" -#include "common/seda/stage_event.h" -namespace common { - -/** - * @file Event Dispatcher - * @author Longda - * @date 8/20/07 - */ - -class DispatchContext; - -/** - * A stage which stores and re-orders events - * The EventDispatcher stage is designed to assert control over the order - * of events that move through the Seda pipeline. The EventDispatcher stage - * has a hash table that stores events for later processing. As each event - * arrives at the Dispatcher, a test is applied to determine whether it - * should be allowed to proceed. This test is implemented in subclasses - * and uses state from the event and state held in the dispatcher itself. - * If the event is meant to be delayed, it is hashed and stored. The - * dispatcher also provides an interface that "wakes up" a stored, event - * and re-applies the dispatch test. This wake-up interface can be called - * from a background thread, or from a callback associated with an event - * that has already been dispatched. - * - * The EventDispatcher class is designed to be specialized by adding a - * specific implementation of the dispatch test for events, and a method - * or process for waking up stored events at the appropriate time. - */ - -class EventDispatcher : public Stage -{ - - // public interface operations - -public: - typedef enum - { - SEND_EVENT = 0, - STORE_EVENT, - FAIL_EVENT - } status_t; - - /** - * Destructor - * @pre stage is not connected - * @post pending events are deleted and stage is destroyed - */ - virtual ~EventDispatcher(); - - /** - * Process an event - * Check if the event can be dispatched. If not, hash it and store - * it. If so, send it on to the next stage - * - * @param[in] event Pointer to event that must be handled. - * @post event must not be de-referenced by caller after return - */ - void handle_event(StageEvent *event); - - // Note, EventDispatcher is an abstract class and needs no make_stage() - -protected: - /** - * Constructor - * @param[in] tag The label that identifies this stage. - * - * @pre tag is non-null and points to null-terminated string - * @post event queue is empty - * @post stage is not connected - */ - EventDispatcher(const char *tag); - - /** - * Initialize stage params and validate outputs - * @pre Stage not connected - * @return TRUE if and only if outputs are valid and init succeeded. - */ - bool initialize(); - - // set properties for this object - bool set_properties() { return true; } - - /** - * Cleanup stage after disconnection - * After disconnection is completed, cleanup any resources held by the - * stage and prepare for destruction or re-initialization. - */ - virtual void cleanup(); - - /** - * Dispatch test - * @param[in] ev Pointer to event that should be tested - * @param[in/out] Pointer to context object - * @param[out] hash Hash value for event - * - * @pre event_lock_ is locked - * @post hash is calculated if return val is false - * @return SEND_EVENT if ok to send the event down the pipeline; - * ctx is NULL - * STORE_EVENT if event should be stored; ctx will be stored - * FAIL_EVENT if failure, and event has been completed; - * ctx is NULL - */ - virtual status_t dispatch_event(StageEvent *ev, DispatchContext *&ctx, std::string &hash) = 0; - - /** - * Wake up a stored event - * @pre event_lock_ is locked - * @return true if an event was found on hash-chain associated with - * hashkey and sent to next stage - * false no event was found on hash-chain - */ - bool wakeup_event(std::string hashkey); - - // implementation state - - typedef std::pair StoredEvent; - typedef std::map> EventHash; - - EventHash event_store_; // events stored here while waiting - pthread_mutex_t event_lock_; // protects access to event_store_ - Stage *next_stage_; // target for dispatched events - -protected: -}; - -/** - * Class to store context info with the stored event. Subclasses should - * derive from this base class. - */ -class DispatchContext -{ -public: - virtual ~DispatchContext() {} -}; - -} // namespace common diff --git a/deps/common/seda/example_stage.cpp b/deps/common/seda/example_stage.cpp deleted file mode 100644 index 3bf7469a4..000000000 --- a/deps/common/seda/example_stage.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2021/4/13. -// - -#include "example_stage.h" - -#include -#include - -#include "common/conf/ini.h" -#include "common/io/io.h" -#include "common/lang/string.h" -#include "common/log/log.h" -#include "common/seda/timer_stage.h" - -using namespace common; - -// Constructor -ExampleStage::ExampleStage(const char *tag) : Stage(tag) {} - -// Destructor -ExampleStage::~ExampleStage() {} - -// Parse properties, instantiate a stage object -Stage *ExampleStage::make_stage(const std::string &tag) -{ - ExampleStage *stage = new ExampleStage(tag.c_str()); - if (stage == NULL) { - LOG_ERROR("new ExampleStage failed"); - return NULL; - } - stage->set_properties(); - return stage; -} - -// Set properties for this object set in stage specific properties -bool ExampleStage::set_properties() -{ - // std::string stageNameStr(stage_name_); - // std::map section = g_properties()->get( - // stageNameStr); - // - // std::map::iterator it; - // - // std::string key; - - return true; -} - -// Initialize stage params and validate outputs -bool ExampleStage::initialize() -{ - LOG_TRACE("Enter"); - - // std::list::iterator stgp = next_stage_list_.begin(); - // mTimerStage = *(stgp++); - // mCommStage = *(stgp++); - - LOG_TRACE("Exit"); - return true; -} - -// Cleanup after disconnection -void ExampleStage::cleanup() -{ - LOG_TRACE("Enter"); - - LOG_TRACE("Exit"); -} - -void ExampleStage::handle_event(StageEvent *event) -{ - LOG_TRACE("Enter\n"); - - LOG_TRACE("Exit\n"); - return; -} - -void ExampleStage::callback_event(StageEvent *event, CallbackContext *context) -{ - LOG_TRACE("Enter\n"); - - LOG_TRACE("Exit\n"); - return; -} diff --git a/deps/common/seda/init.cpp b/deps/common/seda/init.cpp deleted file mode 100644 index 46ddce6b6..000000000 --- a/deps/common/seda/init.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -#include "common/seda/init.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "common/io/io.h" -#include "common/log/log.h" -#include "common/seda/kill_thread.h" -#include "common/seda/metrics_stage.h" -#include "common/seda/seda_config.h" -#include "common/seda/stage_factory.h" -#include "common/seda/thread_pool.h" -#include "common/seda/timer_stage.h" -#include "common/time/datetime.h" -namespace common { - -int init_seda(ProcessParam *process_cfg) -{ - // Initialize the static data structures of threadpool - Threadpool::create_pool_key(); - - // initialize class factory instances here - static StageFactory kill_thread_factory("KillThreads", &KillThreadStage::make_stage); - static StageFactory timer_factory("TimerStage", &TimerStage::make_stage); - static StageFactory seda_stats_factory("MetricsStage", &MetricsStage::make_stage); - - // try to parse the seda configuration files - SedaConfig *config = SedaConfig::get_instance(); - SedaConfig::status_t config_stat; - - config_stat = config->parse(); - if (config_stat != SedaConfig::SUCCESS) { - LOG_ERROR("Error: unable to parse file %s", process_cfg->get_process_name().c_str()); - return errno; - } - - // Log a message to indicate that we are restarting, when looking - // at a log we can see if mmon is restarting us because we keep - // crashing. - LOG_INFO("(Re)Starting State: Pid: %u Time: %s", (unsigned int)getpid(), DateTime::now().to_string_local().c_str()); - LOG_INFO("The process Name is %s", process_cfg->get_process_name().c_str()); - - // try to initialize the seda configuration - config_stat = config->init(); - if (config_stat != SedaConfig::SUCCESS) { - LOG_ERROR("SedaConfig: unable to initialize seda stages"); - return errno; - } - - get_seda_config() = config; - - return 0; -} - -void cleanup_seda() -{ - SedaConfig *seda_config = SedaConfig::get_instance(); - delete seda_config; - SedaConfig::get_instance() = NULL; -} - -} // namespace common \ No newline at end of file diff --git a/deps/common/seda/kill_thread.cpp b/deps/common/seda/kill_thread.cpp deleted file mode 100644 index fc727b366..000000000 --- a/deps/common/seda/kill_thread.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// -// Include Files -#include "common/seda/kill_thread.h" - -#include - -#include "common/seda/thread_pool.h" -namespace common { - -/** - * Notify the pool and kill the thread - * @param[in] event Pointer to event that must be handled. - * - * @post Call never returns. Thread is killed. Pool is notified. - */ -void KillThreadStage::handle_event(StageEvent *event) -{ - get_pool()->thread_kill(); - event->done(); - this->release_event(); - pthread_exit(0); -} - -/** - * Process properties of the classes - * @pre class members are uninitialized - * @post initializing the class members - * @return the class object - */ -Stage *KillThreadStage::make_stage(const std::string &tag) { return new KillThreadStage(tag.c_str()); } - -bool KillThreadStage::set_properties() -{ - // nothing to do - return true; -} - -} // namespace common \ No newline at end of file diff --git a/deps/common/seda/kill_thread.h b/deps/common/seda/kill_thread.h deleted file mode 100644 index 4d2e34d4c..000000000 --- a/deps/common/seda/kill_thread.h +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -#pragma once - -#include - -#include "common/defs.h" -#include "common/seda/stage.h" -namespace common { - -/** - * @file - * @author Longda - * @date 3/16/07 - */ - -class Threadpool; - -/** - * A Stage to kill threads in a thread pool - * The KillThreadStage is scheduled on a thread pool whenever threads - * need to be killed. Each event handled by the stage results in the - * death of the thread. - */ -class KillThreadStage : public Stage -{ - -public: - /** - * parse properties, instantiate a summation stage object - * @pre class members are uninitialized - * @post initializing the class members - * @return Stage instantiated object - */ - static Stage *make_stage(const std::string &tag); - -protected: - /** - * Constructor - * @param[in] tag The label that identifies this stage. - * - * @pre tag is non-null and points to null-terminated string - * @post event queue is empty - * @post stage is not connected - */ - KillThreadStage(const char *tag) : Stage(tag) {} - - /** - * Notify the pool and kill the thread - * @param[in] event Pointer to event that must be handled. - * - * @post Call never returns. Thread is killed. Pool is notified. - */ - void handle_event(StageEvent *event); - - /** - * Handle the callback - * Nothing special for callbacks in this stage. - */ - void callback_event(StageEvent *event, CallbackContext *context) { return; } - - /** - * Initialize stage params - * Ignores next_stage_list_---there are no outputs for this stage. - * - * @pre Stage not connected - * @return true - */ - bool initialize() { return true; } - - /** - * set properties for this object - * @pre class members are uninitialized - * @post initializing the class members - * @return Stage instantiated object - */ - bool set_properties(); - - friend class Threadpool; -}; - -} // namespace common diff --git a/deps/common/seda/metrics_stage.cpp b/deps/common/seda/metrics_stage.cpp deleted file mode 100644 index f8bd8a2f3..000000000 --- a/deps/common/seda/metrics_stage.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2021/4/13. -// - -#include "common/seda/metrics_stage.h" - -#include -#include - -#include "common/conf/ini.h" -#include "common/io/io.h" -#include "common/lang/string.h" -#include "common/log/log.h" -#include "common/metrics/metrics_registry.h" -#include "common/seda/metrics_report_event.h" -#include "common/seda/seda_defs.h" -#include "common/seda/timer_stage.h" -#include "common/time/datetime.h" - -using namespace common; - -MetricsRegistry &get_metric_registry() -{ - static MetricsRegistry metrics_registry; - - return metrics_registry; -} - -// Constructor -MetricsStage::MetricsStage(const char *tag) : Stage(tag) {} - -// Destructor -MetricsStage::~MetricsStage() {} - -// Parse properties, instantiate a stage object -Stage *MetricsStage::make_stage(const std::string &tag) -{ - MetricsStage *stage = new MetricsStage(tag.c_str()); - if (stage == NULL) { - LOG_ERROR("new MetricsStage failed"); - return NULL; - } - stage->set_properties(); - return stage; -} - -// Set properties for this object set in stage specific properties -bool MetricsStage::set_properties() -{ - std::string stage_name_str(stage_name_); - std::map section = get_properties()->get(stage_name_str); - - metric_report_interval_ = DateTime::SECONDS_PER_MIN; - - std::string key = METRCS_REPORT_INTERVAL; - std::map::iterator it = section.find(key); - if (it != section.end()) { - str_to_val(it->second, metric_report_interval_); - } - - return true; -} - -// Initialize stage params and validate outputs -bool MetricsStage::initialize() -{ - std::list::iterator stgp = next_stage_list_.begin(); - timer_stage_ = *(stgp++); - - MetricsReportEvent *report_event = new MetricsReportEvent(); - - add_event(report_event); - return true; -} - -// Cleanup after disconnection -void MetricsStage::cleanup() {} - -void MetricsStage::handle_event(StageEvent *event) -{ - CompletionCallback *cb = new CompletionCallback(this, NULL); - if (cb == NULL) { - LOG_ERROR("Failed to new callback"); - - event->done(); - - return; - } - - TimerRegisterEvent *tm_event = new TimerRegisterEvent(event, metric_report_interval_ * USEC_PER_SEC); - if (tm_event == NULL) { - LOG_ERROR("Failed to new TimerRegisterEvent"); - - delete cb; - - event->done(); - - return; - } - - event->push_callback(cb); - timer_stage_->add_event(tm_event); - - return; -} - -void MetricsStage::callback_event(StageEvent *event, CallbackContext *context) -{ - MetricsRegistry &metrics_registry = get_metrics_registry(); - - metrics_registry.snapshot(); - metrics_registry.report(); - - // do it again. - add_event(event); - - return; -} diff --git a/deps/common/seda/metrics_stage.h b/deps/common/seda/metrics_stage.h deleted file mode 100644 index dd698e4b7..000000000 --- a/deps/common/seda/metrics_stage.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2021/4/13. -// - -#ifndef __COMMON_SEDA_METRICS_STAGE_H__ -#define __COMMON_SEDA_METRICS_STAGE_H__ - -#include "common/seda/stage.h" - -namespace common { - -class MetricsStage : public Stage -{ -public: - ~MetricsStage(); - static Stage *make_stage(const std::string &tag); - -protected: - // common function - MetricsStage(const char *tag); - bool set_properties(); - - bool initialize(); - void cleanup(); - void handle_event(StageEvent *event); - void callback_event(StageEvent *event, CallbackContext *context); - -protected: -private: - Stage *timer_stage_ = nullptr; - // report metrics every @metric_report_interval_ seconds - int metric_report_interval_ = 10; -}; -} // namespace common -#endif //__COMMON_SEDA_METRICS_STAGE_H__ diff --git a/deps/common/seda/seda_config.cpp b/deps/common/seda/seda_config.cpp deleted file mode 100644 index e5463c6e8..000000000 --- a/deps/common/seda/seda_config.cpp +++ /dev/null @@ -1,490 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// -#include "common/seda/seda_config.h" - -#include -#include -#include -#include -#include - -#include "common/lang/string.h" -#include "common/log/log.h" -#include "common/os/os.h" -#include "common/seda/init.h" -#include "common/seda/stage_factory.h" -namespace common { - -SedaConfig *SedaConfig::instance_ = NULL; - -SedaConfig *&SedaConfig::get_instance() -{ - if (instance_ == NULL) { - instance_ = new SedaConfig(); - ASSERT((instance_ != NULL), "failed to allocate SedaConfig"); - } - return instance_; -} - -// Constructor -SedaConfig::SedaConfig() : cfg_file_(), cfg_str_(), thread_pools_(), stages_() { return; } - -// Destructor -SedaConfig::~SedaConfig() -{ - ASSERT(instance_, "Instance should not be null"); - // check to see if clean-up is necessary - if ((!thread_pools_.empty()) || (!stages_.empty())) { - cleanup(); - } - - instance_ = NULL; -} - -// Set the file holding the configuration -void SedaConfig::set_cfg_filename(const char *filename) -{ - cfg_str_.clear(); - cfg_file_.clear(); - if (filename != NULL) { - cfg_file_.assign(filename); - } - return; -} - -// Set the string holding the configuration -void SedaConfig::set_cfg_string(const char *config_str) -{ - cfg_str_.clear(); - cfg_file_.clear(); - if (config_str != NULL) { - cfg_str_.assign(config_str); - } - return; -} - -// Parse config file or string -SedaConfig::status_t SedaConfig::parse() -{ - // first parse the config - try { - // skip parse in this implementation - // all configuration will be put into one file - } catch (const std::exception &e) { - std::string err_msg(e.what()); - LOG_ERROR("Seda config parse failed w/ error %s", err_msg.c_str()); - return PARSEFAIL; - } - LOG_DEBUG("Seda config parse success\n"); - - return SUCCESS; -} - -// instantiate the parsed SEDA configuration -SedaConfig::status_t SedaConfig::instantiate_cfg() -{ - status_t stat = SUCCESS; - - // instantiate the configuration - stat = instantiate(); - - return stat; -} - -// start the configuration - puts the stages_ into action -SedaConfig::status_t SedaConfig::start() -{ - status_t stat = SUCCESS; - - ASSERT(thread_pools_.size(), "Configuration not yet instantiated"); - - // start the stages_ one by one. connect() calls - std::map::iterator iter = stages_.begin(); - std::map::iterator end = stages_.end(); - - while (iter != end) { - if (iter->second != NULL) { - Stage *stg = iter->second; - bool ret = stg->connect(); - if (!ret) { - cleanup(); - stat = INITFAIL; - break; - } - } - iter++; - } - - return stat; -} - -// Initialize the thread_pools_ and stages_ -SedaConfig::status_t SedaConfig::init() -{ - status_t stat = SUCCESS; - - // check the preconditions - ASSERT(stages_.empty(), "Attempt to initialize sedaconfig twice"); - ASSERT(thread_pools_.empty(), "Attempt to initialize sedaconfig twice"); - - // instantiate the parsed config - stat = instantiate(); - if (stat) { - return stat; - } - - // start it running - stat = start(); - if (stat) { - return stat; - } - - return SUCCESS; -} - -// Clean-up the threadpool and stages_ -void SedaConfig::cleanup() -{ - // first disconnect all stages_ - if (stages_.empty() == false) { - std::map::iterator iter = stages_.begin(); - std::map::iterator end = stages_.end(); - while (iter != end) { - if (iter->second != NULL) { - Stage *stg = iter->second; - if (stg->is_connected()) { - stg->disconnect(); - } - } - iter++; - } - } - LOG_TRACE("stages_ disconnected\n"); - - // now delete all stages_ and thread_pools_ - clear_config(); -} - -void SedaConfig::init_event_history() -{ - std::map base_section = get_properties()->get(SEDA_BASE_NAME); - std::map::iterator it; - std::string key; - - // check whether event histories are enabled - bool ev_hist = false; - key = EVENT_HISTORY; - it = base_section.find(key); - if (it != base_section.end()) { - if (it->second.compare("true") == 0) { - ev_hist = true; - } - } - - get_event_history_flag() = ev_hist; - - // set max event hops - uint32_t max_event_hops = 100; - key = MAX_EVENT_HISTORY_NUM; - it = base_section.find(key); - if (it != base_section.end()) { - str_to_val(it->second, max_event_hops); - } - get_max_event_hops() = max_event_hops; - - LOG_INFO("Successfully init_event_history, EventHistory:%d, MaxEventHops:%u", (int)ev_hist, max_event_hops); - return; -} - -SedaConfig::status_t SedaConfig::init_thread_pool() -{ - try { - - std::map base_section = get_properties()->get(SEDA_BASE_NAME); - std::map::iterator it; - std::string key; - - // get thread pool names - key = THREAD_POOLS_NAME; - it = base_section.find(key); - if (it == base_section.end()) { - LOG_ERROR("Configuration hasn't set %s", key.c_str()); - return INITFAIL; - } - - std::string pool_names = it->second; - std::vector name_list; - std::string split_tag; - split_tag.assign(1, Ini::CFG_DELIMIT_TAG); - split_string(pool_names, split_tag, name_list); - - int cpu_num = getCpuNum(); - std::string default_cpu_num_str; - val_to_str(cpu_num, default_cpu_num_str); - - for (size_t pos = 0; pos != name_list.size(); pos++) { - std::string &thread_name = name_list[pos]; - - // get count number - key = COUNT; - std::string count_str = get_properties()->get(key, default_cpu_num_str, thread_name); - - int thread_count = 1; - str_to_val(count_str, thread_count); - if (thread_count < 1) { - LOG_INFO("Thread number of %s is %d, it is same as cpu's cores.", thread_name.c_str(), cpu_num); - thread_count = cpu_num; - } - const int max_thread_count = 1000000; - if (thread_count >= max_thread_count) { - LOG_ERROR("Thread number is too big: %d(max:%d)", thread_count, max_thread_count); - return INITFAIL; - } - - Threadpool *thread_pool = new Threadpool(thread_count, thread_name); - if (thread_pool == NULL) { - LOG_ERROR("Failed to new %s threadpool\n", thread_name.c_str()); - return INITFAIL; - } - thread_pools_[thread_name] = thread_pool; - } - - if (thread_pools_.find(DEFAULT_THREAD_POOL) == thread_pools_.end()) { - LOG_ERROR("There is no default thread pool %s, please add it.", DEFAULT_THREAD_POOL); - return INITFAIL; - } - - } catch (std::exception &e) { - LOG_ERROR("Failed to init thread_pools_:%s\n", e.what()); - clear_config(); - return INITFAIL; - } - - int pools = thread_pools_.size(); - if (pools < 1) { - LOG_ERROR("Invalid number of thread_pools_:%d", pools); - clear_config(); - return INITFAIL; - } - - return SUCCESS; -} - -std::string SedaConfig::get_thread_pool(std::string &stage_name) -{ - std::string ret = DEFAULT_THREAD_POOL; - // Get thread pool - std::map stage_section = get_properties()->get(stage_name); - std::map::iterator itt; - std::string thread_pool_id = THREAD_POOL_ID; - itt = stage_section.find(thread_pool_id); - if (itt == stage_section.end()) { - LOG_INFO("Not set thread_pool_id for %s, use default threadpool %s", stage_name.c_str(), DEFAULT_THREAD_POOL); - return ret; - } - - std::string thread_name = itt->second; - if (thread_name.empty()) { - LOG_ERROR("Failed to set %s of the %s, use the default threadpool %s", - thread_pool_id.c_str(), - stage_name.c_str(), - DEFAULT_THREAD_POOL); - return ret; - } - - if (thread_pools_.find(thread_name) == thread_pools_.end()) { - LOG_ERROR("The stage %s's threadpool %s is invalid, use the default " - "threadpool %s", - stage_name.c_str(), - thread_name.c_str(), - DEFAULT_THREAD_POOL); - return ret; - } - return thread_name; -} - -SedaConfig::status_t SedaConfig::init_stages() -{ - try { - std::map base_section = get_properties()->get(SEDA_BASE_NAME); - std::map::iterator it; - std::string key; - - // get stage names - key = STAGES; - it = base_section.find(key); - if (it == base_section.end()) { - LOG_ERROR("Hasn't set stages name in %s", key.c_str()); - clear_config(); - return INITFAIL; - } - - std::string split_tag; - split_tag.assign(1, Ini::CFG_DELIMIT_TAG); - split_string(it->second, split_tag, stage_names_); - - for (std::vector::iterator it = stage_names_.begin(); it != stage_names_.end(); it++) { - std::string stage_name(*it); - - std::string thread_name = get_thread_pool(stage_name); - Threadpool *t = thread_pools_[thread_name]; - - Stage *stage = StageFactory::make_instance(stage_name); - if (stage == NULL) { - LOG_ERROR("Failed to make instance of stage %s", stage_name.c_str()); - clear_config(); - return INITFAIL; - } - stages_[stage_name] = stage; - stage->set_pool(t); - - LOG_INFO("Stage %s use threadpool %s.", stage_name.c_str(), thread_name.c_str()); - } // end for stage - - } catch (std::exception &e) { - LOG_ERROR("Failed to parse stages information, please check, err:%s", e.what()); - clear_config(); - return INITFAIL; - } - - if (stages_.size() < 1) { - LOG_ERROR("Invalid number of stages_: %lu\n", stages_.size()); - clear_config(); - return INITFAIL; - } - - return SUCCESS; -} - -SedaConfig::status_t SedaConfig::gen_next_stages() -{ - try { - for (std::vector::iterator it_name = stage_names_.begin(); it_name != stage_names_.end(); it_name++) { - - std::string stage_name(*it_name); - Stage *stage = stages_[stage_name]; - - std::map stage_section = get_properties()->get(stage_name); - std::map::iterator it; - std::string next_stage_id = NEXT_STAGES; - it = stage_section.find(next_stage_id); - if (it == stage_section.end()) { - continue; - } - - std::string next_stage_names = it->second; - - std::vector next_stage_name_list; - std::string split_tag; - split_tag.assign(1, Ini::CFG_DELIMIT_TAG); - split_string(next_stage_names, split_tag, next_stage_name_list); - - for (std::vector::iterator next_it = next_stage_name_list.begin(); - next_it != next_stage_name_list.end(); - next_it++) { - std::string &next_stage_name = *next_it; - Stage *next_stage = stages_[next_stage_name]; - stage->push_stage(next_stage); - } - - } // end for stage - } catch (std::exception &e) { - LOG_ERROR("Failed to get next stages"); - clear_config(); - return INITFAIL; - } - return SUCCESS; -} - -// instantiate the thread_pools_ and stages_ -SedaConfig::status_t SedaConfig::instantiate() -{ - - init_event_history(); - - SedaConfig::status_t status = init_thread_pool(); - if (status) { - LOG_ERROR("Failed to init thread pool\n"); - return status; - } - - status = init_stages(); - if (status) { - LOG_ERROR("Failed init stages_\n"); - return status; - } - - status = gen_next_stages(); - if (status) { - LOG_ERROR("Failed to generate next stage list\n"); - return status; - } - - return SUCCESS; -} - -// delete all thread_pools_ and stages_ -void SedaConfig::clear_config() -{ - // delete stages_ - std::map::iterator s_iter = stages_.begin(); - std::map::iterator s_end = stages_.end(); - while (s_iter != s_end) { - if (s_iter->second != NULL) { - - Stage *stg = s_iter->second; - LOG_INFO("Stage %s deleted.", stg->get_name()); - ASSERT((!stg->is_connected()), "%s%s", "Stage connected in clear_config ", stg->get_name()); - delete stg; - s_iter->second = NULL; - } - s_iter++; - } - stages_.clear(); - LOG_INFO("Seda Stage released"); - - // delete thread_pools_ - std::map::iterator t_iter = thread_pools_.begin(); - std::map::iterator t_end = thread_pools_.end(); - while (t_iter != t_end) { - if (t_iter->second != NULL) { - LOG_INFO("ThreadPool %s deleted.", t_iter->first.c_str()); - delete t_iter->second; - t_iter->second = NULL; - } - t_iter++; - } - thread_pools_.clear(); - LOG_INFO("Seda thread pools released"); -} - -void SedaConfig::get_stage_names(std::vector &names) const { names = stage_names_; } - -void SedaConfig::get_stage_queue_status(std::vector &stats) const -{ - for (std::map::const_iterator i = stages_.begin(); i != stages_.end(); ++i) { - Stage *stg = (*i).second; - stats.push_back(stg->qlen()); - } -} - -// Global seda config object -SedaConfig *&get_seda_config() -{ - static SedaConfig *seda_config = NULL; - - return seda_config; -} - -} // namespace common \ No newline at end of file diff --git a/deps/common/seda/seda_config.h b/deps/common/seda/seda_config.h deleted file mode 100644 index c0bf5e774..000000000 --- a/deps/common/seda/seda_config.h +++ /dev/null @@ -1,233 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -#ifndef __COMMON_SEDA_SEDA_CONFIG_H__ -#define __COMMON_SEDA_SEDA_CONFIG_H__ - -// Include Files -#include -#include -#include -#include - -#include "common/seda/seda_defs.h" -#include "common/seda/thread_pool.h" - -namespace common { - -// keywords of sedaconfig - -/** - * A class to configure seda stages - * Each application uses an xml file to define the stages that make up the - * application, the threadpool that the stages use, and the parameters that - * are passed to configure each individual stage. The SedaConfig class - * consumes this xml file, parses it, and instantiates the indicated - * configuration. It also then provides access to individual stages_ and - * threadpools within the configuration. The parameters passed to each - * stage consists of the global attributes defined for all the seda stages_ - * in the seda instance as well as the attributes defined for that specific - * stage. The attributes defined for each stage will override the global - * attributes in case of duplicate attributes - */ - -class SedaConfig -{ - -public: - typedef enum - { - SUCCESS = 0, - INITFAIL, - PARSEFAIL - } status_t; - - static SedaConfig *&get_instance(); - - /** - * Destructor - * @post configuration is deleted - */ - ~SedaConfig(); - - /** - * Set the file holding the configuration - * @pre filename is a null-terminated string, or \c NULL - * @post config filename is initialized, config string is empty - */ - void set_cfg_filename(const char *filename); - - /** - * Set the string holding the configuration - * @pre config_str is a null-terminated string, or \c NULL - * @post config string is initialized, config filename is empty - */ - void set_cfg_string(const char *config_str); - - /** - * Parse config file or string - * parse the seda config file or string and build an in-memory - * representation of the config. Also, update global properties object - * with global seda properties from the config. - * - * @post config file or string is parsed and global seda properties - * are added to global properties object - * @returns SUCCESS if parsing succeeds - * PARSEFAIL if parsing fails - */ - status_t parse(); - - /** - * instantiate the parsed configuration - * Use the parsed configuration to instantiate the thread pools - * and stages_ specificed, but do not start it running. - * - * @pre configuration has been successfully parsed - * @post upon SUCCESS, thread pools and stages_ are created, - * ready to be started. - */ - status_t instantiate_cfg(); - - /** - * start the parsed, instantiated configuration - * @pre configuration parsed and instantiated - * @post if SUCCESS, the SEDA pipleine is now running. - * - */ - status_t start(); - - /** - * Complete Initialization of the mThreadPools and stages_ - * Use the parsed config to initialize the required mThreadPools and - * stages_, and start them running. If the config has not yet been - * parsed then try to parse it first. The init function combines - * parse(), instantiate() and start() - * - * @pre empty mThreadPools and stages_ - * @post if returns SUCCESS then - * mThreadPools and stages_ created/initialized and running - * @post if returns INITFAIL or PARSEFAIL then - * mThreadPools and stage list are empty - */ - status_t init(); - - /** - * Clean-up the threadpool and stages_ - * @post all stages_ disconnected and deleted, all mThreadPools deleted - */ - void cleanup(); - - /** - * get the desired stage given a string - * - * @param[in] stagename take in the stage name and convert it to a Stage - * @pre - * @return a reference to the Stage - */ - Stage *get_stage(const char *stagename); - - /** - * get the desired threadpool a string - * - * @param[in] index take in the index for threadpool - * @pre - * @return a reference to the ThreadPool - */ - Threadpool &get_thread_pool(const int index); - - /** - * Get a list of all stage names - * @param[in/out] names names of all stages_ - */ - void get_stage_names(std::vector &names) const; - - /** - * Query the number of queued events at each stage. - * @param[in/out] stats number of events enqueued at each - * stage. - */ - void get_stage_queue_status(std::vector &stats) const; - - std::map::iterator begin(); - std::map::iterator end(); - -private: - // Constructor - SedaConfig(); - - /** - * instantiate the mThreadPools and stages_ - * Instantiate the mThreadPools and stages_ defined in the configuration - * - * @pre cfg_ptr is not NULL - * @post returns SUCCESS ==> all mThreadPools and stages_ are created - * returns INITFAIL ==> mThreadPools and stages_ are deleted - */ - status_t instantiate(); - - status_t init_thread_pool(); - - std::string get_thread_pool(std::string &stage_name); - - status_t init_stages(); - status_t gen_next_stages(); - - /** - * delete all mThreadPools and stages_ - * @pre all existing stages_ are disconnected - * @post all mThreadPools and stages_ are deleted - */ - void clear_config(); - - /** - * init event history setting - * Setting max_event_hops, event_history_flag - */ - void init_event_history(); - - SedaConfig &operator=(const SedaConfig &cevtout); - - static SedaConfig *instance_; - - // In old logic, SedaConfig will parse seda configure file - // but here, only one configure file - std::string cfg_file_; - std::string cfg_str_; - - std::map thread_pools_; - std::map stages_; - std::vector stage_names_; -}; - -inline std::map::iterator SedaConfig::begin() { return stages_.begin(); } - -inline std::map::iterator SedaConfig::end() { return stages_.end(); } - -inline Stage *SedaConfig::get_stage(const char *stagename) -{ - if (stagename) { - std::string sname(stagename); - return stages_[stagename]; - } - return NULL; -} - -// Global seda config object -SedaConfig *&get_seda_config(); - -bool &get_event_history_flag(); -uint32_t &get_max_event_hops(); - -} // namespace common -#endif //__COMMON_SEDA_SEDA_CONFIG_H__ diff --git a/deps/common/seda/stage.cpp b/deps/common/seda/stage.cpp deleted file mode 100644 index 775a0b1e0..000000000 --- a/deps/common/seda/stage.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -// Include Files -#include "common/seda/stage.h" - -#include -#include - -#include "common/defs.h" -#include "common/lang/mutex.h" -#include "common/lang/string.h" -#include "common/log/log.h" -#include "common/seda/init.h" -#include "common/seda/thread_pool.h" -namespace common { - -/** - * Constructor - * @param[in] tag The label that identifies this stage. - * - * @pre tag is non-null and points to null-terminated string - * @post event queue is empty - * @post stage is not connected - */ -Stage::Stage(const char *tag) : next_stage_list_(), event_list_(), connected_(false), event_ref_(0) -{ - assert(tag != NULL); - - MUTEX_INIT(&list_mutex_, NULL); - COND_INIT(&disconnect_cond_, NULL); - stage_name_ = new char[strlen(tag) + 1]; - snprintf(stage_name_, strlen(tag) + 1, "%s", tag); -} - -/** - * Destructor - * @pre stage is not connected - * @post pending events are deleted and stage is destroyed - */ -Stage::~Stage() -{ - assert(!connected_); - MUTEX_LOCK(&list_mutex_); - while (event_list_.size() > 0) { - delete *(event_list_.begin()); - event_list_.pop_front(); - } - MUTEX_UNLOCK(&list_mutex_); - next_stage_list_.clear(); - - MUTEX_DESTROY(&list_mutex_); - COND_DESTROY(&disconnect_cond_); - delete[] stage_name_; -} - -/** - * Connect this stage to pipeline and threadpool - * Connect the output of this stage to the inputs of the stages in - * the provided stage list. Each subclass will validate the provided - * stage list to be sure it is appropriate. If the validation succeeds, - * connect to the Threadpool and start processing. - * - * @param[in,out] stageList Stages that come next in the pipeline. - * @param[in] pool Threadpool which will handle events - * - * @pre stage is not connected - * @post next_stage_list_ == original stageList - * @post stageList is empty - * @post th_pool_ == pool - * @return true if the connection succeeded, else false - */ -bool Stage::connect() -{ - LOG_TRACE("%s%s", "enter", stage_name_); - assert(!connected_); - assert(th_pool_ != NULL); - - bool success = false; - unsigned int backlog = 0; - - success = initialize(); - if (success) { - MUTEX_LOCK(&list_mutex_); - backlog = event_list_.size(); - event_ref_ = backlog; - connected_ = true; - MUTEX_UNLOCK(&list_mutex_); - } - - // if connection succeeded, schedule all the events in the queue - if (connected_) { - while (backlog > 0) { - th_pool_->schedule(this); - backlog--; - } - } - - LOG_TRACE("%s%s%d", "exit", stage_name_, connected_); - return success; -} - -/** - * Disconnect this stage from the pipeline and threadpool - * Block stage from being scheduled. Wait for currently processing - * and scheduled events to complete, then disconnect from the threadpool. - * Disconnect the output of this stage from the inputs of the stages in the - * next_stage_list_. - * - * @pre stage is connected - * @post next_stage_list_ empty - * @post th_pool_ NULL - * @post stage is not connected - */ -void Stage::disconnect() -{ - assert(connected_ == true); - - LOG_TRACE("%s%s", "enter", stage_name_); - MUTEX_LOCK(&list_mutex_); - disconnect_prepare(); - connected_ = false; - while (event_ref_ > 0) { - COND_WAIT(&disconnect_cond_, &list_mutex_); - } - th_pool_ = NULL; - next_stage_list_.clear(); - cleanup(); - MUTEX_UNLOCK(&list_mutex_); - LOG_TRACE("%s%s", "exit", stage_name_); -} - -/** - * Add an event to the queue. - * @param[in] event Event to add to queue. - * - * @pre event non-null - * @post event added to the end of event queue - * @post event must not be de-referenced by caller after return - */ -void Stage::add_event(StageEvent *event) -{ - assert(event != NULL); - - MUTEX_LOCK(&list_mutex_); - - // add event to back of queue - event_list_.push_back(event); - - if (connected_) { - assert(th_pool_ != NULL); - - event_ref_++; - MUTEX_UNLOCK(&list_mutex_); - th_pool_->schedule(this); - } else { - MUTEX_UNLOCK(&list_mutex_); - } -} - -/** - * Query length of queue - * @return length of event queue. - */ -unsigned long Stage::qlen() const -{ - unsigned long res; - - MUTEX_LOCK(&list_mutex_); - res = event_list_.size(); - MUTEX_UNLOCK(&list_mutex_); - return res; -} - -/** - * Query whether the queue is empty - * @return \c true if the queue is empty; \c false otherwise - */ -bool Stage::qempty() const -{ - bool empty = false; - - MUTEX_LOCK(&list_mutex_); - empty = event_list_.empty(); - MUTEX_UNLOCK(&list_mutex_); - return empty; -} - -/** - * Remove an event from the queue. Called only by service thread. - * - * @pre queue not empty. - * @return first event on queue. - * @post first event on queue is removed from queue. - */ -StageEvent *Stage::remove_event() -{ - MUTEX_LOCK(&list_mutex_); - - assert(!event_list_.empty()); - - StageEvent *se = *(event_list_.begin()); - event_list_.pop_front(); - MUTEX_UNLOCK(&list_mutex_); - - return se; -} - -/** - * Release event reference on stage. Called only by service thread. - * - * @post event ref count on stage is decremented - */ -void Stage::release_event() -{ - MUTEX_LOCK(&list_mutex_); - event_ref_--; - if (!connected_ && event_ref_ == 0) { - COND_SIGNAL(&disconnect_cond_); - } - MUTEX_UNLOCK(&list_mutex_); -} - -} // namespace common diff --git a/deps/common/seda/stage.h b/deps/common/seda/stage.h deleted file mode 100644 index e41386c8e..000000000 --- a/deps/common/seda/stage.h +++ /dev/null @@ -1,329 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -#ifndef __COMMON_SEDA_STAGE_H__ -#define __COMMON_SEDA_STAGE_H__ - -// Include Files -#include -#include - -// project headers -#include "common/defs.h" -#include "common/log/log.h" - -// seda headers -#include "common/seda/stage_event.h" -namespace common { - -class Threadpool; -class CallbackContext; - -/** - * A Stage in a staged event-driven architecture - * The Stage class consists of a queue of events and a link to a - * Threadpool. The threads in the pool handle the events on the queue. - * The key method of the Stage class is handle_event(), which is abstract. - * Each subclass of Stage will fill in the handle_event() method with - * appropriate code for that stage. The expected behavior of handle_event() - * is to perform stage-specific processing for the event argument, and - * complete by passing the event on to the queue of the next appropriate - * stage. Occasionally, handle_event may cause a new event to be created, - * or the current event to be freed. The generic Stage class handles - * maintenance of the event queue, and scheduling the stage on the runqueue - * of the Threadpool as long as events are in the queue. - * Subclasses of Stage may also add data members that represent - * stage-specific state and are accessed from the handle_event() method. - *

- * A stage can register a callback on an event that it passes to another - * stage later in the pipeline. When the callback is invoked, it calls - * the callback_event() virtual function from the stage that set the - * callback. - *

- * An event is an argument to the handle_event() method. - * The Event class is generic and is intended to be specialized by - * subclasses in order to represent real events. handle_event() is - * expected to use C++ introspection (dynamic cast) to determine the - * specific sub-class of event that it is dealing with on each invocation. - *

- * After a Stage is constructed, nothing happens because the stage is not - * yet initialized. At this point the queue is empty and the stage is not - * yet connected to any Threadpool or Stage pipeline. In general, init - * and configuration of stages is handled by the SedaConfig class which - * picks up configuration information from a central config file. Processing - * for the stage does not start until the SedaConfig class calls connect(). - * connect() hooks the stage up to a Threadpool and connects the output of - * the stage to the input of some other stages. A single Threadpool can be - * dedicated to a single Stage, or multiple Stages can share the same - * Threadpool. disconnect() performs the opposite task, breaking the - * connection between a Stage and its Threadpool and pipeline successors. - * disconnect is currently called by the SedaConfig object on each Stage - * in the pipeline when the SedaConfig object is destroyed. - * - * There are three virtual functions that allow a stage class to customize - * the initialization/finalization process. set_properties() is called - * by the SedaConfig object immediately after the stage is created, and - * is used to pass stage-specific configuration options via a Properties - * class. initialize() is called from connect(). The purpose of - * initialize() is to allow the stage class to verify that the next_stage_list_ - * contains valid stages (number, type, etc), and to initialize any stage - * state (such as aliases for members of the next stage list) prior to - * stage connection. cleanup() is called from disconnect to allow the - * stage class to release resources before entering the disconnected state. - * initialize() and cleanup() should be coded so that a stage can be - * repeatedly disconnected then re-connected and continue to function - * properly. - */ -class Stage -{ - - // public interface operations - -public: - /** - * Destructor - * @pre stage is not connected - * @post pending events are deleted and stage is destroyed - */ - virtual ~Stage(); - - /** - * parse properties, instantiate a summation stage object - * @pre class members are uninitialized - * @post initializing the class members - * @return Stage instantiated object - */ - static Stage *make_stage(const std::string &tag); - - /** - * Return the Threadpool object - * @return reference to the Threadpool for this Stage - */ - Threadpool *get_pool() { return th_pool_; } - - /** - * Push stage to the list of the next stages - * @param[in] stage pointer - * - * @pre stage not connected - * @post added a new stage - */ - void push_stage(Stage *); - - /** - * Get name of the stage - * @return return the name of this stage which is the class name - */ - const char *get_name(); - - /** - * Set threadpool - * @param[in] threadpool pointer - * - * @pre stage not connected - * @post seed threadpool for this stage - */ - void set_pool(Threadpool *); - - /** - * Connect this stage to pipeline and threadpool - * Connect the output of this stage to the inputs of the stages in - * the provided stage list. Each subclass will validate the provided - * stage list to be sure it is appropriate. If the validation succeeds, - * connect to the Threadpool and start processing. - * - * @param[in,out] stageList Stages that come next in the pipeline. - * @param[in] pool Threadpool which will handle events - * - * @pre stage is not connected - * @post next_stage_list_ == original stageList - * @post stageList is empty - * @post th_pool_ == pool - * @return TRUE if the connection succeeded, else FALSE - */ - bool connect(); - - /** - * Disconnect this stage from the pipeline and threadpool - * Block stage from being scheduled. Wait for currently processing - * and queued events to complete, then disconnect from the threadpool. - * Disconnect the output of this stage from the inputs of the stages in - * the next_stage_list_. - * - * @pre stage is connected - * @post next_stage_list_ empty - * @post th_pool_ NULL - * @post stage is not connected - */ - void disconnect(); - - /** - * Add an event to the queue. - * This will trigger thread switch, you can use handle_event without thread switch - * @param[in] event Event to add to queue. - * - * @pre event non-null - * @post event added to the end of event queue - * @post event must not be de-referenced by caller after return - * @post event ref count on stage is incremented - */ - void add_event(StageEvent *event); - - /** - * Query length of queue - * @return length of event queue. - */ - unsigned long qlen() const; - - /** - * Query whether the queue is empty - * @return \c true if the queue is empty; \c false otherwise - */ - bool qempty() const; - - /** - * Query whether stage is connected - * @return true if stage is connected - */ - bool is_connected() const { return connected_; } - - /** - * Perform Stage-specific processing for an event - * Processing one event without switch thread. - * Handle the event according to requirements of specific stage. Pure - * virtual member function. - * - * @param[in] event Pointer to event that must be handled. - * - * @post event must not be de-referenced by caller after return - */ - virtual void handle_event(StageEvent *event) = 0; - - /** - * Perform Stage-specific callback processing for an event - * Implement callback processing according to the requirements of - * specific stage. Pure virtual member function. - * - * @param[in] event Pointer to event that initiated the callback. - */ - virtual void callback_event(StageEvent *event, CallbackContext *context) {} - - /** - * Perform Stage-specific callback processing for a timed out event - * A stage only need to implement this interface if the down-stream - * stages support event timeout detection. - */ - virtual void timeout_event(StageEvent *event, CallbackContext *context) - { - LOG_INFO("get a timed out evnet in %s timeout_event\n", stage_name_); - this->callback_event(event, context); - } - -protected: - /** - * Constructor - * @param[in] tag The label that identifies this stage. - * - * @pre tag is non-null and points to null-terminated string - * @post event queue is empty - * @post stage is not connected - */ - Stage(const char *tag); - - /** - * Remove an event from the queue. Called only by service thread. - * - * @pre queue not empty - * @return first event on queue. - * @post first event on queue is removed from queue. - */ - StageEvent *remove_event(); - - /** - * Release event reference on stage. Called only by service thread. - * - * @post event ref count on stage is decremented - */ - void release_event(); - - /** - * Initialize stage params and validate outputs - * Validate the next_stage_list_ according to the requirements of the - * specific stage. Initialize stage specific params. Pure virtual - * member function. - * - * @pre Stage not connected - * @return TRUE if and only if outputs are valid and init succeeded. - */ - virtual bool initialize() { return true; } - - /** - * set properties for this object - * @pre class members are uninitialized - * @post initializing the class members - * @return Stage instantiated object - */ - virtual bool set_properties() { return true; } - - /** - * Prepare to disconnect the stage. - * This function is called to allow a stage to perform - * stage-specific actions in preparation for disconnecting it - * from the pipeline. Most stages will not need to implement - * this function. - */ - virtual void disconnect_prepare() { return; } - - /** - * Cleanup stage after disconnection - * After disconnection is completed, cleanup any resources held by the - * stage and prepare for destruction or re-initialization. - */ - virtual void cleanup() { return; } - - // pipeline state - std::list next_stage_list_; // next stage(s) in the pipeline - - // implementation state - char *stage_name_; // name of stage - - friend class Threadpool; - -private: - std::deque event_list_; // event queue - mutable pthread_mutex_t list_mutex_; // protects the event queue - pthread_cond_t disconnect_cond_; // wait here for disconnect - bool connected_; // is stage connected to pool? - unsigned long event_ref_; // # of outstanding events - Threadpool *th_pool_ = nullptr; // Threadpool for this stage -}; - -inline void Stage::set_pool(Threadpool *th) -{ - ASSERT((th != NULL), "threadpool not available for stage %s", this->get_name()); - ASSERT(!connected_, "attempt to set threadpool while connected: %s", this->get_name()); - th_pool_ = th; -} - -inline void Stage::push_stage(Stage *st) -{ - ASSERT((st != NULL), "next stage not available for stage %s", this->get_name()); - ASSERT(!connected_, "attempt to set push stage while connected: %s", this->get_name()); - next_stage_list_.push_back(st); -} - -inline const char *Stage::get_name() { return stage_name_; } - -} // namespace common -#endif // __COMMON_SEDA_STAGE_H__ diff --git a/deps/common/seda/stage_event.cpp b/deps/common/seda/stage_event.cpp deleted file mode 100644 index 8704b7d5b..000000000 --- a/deps/common/seda/stage_event.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -// Include Files -#include "common/seda/stage_event.h" - -#include -#include - -#include "common/defs.h" -#include "common/lang/mutex.h" -#include "common/log/log.h" -#include "common/seda/callback.h" -#include "common/time/timeout_info.h" -namespace common { - -// Constructor -StageEvent::StageEvent() : comp_cb_(NULL), ud_(NULL), cb_flag_(false), history_(NULL), stage_hops_(0), tm_info_(NULL) {} - -// Destructor -StageEvent::~StageEvent() -{ - // clear all pending callbacks - while (comp_cb_) { - CompletionCallback *top = comp_cb_; - comp_cb_ = comp_cb_->pop_callback(); - delete top; - } - - // delete the history_ if present - if (history_) { - history_->clear(); - delete history_; - } - - if (tm_info_) { - tm_info_->detach(); - tm_info_ = NULL; - } -} - -// Processing for this event is done; callbacks executed -void StageEvent::done() -{ - CompletionCallback *top; - - if (comp_cb_) { - top = comp_cb_; - mark_callback(); - top->event_reschedule(this); - } else { - delete this; - } -} - -// Processing for this event is done; callbacks executed immediately -void StageEvent::done_immediate() -{ - CompletionCallback *top; - - if (comp_cb_) { - top = comp_cb_; - clear_callback(); - comp_cb_ = comp_cb_->pop_callback(); - top->event_done(this); - delete top; - } else { - delete this; - } -} - -void StageEvent::done_timeout() -{ - CompletionCallback *top; - - if (comp_cb_) { - top = comp_cb_; - clear_callback(); - comp_cb_ = comp_cb_->pop_callback(); - top->event_timeout(this); - delete top; - } else { - delete this; - } -} - -// Push the completion callback onto the stack -void StageEvent::push_callback(CompletionCallback *cb) -{ - cb->push_callback(comp_cb_); - comp_cb_ = cb; -} - -void StageEvent::set_user_data(UserData *u) -{ - ud_ = u; - return; -} - -UserData *StageEvent::get_user_data() { return ud_; } - -// Add stage to list of stages which have handled this event -void StageEvent::save_stage(Stage *stg, HistType type) -{ - if (!history_) { - history_ = new std::list; - } - if (history_) { - history_->push_back(std::make_pair(stg, type)); - stage_hops_++; - ASSERT(stage_hops_ <= get_max_event_hops(), "Event exceeded max hops"); - } -} - -void StageEvent::set_timeout_info(time_t deadline) -{ - TimeoutInfo *tmi = new TimeoutInfo(deadline); - set_timeout_info(tmi); -} - -void StageEvent::set_timeout_info(const StageEvent &ev) { set_timeout_info(ev.tm_info_); } - -void StageEvent::set_timeout_info(TimeoutInfo *tmi) -{ - // release the previous timeout info - if (tm_info_) { - tm_info_->detach(); - } - - tm_info_ = tmi; - if (tm_info_) { - tm_info_->attach(); - } -} - -bool StageEvent::has_timed_out() -{ - if (!tm_info_) { - return false; - } - - return tm_info_->has_timed_out(); -} - -// Accessor function which wraps value for max hops an event is allowed -uint32_t &get_max_event_hops() -{ - static uint32_t max_event_hops = 0; - return max_event_hops; -} - -// Accessor function which wraps value for event history flag -bool &get_event_history_flag() -{ - static bool event_history_flag = false; - return event_history_flag; -} - -} // namespace common \ No newline at end of file diff --git a/deps/common/seda/stage_event.h b/deps/common/seda/stage_event.h deleted file mode 100644 index 8fd1b4de7..000000000 --- a/deps/common/seda/stage_event.h +++ /dev/null @@ -1,173 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// -#pragma once - -// Include Files -#include -#include -#include -#include - -#include "common/defs.h" -namespace common { - -class CompletionCallback; -class UserData; -class Stage; -class TimeoutInfo; - -//! An event in a staged event-driven architecture -/** - * Abstract base class for all events. Each event can reference - * a stack of completion callbacks. A callback is added to an event using - * the push_callback() method. The first completion callback on the stack - * is invoked by calling the done() method of StageEvent. The Stage code - * handling an event (either in handle_event() or callback_event()) has - * certain responsibilities regarding how the event and its associated - * callbacks are processed. When a stage finishes processing an - * event it has the following options: - *

    - *
  • Pass the event along to another stage for processing. In this - * case the responsibility to eventually call done() passes to the next - * stage. - *
  • Pass the event along to another stage for processing but add a - * callback to the event for the current stage. In this case, the stage - * must create a callback object and use the push_callback() interface - * to add the callback to the top of the event's callback stack. Again, - * the responsibility for calling done() passes to the next stage. - *
  • Dispose of the event. This is achieved by calling done(). After - * calling done() the stage must not access the event again. Note that - * done() will result in callbacks attached to the event being executed - * asynchronously by the threadpool of the stage which set the callback. - * Calling done_immediate() has the same effect as done(), except that the - * callbacks are executed on the current stack. - *
- */ - -class StageEvent -{ - -public: - // Interface for collecting debugging information - typedef enum - { - HANDLE_EV = 0, - CALLBACK_EV, - TIMEOUT_EV - } HistType; - - /** - * Constructor - * Should not create StageEvents on the stack. done() assumes that - * event is dynamically allocated. - */ - StageEvent(); - - /** - * Destructor - * Should only be called from done(), or from Stage class - * during cleanup. Public for now, because constructor is public. - */ - virtual ~StageEvent(); - - // Processing for this event is done; execute callbacks - // this will trigger thread switch if there are callbacks, this will be async - // Calling done_immediate won't trigger thread switch, this will be synchonized - void done(); - - // Processing for this event is done; execute callbacks immediately - // Calling done_immediate won't trigger thread switch, this will be synchonized - void done_immediate(); - - /** - * Processing for this event is done if the event has timed out - * \c timeout_event() will be called instead of \c callback_event() - * if the event has timed out. - */ - void done_timeout(); - - // Set the completion callback - void push_callback(CompletionCallback *cb); - - /** - * Set the originating event that caused this event. - * The caller is responsible for recovering the memory associated with - * the \c UserData object. - */ - void set_user_data(UserData *u); - - // Get the originating event the caused this event. - UserData *get_user_data(); - - // True if event represents a callback - bool is_callback() { return cb_flag_; } - - // Add stage to list of stages which have handled this event - void save_stage(Stage *stg, HistType type); - - /** - * Set a timeout info into the event - * @param[in] deadline deadline of the timeout - */ - void set_timeout_info(time_t deadline); - - // Share a timeout info with another \c StageEvent - void set_timeout_info(const StageEvent &ev); - - // If the event has timed out (and should be dropped) - bool has_timed_out(); - -private: - typedef std::pair HistEntry; - - // Interface to allow callbacks to be run on target stage's threads - void mark_callback() { cb_flag_ = true; } - void clear_callback() { cb_flag_ = false; } - - // Set a timeout info into the event - void set_timeout_info(TimeoutInfo *tmi); - - CompletionCallback *comp_cb_; // completion callback stack for this event - UserData *ud_; // user data associated with event by caller - bool cb_flag_; // true if this event is a callback - std::list *history_; // List of stages which have handled ev - uint32_t stage_hops_; // Number of stages which have handled ev - TimeoutInfo *tm_info_; // the timeout info for this event -}; - -/** - * - * \brief An opaque data structure that can be associated with any event to - * maintain the state of the calling stage. - * - * Setting the \c UserData member is optional. The caller may elect to set - * the \c UserData member to maintain state across a request to another - * stage. If the \c UserData member is used, the calling state should create - * a new class derived from \c UserData to store state relevant to its - * processing. When the called stage invokes the \c CompletionCallback, the - * originating stage can access the \c UserData member to recover its state. - */ -class UserData -{ -public: - /** - * \brief A virtual destructor to enable the use of dynamic casts. - */ - virtual ~UserData() { return; } -}; - -bool &get_event_history_flag(); -uint32_t &get_max_event_hops(); - -} // namespace common diff --git a/deps/common/seda/thread_pool.cpp b/deps/common/seda/thread_pool.cpp deleted file mode 100644 index c29d5ce3c..000000000 --- a/deps/common/seda/thread_pool.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -// Include Files -#include "common/seda/thread_pool.h" - -#include - -#include "common/lang/mutex.h" -#include "common/log/log.h" -#include "common/seda/stage.h" -namespace common { - -extern bool &get_event_history_flag(); - -/** - * Constructor - * @param[in] threads The number of threads to create. - * - * @post thread pool has threads threads running - */ -Threadpool::Threadpool(unsigned int threads, const std::string &name) - : run_queue_(), - eventhist_(get_event_history_flag()), - nthreads_(0), - threads_to_kill_(0), - n_idles_(0), - killer_("KillThreads"), - name_(name) -{ - LOG_TRACE("Enter, thread number:%d", threads); - MUTEX_INIT(&run_mutex_, NULL); - COND_INIT(&run_cond_, NULL); - MUTEX_INIT(&thread_mutex_, NULL); - COND_INIT(&thread_cond_, NULL); - add_threads(threads); - LOG_TRACE("exit"); -} - -/** - * Destructor - * Kills all threads and destroys pool. - * - * @post all threads are destroyed and pool is destroyed - */ -Threadpool::~Threadpool() -{ - LOG_TRACE("%s", "enter"); - // kill all the remaining service threads - kill_threads(nthreads_); - - run_queue_.clear(); - MUTEX_DESTROY(&run_mutex_); - COND_DESTROY(&run_cond_); - MUTEX_DESTROY(&thread_mutex_); - COND_DESTROY(&thread_cond_); - LOG_TRACE("%s", "exit"); -} - -/** - * Query number of threads. - * @return number of threads in the thread pool. - */ -unsigned int Threadpool::num_threads() -{ - MUTEX_LOCK(&thread_mutex_); - unsigned int result = nthreads_; - MUTEX_UNLOCK(&thread_mutex_); - return result; -} - -/** - * Add threads to the pool - * @param[in] threads Number of threads to add to the pool. - * - * @post 0 <= (# of threads in pool) - (original # of threads in pool) - * <= threads - * @return number of thread successfully created - */ -unsigned int Threadpool::add_threads(unsigned int threads) -{ - unsigned int i; - pthread_t pthread; - pthread_attr_t pthread_attrs; - LOG_TRACE("%s adding threads enter%d", name_.c_str(), threads); - // create all threads as detached. We will not try to join them. - pthread_attr_init(&pthread_attrs); - pthread_attr_setdetachstate(&pthread_attrs, PTHREAD_CREATE_DETACHED); - - MUTEX_LOCK(&thread_mutex_); - - // attempt to start the requested number of threads - for (i = 0; i < threads; i++) { - int stat = pthread_create(&pthread, &pthread_attrs, Threadpool::run_thread, (void *)this); - if (stat != 0) { - LOG_WARN("Failed to create one thread\n"); - break; - } - } - nthreads_ += i; - MUTEX_UNLOCK(&thread_mutex_); - LOG_TRACE("%s%d", "adding threads exit", threads); - return i; -} - -/** - * Kill threads in pool - * Blocks until the requested number of threads are killed. Won't - * kill more than current number of threads. - * - * @param[in] threads Number of threads to kill. - * - * @post (original # of threads in pool) - (# of threads in pool) - * <= threads - * @return number of threads successfully killed. - */ -unsigned int Threadpool::kill_threads(unsigned int threads) -{ - LOG_TRACE("%s%d", "enter - threads to kill", threads); - MUTEX_LOCK(&thread_mutex_); - - // allow only one thread kill transaction at a time - if (threads_to_kill_ > 0) { - MUTEX_UNLOCK(&thread_mutex_); - return 0; - } - - // check the limit - if (threads > nthreads_) { - threads = nthreads_; - } - - // connect the kill thread stage to this pool - killer_.set_pool(this); - killer_.connect(); - - // generate an appropriate number of kill thread events... - int i = gen_kill_thread_events(threads); - - // set the counter and wait for events to be picked up. - threads_to_kill_ = i; - COND_WAIT(&thread_cond_, &thread_mutex_); - - killer_.disconnect(); - - MUTEX_UNLOCK(&thread_mutex_); - LOG_TRACE("%s", "exit"); - return i; -} - -/** - * Internal thread kill. - * Internal operation called only when a thread kill event is processed. - * Reduces the count of active threads, and, if this is the last pending - * kill, signals the waiting kill_threads method. - */ -void Threadpool::thread_kill() -{ - MUTEX_LOCK(&thread_mutex_); - - nthreads_--; - threads_to_kill_--; - if (threads_to_kill_ == 0) { - // signal the condition, in case someone is waiting there... - COND_SIGNAL(&thread_cond_); - } - - MUTEX_UNLOCK(&thread_mutex_); -} - -/** - * Internal generate kill thread events - * Internal operation called by kill_threads(). Generates the requested - * number of kill thread events and schedules them. - * - * @pre thread mutex is locked. - * @pre to_kill <= current number of threads - * @return number of kill thread events successfully scheduled - */ -unsigned int Threadpool::gen_kill_thread_events(unsigned int to_kill) -{ - LOG_TRACE("%s%d", "enter", to_kill); - assert(MUTEX_TRYLOCK(&thread_mutex_) != 0); - assert(to_kill <= nthreads_); - - unsigned int i; - for (i = 0; i < to_kill; i++) { - - // allocate kill thread event and put it on the list... - StageEvent *sevent = new StageEvent(); - if (sevent == NULL) { - break; - } - killer_.add_event(sevent); - } - LOG_TRACE("%s%d", "exit", to_kill); - return i; -} - -/** - * Schedule stage with some work - * Schedule a stage with some work to be done on the run queue. - * - * @param[in] stage Reference to stage to be scheduled. - * - * @pre stage must have a non-empty queue. - * @post stage is scheduled on the run queue. - */ -void Threadpool::schedule(Stage *stage) -{ - assert(!stage->qempty()); - - MUTEX_LOCK(&run_mutex_); - bool was_empty = run_queue_.empty(); - run_queue_.push_back(stage); - // let current thread continue to run the target stage if there is - // only one event and the target stage is in the same thread pool - if (was_empty == false || this != get_thread_pool_ptr()) { - // wake up if there is idle thread - if (n_idles_ > 0) { - COND_SIGNAL(&run_cond_); - } - } - MUTEX_UNLOCK(&run_mutex_); -} - -// Get name of thread pool -const std::string &Threadpool::get_name() { return name_; } - -/** - * Internal thread control function - * Function which contains the control loop for each service thread. - * Should not be called except when a thread is created. - */ -void *Threadpool::run_thread(void *pool_ptr) -{ - Threadpool *pool = (Threadpool *)pool_ptr; - - // save thread pool pointer - set_thread_pool_ptr(pool); - - // this is not portable, but is easier to map to LWP - int64_t threadid = gettid(); - LOG_INFO("threadid = %llx, threadname = %s", threadid, pool->get_name().c_str()); -#ifdef __APPLE__ - pthread_setname_np(pool->get_name().c_str()); -#else - pthread_setname_np(pthread_self(), pool->get_name().c_str()); -#endif - - // enter a loop where we continuously look for events from Stages on - // the run_queue_ and handle the event. - while (1) { - MUTEX_LOCK(&(pool->run_mutex_)); - - // wait for some stage to be scheduled - while (pool->run_queue_.empty()) { - (pool->n_idles_)++; - COND_WAIT(&(pool->run_cond_), &(pool->run_mutex_)); - (pool->n_idles_)--; - } - - assert(!pool->run_queue_.empty()); - Stage *run_stage = *(pool->run_queue_.begin()); - pool->run_queue_.pop_front(); - MUTEX_UNLOCK(&(pool->run_mutex_)); - - StageEvent *event = run_stage->remove_event(); - - // need to check if this is a rescheduled callback - if (event->is_callback()) { -#ifdef ENABLE_STAGE_LEVEL_TIMEOUT - // check if the event has timed out. - if (event->has_timed_out()) { - event->done_timeout(); - } else { - event->done_immediate(); - } -#else - event->done_immediate(); -#endif - } else { - if (pool->eventhist_) { - event->save_stage(run_stage, StageEvent::HANDLE_EV); - } - -#ifdef ENABLE_STAGE_LEVEL_TIMEOUT - // check if the event has timed out - if (event->has_timed_out()) { - event->done(); - } else { - run_stage->handle_event(event); - } -#else - run_stage->handle_event(event); -#endif - } - run_stage->release_event(); - } - LOG_TRACE("exit %p", pool_ptr); - LOG_INFO("Begin to exit, threadid = %llx, threadname = %s", threadid, pool->get_name().c_str()); - - // the dummy compiler need this - pthread_exit(NULL); -} - -pthread_key_t Threadpool::pool_ptr_key_; - -void Threadpool::create_pool_key() -{ - // init the thread specific to store thread pool pointer - // this is called in main thread, so no pthread_once is needed - pthread_key_create(&pool_ptr_key_, NULL); -} - -void Threadpool::del_pool_key() { pthread_key_delete(pool_ptr_key_); } - -void Threadpool::set_thread_pool_ptr(const Threadpool *thd_Pool) { pthread_setspecific(pool_ptr_key_, thd_Pool); } - -const Threadpool *Threadpool::get_thread_pool_ptr() { return (const Threadpool *)pthread_getspecific(pool_ptr_key_); } - -} // namespace common diff --git a/deps/common/seda/thread_pool.h b/deps/common/seda/thread_pool.h deleted file mode 100644 index 671e05108..000000000 --- a/deps/common/seda/thread_pool.h +++ /dev/null @@ -1,170 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -#ifndef __COMMON_SEDA_THREAD_POOL_H__ -#define __COMMON_SEDA_THREAD_POOL_H__ - -#include - -#include "common/defs.h" -#include "common/seda/kill_thread.h" -namespace common { - -class Stage; - -/** - * A thread pool for one or more seda stages - * The Threadpool class consists of a pool of worker threads and a - * scheduling queue of active seda Stages that have events that need - * processing. Each thread in the thread pool constantly examines the - * head of the scheduling queue for a scheduled stage. It then removes - * the scheduled stage from the queue, and selects an event from the - * Stage's event queue for processing. The thread then processes the event - * using the Stage's handle_event() member function before starting the - * process over. If the thread finds the scheduling queue empty, it sleeps - * on a condition waiting for Stages to schedule themselves. - *

- * The number of threads in the pool can be controlled by clients. On - * creation, the caller provides a parameter indicating the initial number - * of worker threads, but this number can be adjusted at any time by using - * the add_threads(), num_threads(), and kill_threads() interfaces. - */ -class Threadpool -{ - -public: - // Initialize the static data structures of ThreadPool - static void create_pool_key(); - - // Finalize the static data structures of ThreadPool - static void del_pool_key(); - - /** - * Constructor - * @param[in] threads The number of threads to create. - * @param[in] name Name of the thread pool. - * - * @post thread pool has threads threads running - */ - Threadpool(unsigned int threads, const std::string &name = std::string()); - - /** - * Destructor - * Kills all threads and destroys pool. - * - * @post all threads are destroyed and pool is destroyed - */ - virtual ~Threadpool(); - - /** - * Query number of threads. - * @return number of threads in the thread pool. - */ - unsigned int num_threads(); - - /** - * Add threads to the pool - * @param[in] threads Number of threads to add to the pool. - * - * @post 0 <= (# of threads in pool) - (original # of threads in pool) - * <= threads - * @return number of thread successfully created - */ - unsigned int add_threads(unsigned int threads); - - /** - * Kill threads in pool - * Blocks until the requested number of threads are killed. Won't - * kill more than current number of threads. - * - * @param[in] threads Number of threads to kill. - * - * @post (original # of threads in pool) - (# of threads in pool) - * <= threads - * @return number of threads successfully killed. - */ - unsigned int kill_threads(unsigned int threads); - - /** - * Schedule stage with some work - * Schedule a stage with some work to be done on the run queue. - * - * @param[in] stage Reference to stage to be scheduled. - * - * @pre stage must have a non-empty queue. - * @post stage is scheduled on the run queue. - */ - void schedule(Stage *stage); - - // Get name of thread pool - const std::string &get_name(); - -protected: - /** - * Internal thread kill. - * Internal operation called only when a thread kill event is processed. - * Reduces the count of active threads, and, if this is the last pending - * kill, signals the waiting kill_threads method. - */ - void thread_kill(); - - /** - * Internal generate kill thread events - * Internal operation called by kill_threads(). Generates the requested - * number of kill thread events and schedules them. - * - * @pre thread mutex is locked. - * @pre to_kill <= current number of threads - * @return number of kill thread events successfully scheduled - */ - unsigned int gen_kill_thread_events(unsigned int to_kill); - -private: - /** - * Internal thread control function - * Function which contains the control loop for each service thread. - * Should not be called except when a thread is created. - */ - static void *run_thread(void *pool_ptr); - - // Save the thread pool pointer for this thread - static void set_thread_pool_ptr(const Threadpool *thd_pool); - - // Get the thread pool pointer for this thread - static const Threadpool *get_thread_pool_ptr(); - - // run queue state - pthread_mutex_t run_mutex_; //< protects the run queue - pthread_cond_t run_cond_; //< wait here for stage to be scheduled - std::deque run_queue_; //< list of stages with work to do - bool eventhist_; //< is event history enabled? - - // thread state - pthread_mutex_t thread_mutex_; //< protects thread state - pthread_cond_t thread_cond_; //< wait here when killing threads - unsigned int nthreads_; //< number of service threads - unsigned int threads_to_kill_; //< number of pending kill events - unsigned int n_idles_; //< number of idle threads - KillThreadStage killer_; //< used to kill threads - std::string name_; //< name of threadpool - - // key of thread specific to store thread pool pointer - static pthread_key_t pool_ptr_key_; - - // allow KillThreadStage to kill threads - friend class KillThreadStage; -}; - -} // namespace common -#endif // __COMMON_SEDA_THREAD_POOL_H__ diff --git a/deps/common/seda/timer_stage.cpp b/deps/common/seda/timer_stage.cpp deleted file mode 100644 index 19f3f587c..000000000 --- a/deps/common/seda/timer_stage.cpp +++ /dev/null @@ -1,483 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -#include "common/seda/timer_stage.h" - -#include -#include - -#include -#include -#include -#include - -#include "common/lang/mutex.h" -#include "common/log/log.h" -namespace common { - -#define TIMEVAL_EQUAL(t1, t2) ((t1.tv_sec == t2.tv_sec) && (t1.tv_usec == t2.tv_usec)) -#define TIMEVAL_LESS_THAN(t1, t2) ((t1.tv_sec < t2.tv_sec) || ((t1.tv_sec == t2.tv_sec) && (t1.tv_usec < t2.tv_usec))) - -struct timeval sub_timeval(const struct timeval *t1, const struct timeval *t2) -{ - struct timeval result; - result.tv_sec = t1->tv_sec - t2->tv_sec; - result.tv_usec = t1->tv_usec - t2->tv_usec; - while (result.tv_usec < 0) { - --result.tv_sec; - result.tv_usec += USEC_PER_SEC; - } - return result; -} - -struct timeval add_timeval(const struct timeval *t1, const struct timeval *t2) -{ - struct timeval result; - result.tv_sec = t1->tv_sec + t2->tv_sec; - result.tv_usec = t1->tv_usec + t2->tv_usec; - while (result.tv_usec >= USEC_PER_SEC) { - ++result.tv_sec; - result.tv_usec -= USEC_PER_SEC; - } - return result; -} - -void realtime_to_monotonic(const struct timeval *time_RT, struct timeval *time_Mono) -{ - - struct timeval time_now; - gettimeofday(&time_now, NULL); - struct timeval time_offset; - time_offset = sub_timeval(time_RT, &time_now); - - struct timespec time_ts; - clock_gettime(CLOCK_MONOTONIC, &time_ts); - - struct timeval time_temp; - time_temp.tv_sec = time_ts.tv_sec; - time_temp.tv_usec = time_ts.tv_nsec / NSEC_PER_USEC; - time_temp = add_timeval(&time_temp, &time_offset); - - time_Mono->tv_sec = time_temp.tv_sec; - time_Mono->tv_usec = time_temp.tv_usec; -} - -uint64_t TimerToken::next_nonce() -{ - static uint64_t nonce_cntr = 0; - static pthread_mutex_t tt_mutex = PTHREAD_MUTEX_INITIALIZER; - - pthread_mutex_lock(&tt_mutex); - uint64_t n = nonce_cntr++; - pthread_mutex_unlock(&tt_mutex); - - return n; -} - -TimerToken::TimerToken() -{ - struct timeval t; - memset(&t, 0, sizeof(struct timeval)); - uint64_t n = next_nonce(); - set(t, n); - return; -} - -TimerToken::TimerToken(const struct timeval &t) -{ - uint64_t n = next_nonce(); - set(t, n); - return; -} - -TimerToken::TimerToken(const TimerToken &tt) -{ - set(tt.time, tt.nonce); - return; -} - -void TimerToken::set(const struct timeval &t, uint64_t n) -{ - memcpy(&time, &t, sizeof(struct timeval)); - nonce = n; - return; -} - -const struct timeval &TimerToken::get_time() const { return time; } - -uint64_t TimerToken::get_nonce() const { return nonce; } - -bool TimerToken::operator<(const TimerToken &other) const -{ - if (TIMEVAL_LESS_THAN(time, other.time)) - return true; - if (TIMEVAL_EQUAL(time, other.time)) - return (nonce < other.nonce); - return false; -} - -TimerToken &TimerToken::operator=(const TimerToken &src) -{ - set(src.time, src.nonce); - return *this; -} - -std::string TimerToken::to_string() const -{ - std::string s; - std::ostringstream ss(s); - ss << time.tv_sec << ":" << time.tv_usec << "-" << nonce; - return ss.str(); -} - -TimerRegisterEvent::TimerRegisterEvent(StageEvent *cb, uint64_t time_relative_usec) - : TimerEvent(), timer_cb_(cb), token_() -{ - struct timespec timer_spec; - clock_gettime(CLOCK_MONOTONIC, &timer_spec); - - timer_when_.tv_sec = timer_spec.tv_sec; - timer_when_.tv_usec = (timer_spec.tv_nsec / NSEC_PER_USEC); - - timer_when_.tv_usec += time_relative_usec; - if (timer_when_.tv_usec >= USEC_PER_SEC) { - timer_when_.tv_sec += (timer_when_.tv_usec / USEC_PER_SEC); - timer_when_.tv_usec = (timer_when_.tv_usec % USEC_PER_SEC); - } - - return; -} - -TimerRegisterEvent::TimerRegisterEvent(StageEvent *cb, struct timeval &time_absolute) - : TimerEvent(), timer_cb_(cb), token_() -{ - realtime_to_monotonic(&time_absolute, &timer_when_); - return; -} - -TimerRegisterEvent::~TimerRegisterEvent() { return; } - -const struct timeval &TimerRegisterEvent::get_time() { return timer_when_; } - -StageEvent *TimerRegisterEvent::get_callback_event() { return timer_cb_; } - -StageEvent *TimerRegisterEvent::adopt_callback_event() -{ - StageEvent *e = timer_cb_; - timer_cb_ = NULL; - return e; -} - -void TimerRegisterEvent::set_cancel_token(const TimerToken &t) -{ - token_ = t; - return; -} - -std::unique_ptr TimerRegisterEvent::get_cancel_token() -{ - const TimerToken *token_cp = new TimerToken(token_); - std::unique_ptr token_ptr(token_cp); - return token_ptr; -} - -TimerCancelEvent::TimerCancelEvent(const TimerToken &cancel_token) - : TimerEvent(), token_(cancel_token), cancelled_(false) -{ - return; -} - -TimerCancelEvent::~TimerCancelEvent() { return; } - -const TimerToken &TimerCancelEvent::get_token() { return token_; } - -void TimerCancelEvent::set_success(bool s) -{ - cancelled_ = s; - return; -} - -bool TimerCancelEvent::get_success() { return cancelled_; } - -TimerStage::TimerStage(const char *tag) - : Stage(tag), - timer_queue_(&TimerStage::timer_token_less_than), - shutdown_(false), - num_events_(0), - timer_thread_id_(0) -{ - pthread_mutex_init(&timer_mutex_, NULL); - pthread_condattr_t condattr; - pthread_condattr_init(&condattr); -#ifdef LINUX - pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC); -#endif - pthread_cond_init(&timer_condv_, &condattr); - pthread_condattr_destroy(&condattr); - return; -} - -TimerStage::~TimerStage() -{ - for (timer_queue_t::iterator i = timer_queue_.begin(); i != timer_queue_.end(); ++i) { - delete i->second; - } - - num_events_ = 0; - - pthread_mutex_destroy(&timer_mutex_); - pthread_cond_destroy(&timer_condv_); - - return; -} - -Stage *TimerStage::make_stage(const std::string &tag) -{ - TimerStage *s = new TimerStage(tag.c_str()); - ASSERT(s != NULL, "Failed to instantiate stage."); - if (!s->set_properties()) { - LOG_PANIC("failed to set properties.\n"); - delete s; - s = NULL; - } - - return s; -} - -bool TimerStage::set_properties() -{ - // No configuration is stored in the system properties. - return true; -} - -bool TimerStage::initialize() -{ - // The TimerStage does not send messages to any other stage. - ASSERT(next_stage_list_.size() == 0, "Invalid NextStages list."); - - // Start the thread to maintain the timer - const pthread_attr_t *thread_attrs = NULL; - void *thread_args = (void *)this; - int status = pthread_create(&timer_thread_id_, thread_attrs, &TimerStage::start_timer_thread, thread_args); - if (status != 0) - LOG_ERROR("failed to create timer thread: status=%d\n", status); - else -#ifdef __APPLE__ - pthread_setname_np("TimerStage"); -#else - pthread_setname_np(timer_thread_id_, "TimerStage"); -#endif - - return (status == 0); -} - -uint32_t TimerStage::get_num_events() { return num_events_; } - -void TimerStage::disconnect_prepare() -{ - LOG_INFO("received signal to initiate shutdown_.\n"); - pthread_mutex_lock(&timer_mutex_); - shutdown_ = true; - pthread_cond_signal(&timer_condv_); - pthread_mutex_unlock(&timer_mutex_); - - LOG_TRACE("waiting for timer maintenance thread to terminate.\n"); - void **return_val_ptr = NULL; - int status; - status = pthread_join(timer_thread_id_, return_val_ptr); - LOG_TRACE("timer maintenance thread terminated: status=%d\n", status); - - return; -} - -void TimerStage::handle_event(StageEvent *event) -{ - TimerEvent *e = dynamic_cast(event); - if (e == NULL) { - LOG_WARN("received event of unexpected type: typeid=%s\n", typeid(*event).name()); - return; // !!! EARLY EXIT !!! - } - - TimerRegisterEvent *register_ev = dynamic_cast(event); - if (register_ev != NULL) { - register_timer(*register_ev); - return; // !!! EARLY EXIT !!! - } - - TimerCancelEvent *cancel_ev = dynamic_cast(event); - if (cancel_ev != NULL) { - cancel_timer(*cancel_ev); - return; // !!! EARLY EXIT !!! - } - - return; -} - -void TimerStage::callback_event(StageEvent *e, CallbackContext *ctx) { return; } - -void TimerStage::register_timer(TimerRegisterEvent ®_ev) -{ - const TimerToken tt(reg_ev.get_time()); - - LOG_TRACE("registering event: token=%s\n", tt.to_string().c_str()); - - bool check_timer = false; - pthread_mutex_lock(&timer_mutex_); - - // add the event to the timer queue - StageEvent *timer_cb = reg_ev.adopt_callback_event(); - std::pair result = timer_queue_.insert(std::make_pair(tt, timer_cb)); - ASSERT(result.second, - "Internal error--" - "failed to register timer because token is not unique."); - ++num_events_; - - // if event was added to the head of queue, schedule a timer check - if (result.first == timer_queue_.begin()) - check_timer = true; - - pthread_mutex_unlock(&timer_mutex_); - - reg_ev.set_cancel_token(tt); - reg_ev.done(); - - if (check_timer) - trigger_timer_check(); - - return; -} - -void TimerStage::cancel_timer(TimerCancelEvent &cancel_ev) -{ - pthread_mutex_lock(&timer_mutex_); - bool success = false; - timer_queue_t::iterator it = timer_queue_.find(cancel_ev.get_token()); - if (it != timer_queue_.end()) { - success = true; - - // delete the canceled timer event - delete it->second; - - timer_queue_.erase(it); - --num_events_; - } - pthread_mutex_unlock(&timer_mutex_); - - LOG_DEBUG("cancelling event: token=%s, success=%d\n", cancel_ev.get_token().to_string().c_str(), (int)success); - - cancel_ev.set_success(success); - cancel_ev.done(); - - return; -} - -void TimerStage::trigger_timer_check() -{ - LOG_TRACE("signaling timer thread to complete timer check\n"); - - pthread_mutex_lock(&timer_mutex_); - pthread_cond_signal(&timer_condv_); - pthread_mutex_unlock(&timer_mutex_); - - return; -} - -void *TimerStage::start_timer_thread(void *arg) -{ - TimerStage *tstage = static_cast(arg); - ASSERT(tstage != NULL, "Internal error--failed to start timer thread."); - tstage->check_timer(); - return NULL; -} - -void TimerStage::check_timer() -{ - pthread_mutex_lock(&timer_mutex_); - - while (true) { - struct timespec ts_now; - clock_gettime(CLOCK_MONOTONIC, &ts_now); - - struct timeval now; - now.tv_sec = ts_now.tv_sec; - now.tv_usec = ts_now.tv_nsec / NSEC_PER_USEC; - - // LOG_TRACE("checking timer: sec=%ld, usec=%ld", now.tv_sec, now.tv_usec); - - // Trigger all events for which the trigger time has already passed. - timer_queue_t::iterator first = timer_queue_.begin(); - timer_queue_t::iterator last; - std::list done_events; - for (last = first; last != timer_queue_.end(); ++last) { - if (TIMEVAL_LESS_THAN(now, last->first.get_time())) - break; - done_events.push_back(last->second); - } - timer_queue_.erase(first, last); - if (!done_events.empty()) { - // It is ok to hold the mutex while executing this loop. - // Triggering the events only enqueues the event on the - // caller's queue--it does not perform any real work. -#ifdef __MACH__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpotentially-evaluated-expression" -#endif - for (std::list::iterator i = done_events.begin(); i != done_events.end(); ++i) { - LOG_TRACE( - "triggering timer event: sec=%ld, usec=%ld, typeid=%s\n", now.tv_sec, now.tv_usec, typeid(*(*i)).name()); - (*i)->done(); - --num_events_; - } - } -#ifdef __MACH__ -#pragma GCC diagnostic pop -#endif - done_events.clear(); - - // Check if the 'shutdown' signal has been received. The - // stage must not release the mutex between this check and the - // call to wait on the condition variable. - if (shutdown_) { - LOG_INFO("received shutdown signal, abandoning timer maintenance\n"); - break; // !!! EARLY EXIT !!! - } - - // Sleep until the next service interval. - first = timer_queue_.begin(); - if (first == timer_queue_.end()) { - // If no timer events are registered, sleep indefinately. - // (When new events are registered, the condition variable - // will be signalled to allow service to resume.) - LOG_TRACE("sleeping indefinately\n"); - pthread_cond_wait(&timer_condv_, &timer_mutex_); - } else { - // If timer events are registered, sleep until the first - // event should be triggered. - struct timespec ts; - ts.tv_sec = first->first.get_time().tv_sec; - ts.tv_nsec = first->first.get_time().tv_usec * NSEC_PER_USEC; - - // LOG_TRACE("sleeping until next deadline: sec=%ld, nsec=%ld\n", ts.tv_sec, ts.tv_nsec); - pthread_cond_timedwait(&timer_condv_, &timer_mutex_, &ts); - } - } - - pthread_mutex_unlock(&timer_mutex_); - - return; -} - -bool TimerStage::timer_token_less_than(const TimerToken &tt1, const TimerToken &tt2) { return (tt1 < tt2); } - -} // namespace common diff --git a/deps/common/seda/timer_stage.h b/deps/common/seda/timer_stage.h deleted file mode 100644 index 250acd35f..000000000 --- a/deps/common/seda/timer_stage.h +++ /dev/null @@ -1,321 +0,0 @@ -/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. -miniob is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. */ - -// -// Created by Longda on 2010 -// - -#pragma once - -#include -#include -#include - -#include "common/log/log.h" -#include "common/seda/callback.h" -#include "common/seda/stage.h" -#include "common/seda/stage_event.h" -namespace common { - -#define NSEC_PER_SEC 1000000000 -#define USEC_PER_SEC 1000000 -#define NSEC_PER_USEC 1000 - -/** - * \author longda - * \date October 22, 2007 - * - * \brief A token that can be used to cancel an event that has been - * registered with the \c TimerStage. - * - * A token uniquely identifies events managed by the \c TimerStage. - * It should be considered opaque to all components other than the \c - * TimerStage. - */ -class TimerToken -{ -public: - TimerToken(); - TimerToken(const struct timeval &t); - TimerToken(const TimerToken &tt); - const struct timeval &get_time() const; - uint64_t get_nonce() const; - bool operator<(const TimerToken &other) const; - TimerToken &operator=(const TimerToken &src); - std::string to_string() const; - - friend bool timer_token_less_than(const TimerToken &tt1, const TimerToken &tt2); - -private: - void set(const struct timeval &t, uint64_t n); - static uint64_t next_nonce(); - - struct timeval time; - uint64_t nonce; -}; - -/** - * \author Longda - * \date October 22, 2007 - * - * \brief An abstract base class for all timer-related events. - */ -class TimerEvent : public StageEvent -{ -public: - TimerEvent() : StageEvent() { return; } - virtual ~TimerEvent() { return; } -}; - -/** - * \author Longda - * \date October 22, 2007 - * - * \brief An event requesting to register a timer callback with the - * \c TimerStage. - * - * This request includes an event to trigger and a time (specified in - * either relative time or absolute time) when the event should - * trigger. The event will never be triggered before the requested - * time. Depending on factors such as the load on the system and the - * number of available threads, the event may be triggered later than - * the requested time. - */ -class TimerRegisterEvent : public TimerEvent -{ -public: - /** - * \brief Create an event to request the registration of a timer - * callback using relative time. - * - * \arg cb - * The event to trigger when the timer fires. - * \arg time_relative_usec - * The amount of time (in microseconds) before the timer - * triggering the callback should fire. - */ - TimerRegisterEvent(StageEvent *cb, uint64_t time_relative_usec); - - /** - * \brief Create an event to request the registration of a timer - * callback using absolute time. - * - * \arg cb - * The event to trigger when the timer fires. - * \arg time_absolute - * The absolute time when the timer triggering the callback - * should fire. - * - * Notes: - * Change system time will affect CLOCK_REALTIME and may - * affect timers. - */ - TimerRegisterEvent(StageEvent *cb, struct timeval &time_absolute); - - /** - * \brief Destroy the event. - */ - ~TimerRegisterEvent(); - - /** - * \brief Get the opaque token that can be used later to cancel - * the timer callback created by this event. - * - * This function will copy the token and then return a pointer to - * the token. The caller should treat the value that is returned - * as opaque. The copy of the token will be destroyed - * automatically when the pointer to the token falls out of - * scope. - * - * The result of this function is undefined if it is called - * before the \c TimerStage has handled the event. - * - * \return - * A pointer to a token that can be used to cancel the timer - * callback that was created by this request. - */ - std::unique_ptr get_cancel_token(); - - /** - * \brief Get the absolute time when the callback is to be triggered. - * - * \return - * The absolute time when the callback is to be triggered. - */ - const struct timeval &get_time(); - - /** - * \brief Get the callback event to be triggered by the timer. - * - * \return - * The callback event to be invoked after the timer fires. - */ - StageEvent *get_callback_event(); - - /** - * \brief Assume responsiblity for management of callback event. - * - * The \c TimerStage will use this method to dissociate the - * callback event from the register event. The \c TimerStage - * will then hold the callback event until the appropriate timer - * fires. - */ - StageEvent *adopt_callback_event(); - - /** - * \brief Assign the token that can be used to cancel the timer - * event. - * - * \arg t - * The opaque token that the caller can use to later cancel the - * callback event. - */ - void set_cancel_token(const TimerToken &t); - -private: - StageEvent *timer_cb_; - struct timeval timer_when_; - TimerToken token_; -}; - -/** - * - * \brief An event requesting to cancel a timer callback that was - * previously registered with the \c TimerStage. - * - * The success of the cancellation request is not assured. The - * success of the request will depend on the relative ordering of - * events in the system. The \c TimerStage will report whether the - * event was cancelled successfully or not. If the \c TimerStage - * reports that the event is cancelled, it will not invoke trigger - * the associated callback event. - */ -class TimerCancelEvent : public TimerEvent -{ -public: - /** - * \brief Create an event to request the cancellation of a timer - * callback that was previously set. - * - * \arg cancel_token - * A pointer to the opaque token (obtained from \c - * TimerRegisterEvent.get_cancel_token()) that identifies the - * timer callback to be cancelled. - */ - TimerCancelEvent(const TimerToken &cancel_token); - - /** - * \brief Destroy the event. - */ - ~TimerCancelEvent(); - - /** - * \brief Report whether the event was successfully cancelled. - * - * \return - * \c true if the event was cancelled before it was triggered; - * \c false otherwise - */ - bool get_success(); - - /** - * \brief Set the status reporting whether the event was - * successfully cancelled. - * - * \arg s - * \c true if the event was successfully cancelled; \c false - * otherwise - */ - void set_success(bool s); - - /** - * \brief Get the token corresponding to the event to be cancelled. - */ - const TimerToken &get_token(); - -private: - TimerToken token_; - bool cancelled_; -}; - -/** - * - * \brief A stage that triggers event callbacks at some future time. - * - * The \c TimerStage is designed to trigger event callbacks at some - * time in the future, as requested by the user of the \c TimerStage. - * - * To request that an event be triggered in the future, the user - * should submit a \c TimerRegisterEvent that include the event to be - * triggered and the time (in either absolute or relative form) when - * it should be triggered. If the user sets the callback of the \c - * TimerRegisterEvent, the \c TimerStage will return to the user a - * token by which the registration can be cancelled. - * - * To cancel a registered timer event, the user should submit a \c - * TimerCancelEvent that includes the token returned to the user in - * the previous \c TimerRegisterEvent. If the request to cancel the - * event reaches the \c TimerStage before the event is triggered, the - * \c TimerStage will cancel the event. The \c TimerStage will - * report, via the callback of the \c TimerCancelEvent, whether the - * event was successfully cancelled. - * - * The \c TimerStage will invoke the callback of an event when the - * time requested by \c TimerRegisterEvent is reached. A callback - * will never be invoked before the requested time; the punctuality - * of the event triggering will depend on the load on the system. - * - * Implementation note: The \c TimerStage creates an internal thread - * to maintain the timer. - */ -class TimerStage : public Stage -{ -public: - ~TimerStage(); - static Stage *make_stage(const std::string &tag); - - /** - * \brief Return the number of events that have been registered - * but not yet triggered or cancelled. - */ - uint32_t get_num_events(); - -protected: - TimerStage(const char *tag); - bool set_properties(); - bool initialize(); - void handle_event(StageEvent *event); - void callback_event(StageEvent *event, CallbackContext *context); - void disconnect_prepare(); - - // For ordering the keys in the timer_queue_. - static bool timer_token_less_than(const TimerToken &tt1, const TimerToken &tt2); - -private: - void register_timer(TimerRegisterEvent ®_ev); - void cancel_timer(TimerCancelEvent &cancel_ev); - bool timeval_less_than(const struct timeval &t1, const struct timeval &t2); - void trigger_timer_check(); - void check_timer(); - - static void *start_timer_thread(void *arg); - - typedef std::map timer_queue_t; - timer_queue_t timer_queue_; - - pthread_mutex_t timer_mutex_; - pthread_cond_t timer_condv_; - - bool shutdown_; // true if stage has received the shutdown signal - uint32_t num_events_; // the number of timer events currently outstanding - pthread_t timer_thread_id_; // thread id of the timer maintenance thread -}; - -} // namespace common diff --git a/deps/common/seda/example_stage.h b/deps/common/thread/runnable.h similarity index 53% rename from deps/common/seda/example_stage.h rename to deps/common/thread/runnable.h index 6f0409497..fe9ae1c22 100644 --- a/deps/common/seda/example_stage.h +++ b/deps/common/thread/runnable.h @@ -9,29 +9,41 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. */ // -// Created by Longda on 2021/4/13. +// Created by Wangyunlai on 2023/01/11. // #pragma once -#include "common/seda/stage.h" +#include namespace common { -class ExampleStage : public Stage +/** + * @brief 可执行对象接口 + * @ingroup ThreadPool + */ +class Runnable { public: - ~ExampleStage(); - static Stage *make_stage(const std::string &tag); - -protected: - // common function - ExampleStage(const char *tag); - bool set_properties(); - - bool initialize(); - void cleanup(); - void handle_event(StageEvent *event); - void callback_event(StageEvent *event, CallbackContext *context); + Runnable() = default; + virtual ~Runnable() = default; + + virtual void run() = 0; +}; + +/** + * @brief 可执行对象适配器,方便使用lambda表达式 + * @ingroup ThreadPool + */ +class RunnableAdaptor : public Runnable +{ +public: + RunnableAdaptor(std::function callable) : callable_(callable) {} + + void run() override { callable_(); } + +private: + std::function callable_; }; + } // namespace common diff --git a/deps/common/seda/metrics_report_event.cpp b/deps/common/thread/thread_pool.h similarity index 81% rename from deps/common/seda/metrics_report_event.cpp rename to deps/common/thread/thread_pool.h index 45f3a114c..ef3716fb0 100644 --- a/deps/common/seda/metrics_report_event.cpp +++ b/deps/common/thread/thread_pool.h @@ -9,7 +9,15 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. */ // -// Created by Longda on 2021/4/20. +// Created by Wangyunlai on 2023/01/11. // -#include "metrics_report_event.h" +#pragma once + +namespace common { +class ThreadPool +{ +public: +}; + +} // namespace common \ No newline at end of file diff --git a/deps/common/thread/thread_pool_executor.cpp b/deps/common/thread/thread_pool_executor.cpp new file mode 100644 index 000000000..de6b40180 --- /dev/null +++ b/deps/common/thread/thread_pool_executor.cpp @@ -0,0 +1,220 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2023/01/11. +// + +#include + +#include "common/thread/thread_pool_executor.h" +#include "common/log/log.h" +#include "common/queue/simple_queue.h" +#include "common/thread/thread_util.h" + +using namespace std; + +namespace common { + +int ThreadPoolExecutor::init(const char *name, int core_pool_size, int max_pool_size, long keep_alive_time_ms) +{ + unique_ptr>> queue_ptr(new (nothrow) SimpleQueue>()); + return init(name, core_pool_size, max_pool_size, keep_alive_time_ms, std::move(queue_ptr)); +} + +int ThreadPoolExecutor::init(const char *name, int core_pool_size, int max_pool_size, long keep_alive_time_ms, + unique_ptr>> &&work_queue) +{ + if (state_ != State::NEW) { + LOG_ERROR("invalid state. state=%d", state_); + return -1; + } + + if (core_pool_size < 0 || max_pool_size <= 0 || core_pool_size > max_pool_size) { + LOG_ERROR("invalid argument. core_pool_size=%d, max_pool_size=%d", core_pool_size, max_pool_size); + return -1; + } + + if (name != nullptr) { + pool_name_ = name; + } + + core_pool_size_ = core_pool_size; + max_pool_size_ = max_pool_size; + keep_alive_time_ms_ = chrono::milliseconds(keep_alive_time_ms); + work_queue_ = std::move(work_queue); + + while (static_cast(threads_.size()) < core_pool_size_) { + if (create_thread(true /*core_thread*/) != 0) { + LOG_ERROR("create thread failed"); + return -1; + } + } + + state_ = State::RUNNING; + return 0; +} + +ThreadPoolExecutor::~ThreadPoolExecutor() +{ + if (state_ != State::TERMINATED) { + shutdown(); + await_termination(); + } +} + +int ThreadPoolExecutor::shutdown() +{ + if (state_ != State::RUNNING) { + return 0; + } + + state_ = State::TERMINATING; + return 0; +} + +int ThreadPoolExecutor::execute(const function &callable) +{ + unique_ptr task_ptr(new RunnableAdaptor(callable)); + return this->execute(std::move(task_ptr)); +} + +int ThreadPoolExecutor::execute(unique_ptr &&task) +{ + if (state_ != State::RUNNING) { + LOG_WARN("[%s] cannot submit task. state=%d", pool_name_.c_str(), state_); + return -1; + } + + int ret = work_queue_->push(std::move(task)); + int task_size = work_queue_->size(); + if (task_size > pool_size() - active_count()) { + extend_thread(); + } + return ret; +} + +int ThreadPoolExecutor::await_termination() +{ + if (state_ != State::TERMINATING) { + return -1; + } + + while (threads_.size() > 0) { + this_thread::sleep_for(200ms); + } + return 0; +} + +void ThreadPoolExecutor::thread_func() +{ + LOG_INFO("[%s] thread started", pool_name_.c_str()); + + int ret = thread_set_name(pool_name_.c_str()); + if (ret != 0) { + LOG_WARN("[%s] set thread name failed", pool_name_.c_str()); + } + + lock_.lock(); + auto iter = threads_.find(this_thread::get_id()); + if (iter == threads_.end()) { + LOG_WARN("[%s] cannot find thread state of %lx", pool_name_.c_str(), this_thread::get_id()); + return; + } + ThreadData &thread_data = iter->second; + lock_.unlock(); + + using Clock = chrono::steady_clock; + + chrono::time_point idle_deadline = Clock::now(); + if (!thread_data.core_thread && keep_alive_time_ms_.count() > 0) { + idle_deadline += keep_alive_time_ms_; + } + + /// 这里使用最粗暴的方式检测线程是否可以退出了 + /// 但是实际上,如果当前的线程个数比任务数要多,或者差不多,而且任务执行都很快的时候, + /// 并不需要保留这么多线程 + while (thread_data.core_thread || Clock::now() < idle_deadline) { + unique_ptr task; + + int ret = work_queue_->pop(task); + if (0 == ret && task) { + thread_data.idle = false; + ++active_count_; + task->run(); + --active_count_; + thread_data.idle = true; + ++task_count_; + + if (keep_alive_time_ms_.count() > 0) { + idle_deadline = Clock::now() + keep_alive_time_ms_; + } + } + if (state_ != State::RUNNING && work_queue_->size() == 0) { + break; + } + } + + thread_data.terminated = true; + thread_data.thread_ptr->detach(); + delete thread_data.thread_ptr; + thread_data.thread_ptr = nullptr; + + lock_.lock(); + threads_.erase(this_thread::get_id()); + lock_.unlock(); + + LOG_INFO("[%s] thread exit", pool_name_.c_str()); +} + +int ThreadPoolExecutor::create_thread(bool core_thread) +{ + lock_guard guard(lock_); + return create_thread_locked(core_thread); +} + +int ThreadPoolExecutor::create_thread_locked(bool core_thread) +{ + thread *thread_ptr = new (nothrow) thread(&ThreadPoolExecutor::thread_func, this); + if (thread_ptr == nullptr) { + LOG_ERROR("create thread failed"); + return -1; + } + + ThreadData thread_data; + thread_data.core_thread = core_thread; + thread_data.idle = true; + thread_data.terminated = false; + thread_data.thread_ptr = thread_ptr; + threads_[thread_ptr->get_id()] = thread_data; + + if (static_cast(threads_.size()) > largest_pool_size_) { + largest_pool_size_ = static_cast(threads_.size()); + } + return 0; +} + +int ThreadPoolExecutor::extend_thread() +{ + lock_guard guard(lock_); + + // 超过最大线程数,不再创建 + if (pool_size() >= max_pool_size_) { + return 0; + } + // 任务数比空闲线程数少,不创建新线程 + if (work_queue_->size() <= pool_size() - active_count()) { + return 0; + } + + return create_thread_locked(false /*core_thread*/); +} + +} // end namespace common diff --git a/deps/common/thread/thread_pool_executor.h b/deps/common/thread/thread_pool_executor.h new file mode 100644 index 000000000..659d7978e --- /dev/null +++ b/deps/common/thread/thread_pool_executor.h @@ -0,0 +1,178 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2023/01/11. +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "common/queue/queue.h" +#include "common/thread/runnable.h" + +namespace common { + +/** + * @brief 模拟java ThreadPoolExecutor 做一个简化的线程池 + * @defgroup ThreadPool + * @details 一个线程池包含一个任务队列和一组线程,当有任务提交时,线程池会从任务队列中取出任务分配给一个线程执行。 + * 这里的接口设计参考了Java的线程池ThreadPoolExecutor,但是简化了很多。 + * + * 这个线程池支持自动伸缩。 + * 线程分为两类,一类是核心线程,一类是普通线程。核心线程不会退出,普通线程会在空闲一段时间后退出。 + * 线程池有一个任务队列,收到的任务会放到任务队列中。当任务队列中任务的个数比当前线程个数多时,就会 + * 创建新的线程。 + */ +class ThreadPoolExecutor +{ +public: + ThreadPoolExecutor() = default; + virtual ~ThreadPoolExecutor(); + + /** + * @brief 初始化线程池 + * + * @param name 线程池名称 + * @param core_size 核心线程个数。核心线程不会退出 + * @param max_size 线程池最大线程个数 + * @param keep_alive_time_ms 非核心线程空闲多久后退出 + */ + int init(const char *name, int core_size, int max_size, long keep_alive_time_ms); + + /** + * @brief 初始化线程池 + * + * @param name 线程池名称 + * @param core_size 核心线程个数。核心线程不会退出 + * @param max_size 线程池最大线程个数 + * @param keep_alive_time_ms 非核心线程空闲多久后退出 + * @param work_queue 任务队列 + */ + int init(const char *name, int core_pool_size, int max_pool_size, long keep_alive_time_ms, + std::unique_ptr>> &&work_queue); + + /** + * @brief 提交一个任务,不一定可以立即执行 + * + * @param task 任务 + * @return int 成功放入队列返回0 + */ + int execute(std::unique_ptr &&task); + + /** + * @brief 提交一个任务,不一定可以立即执行 + * + * @param callable 任务 + * @return int 成功放入队列返回0 + */ + int execute(const std::function &callable); + + /** + * @brief 关闭线程池 + */ + int shutdown(); + /** + * @brief 等待线程池处理完所有任务并退出 + */ + int await_termination(); + +public: + /** + * @brief 当前活跃线程的个数,就是正在处理任务的线程个数 + */ + int active_count() const { return active_count_.load(); } + /** + * @brief 核心线程个数 + */ + int core_pool_size() const { return core_pool_size_; } + /** + * @brief 线程池中线程个数 + */ + int pool_size() const { return static_cast(threads_.size()); } + /** + * @brief 曾经达到过的最大线程个数 + */ + int largest_pool_size() const { return largest_pool_size_; } + /** + * @brief 处理过的任务个数 + */ + int64_t task_count() const { return task_count_.load(); } + +private: + /** + * @brief 创建一个线程 + * + * @param core_thread 是否是核心线程 + */ + int create_thread(bool core_thread); + /** + * @brief 创建一个线程。调用此函数前已经加锁 + * + * @param core_thread 是否是核心线程 + */ + int create_thread_locked(bool core_thread); + /** + * @brief 检测是否需要扩展线程,如果需要就扩展 + */ + int extend_thread(); + +private: + /** + * @brief 线程函数。从队列中拉任务并执行 + */ + void thread_func(); + +private: + /** + * @brief 线程池的状态 + */ + enum class State + { + NEW, //! 新建状态 + RUNNING, //! 正在运行 + TERMINATING, //! 正在停止 + TERMINATED //! 已经停止 + }; + + struct ThreadData + { + bool core_thread = false; /// 是否是核心线程 + bool idle = false; /// 是否空闲 + bool terminated = false; /// 是否已经退出 + std::thread *thread_ptr = nullptr; /// 线程指针 + }; + +private: + State state_ = State::NEW; /// 线程池状态 + + int core_pool_size_ = 0; /// 核心线程个数 + int max_pool_size_ = 0; /// 最大线程个数 + std::chrono::milliseconds keep_alive_time_ms_; /// 非核心线程空闲多久后退出 + + std::unique_ptr>> work_queue_; /// 任务队列 + + mutable std::mutex lock_; /// 保护线程池内部数据的锁 + std::map threads_; /// 线程列表 + + int largest_pool_size_ = 0; /// 历史上达到的最大的线程个数 + std::atomic task_count_ = 0; /// 处理过的任务个数 + std::atomic active_count_ = 0; /// 活跃线程个数 + std::string pool_name_; /// 线程池名称 +}; + +} // namespace common diff --git a/deps/common/seda/metrics_report_event.h b/deps/common/thread/thread_util.cpp similarity index 61% rename from deps/common/seda/metrics_report_event.h rename to deps/common/thread/thread_util.cpp index 085d44678..b167bef0c 100644 --- a/deps/common/seda/metrics_report_event.h +++ b/deps/common/thread/thread_util.cpp @@ -9,25 +9,25 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. */ // -// Created by Longda on 2021/4/20. +// Created by Wangyunlai on 2023/01/15. // -#ifndef __COMMON_SEDA_METRICS_REPORT_EVENT_H__ -#define __COMMON_SEDA_METRICS_REPORT_EVENT_H__ - -#include "common/seda/stage_event.h" +#include +#include namespace common { -class MetricsReportEvent : public StageEvent -{ -public: - MetricsReportEvent(){ - }; +int thread_set_name(const char *name) +{ + const int namelen = 16; + char buf[namelen]; + snprintf(buf, namelen, "%s", name); - ~MetricsReportEvent(){ +#ifdef __APPLE__ + return pthread_setname_np(buf); +#elif __linux__ + return pthread_setname_np(pthread_self(), buf); +#endif +} - }; -}; -} // namespace common -#endif //__COMMON_SEDA_METRICS_REPORT_EVENT_H__ +} // namespace common \ No newline at end of file diff --git a/deps/common/seda/init.h b/deps/common/thread/thread_util.h similarity index 51% rename from deps/common/seda/init.h rename to deps/common/thread/thread_util.h index a2ff7bf97..e4ea458b2 100644 --- a/deps/common/seda/init.h +++ b/deps/common/thread/thread_util.h @@ -9,32 +9,20 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. */ // -// Created by Longda on 2010 +// Created by Wangyunlai on 2023/01/15. // -#ifndef __COMMON_SEDA_INIT_H__ -#define __COMMON_SEDA_INIT_H__ +#pragma once -// Basic includes -#include -#include -#include -#include -#include -#include -#include - -#include "common/conf/ini.h" -#include "common/defs.h" -#include "common/os/process_param.h" namespace common { /** - * start the seda process, do this will trigger all threads + * @brief 设置当前线程的名字 + * @details 设置当前线程的名字可以帮助调试多线程程序,比如在gdb或者 top -H命令可以看到线程名字。 + * pthread_setname_np在Linux和Mac上实现不同。Linux上可以指定线程号设置名称,但是Mac上不行。 + * @param name 线程的名字。按照linux手册中描述,包括\0在内,不要超过16个字符 + * @return int 设置成功返回0 */ -int init_seda(ProcessParam *process_cfg); - -void cleanup_seda(); +int thread_set_name(const char *name); -} // namespace common -#endif // __COMMON_SEDA_INIT_H__ +} // namespace common \ No newline at end of file diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 7ea8f9617..1f88f6253 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -21,6 +21,7 @@ - [表达式解析](./design/miniob-sql-expression.md) - [B+树实现](./design/miniob-bplus-tree.md) - [并发B+树实现](./design/miniob-bplus-tree-concurrency.md) + - [线程池模型](./design/miniob-thread-model.md) - [Doxy代码文档](./design/doxy/html/index.html) - [OceanBase 数据库大赛](./game/introduction.md) diff --git a/docs/src/design/images/thread-model-one-thread-per-connection.png b/docs/src/design/images/thread-model-one-thread-per-connection.png new file mode 100644 index 000000000..8bd35b7bb Binary files /dev/null and b/docs/src/design/images/thread-model-one-thread-per-connection.png differ diff --git a/docs/src/design/images/thread-model-thread-pool.png b/docs/src/design/images/thread-model-thread-pool.png new file mode 100644 index 000000000..e0ecf370f Binary files /dev/null and b/docs/src/design/images/thread-model-thread-pool.png differ diff --git a/docs/src/design/introduction.md b/docs/src/design/introduction.md index f0882cdf6..f61fe476b 100644 --- a/docs/src/design/introduction.md +++ b/docs/src/design/introduction.md @@ -9,4 +9,5 @@ - [MySQL Protocol](./miniob-mysql-protocol.md) - [B+树实现](./miniob-bplus-tree.md) - [并发B+树实现](./miniob-bplus-tree-concurrency.md) +- [线程池模型](./miniob-thread-model.md) - [Doxy文档](./doxy/html/index.html) \ No newline at end of file diff --git a/docs/src/design/miniob-thread-model.md b/docs/src/design/miniob-thread-model.md new file mode 100644 index 000000000..c6851d5c0 --- /dev/null +++ b/docs/src/design/miniob-thread-model.md @@ -0,0 +1,49 @@ +> 本篇文档介绍 MiniOB 中的线程池模型。 + +# 简介 +多线程是提高系统资源利用率的一种常用手段,也是我们学习软件开发进阶的必经之路。 +MiniOB 实现了一个可扩展的线程模型,当前支持两种线程池模型: +- 一个连接一个线程; +- 一个线程池处理所有连接。 + +> 这种设计是模仿了MySQL/MariaDB的线程模型设计。 + +# 线程模型设计 + +## 命令行参数 +当前MiniOB的线程池模型通过命令行接口指定使用哪种类型: +```bash +# 一个连接一个线程(默认) +observer -T=one-thread-per-connection +# 一个线程池处理所有连接 +observer -T=java-thread-pool +``` + +ThreadHandler::create 会根据传入的名字创建对应的 ThreadHandler 对象。 + +## 线程池模型做什么 +这个模型并不负责所有的任务,只处理客户端发来的请求。包括监听客户端是否有消息到达、处理SQL请求与返回应答、关闭连接等。 + +线程模型并不负责监听新的客户端连接,这是在主线程中做的事情,参考 `NetServer::serve`。当有新的连接到达时,会调用 `ThreadHandler::new_connection`,线程模型按照自己的模型设计来处理新来的连接。 + +## 一个连接一个线程 +OneThreadPerConnectionThreadHandler 会为每个连接创建一个线程,这个线程负责监听这个连接是否有消息到达、处理SQL请求与返回应答、关闭连接等。 + +OneThreadPerConnectionThreadHandler + +## 线程池模型 +JavaThreadPoolThreadHandler 会创建一个线程池,线程池中一个线程负责监听所有连接是否有消息到达。如果有消息到达,就将这个连接对象放入线程池任务队列中,等待线程池中的线程来处理。在某个连接的任务处理完成之前,不会监听它的新消息。 + +这个线程池使用libevent实现消息监听,参考 `JavaThreadPoolThreadHandler::start`。 + +这里解释一下为什么叫做JavaThreadPoolThreadHandler,因为这个线程池的设计是参考了Java的线程池设计,但是做了简化,参考 `ThreadPoolExecutor`。 + +ThreadPoolExecutor 是一个简单的可伸缩线程池。当任务比当前空闲线程多的时候,就会扩容。当某些线程空闲时间比较久,就会自动退出。 + +JavaThreadPoolThreadHandler + + +# 参考 +- [MySQL Percona Thread Pool](https://docs.percona.com/percona-server/5.7/performance/threadpool.html#handling-of-long-network-waits) +- [MariaDB Thread Pool](https://mariadb.com/kb/en/thread-groups-in-the-unix-implementation-of-the-thread-pool/) +- [Java ThreadPoolExecutor](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html) \ No newline at end of file diff --git a/docs/src/how_to_run.md b/docs/src/how_to_run.md index 4116b95d3..4e7ce5336 100644 --- a/docs/src/how_to_run.md +++ b/docs/src/how_to_run.md @@ -57,6 +57,7 @@ bash build.sh -DCONCURRENCY=ON | -s | 服务端监听的unix socket文件。如果不指定,并且没有使用TCP或cli的方式启动,就会使用TCP的方式启动服务端。 | | -P | 使用的通讯协议。当前支持文本协议(plain,也是默认值),MySQL协议(mysql),直接交互(cli)。
使用plain协议时,请使用自带的obclient连接服务端。
使用mysql协议时,使用mariadb或mysql客户端连接。
直接交互模式(cli)不需要使用客户端连接,因此无法开启多个连接。 | | -t | 事务模型。没有事务(vacuous,默认值)和MVCC(mvcc)。 使用mvcc时一定要编译支持并发模式的代码。 | +| -T | 线程模型。一个连接一个线程(one-thread-per-connection,默认值)和一个线程池处理所有连接(java-thread-pool)。 | | -n | buffer pool 的内存大小,单位字节。 | **更多** diff --git a/etc/observer.ini b/etc/observer.ini index ea6812f20..305b55893 100644 --- a/etc/observer.ini +++ b/etc/observer.ini @@ -16,42 +16,3 @@ LOG_FILE_LEVEL=5 LOG_CONSOLE_LEVEL=1 # the module's log will output whatever level used. #DefaultLogModules="server.cpp,client.cpp" - -# seda's configuration -[SEDA_BASE] -# record every event -EventHistory=false -# max history event's number, default is 100 -MaxEventHistoryNum=100 -# threadpools' name, it will contain the threadpool's section -ThreadPools=SQLThreads,IOThreads,DefaultThreads -# stage list -STAGES=SessionStage - -[NET] -CLIENT_ADDRESS=INADDR_ANY -MAX_CONNECTION_NUM=8192 -PORT=6789 - -[SQLThreads] -# the thread number of this threadpool, 0 means cpu's cores. -# if miss the setting of count, it will use cpu's core number; -count=3 -#count=0 - -[IOThreads] -# the thread number of this threadpool, 0 means cpu's cores. -# if miss the setting of count, it will use cpu's core number; -count=3 -#count=0 - -[DefaultThreads] -# If Stage haven't set threadpool, it will use this threadpool -# This threadpool is used for backend operation, such as timer, sedastats and so on. -# the thread number of this threadpool, 0 means cpu's cores. -# if miss the setting of count, it will use cpu's core number; -count=3 -#count=0 - -[SessionStage] -ThreadId=SQLThreads diff --git a/src/observer/common/init.cpp b/src/observer/common/init.cpp index 4d4c1dfdc..ef25a8ef8 100644 --- a/src/observer/common/init.cpp +++ b/src/observer/common/init.cpp @@ -21,8 +21,6 @@ See the Mulan PSL v2 for more details. */ #include "common/os/pidfile.h" #include "common/os/process.h" #include "common/os/signal.h" -#include "common/seda/init.h" -#include "common/seda/stage_factory.h" #include "global_context.h" #include "session/session.h" #include "session/session_stage.h" @@ -133,7 +131,6 @@ void cleanup_log() int prepare_init_seda() { - static StageFactory session_stage_factory("SessionStage", &SessionStage::make_stage); return 0; } @@ -226,16 +223,6 @@ int init(ProcessParam *process_param) return rc; } - // seda is used for backend async event handler - // the latency of seda is slow, it isn't used for critical latency - // environment. - prepare_init_seda(); - rc = init_seda(process_param); - if (rc) { - LOG_ERROR("Failed to init seda configuration!"); - return rc; - } - // Block interrupt signals before creating child threads. // setSignalHandler(sig_handler); // sigset_t newSigset, oset; diff --git a/src/observer/event/session_event.h b/src/observer/event/session_event.h index 97e933d39..323a37b54 100644 --- a/src/observer/event/session_event.h +++ b/src/observer/event/session_event.h @@ -16,7 +16,6 @@ See the Mulan PSL v2 for more details. */ #include -#include "common/seda/stage_event.h" #include "event/sql_debug.h" #include "sql/executor/sql_result.h" @@ -27,7 +26,7 @@ class Communicator; * @brief 表示一个SQL请求 * */ -class SessionEvent : public common::StageEvent +class SessionEvent { public: SessionEvent(Communicator *client); diff --git a/src/observer/event/sql_event.h b/src/observer/event/sql_event.h index 6d0e0a00e..be3009f66 100644 --- a/src/observer/event/sql_event.h +++ b/src/observer/event/sql_event.h @@ -14,7 +14,6 @@ See the Mulan PSL v2 for more details. */ #pragma once -#include "common/seda/stage_event.h" #include "sql/operator/physical_operator.h" #include #include @@ -26,7 +25,7 @@ class ParsedSqlNode; /** * @brief 与SessionEvent类似,也是处理SQL请求的事件,只是用在SQL的不同阶段 */ -class SQLStageEvent : public common::StageEvent +class SQLStageEvent { public: SQLStageEvent(SessionEvent *event, const std::string &sql); diff --git a/src/observer/main.cpp b/src/observer/main.cpp index e91edbd50..091f51637 100644 --- a/src/observer/main.cpp +++ b/src/observer/main.cpp @@ -15,6 +15,18 @@ See the Mulan PSL v2 for more details. */ * Author: Longda Feng */ +/** + * @mainpage MiniOB + * + * MiniOB 是 OceanBase 与华中科技大学联合开发的、面向"零"基础同学的数据库入门学习项目。 + * + * MiniOB 设计的目标是面向在校学生、数据库从业者、爱好者,或者对基础技术有兴趣的爱好者, 整体代码量少,易于上手并学习, + * 是一个系统性的数据库学习项目。miniob 设置了一系列由浅入深的题目,以帮助同学们"零"基础入门, + * 让同学们快速了解数据库并深入学习数据库内核,期望通过相关训练之后,能够熟练掌握数据库内核模块的功能与协同关系, + * 并能够在使用数据库时,设计出高效的 SQL 。miniob 为了更好的学习数据库实现原理, + * 对诸多模块都做了简化,比如不考虑并发操作, 安全特性, 复杂的事物管理等功能。 + */ + #include #include #include @@ -24,6 +36,7 @@ See the Mulan PSL v2 for more details. */ #include "common/lang/string.h" #include "common/os/process.h" #include "common/os/signal.h" +#include "common/log/log.h" #include "net/server.h" #include "net/server_param.h" @@ -42,6 +55,7 @@ void usage() cout << "-s: use unix socket and the argument is socket address" << endl; cout << "-P: protocol. {plain(default), mysql, cli}." << endl; cout << "-t: transaction model. {vacuous(default), mvcc}." << endl; + cout << "-T: thread handling model. {one-thread-per-connection(default),java-thread-pool}." << endl; cout << "-n: buffer pool memory size in byte" << endl; } @@ -56,7 +70,7 @@ void parse_parameter(int argc, char **argv) // Process args int opt; extern char *optarg; - while ((opt = getopt(argc, argv, "dp:P:s:t:f:o:e:hn:")) > 0) { + while ((opt = getopt(argc, argv, "dp:P:s:t:T:f:o:e:hn:")) > 0) { switch (opt) { case 's': process_param->set_unix_socket_path(optarg); break; case 'p': process_param->set_server_port(atoi(optarg)); break; @@ -65,6 +79,7 @@ void parse_parameter(int argc, char **argv) case 'o': process_param->set_std_out(optarg); break; case 'e': process_param->set_std_err(optarg); break; case 't': process_param->set_trx_kit_name(optarg); break; + case 'T': process_param->set_thread_handling_name(optarg); break; case 'n': process_param->set_buffer_pool_memory_size(atoi(optarg)); break; case 'h': usage(); @@ -125,8 +140,15 @@ Server *init_server() server_param.use_unix_socket = true; server_param.unix_socket_path = process_param->get_unix_socket_path(); } + server_param.thread_handling = process_param->thread_handling_name(); + + Server *server = nullptr; + if (server_param.use_std_io) { + server = new CliServer(server_param); + } else { + server = new NetServer(server_param); + } - Server *server = new Server(server_param); return server; } @@ -146,6 +168,11 @@ void *quit_thread_func(void *_signum) } void quit_signal_handle(int signum) { + // 防止多次调用退出 + // 其实正确的处理是,应该全局性的控制来防止出现“多次”退出的状态,包括发起信号 + // 退出与进程主动退出 + set_signal_handler(nullptr); + pthread_t tid; pthread_create(&tid, nullptr, quit_thread_func, (void *)(intptr_t)signum); } @@ -154,7 +181,7 @@ int main(int argc, char **argv) { int rc = STATUS_SUCCESS; - setSignalHandler(quit_signal_handle); + set_signal_handler(quit_signal_handle); parse_parameter(argc, argv); @@ -166,7 +193,6 @@ int main(int argc, char **argv) } g_server = init_server(); - Server::init(); g_server->serve(); LOG_INFO("Server stopped"); @@ -174,16 +200,5 @@ int main(int argc, char **argv) cleanup(); delete g_server; + return 0; } - -/** - * @mainpage MiniOB - * - * MiniOB 是 OceanBase 与华中科技大学联合开发的、面向"零"基础同学的数据库入门学习项目。 - * - * MiniOB 设计的目标是面向在校学生、数据库从业者、爱好者,或者对基础技术有兴趣的爱好者, 整体代码量少,易于上手并学习, - * 是一个系统性的数据库学习项目。miniob 设置了一系列由浅入深的题目,以帮助同学们"零"基础入门, - * 让同学们快速了解数据库并深入学习数据库内核,期望通过相关训练之后,能够熟练掌握数据库内核模块的功能与协同关系, - * 并能够在使用数据库时,设计出高效的 SQL 。miniob 为了更好的学习数据库实现原理, - * 对诸多模块都做了简化,比如不考虑并发操作, 安全特性, 复杂的事物管理等功能。 - */ diff --git a/src/observer/net/cli_communicator.cpp b/src/observer/net/cli_communicator.cpp index a4e81015c..d7bb41173 100644 --- a/src/observer/net/cli_communicator.cpp +++ b/src/observer/net/cli_communicator.cpp @@ -12,9 +12,13 @@ See the Mulan PSL v2 for more details. */ // Created by Wangyunlai on 2023/06/25. // +#include +#include + #include "net/cli_communicator.h" #include "common/lang/string.h" #include "common/log/log.h" +#include "common/os/signal.h" #include "event/session_event.h" #include "net/buffered_writer.h" @@ -32,9 +36,21 @@ using namespace common; #ifdef USE_READLINE const string HISTORY_FILE = string(getenv("HOME")) + "/.miniob.history"; time_t last_history_write_time = 0; +sigjmp_buf ctrlc_buf; +bool ctrlc_flag = false; + +void handle_signals(int signo) { + if (signo == SIGINT) { + ctrlc_flag = true; + siglongjmp(ctrlc_buf, 1); + } +} char *my_readline(const char *prompt) { + static sighandler_t setup_signal_handler = signal(SIGINT, handle_signals); + (void)setup_signal_handler; + int size = history_length; if (size == 0) { read_history(HISTORY_FILE.c_str()); @@ -45,6 +61,15 @@ char *my_readline(const char *prompt) } } + while ( sigsetjmp( ctrlc_buf, 1 ) != 0 ); + + if (ctrlc_flag) { + char *line = (char *)malloc(strlen("exit") + 1); + strcpy(line, "exit"); + printf("\n"); + return line; + } + char *line = readline(prompt); if (line != nullptr && line[0] != 0) { add_history(line); @@ -89,18 +114,16 @@ char *my_readline(const char *prompt) */ bool is_exit_command(const char *cmd) { - return 0 == strncasecmp("exit", cmd, 4) || 0 == strncasecmp("bye", cmd, 3) || 0 == strncasecmp("\\q", cmd, 2) - || 0 == strncasecmp("interrupted", cmd, 11); + return 0 == strncasecmp("exit", cmd, 4) + || 0 == strncasecmp("bye", cmd, 3) + || 0 == strncasecmp("\\q", cmd, 2) + || 0 == strncasecmp("interrupted", cmd, 11); } char *read_command() { const char *prompt_str = "miniob > "; - char *input_command = nullptr; - for (input_command = my_readline(prompt_str); is_blank(input_command); input_command = my_readline(prompt_str)) { - free(input_command); - input_command = nullptr; - } + char *input_command = my_readline(prompt_str); return input_command; } @@ -132,10 +155,18 @@ RC CliCommunicator::read_event(SessionEvent *&event) { event = nullptr; char *command = read_command(); + if (nullptr == command) { + return RC::SUCCESS; + } + + if (is_blank(command)) { + free(command); + return RC::SUCCESS; + } if (is_exit_command(command)) { free(command); - event = nullptr; + exit_ = true; return RC::SUCCESS; } diff --git a/src/observer/net/cli_communicator.h b/src/observer/net/cli_communicator.h index 6ca6cc034..e0ce7f86d 100644 --- a/src/observer/net/cli_communicator.h +++ b/src/observer/net/cli_communicator.h @@ -32,6 +32,10 @@ class CliCommunicator : public PlainCommunicator RC read_event(SessionEvent *&event) override; RC write_result(SessionEvent *event, bool &need_disconnect) override; + bool exit() const { return exit_; } + private: + bool exit_ = false; ///< 是否需要退出 + int write_fd_ = -1; ///< 与使用远程通讯模式不同,如果读数据使用标准输入,那么输出应该是标准输出 }; diff --git a/src/observer/net/communicator.h b/src/observer/net/communicator.h index c2d82fd33..377627061 100644 --- a/src/observer/net/communicator.h +++ b/src/observer/net/communicator.h @@ -68,20 +68,19 @@ class Communicator */ Session *session() const { return session_; } - /** - * @brief libevent使用的数据,参考server.cpp - */ - struct event &read_event() { return read_event_; } - /** * @brief 对端地址 * 如果是unix socket,可能没有意义 */ const char *addr() const { return addr_.c_str(); } + /** + * @brief 关联的文件描述符 + */ + int fd() const { return fd_; } + protected: Session *session_ = nullptr; - struct event read_event_; std::string addr_; BufferedWriter *writer_ = nullptr; int fd_ = -1; @@ -94,7 +93,7 @@ class Communicator enum class CommunicateProtocol { PLAIN, ///< 以'\0'结尾的协议 - CLI, ///< 与客户端进行交互的协议 + CLI, ///< 与客户端进行交互的协议。CLI 不应该是一种协议,只是一种通讯的方式而已 MYSQL, ///< mysql通讯协议。具体实现参考 MysqlCommunicator }; diff --git a/src/observer/net/java_thread_pool_thread_handler.cpp b/src/observer/net/java_thread_pool_thread_handler.cpp new file mode 100644 index 000000000..9dd1957e8 --- /dev/null +++ b/src/observer/net/java_thread_pool_thread_handler.cpp @@ -0,0 +1,248 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2024/01/12. +// + +#include "event2/event.h" +#include "event2/thread.h" +#include "net/java_thread_pool_thread_handler.h" +#include "net/sql_task_handler.h" +#include "net/communicator.h" +#include "common/log/log.h" +#include "common/thread/runnable.h" +#include "common/queue/simple_queue.h" + +using namespace std; +using namespace common; + +/** + * @brief libevent 消息回调函数的参数 + * + */ +struct EventCallbackAg +{ + JavaThreadPoolThreadHandler *host = nullptr; + Communicator *communicator = nullptr; + struct event *ev = nullptr; +}; + +JavaThreadPoolThreadHandler::~JavaThreadPoolThreadHandler() +{ + this->stop(); + this->await_stop(); +} + +RC JavaThreadPoolThreadHandler::start() +{ + if (nullptr != event_base_) { + LOG_ERROR("event base has been initialized"); + return RC::INTERNAL; + } + + // 在多线程场景下使用libevent,先执行这个函数 + evthread_use_pthreads(); + // 创建一个event_base对象,这个对象是libevent的主要对象,所有的事件都会注册到这个对象中 + event_base_ = event_base_new(); + if (nullptr == event_base_) { + LOG_ERROR("failed to create event base"); + return RC::INTERNAL; + } + + // 创建线程池 + // 这里写死了线程池的大小,实际上可以从配置文件中读取 + int ret = executor_.init("SQL", // name + 2, // core size + 8, // max size + 60*1000 // keep alive time + ); + if (0 != ret) { + LOG_ERROR("failed to init thread pool executor"); + return RC::INTERNAL; + } + + // libevent 的监测消息循环主体,要放在一个线程中执行 + // event_loop_thread 是运行libevent 消息监测循环的函数,会长期运行,并且会放到线程池中占据一个线程 + auto event_worker = std::bind(&JavaThreadPoolThreadHandler::event_loop_thread, this); + ret = executor_.execute(event_worker); + if (0 != ret) { + LOG_ERROR("failed to execute event worker"); + return RC::INTERNAL; + } + + return RC::SUCCESS; +} + +/** + * @brief libevent 的消息事件回调函数 + * @details 当libevent检测到某个连接上有消息到达时,比如客户端发消息过来、客户端断开连接等,就会 + * 调用这个回调函数。 + * 按照libevent的描述,我们不应该在回调函数中执行比较耗时的操作,因为回调函数是运行在libevent的 + * 事件检测主循环中。 + * @param fd 有消息的连接的文件描述符 + * @param event 事件类型,比如EV_READ表示有消息到达,EV_CLOSED表示连接断开 + * @param arg 我们注册给libevent的回调函数的参数 + */ +static void event_callback(evutil_socket_t fd, short event, void *arg) +{ + if (event & (EV_READ | EV_CLOSED)) { + LOG_TRACE("got event. fd=%d, event=%d", fd, event); + EventCallbackAg *ag = (EventCallbackAg *)arg; + JavaThreadPoolThreadHandler *handler = ag->host; + handler->handle_event(ag); + } else { + LOG_ERROR("unexpected event. fd=%d, event=%d", fd, event); + } +} + +void JavaThreadPoolThreadHandler::handle_event(EventCallbackAg *ag) +{ + /* + 当前函数是一个libevent的回调函数。按照libevent的要求,我们不能在这个函数中执行比较耗时的操作, + 因为它是运行在libevent主消息循环处理函数中的。 + + 我们这里在收到消息时就把它放到线程池中处理。 + */ + + // sql_handler 是一个回调函数 + auto sql_handler = [this, ag]() { + RC rc = sql_task_handler_.handle_event(ag->communicator); // 这里会有接收消息、处理请求然后返回结果一条龙服务 + if (RC::SUCCESS != rc) { + LOG_WARN("failed to handle sql task. rc=%s", strrc(rc)); + this->close_connection(ag->communicator); + } else if (0 != event_add(ag->ev, nullptr)) { + // 由于我们在创建事件对象时没有增加 EV_PERSIST flag,所以我们每次都要处理完成后再把事件加回到event_base中。 + // 当然我们也不能使用 EV_PERSIST flag,否则我们在处理请求过程中,可能还会收到客户端的消息,这样就会导致并发问题。 + LOG_ERROR("failed to add event. fd=%d, communicator=%p", event_get_fd(ag->ev), this); + this->close_connection(ag->communicator); + } else { + // 添加event后就不应该再访问communicator了,因为可能会有另一个线程处理当前communicator + // 或者就需要加锁处理并发问题 + // LOG_TRACE("add event. fd=%d, communicator=%p", event_get_fd(ag->ev), this); + } + }; + + executor_.execute(sql_handler); +} + +void JavaThreadPoolThreadHandler::event_loop_thread() +{ + LOG_INFO("event base dispatch begin"); + // event_base_dispatch 也可以完成这个事情。 + // event_base_loop 会等待所有事件都结束 + // 如果不增加 EVLOOP_NO_EXIT_ON_EMPTY 标识,当前事件都处理完成后,就会退出循环 + // 加上这个标识就是即使没有事件,也等在这里 + event_base_loop(event_base_, EVLOOP_NO_EXIT_ON_EMPTY); + LOG_INFO("event base dispatch end"); +} + +RC JavaThreadPoolThreadHandler::new_connection(Communicator *communicator) +{ + int fd = communicator->fd(); + LOG_INFO("new connection. fd=%d", fd); + EventCallbackAg *ag = new EventCallbackAg; + ag->host = this; + ag->communicator = communicator; + ag->ev = nullptr; + /// 创建一个libevent事件对象。其中EV_READ表示可读事件,就是客户端发消息时会触发事件。 + /// EV_ET 表示边缘触发,有消息时只会触发一次,不会重复触发。这个标识在Linux平台上是支持的,但是有些平台不支持。 + /// 使用EV_ET边缘触发时需要注意一个问题,就是每次一定要把客户端发来的消息都读取完,直到read返回EAGAIN为止。 + /// 我们这里不使用边缘触发。 + /// 注意这里没有加 EV_PERSIST,表示事件触发后会自动从event_base中删除,需要自己再手动加上这个标识。这是有必 + /// 要的,因为客户端发出一个请求后,我们再返回客户端消息之前,不再希望接收新的消息。 + struct event *ev = event_new(event_base_, fd, EV_READ, event_callback, ag); + if (nullptr == ev) { + LOG_ERROR("failed to create event"); + return RC::INTERNAL; + } + ag->ev = ev; + + lock_.lock(); + event_map_[communicator] = ag; + lock_.unlock(); + + int ret = event_add(ev, nullptr); + if (0 != ret) { + LOG_ERROR("failed to add event. fd=%d, communicator=%p, ret=%d", fd, communicator, ret); + event_free(ev); + return RC::INTERNAL; + } + LOG_TRACE("add event success. fd=%d, communicator=%p", fd, communicator); + + return RC::SUCCESS; +} + +RC JavaThreadPoolThreadHandler::close_connection(Communicator *communicator) +{ + EventCallbackAg *ag = nullptr; + + { + lock_guard guard(lock_); + auto iter = event_map_.find(communicator); + if (iter == event_map_.end()) { + LOG_ERROR("cannot find event for communicator %p", communicator); + return RC::INTERNAL; + } + + ag = iter->second; + event_map_.erase(iter); + } + + if (ag->ev) { + event_del(ag->ev); // 把当前事件从event_base中删除 + event_free(ag->ev); // 释放event对象 + ag->ev = nullptr; + } + delete ag; + delete communicator; + + LOG_INFO("close connection. communicator = %p", communicator); + return RC::SUCCESS; +} + +RC JavaThreadPoolThreadHandler::stop() +{ + LOG_INFO("begin to stop java threadpool thread handler"); + + if (nullptr != event_base_) { + // 退出libevent的消息循环 + // 这里会一直等待libevent停止,nullptr是超时时间 + event_base_loopexit(event_base_, nullptr); + + // 停止线程池 + // libevent停止后,就不会再有新的消息到达,也不会再往线程池中添加新的任务了。 + executor_.shutdown(); + } + + LOG_INFO("end to stop java threadpool thread handler"); + return RC::SUCCESS; +} + +RC JavaThreadPoolThreadHandler::await_stop() +{ + LOG_INFO("begin to await event base stopped"); + if (nullptr != event_base_) { + // 等待线程池中所有的任务处理完成 + executor_.await_termination(); + event_base_free(event_base_); + event_base_ = nullptr; + } + + for (auto kv : event_map_) { + event_free(kv.second->ev); + delete kv.second; + delete kv.first; + } + event_map_.clear(); + + LOG_INFO("end to await event base stopped"); + return RC::SUCCESS; +} diff --git a/src/observer/net/java_thread_pool_thread_handler.h b/src/observer/net/java_thread_pool_thread_handler.h new file mode 100644 index 000000000..37652fb6a --- /dev/null +++ b/src/observer/net/java_thread_pool_thread_handler.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2024/01/11. +// + +#pragma once + +#include + +#include "net/thread_handler.h" +#include "net/sql_task_handler.h" +#include "common/thread/thread_pool_executor.h" + +struct EventCallbackAg; + +/** + * @brief 简单线程池模型。使用了模拟Java线程池接口的线程池,所以脚JavaThreadPool + * @ingroup ThreadHandler + * @details 使用线程池处理连接上的消息。使用libevent监听连接事件。 + * libevent 是一个常用并且高效的异步事件消息库,可以阅读手册了解更多。 + * [libevent 手册](https://libevent.org/doc/index.html) + */ +class JavaThreadPoolThreadHandler : public ThreadHandler +{ +public: + JavaThreadPoolThreadHandler() = default; + virtual ~JavaThreadPoolThreadHandler(); + + //! copydoc ThreadHandler::start + virtual RC start() override; + //! copydoc ThreadHandler::stop + virtual RC stop() override; + //! copydoc ThreadHandler::await_stop + virtual RC await_stop() override; + + //! copydoc ThreadHandler::new_connection + virtual RC new_connection(Communicator *communicator) override; + //! copydoc ThreadHandler::close_connection + virtual RC close_connection(Communicator *communicator) override; + +public: + /** + * @brief 使用libevent处理消息时,需要有一个回调函数,这里就相当于libevent的回调函数 + * + * @param ag 处理消息回调时的参数,比如libevent的event、连接等 + */ + void handle_event(EventCallbackAg *ag); + + /** + * @brief libevent监听连接消息事件的回调函数 + * @details 这个函数会长时间运行在线程中,并占用线程池中的一个线程 + */ + void event_loop_thread(); + +private: + std::mutex lock_; + struct event_base *event_base_ = nullptr; /// libevent 的event_base + common::ThreadPoolExecutor executor_; /// 线程池 + std::map event_map_; /// 每个连接与它关联的数据 + + SqlTaskHandler sql_task_handler_; /// SQL请求处理器 +}; \ No newline at end of file diff --git a/src/observer/net/mysql_communicator.cpp b/src/observer/net/mysql_communicator.cpp index 3e56cc624..247bb90aa 100644 --- a/src/observer/net/mysql_communicator.cpp +++ b/src/observer/net/mysql_communicator.cpp @@ -626,7 +626,8 @@ RC MysqlCommunicator::read_event(SessionEvent *&event) return RC::IOERR_READ; } - LOG_TRACE("read packet header. length=%d, sequence_id=%d", sizeof(packet_header), packet_header.sequence_id); + LOG_TRACE("read packet header. length=%d, sequence_id=%d, payload_length=%d, fd=%d", + sizeof(packet_header), packet_header.sequence_id, packet_header.payload_length, fd_); sequence_id_ = packet_header.sequence_id + 1; std::vector buf(packet_header.payload_length); @@ -637,14 +638,14 @@ RC MysqlCommunicator::read_event(SessionEvent *&event) return RC::IOERR_READ; } - LOG_TRACE("read packet payload length=%d", packet_header.payload_length); - event = nullptr; if (!authed_) { /// 还没有做过认证,就先需要完成握手阶段 uint32_t client_flag = *(uint32_t *)buf.data(); // TODO should use decode (little endian as default) LOG_INFO("client handshake response with capabilities flag=%d", client_flag); - client_capabilities_flag_ = client_flag; + /// 经过测试sysbench 虽然发出的鉴权包中带了CLIENT_DEPRECATE_EOF标记,但是在接收row result set 时, + // 不能识别最后一个OK Packet。强制清除该标识,表现正常 + client_capabilities_flag_ = (client_flag & (~CLIENT_DEPRECATE_EOF)); // send ok packet and return OkPacket ok_packet; ok_packet.packet_header.sequence_id = sequence_id_; diff --git a/src/observer/net/one_thread_per_connection_thread_handler.cpp b/src/observer/net/one_thread_per_connection_thread_handler.cpp new file mode 100644 index 000000000..1cc5e7a41 --- /dev/null +++ b/src/observer/net/one_thread_per_connection_thread_handler.cpp @@ -0,0 +1,175 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2024/01/10. +// + +#include +#include + +#include "net/one_thread_per_connection_thread_handler.h" +#include "common/log/log.h" +#include "common/thread/thread_util.h" +#include "net/communicator.h" +#include "net/sql_task_handler.h" + +using namespace std; +using namespace common; + +class Worker +{ +public: + Worker(ThreadHandler &host, Communicator *communicator) + : host_(host), communicator_(communicator) + {} + ~Worker() + { + if (thread_ != nullptr) { + stop(); + join(); + } + } + + RC start() + { + thread_ = new std::thread(std::ref(*this)); + return RC::SUCCESS; + } + + RC stop() + { + running_ = false; + return RC::SUCCESS; + } + + RC join() + { + if (thread_) { + if (thread_->get_id() == std::this_thread::get_id()) { + thread_->detach(); // 如果当前线程join当前线程,就会卡死 + } else { + thread_->join(); + } + delete thread_; + thread_ = nullptr; + } + return RC::SUCCESS; + } + + void operator()() + { + LOG_INFO("worker thread start. communicator = %p", communicator_); + int ret = thread_set_name("SQLWorker"); + if (ret != 0) { + LOG_WARN("failed to set thread name. ret = %d", ret); + } + + struct pollfd poll_fd; + poll_fd.fd = communicator_->fd(); + poll_fd.events = POLLIN; + poll_fd.revents = 0; + + while (running_) { + int ret = poll(&poll_fd, 1, 500); + if (ret < 0) { + LOG_WARN("poll error. fd = %d, ret = %d, error=%s", poll_fd.fd, ret, strerror(errno)); + break; + } else if (0 == ret) { + // LOG_TRACE("poll timeout. fd = %d", poll_fd.fd); + continue; + } + + if (poll_fd.revents & (POLLERR | POLLHUP | POLLNVAL)) { + LOG_WARN("poll error. fd = %d, revents = %d", poll_fd.fd, poll_fd.revents); + break; + } + + RC rc = task_handler_.handle_event(communicator_); + if (OB_FAIL(rc)) { + LOG_ERROR("handle error. rc = %s", strrc(rc)); + break; + } + } + + LOG_INFO("worker thread stop. communicator = %p", communicator_); + host_.close_connection(communicator_); /// 连接关闭后,当前对象会被删除 + } + +private: + ThreadHandler &host_; + SqlTaskHandler task_handler_; + Communicator *communicator_ = nullptr; + std::thread *thread_ = nullptr; + volatile bool running_ = true; +}; + +OneThreadPerConnectionThreadHandler::~OneThreadPerConnectionThreadHandler() +{ + stop(); + await_stop(); +} + +RC OneThreadPerConnectionThreadHandler::new_connection(Communicator *communicator) +{ + lock_guard guard(lock_); + + auto iter = thread_map_.find(communicator); + if (iter != thread_map_.end()) { + LOG_WARN("connection already exists. communicator = %p", communicator); + return RC::FILE_EXIST; + } + + Worker *worker = new Worker(*this, communicator); + thread_map_[communicator] = worker; + return worker->start(); +} + +RC OneThreadPerConnectionThreadHandler::close_connection(Communicator *communicator) +{ + lock_.lock(); + auto iter = thread_map_.find(communicator); + if (iter == thread_map_.end()) { + LOG_WARN("connection not exists. communicator = %p", communicator); + lock_.unlock(); + return RC::FILE_NOT_EXIST; + } + + Worker *worker = iter->second; + thread_map_.erase(iter); + lock_.unlock(); + + worker->stop(); + worker->join(); + delete worker; + delete communicator; + LOG_INFO("close connection. communicator = %p", communicator); + return RC::SUCCESS; +} + +RC OneThreadPerConnectionThreadHandler::stop() +{ + lock_guard guard(lock_); + for (auto iter = thread_map_.begin(); iter != thread_map_.end(); ++iter) { + Worker *worker = iter->second; + worker->stop(); + } + return RC::SUCCESS; +} + +RC OneThreadPerConnectionThreadHandler::await_stop() +{ + LOG_INFO("begin to await stop one thread per connection thread handler"); + while (!thread_map_.empty()) { + this_thread::sleep_for(chrono::milliseconds(100)); + } + LOG_INFO("end to await stop one thread per connection thread handler"); + return RC::SUCCESS; +} diff --git a/src/observer/net/one_thread_per_connection_thread_handler.h b/src/observer/net/one_thread_per_connection_thread_handler.h new file mode 100644 index 000000000..9e27a3219 --- /dev/null +++ b/src/observer/net/one_thread_per_connection_thread_handler.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2024/01/10. +// + +#include +#include + +#include "net/thread_handler.h" + +class Worker; + +/** + * @brief 一个连接一个线程的线程模型 + * @ingroup ThreadHandler + */ +class OneThreadPerConnectionThreadHandler : public ThreadHandler +{ +public: + OneThreadPerConnectionThreadHandler() = default; + virtual ~OneThreadPerConnectionThreadHandler(); + + //! @copydoc ThreadHandler::start + virtual RC start() override { return RC::SUCCESS; } + + //! @copydoc ThreadHandler::stop + virtual RC stop() override; + //! @copydoc ThreadHandler::await_stop + virtual RC await_stop() override; + + //! @copydoc ThreadHandler::new_connection + virtual RC new_connection(Communicator *communicator) override; + //! @copydoc ThreadHandler::close_connection + virtual RC close_connection(Communicator *communicator) override; + +private: + /// 记录一个连接Communicator关联的线程数据 + std::unordered_map thread_map_; // 当前编译器没有支持jthread + /// 保护线程安全的锁 + std::mutex lock_; +}; \ No newline at end of file diff --git a/src/observer/net/server.cpp b/src/observer/net/server.cpp index bab3917f5..15d5064ea 100644 --- a/src/observer/net/server.cpp +++ b/src/observer/net/server.cpp @@ -16,7 +16,6 @@ See the Mulan PSL v2 for more details. */ #include #include -#include #include #include #include @@ -27,20 +26,24 @@ See the Mulan PSL v2 for more details. */ #include #include #include +#include + +#include #include "common/ini_setting.h" #include "common/io/io.h" #include "common/lang/mutex.h" #include "common/log/log.h" -#include "common/seda/seda_config.h" #include "event/session_event.h" +#include "session/session_stage.h" #include "net/communicator.h" +#include "net/cli_communicator.h" #include "session/session.h" +#include "net/thread_handler.h" +#include "net/sql_task_handler.h" using namespace common; -Stage *Server::session_stage_ = nullptr; - ServerParam::ServerParam() { listen_addr = INADDR_ANY; @@ -48,18 +51,16 @@ ServerParam::ServerParam() port = PORT_DEFAULT; } -Server::Server(ServerParam input_server_param) : server_param_(input_server_param) {} +NetServer::NetServer(const ServerParam &input_server_param) : Server(input_server_param) {} -Server::~Server() +NetServer::~NetServer() { if (started_) { shutdown(); } } -void Server::init() { session_stage_ = get_seda_config()->get_stage(SESSION_STAGE_NAME); } - -int Server::set_non_block(int fd) +int NetServer::set_non_block(int fd) { int flags = fcntl(fd, F_GETFL); if (flags == -1) { @@ -75,35 +76,8 @@ int Server::set_non_block(int fd) return 0; } -void Server::close_connection(Communicator *communicator) -{ - LOG_INFO("Close connection of %s.", communicator->addr()); - event_del(&communicator->read_event()); - delete communicator; -} - -void Server::recv(int fd, short ev, void *arg) +void NetServer::accept(int fd) { - Communicator *comm = (Communicator *)arg; - - SessionEvent *event = nullptr; - - RC rc = comm->read_event(event); - if (rc != RC::SUCCESS) { - close_connection(comm); - return; - } - - if (event == nullptr) { - LOG_WARN("event is null while read event return success"); - return; - } - session_stage_->add_event(event); -} - -void Server::accept(int fd, short ev, void *arg) -{ - Server *instance = (Server *)arg; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); @@ -125,14 +99,14 @@ void Server::accept(int fd, short ev, void *arg) address << ip_addr << ":" << addr.sin_port; std::string addr_str = address.str(); - ret = instance->set_non_block(client_fd); + ret = set_non_block(client_fd); if (ret < 0) { LOG_ERROR("Failed to set socket of %s as non blocking, %s", addr_str.c_str(), strerror(errno)); ::close(client_fd); return; } - if (!instance->server_param_.use_unix_socket) { + if (!server_param_.use_unix_socket) { // unix socket不支持设置NODELAY int yes = 1; ret = setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)); @@ -143,7 +117,7 @@ void Server::accept(int fd, short ev, void *arg) } } - Communicator *communicator = instance->communicator_factory_.create(instance->server_param_.protocol); + Communicator *communicator = communicator_factory_.create(server_param_.protocol); RC rc = communicator->init(client_fd, new Session(Session::default_session()), addr_str); if (rc != RC::SUCCESS) { @@ -152,19 +126,9 @@ void Server::accept(int fd, short ev, void *arg) return; } - event_set(&communicator->read_event(), client_fd, EV_READ | EV_PERSIST, recv, communicator); - - ret = event_base_set(instance->event_base_, &communicator->read_event()); - if (ret < 0) { - LOG_ERROR("Failed to do event_base_set for read event of %s into libevent, %s", - communicator->addr(), strerror(errno)); - delete communicator; - return; - } - - ret = event_add(&communicator->read_event(), nullptr); - if (ret < 0) { - LOG_ERROR("Failed to event_add for read event of %s into libevent, %s", communicator->addr(), strerror(errno)); + rc = thread_handler_->new_connection(communicator); + if (OB_FAIL(rc)) { + LOG_WARN("failed to handle new connection. rc=%s", strrc(rc)); delete communicator; return; } @@ -172,10 +136,10 @@ void Server::accept(int fd, short ev, void *arg) LOG_INFO("Accepted connection from %s\n", communicator->addr()); } -int Server::start() +int NetServer::start() { if (server_param_.use_std_io) { - return start_stdin_server(); + return -1; } else if (server_param_.use_unix_socket) { return start_unix_socket_server(); } else { @@ -183,7 +147,7 @@ int Server::start() } } -int Server::start_tcp_server() +int NetServer::start_tcp_server() { int ret = 0; struct sockaddr_in sa; @@ -229,26 +193,12 @@ int Server::start_tcp_server() } LOG_INFO("Listen on port %d", server_param_.port); - listen_ev_ = event_new(event_base_, server_socket_, EV_READ | EV_PERSIST, accept, this); - if (listen_ev_ == nullptr) { - LOG_ERROR("Failed to create listen event, %s.", strerror(errno)); - ::close(server_socket_); - return -1; - } - - ret = event_add(listen_ev_, nullptr); - if (ret < 0) { - LOG_ERROR("event_add(): can not add accept event into libevent, %s", strerror(errno)); - ::close(server_socket_); - return -1; - } - started_ = true; LOG_INFO("Observer start success"); return 0; } -int Server::start_unix_socket_server() +int NetServer::start_unix_socket_server() { int ret = 0; server_socket_ = socket(PF_UNIX, SOCK_STREAM, 0); @@ -286,101 +236,113 @@ int Server::start_unix_socket_server() } LOG_INFO("Listen on unix socket: %s", sockaddr.sun_path); - listen_ev_ = event_new(event_base_, server_socket_, EV_READ | EV_PERSIST, accept, this); - if (listen_ev_ == nullptr) { - LOG_ERROR("Failed to create listen event, %s.", strerror(errno)); - ::close(server_socket_); - return -1; - } - - ret = event_add(listen_ev_, nullptr); - if (ret < 0) { - LOG_ERROR("event_add(): can not add accept event into libevent, %s", strerror(errno)); - ::close(server_socket_); - return -1; - } - started_ = true; LOG_INFO("Observer start success"); return 0; } -int Server::start_stdin_server() +int NetServer::serve() { - Communicator *communicator = communicator_factory_.create(server_param_.protocol); + thread_handler_ = ThreadHandler::create(server_param_.thread_handling.c_str()); + if (thread_handler_ == nullptr) { + LOG_ERROR("Failed to create thread handler: %s", server_param_.thread_handling.c_str()); + return -1; + } - RC rc = communicator->init(STDIN_FILENO, new Session(Session::default_session()), "stdin"); + RC rc = thread_handler_->start(); if (OB_FAIL(rc)) { - LOG_WARN("failed to init cli communicator. rc=%s", strrc(rc)); + LOG_ERROR("failed to start thread handler: %s", strrc(rc)); return -1; } - started_ = true; - - while (started_) { - SessionEvent *event = nullptr; - - rc = communicator->read_event(event); - if (OB_FAIL(rc)) { - LOG_WARN("failed to read event. rc=%s", strrc(rc)); - return -1; - } + int retval = start(); + if (retval == -1) { + LOG_PANIC("Failed to start network"); + exit(-1); + } - if (event == nullptr) { - break; + if (!server_param_.use_std_io) { + struct pollfd poll_fd; + poll_fd.fd = server_socket_; + poll_fd.events = POLLIN; + poll_fd.revents = 0; + + while (started_) { + int ret = poll(&poll_fd, 1, 500); + if (ret < 0) { + LOG_WARN("[listen socket] poll error. fd = %d, ret = %d, error=%s", poll_fd.fd, ret, strerror(errno)); + break; + } else if (0 == ret) { + // LOG_TRACE("poll timeout. fd = %d", poll_fd.fd); + continue; + } + + if (poll_fd.revents & (POLLERR | POLLHUP | POLLNVAL)) { + LOG_ERROR("poll error. fd = %d, revents = %d", poll_fd.fd, poll_fd.revents); + break; + } + + this->accept(server_socket_); } - - /// 在当前线程立即处理对应的事件 - session_stage_->handle_event(event); } - delete communicator; - communicator = nullptr; + thread_handler_->stop(); + thread_handler_->await_stop(); + delete thread_handler_; + thread_handler_ = nullptr; + + started_ = false; + LOG_INFO("NetServer quit"); return 0; } -int Server::serve() +void NetServer::shutdown() { - evthread_use_pthreads(); - event_base_ = event_base_new(); - if (event_base_ == nullptr) { - LOG_ERROR("Failed to create event base, %s.", strerror(errno)); - exit(-1); - } + LOG_INFO("NetServer shutting down"); - int retval = start(); - if (retval == -1) { - LOG_PANIC("Failed to start network"); - exit(-1); - } + // cleanup + started_ = false; +} - if (!server_param_.use_std_io) { - event_base_dispatch(event_base_); +//////////////////////////////////////////////////////////////////////////////// + +CliServer::CliServer(const ServerParam &input_server_param) : Server(input_server_param) {} + +CliServer::~CliServer() +{ + if (started_) { + shutdown(); } +} - if (listen_ev_ != nullptr) { - event_del(listen_ev_); - event_free(listen_ev_); - listen_ev_ = nullptr; +int CliServer::serve() +{ + CliCommunicator communicator; + + RC rc = communicator.init(STDIN_FILENO, new Session(Session::default_session()), "stdin"); + if (OB_FAIL(rc)) { + LOG_WARN("failed to init cli communicator. rc=%s", strrc(rc)); + return -1; } - if (event_base_ != nullptr) { - event_base_free(event_base_); - event_base_ = nullptr; + started_ = true; + + SqlTaskHandler task_handler; + while (started_ && !communicator.exit()) { + rc = task_handler.handle_event(&communicator); + if (OB_FAIL(rc)) { + started_ = false; + } } started_ = false; - LOG_INFO("Server quit"); return 0; } -void Server::shutdown() +void CliServer::shutdown() { - LOG_INFO("Server shutting down"); + LOG_INFO("CliServer shutting down"); // cleanup - if (event_base_ != nullptr && started_) { - started_ = false; - event_base_loopexit(event_base_, nullptr); - } + started_ = false; } diff --git a/src/observer/net/server.h b/src/observer/net/server.h index 6325d3170..2e39cb0ab 100644 --- a/src/observer/net/server.h +++ b/src/observer/net/server.h @@ -14,10 +14,10 @@ See the Mulan PSL v2 for more details. */ #pragma once -#include "common/seda/stage.h" #include "net/server_param.h" class Communicator; +class ThreadHandler; /** * @brief 负责接收客户端消息并创建任务 @@ -28,16 +28,25 @@ class Communicator; class Server { public: - Server(ServerParam input_server_param); - ~Server(); + Server(const ServerParam &input_server_param) : server_param_(input_server_param) {} + virtual ~Server() {} + virtual int serve() = 0; + virtual void shutdown() = 0; + +protected: + ServerParam server_param_; ///< 服务启动参数 +}; + +class NetServer : public Server +{ public: - static void init(); - static void close_connection(Communicator *comm); + NetServer(const ServerParam &input_server_param); + virtual ~NetServer(); public: - int serve(); - void shutdown(); + int serve() override; + void shutdown() override; private: /** @@ -45,17 +54,8 @@ class Server * @details 此函数作为libevent中监听套接字对应的回调函数 * @param fd libevent回调函数传入的参数,即监听套接字 * @param ev 本次触发的事件,通常是EV_READ - * @param arg 在注册libevent回调函数时,传入的参数,即Server对象 */ - static void accept(int fd, short ev, void *arg); - /** - * @brief 接收到客户端消息时,调用此函数创建任务 - * @details 此函数作为libevent中客户端套接字对应的回调函数。当有新的消息到达时,调用此函数创建任务。 - * @param fd libevent回调函数传入的参数,即客户端套接字 - * @param ev 本次触发的事件,通常是EV_READ - * @param arg 在注册libevent回调函数时,传入的参数,即Communicator对象 - */ - static void recv(int fd, short ev, void *arg); + void accept(int fd); private: /** @@ -77,18 +77,24 @@ class Server */ int start_unix_socket_server(); - int start_stdin_server(); - private: volatile bool started_ = false; - int server_socket_ = -1; ///< 监听套接字,是一个描述符 - struct event_base *event_base_ = nullptr; ///< libevent对象 - struct event *listen_ev_ = nullptr; ///< libevent监听套接字事件 - - ServerParam server_param_; ///< 服务启动参数 + int server_socket_ = -1; ///< 监听套接字,是一个描述符 CommunicatorFactory communicator_factory_; ///< 通过这个对象创建新的Communicator对象 - - static common::Stage *session_stage_; ///< 通过这个对象创建新的请求任务 + ThreadHandler *thread_handler_ = nullptr; }; + +class CliServer : public Server +{ +public: + CliServer(const ServerParam &input_server_param); + virtual ~CliServer(); + + int serve() override; + void shutdown() override; + +private: + volatile bool started_ = false; +}; \ No newline at end of file diff --git a/src/observer/net/server_param.h b/src/observer/net/server_param.h index 1a196784e..c4eaddeb2 100644 --- a/src/observer/net/server_param.h +++ b/src/observer/net/server_param.h @@ -46,4 +46,6 @@ class ServerParam bool use_unix_socket = false; CommunicateProtocol protocol; ///< 通讯协议,目前支持文本协议和mysql协议 + + std::string thread_handling; ///< 线程池模型 }; diff --git a/src/observer/net/sql_task_handler.cpp b/src/observer/net/sql_task_handler.cpp new file mode 100644 index 000000000..d4d61f5af --- /dev/null +++ b/src/observer/net/sql_task_handler.cpp @@ -0,0 +1,87 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2024/01/10. +// + +#include "net/sql_task_handler.h" +#include "net/communicator.h" +#include "event/session_event.h" +#include "event/sql_event.h" +#include "session/session.h" + +RC SqlTaskHandler::handle_event(Communicator *communicator) +{ + SessionEvent *event = nullptr; + RC rc = communicator->read_event(event); + if (OB_FAIL(rc)) { + return rc; + } + + if (nullptr == event) { + return RC::SUCCESS; + } + + session_stage_.handle_request2(event); + + SQLStageEvent sql_event(event, event->query()); + + (void)handle_sql(&sql_event); + + bool need_disconnect = false; + + rc = communicator->write_result(event, need_disconnect); + LOG_INFO("write result return %s", strrc(rc)); + event->session()->set_current_request(nullptr); + Session::set_current_session(nullptr); + + delete event; + + if (need_disconnect) { + return RC::INTERNAL; + } + return RC::SUCCESS; +} + +RC SqlTaskHandler::handle_sql(SQLStageEvent *sql_event) +{ + RC rc = query_cache_stage_.handle_request(sql_event); + if (OB_FAIL(rc)) { + LOG_TRACE("failed to do query cache. rc=%s", strrc(rc)); + return rc; + } + + rc = parse_stage_.handle_request(sql_event); + if (OB_FAIL(rc)) { + LOG_TRACE("failed to do parse. rc=%s", strrc(rc)); + return rc; + } + + rc = resolve_stage_.handle_request(sql_event); + if (OB_FAIL(rc)) { + LOG_TRACE("failed to do resolve. rc=%s", strrc(rc)); + return rc; + } + + rc = optimize_stage_.handle_request(sql_event); + if (rc != RC::UNIMPLENMENT && rc != RC::SUCCESS) { + LOG_TRACE("failed to do optimize. rc=%s", strrc(rc)); + return rc; + } + + rc = execute_stage_.handle_request(sql_event); + if (OB_FAIL(rc)) { + LOG_TRACE("failed to do execute. rc=%s", strrc(rc)); + return rc; + } + + return rc; +} \ No newline at end of file diff --git a/src/observer/net/sql_task_handler.h b/src/observer/net/sql_task_handler.h new file mode 100644 index 000000000..c69db20cf --- /dev/null +++ b/src/observer/net/sql_task_handler.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2024/01/10. +// + +#pragma once + +#include "common/rc.h" +#include "session/session_stage.h" +#include "sql/executor/execute_stage.h" +#include "sql/optimizer/optimize_stage.h" +#include "sql/parser/parse_stage.h" +#include "sql/parser/resolve_stage.h" +#include "sql/query_cache/query_cache_stage.h" + +class Communicator; +class SQLStageEvent; + +/** + * @brief SQL请求的处理器 + */ +class SqlTaskHandler +{ +public: + SqlTaskHandler() = default; + virtual ~SqlTaskHandler() = default; + + /** + * @brief 指定连接上有数据可读时就读取消息然后处理 + * @details 步骤包含接收请求、处理请求,然后返回应答 + * @param communicator 连接对象 + * @return RC 如果返回失败,就要断开连接 + */ + RC handle_event(Communicator *communicator); + + RC handle_sql(SQLStageEvent *sql_event); + +private: + SessionStage session_stage_; + QueryCacheStage query_cache_stage_; + ParseStage parse_stage_; + ResolveStage resolve_stage_; + OptimizeStage optimize_stage_; + ExecuteStage execute_stage_; +}; \ No newline at end of file diff --git a/src/observer/net/thread_handler.cpp b/src/observer/net/thread_handler.cpp new file mode 100644 index 000000000..941fcdfd9 --- /dev/null +++ b/src/observer/net/thread_handler.cpp @@ -0,0 +1,38 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2024/01/10. +// + +#include + +#include "net/thread_handler.h" +#include "net/one_thread_per_connection_thread_handler.h" +#include "net/java_thread_pool_thread_handler.h" +#include "common/log/log.h" +#include "common/lang/string.h" + +ThreadHandler * ThreadHandler::create(const char *name) +{ + const char *default_name = "one-thread-per-connection"; + if (nullptr == name || common::is_blank(name)) { + name = default_name; + } + + if (0 == strcasecmp(name, default_name)) { + return new OneThreadPerConnectionThreadHandler(); + } else if (0 == strcasecmp(name, "java-thread-pool")) { + return new JavaThreadPoolThreadHandler(); + } else { + LOG_ERROR("unknown thread handler: %s", name); + return nullptr; + } +} \ No newline at end of file diff --git a/src/observer/net/thread_handler.h b/src/observer/net/thread_handler.h new file mode 100644 index 000000000..c5cf0e075 --- /dev/null +++ b/src/observer/net/thread_handler.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2024/01/10. +// + +#pragma once + +#include +#include "common/rc.h" + +class Communicator; + +/** + * @defgroup ThreadHandler + * @brief 线程池处理模型接口 + * @details 处理连接上所有的消息。可以使用不同的模型来处理,当前有一个连接一个线程的模式和线程池模式。 + * 线程模型仅处理与客户端通讯的连接,不处理observer监听套接字。 + */ +class ThreadHandler +{ +public: + ThreadHandler() = default; + virtual ~ThreadHandler() = default; + + /** + * @brief 启动线程模型 + */ + virtual RC start() = 0; + + /** + * @brief 停止线程模型 + */ + virtual RC stop() = 0; + + /** + * @brief 等待线程模型停止 + */ + virtual RC await_stop() = 0; + + /** + * @brief 有新的连接到达时,调用此接口 + * @param communicator 与客户端通讯的对象 + */ + virtual RC new_connection(Communicator *communicator) = 0; + + /** + * @brief 连接断开时,调用此接口。通常都是内部调用 + * @param communicator 与客户端通讯的对象 + */ + virtual RC close_connection(Communicator *communicator) = 0; + +public: + /** + * @brief 创建一个线程模型 + */ + static ThreadHandler *create(const char *name); +}; diff --git a/src/observer/session/session_stage.cpp b/src/observer/session/session_stage.cpp index 147f7b24d..69a89ec8d 100644 --- a/src/observer/session/session_stage.cpp +++ b/src/observer/session/session_stage.cpp @@ -29,61 +29,12 @@ See the Mulan PSL v2 for more details. */ using namespace common; -// Constructor -SessionStage::SessionStage(const char *tag) : Stage(tag) {} - // Destructor SessionStage::~SessionStage() {} -// Parse properties, instantiate a stage object -Stage *SessionStage::make_stage(const std::string &tag) -{ - SessionStage *stage = new (std::nothrow) SessionStage(tag.c_str()); - if (stage == nullptr) { - LOG_ERROR("new ExecutorStage failed"); - return nullptr; - } - stage->set_properties(); - return stage; -} - -// Set properties for this object set in stage specific properties -bool SessionStage::set_properties() -{ - // std::string stageNameStr(stage_name_); - // std::map section = g_properties()->get( - // stageNameStr); - // - // std::map::iterator it; - // - // std::string key; - - return true; -} - -// Initialize stage params and validate outputs -bool SessionStage::initialize() { return true; } - -// Cleanup after disconnection -void SessionStage::cleanup() {} - -void SessionStage::handle_event(StageEvent *event) -{ - // right now, we just support only one event. - handle_request(event); - - event->done_immediate(); - return; -} - -void SessionStage::handle_request(StageEvent *event) +// TODO remove me +void SessionStage::handle_request(SessionEvent *sev) { - SessionEvent *sev = dynamic_cast(event); - if (nullptr == sev) { - LOG_ERROR("Cannot cat event to sessionEvent"); - return; - } - std::string sql = sev->query(); if (common::is_blank(sql.c_str())) { return; @@ -99,12 +50,24 @@ void SessionStage::handle_request(StageEvent *event) RC rc = communicator->write_result(sev, need_disconnect); LOG_INFO("write result return %s", strrc(rc)); if (need_disconnect) { - Server::close_connection(communicator); + // do nothing } sev->session()->set_current_request(nullptr); Session::set_current_session(nullptr); } +void SessionStage::handle_request2(SessionEvent *event) +{ + const std::string &sql = event->query(); + if (common::is_blank(sql.c_str())) { + return; + } + + Session::set_current_session(event->session()); + event->session()->set_current_request(event); + SQLStageEvent sql_event(event, sql); +} + /** * 处理一个SQL语句经历这几个阶段。 * 虽然看起来流程比较多,但是对于大多数SQL来说,更多的可以关注parse和executor阶段。 diff --git a/src/observer/session/session_stage.h b/src/observer/session/session_stage.h index 0c696eac7..2762c66ca 100644 --- a/src/observer/session/session_stage.h +++ b/src/observer/session/session_stage.h @@ -14,7 +14,6 @@ See the Mulan PSL v2 for more details. */ #pragma once -#include "common/seda/stage.h" #include "sql/executor/execute_stage.h" #include "sql/optimizer/optimize_stage.h" #include "sql/parser/parse_stage.h" @@ -37,23 +36,17 @@ See the Mulan PSL v2 for more details. */ * @brief SQL处理的session阶段,也是第一个阶段 * @ingroup SQLStage */ -class SessionStage : public common::Stage +class SessionStage { public: + SessionStage() = default; virtual ~SessionStage(); - static Stage *make_stage(const std::string &tag); -protected: - // common function - SessionStage(const char *tag); - bool set_properties() override; - - bool initialize() override; - void cleanup() override; - void handle_event(common::StageEvent *event) override; +public: + void handle_request2(SessionEvent *event); -protected: - void handle_request(common::StageEvent *event); +public: + void handle_request(SessionEvent *event); RC handle_sql(SQLStageEvent *sql_event); private: diff --git a/src/observer/sql/parser/lex_sql.cpp b/src/observer/sql/parser/lex_sql.cpp index c2551a9c7..e04cf336f 100644 --- a/src/observer/sql/parser/lex_sql.cpp +++ b/src/observer/sql/parser/lex_sql.cpp @@ -1,4 +1,4 @@ -#line 1 "lex_sql.cpp" +#line 2 "lex_sql.cpp" /* 这里的代码会被复制到lex_sql.cpp的最开始位置 定义yy_size_t的原因是因为flex生成的代码,会使用yy_size_t与其他类型的数字 @@ -22,7 +22,7 @@ do { \ } \ while (0); -#line 25 "lex_sql.cpp" +#line 26 "lex_sql.cpp" #define YY_INT_ALIGNED short int @@ -93,7 +93,6 @@ typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; -typedef uint64_t flex_uint64_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; @@ -258,7 +257,7 @@ struct yy_buffer_state /* Number of characters read into yy_ch_buf, not including EOB * characters. */ - yy_size_t yy_n_chars; + int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to @@ -335,7 +334,7 @@ static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); -YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, yy_size_t len , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); void *yyalloc ( yy_size_t , yyscan_t yyscanner ); void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); @@ -382,7 +381,7 @@ static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ - yyleng = (yy_size_t) (yy_cp - yy_bp); \ + yyleng = (int) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; @@ -695,8 +694,8 @@ struct yyguts_t size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; - yy_size_t yy_n_chars; - yy_size_t yyleng_r; + int yy_n_chars; + int yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; @@ -753,7 +752,7 @@ FILE *yyget_out ( yyscan_t yyscanner ); void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); - yy_size_t yyget_leng ( yyscan_t yyscanner ); + int yyget_leng ( yyscan_t yyscanner ); char *yyget_text ( yyscan_t yyscanner ); @@ -832,7 +831,7 @@ static int input ( yyscan_t yyscanner ); if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ - yy_size_t n; \ + int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ @@ -1263,7 +1262,7 @@ case 52: case 53: YY_RULE_SETUP #line 133 "lex_sql.l" -{return yytext[0];} +{ return yytext[0]; } YY_BREAK case 54: /* rule 54 can match eol */ @@ -1476,7 +1475,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) else { - yy_size_t num_to_read = + int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) @@ -1490,7 +1489,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) if ( b->yy_is_our_buffer ) { - yy_size_t new_size = b->yy_buf_size * 2; + int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; @@ -1548,7 +1547,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ - yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) @@ -1655,7 +1654,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) else { /* need more input */ - yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) @@ -2033,12 +2032,12 @@ YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; - yy_size_t i; + int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); @@ -2082,7 +2081,7 @@ static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) do \ { \ /* Undo effects of setting up yytext. */ \ - yy_size_t yyless_macro_arg = (n); \ + int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ @@ -2150,7 +2149,7 @@ FILE *yyget_out (yyscan_t yyscanner) /** Get the length of the current token. * @param yyscanner The scanner object. */ -yy_size_t yyget_leng (yyscan_t yyscanner) +int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; diff --git a/src/observer/sql/parser/lex_sql.h b/src/observer/sql/parser/lex_sql.h index 8796f7ff4..e3761b2f9 100644 --- a/src/observer/sql/parser/lex_sql.h +++ b/src/observer/sql/parser/lex_sql.h @@ -2,7 +2,7 @@ #define yyHEADER_H 1 #define yyIN_HEADER 1 -#line 5 "lex_sql.h" +#line 6 "lex_sql.h" /* 这里的代码会被复制到lex_sql.cpp的最开始位置 定义yy_size_t的原因是因为flex生成的代码,会使用yy_size_t与其他类型的数字 @@ -26,7 +26,7 @@ do { \ } \ while (0); -#line 29 "lex_sql.h" +#line 30 "lex_sql.h" #define YY_INT_ALIGNED short int @@ -97,7 +97,6 @@ typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; -typedef uint64_t flex_uint64_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; @@ -211,7 +210,7 @@ struct yy_buffer_state /* Number of characters read into yy_ch_buf, not including EOB * characters. */ - yy_size_t yy_n_chars; + int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to @@ -255,7 +254,7 @@ void yypop_buffer_state ( yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); -YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, yy_size_t len , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); void *yyalloc ( yy_size_t , yyscan_t yyscanner ); void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); @@ -311,7 +310,7 @@ FILE *yyget_out ( yyscan_t yyscanner ); void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); - yy_size_t yyget_leng ( yyscan_t yyscanner ); + int yyget_leng ( yyscan_t yyscanner ); char *yyget_text ( yyscan_t yyscanner ); diff --git a/src/observer/sql/parser/yacc_sql.cpp b/src/observer/sql/parser/yacc_sql.cpp index 5c4b075b5..08104f338 100644 --- a/src/observer/sql/parser/yacc_sql.cpp +++ b/src/observer/sql/parser/yacc_sql.cpp @@ -1,8 +1,8 @@ -/* A Bison parser, made by GNU Bison 3.7. */ +/* A Bison parser, made by GNU Bison 3.8.2. */ /* Bison implementation for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . */ + along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -45,11 +45,11 @@ define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ -/* Identify Bison output. */ -#define YYBISON 1 +/* Identify Bison output, and Bison version. */ +#define YYBISON 30802 -/* Bison version. */ -#define YYBISON_VERSION "3.7" +/* Bison version string. */ +#define YYBISON_VERSION "3.8.2" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -188,54 +188,52 @@ enum yysymbol_kind_t YYSYMBOL_NUMBER = 46, /* NUMBER */ YYSYMBOL_FLOAT = 47, /* FLOAT */ YYSYMBOL_ID = 48, /* ID */ - YYSYMBOL_PATH = 49, /* PATH */ - YYSYMBOL_SSS = 50, /* SSS */ - YYSYMBOL_STRING_V = 51, /* STRING_V */ - YYSYMBOL_52_ = 52, /* '+' */ - YYSYMBOL_53_ = 53, /* '-' */ - YYSYMBOL_54_ = 54, /* '*' */ - YYSYMBOL_55_ = 55, /* '/' */ - YYSYMBOL_UMINUS = 56, /* UMINUS */ - YYSYMBOL_YYACCEPT = 57, /* $accept */ - YYSYMBOL_commands = 58, /* commands */ - YYSYMBOL_command_wrapper = 59, /* command_wrapper */ - YYSYMBOL_exit_stmt = 60, /* exit_stmt */ - YYSYMBOL_help_stmt = 61, /* help_stmt */ - YYSYMBOL_sync_stmt = 62, /* sync_stmt */ - YYSYMBOL_begin_stmt = 63, /* begin_stmt */ - YYSYMBOL_commit_stmt = 64, /* commit_stmt */ - YYSYMBOL_rollback_stmt = 65, /* rollback_stmt */ - YYSYMBOL_drop_table_stmt = 66, /* drop_table_stmt */ - YYSYMBOL_show_tables_stmt = 67, /* show_tables_stmt */ - YYSYMBOL_desc_table_stmt = 68, /* desc_table_stmt */ - YYSYMBOL_create_index_stmt = 69, /* create_index_stmt */ - YYSYMBOL_drop_index_stmt = 70, /* drop_index_stmt */ - YYSYMBOL_create_table_stmt = 71, /* create_table_stmt */ - YYSYMBOL_attr_def_list = 72, /* attr_def_list */ - YYSYMBOL_attr_def = 73, /* attr_def */ - YYSYMBOL_number = 74, /* number */ - YYSYMBOL_type = 75, /* type */ - YYSYMBOL_insert_stmt = 76, /* insert_stmt */ - YYSYMBOL_value_list = 77, /* value_list */ - YYSYMBOL_value = 78, /* value */ - YYSYMBOL_delete_stmt = 79, /* delete_stmt */ - YYSYMBOL_update_stmt = 80, /* update_stmt */ - YYSYMBOL_select_stmt = 81, /* select_stmt */ - YYSYMBOL_calc_stmt = 82, /* calc_stmt */ - YYSYMBOL_expression_list = 83, /* expression_list */ - YYSYMBOL_expression = 84, /* expression */ - YYSYMBOL_select_attr = 85, /* select_attr */ - YYSYMBOL_rel_attr = 86, /* rel_attr */ - YYSYMBOL_attr_list = 87, /* attr_list */ - YYSYMBOL_rel_list = 88, /* rel_list */ - YYSYMBOL_where = 89, /* where */ - YYSYMBOL_condition_list = 90, /* condition_list */ - YYSYMBOL_condition = 91, /* condition */ - YYSYMBOL_comp_op = 92, /* comp_op */ - YYSYMBOL_load_data_stmt = 93, /* load_data_stmt */ - YYSYMBOL_explain_stmt = 94, /* explain_stmt */ - YYSYMBOL_set_variable_stmt = 95, /* set_variable_stmt */ - YYSYMBOL_opt_semicolon = 96 /* opt_semicolon */ + YYSYMBOL_SSS = 49, /* SSS */ + YYSYMBOL_50_ = 50, /* '+' */ + YYSYMBOL_51_ = 51, /* '-' */ + YYSYMBOL_52_ = 52, /* '*' */ + YYSYMBOL_53_ = 53, /* '/' */ + YYSYMBOL_UMINUS = 54, /* UMINUS */ + YYSYMBOL_YYACCEPT = 55, /* $accept */ + YYSYMBOL_commands = 56, /* commands */ + YYSYMBOL_command_wrapper = 57, /* command_wrapper */ + YYSYMBOL_exit_stmt = 58, /* exit_stmt */ + YYSYMBOL_help_stmt = 59, /* help_stmt */ + YYSYMBOL_sync_stmt = 60, /* sync_stmt */ + YYSYMBOL_begin_stmt = 61, /* begin_stmt */ + YYSYMBOL_commit_stmt = 62, /* commit_stmt */ + YYSYMBOL_rollback_stmt = 63, /* rollback_stmt */ + YYSYMBOL_drop_table_stmt = 64, /* drop_table_stmt */ + YYSYMBOL_show_tables_stmt = 65, /* show_tables_stmt */ + YYSYMBOL_desc_table_stmt = 66, /* desc_table_stmt */ + YYSYMBOL_create_index_stmt = 67, /* create_index_stmt */ + YYSYMBOL_drop_index_stmt = 68, /* drop_index_stmt */ + YYSYMBOL_create_table_stmt = 69, /* create_table_stmt */ + YYSYMBOL_attr_def_list = 70, /* attr_def_list */ + YYSYMBOL_attr_def = 71, /* attr_def */ + YYSYMBOL_number = 72, /* number */ + YYSYMBOL_type = 73, /* type */ + YYSYMBOL_insert_stmt = 74, /* insert_stmt */ + YYSYMBOL_value_list = 75, /* value_list */ + YYSYMBOL_value = 76, /* value */ + YYSYMBOL_delete_stmt = 77, /* delete_stmt */ + YYSYMBOL_update_stmt = 78, /* update_stmt */ + YYSYMBOL_select_stmt = 79, /* select_stmt */ + YYSYMBOL_calc_stmt = 80, /* calc_stmt */ + YYSYMBOL_expression_list = 81, /* expression_list */ + YYSYMBOL_expression = 82, /* expression */ + YYSYMBOL_select_attr = 83, /* select_attr */ + YYSYMBOL_rel_attr = 84, /* rel_attr */ + YYSYMBOL_attr_list = 85, /* attr_list */ + YYSYMBOL_rel_list = 86, /* rel_list */ + YYSYMBOL_where = 87, /* where */ + YYSYMBOL_condition_list = 88, /* condition_list */ + YYSYMBOL_condition = 89, /* condition */ + YYSYMBOL_comp_op = 90, /* comp_op */ + YYSYMBOL_load_data_stmt = 91, /* load_data_stmt */ + YYSYMBOL_explain_stmt = 92, /* explain_stmt */ + YYSYMBOL_set_variable_stmt = 93, /* set_variable_stmt */ + YYSYMBOL_opt_semicolon = 94 /* opt_semicolon */ }; typedef enum yysymbol_kind_t yysymbol_kind_t; @@ -279,6 +277,18 @@ typedef int_least16_t yytype_int16; typedef short yytype_int16; #endif +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + . */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + #if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ typedef __UINT_LEAST8_TYPE__ yytype_uint8; #elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ @@ -376,17 +386,23 @@ typedef int yy_state_fast_t; /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ -# define YYUSE(E) ((void) (E)) +# define YY_USE(E) ((void) (E)) #else -# define YYUSE(E) /* empty */ +# define YY_USE(E) /* empty */ #endif -#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else @@ -548,10 +564,10 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 65 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 137 +#define YYLAST 151 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 57 +#define YYNTOKENS 55 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 40 /* YYNRULES -- Number of rules. */ @@ -560,7 +576,7 @@ union yyalloc #define YYNSTATES 163 /* YYMAXUTOK -- Last valid token kind. */ -#define YYMAXUTOK 307 +#define YYMAXUTOK 305 /* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM @@ -578,7 +594,7 @@ static const yytype_int8 yytranslate[] = 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 54, 52, 2, 53, 2, 55, 2, 2, + 2, 2, 52, 50, 2, 51, 2, 53, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -604,22 +620,22 @@ static const yytype_int8 yytranslate[] = 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 56 + 45, 46, 47, 48, 49, 54 }; #if YYDEBUG - /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { - 0, 171, 171, 179, 180, 181, 182, 183, 184, 185, - 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, - 196, 197, 198, 202, 208, 213, 219, 225, 231, 237, - 244, 250, 258, 272, 282, 301, 304, 317, 325, 335, - 338, 339, 340, 343, 359, 362, 373, 377, 380, 388, - 400, 415, 437, 447, 452, 462, 465, 468, 471, 474, - 478, 481, 489, 496, 508, 513, 524, 527, 541, 544, - 557, 560, 566, 569, 574, 581, 593, 605, 617, 632, - 633, 634, 635, 636, 637, 641, 654, 662, 672, 673 + 0, 173, 173, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 204, 210, 215, 221, 227, 233, 239, + 246, 252, 260, 274, 284, 304, 307, 320, 328, 338, + 341, 342, 343, 346, 363, 366, 377, 381, 385, 394, + 406, 421, 443, 453, 458, 469, 472, 475, 478, 481, + 485, 488, 496, 503, 515, 520, 531, 534, 548, 551, + 564, 567, 573, 576, 581, 588, 600, 612, 624, 639, + 640, 641, 642, 643, 644, 648, 661, 669, 679, 680 }; #endif @@ -641,10 +657,10 @@ static const char *const yytname[] = "TRX_BEGIN", "TRX_COMMIT", "TRX_ROLLBACK", "INT_T", "STRING_T", "FLOAT_T", "HELP", "EXIT", "DOT", "INTO", "VALUES", "FROM", "WHERE", "AND", "SET", "ON", "LOAD", "DATA", "INFILE", "EXPLAIN", "EQ", "LT", - "GT", "LE", "GE", "NE", "NUMBER", "FLOAT", "ID", "PATH", "SSS", - "STRING_V", "'+'", "'-'", "'*'", "'/'", "UMINUS", "$accept", "commands", - "command_wrapper", "exit_stmt", "help_stmt", "sync_stmt", "begin_stmt", - "commit_stmt", "rollback_stmt", "drop_table_stmt", "show_tables_stmt", + "GT", "LE", "GE", "NE", "NUMBER", "FLOAT", "ID", "SSS", "'+'", "'-'", + "'*'", "'/'", "UMINUS", "$accept", "commands", "command_wrapper", + "exit_stmt", "help_stmt", "sync_stmt", "begin_stmt", "commit_stmt", + "rollback_stmt", "drop_table_stmt", "show_tables_stmt", "desc_table_stmt", "create_index_stmt", "drop_index_stmt", "create_table_stmt", "attr_def_list", "attr_def", "number", "type", "insert_stmt", "value_list", "value", "delete_stmt", "update_stmt", @@ -661,21 +677,7 @@ yysymbol_name (yysymbol_kind_t yysymbol) } #endif -#ifdef YYPRINT -/* YYTOKNUM[NUM] -- (External) token number corresponding to the - (internal) symbol number NUM (which must be that of a token). */ -static const yytype_int16 yytoknum[] = -{ - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, - 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, - 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, - 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, - 305, 306, 43, 45, 42, 47, 307 -}; -#endif - -#define YYPACT_NINF (-109) +#define YYPACT_NINF (-97) #define yypact_value_is_default(Yyn) \ ((Yyn) == YYPACT_NINF) @@ -685,32 +687,32 @@ static const yytype_int16 yytoknum[] = #define yytable_value_is_error(Yyn) \ 0 - /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ static const yytype_int8 yypact[] = { - 54, 24, 37, -6, -44, -40, 5, -109, -14, -2, - 0, -109, -109, -109, -109, -109, 1, 9, 54, 42, - 47, -109, -109, -109, -109, -109, -109, -109, -109, -109, - -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, - -109, 12, 14, 23, 25, -6, -109, -109, -109, -6, - -109, -109, 2, 44, -109, 48, 64, -109, -109, 36, - 38, 51, 49, 53, -109, -109, -109, -109, 70, 57, - -109, 59, -16, -109, -6, -6, -6, -6, -6, 50, - 52, 55, -109, 65, 67, 56, -33, 46, 58, 66, - 68, -109, -109, -20, -20, -109, -109, -109, 78, 64, - 84, -41, -109, 62, -109, 76, -5, 88, 95, -109, - 69, 67, -109, -33, -18, -18, -109, 80, -33, 109, - -109, -109, -109, 101, 58, 102, 71, 78, -109, 103, - -109, -109, -109, -109, -109, -109, -41, -41, -41, 67, - 73, 77, 88, -109, 106, -109, -33, 107, -109, -109, - -109, -109, -109, -109, -109, -109, 108, -109, -109, 103, - -109, -109, -109 + -2, 51, 74, 9, -25, -26, 22, -97, 1, 4, + -6, -97, -97, -97, -97, -97, 6, -1, -2, 72, + 80, -97, -97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, + -97, 36, 37, 38, 39, 9, -97, -97, -97, 9, + -97, -97, 12, 60, -97, 58, 71, -97, -97, 43, + 44, 59, 54, 57, -97, -97, -97, -97, 79, 62, + -97, 63, -12, -97, 9, 9, 9, 9, 9, 52, + 53, 55, -97, 69, 70, 56, 32, 61, 64, 65, + 66, -97, -97, -48, -48, -97, -97, -97, 86, 71, + 89, 27, -97, 67, -97, 82, 21, 90, 91, -97, + 68, 70, -97, 32, 26, 26, -97, 84, 32, 109, + -97, -97, -97, 101, 64, 102, 73, 86, -97, 100, + -97, -97, -97, -97, -97, -97, 27, 27, 27, 70, + 75, 76, 90, -97, 106, -97, 32, 107, -97, -97, + -97, -97, -97, -97, -97, -97, 108, -97, -97, 100, + -97, -97, -97 }; - /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE does not specify something else to do. Zero - means the default is an error. */ +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ static const yytype_int8 yydefact[] = { 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, @@ -732,101 +734,105 @@ static const yytype_int8 yydefact[] = 43, 37, 45 }; - /* YYPGOTO[NTERM-NUM]. */ +/* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { - -109, -109, 110, -109, -109, -109, -109, -109, -109, -109, - -109, -109, -109, -109, -109, -15, 6, -109, -109, -109, - -30, -85, -109, -109, -109, -109, 60, 33, -109, -4, - 32, 8, -108, -1, -109, 21, -109, -109, -109, -109 + -97, -97, 110, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, -97, -97, -15, 5, -97, -97, -97, + -29, -85, -97, -97, -97, -97, 77, -28, -97, -4, + 35, 8, -96, -7, -97, 23, -97, -97, -97, -97 }; - /* YYDEFGOTO[NTERM-NUM]. */ -static const yytype_int16 yydefgoto[] = +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_uint8 yydefgoto[] = { - -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 0, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 125, 107, 156, 123, 33, 147, 50, 34, 35, 36, 37, 51, 52, 55, 115, 82, 111, 102, 116, 117, 136, 38, 39, 40, 67 }; - /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule whose - number is the opposite. If YYTABLE_NINF, syntax error. */ +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_uint8 yytable[] = { - 56, 104, 91, 128, 53, 46, 47, 53, 57, 48, - 54, 45, 58, 46, 47, 59, 114, 48, 120, 121, - 122, 74, 130, 131, 132, 133, 134, 135, 129, 60, - 41, 153, 42, 139, 77, 78, 75, 76, 77, 78, - 46, 47, 65, 43, 48, 44, 63, 49, 61, 62, - 66, 148, 150, 114, 75, 76, 77, 78, 1, 2, - 68, 159, 69, 3, 4, 5, 6, 7, 8, 9, - 10, 70, 79, 71, 11, 12, 13, 99, 72, 80, - 14, 15, 73, 81, 83, 85, 84, 88, 16, 86, - 17, 87, 89, 18, 90, 100, 105, 110, 97, 101, - 98, 113, 118, 53, 103, 119, 106, 124, 93, 94, - 95, 96, 126, 138, 108, 140, 109, 127, 141, 144, - 143, 154, 146, 155, 158, 160, 161, 157, 64, 162, - 142, 112, 149, 151, 92, 145, 137, 152 + 56, 104, 1, 2, 77, 78, 91, 3, 4, 5, + 6, 7, 8, 9, 10, 128, 114, 72, 11, 12, + 13, 73, 57, 53, 14, 15, 45, 54, 129, 58, + 59, 74, 16, 139, 17, 60, 63, 18, 75, 76, + 77, 78, 61, 153, 120, 121, 122, 93, 94, 95, + 96, 148, 150, 114, 62, 46, 47, 41, 48, 42, + 49, 159, 75, 76, 77, 78, 130, 131, 132, 133, + 134, 135, 65, 46, 47, 53, 48, 99, 46, 47, + 43, 48, 44, 66, 68, 69, 70, 71, 79, 80, + 81, 83, 84, 85, 86, 87, 88, 89, 90, 100, + 97, 98, 101, 53, 103, 110, 113, 118, 126, 124, + 105, 119, 106, 108, 109, 140, 127, 138, 141, 146, + 143, 144, 155, 154, 158, 160, 161, 157, 64, 142, + 162, 152, 149, 151, 112, 145, 0, 0, 137, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 92 }; -static const yytype_uint8 yycheck[] = +static const yytype_int16 yycheck[] = { - 4, 86, 18, 111, 48, 46, 47, 48, 48, 50, - 54, 17, 7, 46, 47, 29, 101, 50, 23, 24, - 25, 19, 40, 41, 42, 43, 44, 45, 113, 31, - 6, 139, 8, 118, 54, 55, 52, 53, 54, 55, - 46, 47, 0, 6, 50, 8, 37, 53, 48, 48, - 3, 136, 137, 138, 52, 53, 54, 55, 4, 5, - 48, 146, 48, 9, 10, 11, 12, 13, 14, 15, - 16, 48, 28, 48, 20, 21, 22, 81, 45, 31, - 26, 27, 49, 19, 48, 34, 48, 17, 34, 40, - 36, 38, 35, 39, 35, 30, 50, 19, 48, 32, - 48, 17, 40, 48, 48, 29, 48, 19, 75, 76, - 77, 78, 17, 33, 48, 6, 48, 48, 17, 48, - 18, 48, 19, 46, 18, 18, 18, 142, 18, 159, - 124, 99, 136, 137, 74, 127, 115, 138 + 4, 86, 4, 5, 52, 53, 18, 9, 10, 11, + 12, 13, 14, 15, 16, 111, 101, 45, 20, 21, + 22, 49, 48, 48, 26, 27, 17, 52, 113, 7, + 29, 19, 34, 118, 36, 31, 37, 39, 50, 51, + 52, 53, 48, 139, 23, 24, 25, 75, 76, 77, + 78, 136, 137, 138, 48, 46, 47, 6, 49, 8, + 51, 146, 50, 51, 52, 53, 40, 41, 42, 43, + 44, 45, 0, 46, 47, 48, 49, 81, 46, 47, + 6, 49, 8, 3, 48, 48, 48, 48, 28, 31, + 19, 48, 48, 34, 40, 38, 17, 35, 35, 30, + 48, 48, 32, 48, 48, 19, 17, 40, 17, 19, + 49, 29, 48, 48, 48, 6, 48, 33, 17, 19, + 18, 48, 46, 48, 18, 18, 18, 142, 18, 124, + 159, 138, 136, 137, 99, 127, -1, -1, 115, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 74 }; - /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ +/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + state STATE-NUM. */ static const yytype_int8 yystos[] = { 0, 4, 5, 9, 10, 11, 12, 13, 14, 15, - 16, 20, 21, 22, 26, 27, 34, 36, 39, 58, - 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, - 69, 70, 71, 76, 79, 80, 81, 82, 93, 94, - 95, 6, 8, 6, 8, 17, 46, 47, 50, 53, - 78, 83, 84, 48, 54, 85, 86, 48, 7, 29, - 31, 48, 48, 37, 59, 0, 3, 96, 48, 48, - 48, 48, 84, 84, 19, 52, 53, 54, 55, 28, - 31, 19, 87, 48, 48, 34, 40, 38, 17, 35, - 35, 18, 83, 84, 84, 84, 84, 48, 48, 86, - 30, 32, 89, 48, 78, 50, 48, 73, 48, 48, - 19, 88, 87, 17, 78, 86, 90, 91, 40, 29, - 23, 24, 25, 75, 19, 72, 17, 48, 89, 78, - 40, 41, 42, 43, 44, 45, 92, 92, 33, 78, - 6, 17, 73, 18, 48, 88, 19, 77, 78, 86, - 78, 86, 90, 89, 48, 46, 74, 72, 18, 78, - 18, 18, 77 + 16, 20, 21, 22, 26, 27, 34, 36, 39, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 74, 77, 78, 79, 80, 91, 92, + 93, 6, 8, 6, 8, 17, 46, 47, 49, 51, + 76, 81, 82, 48, 52, 83, 84, 48, 7, 29, + 31, 48, 48, 37, 57, 0, 3, 94, 48, 48, + 48, 48, 82, 82, 19, 50, 51, 52, 53, 28, + 31, 19, 85, 48, 48, 34, 40, 38, 17, 35, + 35, 18, 81, 82, 82, 82, 82, 48, 48, 84, + 30, 32, 87, 48, 76, 49, 48, 71, 48, 48, + 19, 86, 85, 17, 76, 84, 88, 89, 40, 29, + 23, 24, 25, 73, 19, 70, 17, 48, 87, 76, + 40, 41, 42, 43, 44, 45, 90, 90, 33, 76, + 6, 17, 71, 18, 48, 86, 19, 75, 76, 84, + 76, 84, 88, 87, 48, 46, 72, 70, 18, 76, + 18, 18, 75 }; - /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ static const yytype_int8 yyr1[] = { - 0, 57, 58, 59, 59, 59, 59, 59, 59, 59, - 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, - 59, 59, 59, 60, 61, 62, 63, 64, 65, 66, - 67, 68, 69, 70, 71, 72, 72, 73, 73, 74, - 75, 75, 75, 76, 77, 77, 78, 78, 78, 79, - 80, 81, 82, 83, 83, 84, 84, 84, 84, 84, - 84, 84, 85, 85, 86, 86, 87, 87, 88, 88, - 89, 89, 90, 90, 90, 91, 91, 91, 91, 92, - 92, 92, 92, 92, 92, 93, 94, 95, 96, 96 + 0, 55, 56, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 70, 71, 71, 72, + 73, 73, 73, 74, 75, 75, 76, 76, 76, 77, + 78, 79, 80, 81, 81, 82, 82, 82, 82, 82, + 82, 82, 83, 83, 84, 84, 85, 85, 86, 86, + 87, 87, 88, 88, 88, 89, 89, 89, 89, 90, + 90, 90, 90, 90, 90, 91, 92, 93, 94, 94 }; - /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ static const yytype_int8 yyr2[] = { 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, @@ -849,6 +855,7 @@ enum { YYENOMEM = -2 }; #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab #define YYRECOVERING() (!!yyerrstatus) @@ -916,12 +923,19 @@ do { \ } while (0) -/* YY_LOCATION_PRINT -- Print the location on the stream. +/* YYLOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ -# ifndef YY_LOCATION_PRINT -# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# ifndef YYLOCATION_PRINT + +# if defined YY_LOCATION_PRINT + + /* Temporary convenience wrapper in case some people defined the + undocumented and private YY_LOCATION_PRINT macros. */ +# define YYLOCATION_PRINT(File, Loc) YY_LOCATION_PRINT(File, *(Loc)) + +# elif defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Print *YYLOCP on YYO. Private, do not rely on its existence. */ @@ -949,15 +963,23 @@ yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) res += YYFPRINTF (yyo, "-%d", end_col); } return res; - } +} + +# define YYLOCATION_PRINT yy_location_print_ -# define YY_LOCATION_PRINT(File, Loc) \ - yy_location_print_ (File, &(Loc)) + /* Temporary convenience wrapper in case some people defined the + undocumented and private YY_LOCATION_PRINT macros. */ +# define YY_LOCATION_PRINT(File, Loc) YYLOCATION_PRINT(File, &(Loc)) # else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) + +# define YYLOCATION_PRINT(File, Loc) ((void) 0) + /* Temporary convenience wrapper in case some people defined the + undocumented and private YY_LOCATION_PRINT macros. */ +# define YY_LOCATION_PRINT YYLOCATION_PRINT + # endif -# endif /* !defined YY_LOCATION_PRINT */ +# endif /* !defined YYLOCATION_PRINT */ # define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ @@ -981,19 +1003,15 @@ yy_symbol_value_print (FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, const char * sql_string, ParsedSqlResult * sql_result, void * scanner) { FILE *yyoutput = yyo; - YYUSE (yyoutput); - YYUSE (yylocationp); - YYUSE (sql_string); - YYUSE (sql_result); - YYUSE (scanner); + YY_USE (yyoutput); + YY_USE (yylocationp); + YY_USE (sql_string); + YY_USE (sql_result); + YY_USE (scanner); if (!yyvaluep) return; -# ifdef YYPRINT - if (yykind < YYNTOKENS) - YYPRINT (yyo, yytoknum[yykind], *yyvaluep); -# endif YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YYUSE (yykind); + YY_USE (yykind); YY_IGNORE_MAYBE_UNINITIALIZED_END } @@ -1009,7 +1027,7 @@ yy_symbol_print (FILE *yyo, YYFPRINTF (yyo, "%s %s (", yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); - YY_LOCATION_PRINT (yyo, *yylocationp); + YYLOCATION_PRINT (yyo, yylocationp); YYFPRINTF (yyo, ": "); yy_symbol_value_print (yyo, yykind, yyvaluep, yylocationp, sql_string, sql_result, scanner); YYFPRINTF (yyo, ")"); @@ -1378,17 +1396,17 @@ static void yydestruct (const char *yymsg, yysymbol_kind_t yykind, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, const char * sql_string, ParsedSqlResult * sql_result, void * scanner) { - YYUSE (yyvaluep); - YYUSE (yylocationp); - YYUSE (sql_string); - YYUSE (sql_result); - YYUSE (scanner); + YY_USE (yyvaluep); + YY_USE (yylocationp); + YY_USE (sql_string); + YY_USE (sql_result); + YY_USE (scanner); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YYUSE (yykind); + YY_USE (yykind); YY_IGNORE_MAYBE_UNINITIALIZED_END } @@ -1477,6 +1495,7 @@ YYLTYPE yylloc = yyloc_default; YYDPRINTF ((stderr, "Starting parse\n")); yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; goto yysetstate; @@ -1503,7 +1522,7 @@ YYLTYPE yylloc = yyloc_default; if (yyss + yystacksize - 1 <= yyssp) #if !defined yyoverflow && !defined YYSTACK_RELOCATE - goto yyexhaustedlab; + YYNOMEM; #else { /* Get the current used size of the three stacks, in elements. */ @@ -1534,7 +1553,7 @@ YYLTYPE yylloc = yyloc_default; # else /* defined YYSTACK_RELOCATE */ /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) - goto yyexhaustedlab; + YYNOMEM; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; @@ -1545,7 +1564,7 @@ YYLTYPE yylloc = yyloc_default; YY_CAST (union yyalloc *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); if (! yyptr) - goto yyexhaustedlab; + YYNOMEM; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); YYSTACK_RELOCATE (yyls_alloc, yyls); @@ -1569,6 +1588,7 @@ YYLTYPE yylloc = yyloc_default; } #endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + if (yystate == YYFINAL) YYACCEPT; @@ -1685,93 +1705,93 @@ YYLTYPE yylloc = yyloc_default; switch (yyn) { case 2: /* commands: command_wrapper opt_semicolon */ -#line 172 "yacc_sql.y" +#line 174 "yacc_sql.y" { std::unique_ptr sql_node = std::unique_ptr((yyvsp[-1].sql_node)); sql_result->add_sql_node(std::move(sql_node)); } -#line 1694 "yacc_sql.cpp" +#line 1714 "yacc_sql.cpp" break; case 23: /* exit_stmt: EXIT */ -#line 202 "yacc_sql.y" +#line 204 "yacc_sql.y" { (void)yynerrs; // 这么写为了消除yynerrs未使用的告警。如果你有更好的方法欢迎提PR (yyval.sql_node) = new ParsedSqlNode(SCF_EXIT); } -#line 1703 "yacc_sql.cpp" +#line 1723 "yacc_sql.cpp" break; case 24: /* help_stmt: HELP */ -#line 208 "yacc_sql.y" +#line 210 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_HELP); } -#line 1711 "yacc_sql.cpp" +#line 1731 "yacc_sql.cpp" break; case 25: /* sync_stmt: SYNC */ -#line 213 "yacc_sql.y" +#line 215 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_SYNC); } -#line 1719 "yacc_sql.cpp" +#line 1739 "yacc_sql.cpp" break; case 26: /* begin_stmt: TRX_BEGIN */ -#line 219 "yacc_sql.y" +#line 221 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_BEGIN); } -#line 1727 "yacc_sql.cpp" +#line 1747 "yacc_sql.cpp" break; case 27: /* commit_stmt: TRX_COMMIT */ -#line 225 "yacc_sql.y" +#line 227 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_COMMIT); } -#line 1735 "yacc_sql.cpp" +#line 1755 "yacc_sql.cpp" break; case 28: /* rollback_stmt: TRX_ROLLBACK */ -#line 231 "yacc_sql.y" +#line 233 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_ROLLBACK); } -#line 1743 "yacc_sql.cpp" +#line 1763 "yacc_sql.cpp" break; case 29: /* drop_table_stmt: DROP TABLE ID */ -#line 237 "yacc_sql.y" +#line 239 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_DROP_TABLE); (yyval.sql_node)->drop_table.relation_name = (yyvsp[0].string); free((yyvsp[0].string)); } -#line 1753 "yacc_sql.cpp" +#line 1773 "yacc_sql.cpp" break; case 30: /* show_tables_stmt: SHOW TABLES */ -#line 244 "yacc_sql.y" +#line 246 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_SHOW_TABLES); } -#line 1761 "yacc_sql.cpp" +#line 1781 "yacc_sql.cpp" break; case 31: /* desc_table_stmt: DESC ID */ -#line 250 "yacc_sql.y" +#line 252 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_DESC_TABLE); (yyval.sql_node)->desc_table.relation_name = (yyvsp[0].string); free((yyvsp[0].string)); } -#line 1771 "yacc_sql.cpp" +#line 1791 "yacc_sql.cpp" break; case 32: /* create_index_stmt: CREATE INDEX ID ON ID LBRACE ID RBRACE */ -#line 259 "yacc_sql.y" +#line 261 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_CREATE_INDEX); CreateIndexSqlNode &create_index = (yyval.sql_node)->create_index; @@ -1782,11 +1802,11 @@ YYLTYPE yylloc = yyloc_default; free((yyvsp[-3].string)); free((yyvsp[-1].string)); } -#line 1786 "yacc_sql.cpp" +#line 1806 "yacc_sql.cpp" break; case 33: /* drop_index_stmt: DROP INDEX ID ON ID */ -#line 273 "yacc_sql.y" +#line 275 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_DROP_INDEX); (yyval.sql_node)->drop_index.index_name = (yyvsp[-2].string); @@ -1794,11 +1814,11 @@ YYLTYPE yylloc = yyloc_default; free((yyvsp[-2].string)); free((yyvsp[0].string)); } -#line 1798 "yacc_sql.cpp" +#line 1818 "yacc_sql.cpp" break; case 34: /* create_table_stmt: CREATE TABLE ID LBRACE attr_def attr_def_list RBRACE */ -#line 283 "yacc_sql.y" +#line 285 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_CREATE_TABLE); CreateTableSqlNode &create_table = (yyval.sql_node)->create_table; @@ -1809,24 +1829,25 @@ YYLTYPE yylloc = yyloc_default; if (src_attrs != nullptr) { create_table.attr_infos.swap(*src_attrs); + delete src_attrs; } create_table.attr_infos.emplace_back(*(yyvsp[-2].attr_info)); std::reverse(create_table.attr_infos.begin(), create_table.attr_infos.end()); delete (yyvsp[-2].attr_info); } -#line 1818 "yacc_sql.cpp" +#line 1839 "yacc_sql.cpp" break; case 35: /* attr_def_list: %empty */ -#line 301 "yacc_sql.y" +#line 304 "yacc_sql.y" { (yyval.attr_infos) = nullptr; } -#line 1826 "yacc_sql.cpp" +#line 1847 "yacc_sql.cpp" break; case 36: /* attr_def_list: COMMA attr_def attr_def_list */ -#line 305 "yacc_sql.y" +#line 308 "yacc_sql.y" { if ((yyvsp[0].attr_infos) != nullptr) { (yyval.attr_infos) = (yyvsp[0].attr_infos); @@ -1836,11 +1857,11 @@ YYLTYPE yylloc = yyloc_default; (yyval.attr_infos)->emplace_back(*(yyvsp[-1].attr_info)); delete (yyvsp[-1].attr_info); } -#line 1840 "yacc_sql.cpp" +#line 1861 "yacc_sql.cpp" break; case 37: /* attr_def: ID type LBRACE number RBRACE */ -#line 318 "yacc_sql.y" +#line 321 "yacc_sql.y" { (yyval.attr_info) = new AttrInfoSqlNode; (yyval.attr_info)->type = (AttrType)(yyvsp[-3].number); @@ -1848,11 +1869,11 @@ YYLTYPE yylloc = yyloc_default; (yyval.attr_info)->length = (yyvsp[-1].number); free((yyvsp[-4].string)); } -#line 1852 "yacc_sql.cpp" +#line 1873 "yacc_sql.cpp" break; case 38: /* attr_def: ID type */ -#line 326 "yacc_sql.y" +#line 329 "yacc_sql.y" { (yyval.attr_info) = new AttrInfoSqlNode; (yyval.attr_info)->type = (AttrType)(yyvsp[0].number); @@ -1860,59 +1881,60 @@ YYLTYPE yylloc = yyloc_default; (yyval.attr_info)->length = 4; free((yyvsp[-1].string)); } -#line 1864 "yacc_sql.cpp" +#line 1885 "yacc_sql.cpp" break; case 39: /* number: NUMBER */ -#line 335 "yacc_sql.y" +#line 338 "yacc_sql.y" {(yyval.number) = (yyvsp[0].number);} -#line 1870 "yacc_sql.cpp" +#line 1891 "yacc_sql.cpp" break; case 40: /* type: INT_T */ -#line 338 "yacc_sql.y" +#line 341 "yacc_sql.y" { (yyval.number)=INTS; } -#line 1876 "yacc_sql.cpp" +#line 1897 "yacc_sql.cpp" break; case 41: /* type: STRING_T */ -#line 339 "yacc_sql.y" +#line 342 "yacc_sql.y" { (yyval.number)=CHARS; } -#line 1882 "yacc_sql.cpp" +#line 1903 "yacc_sql.cpp" break; case 42: /* type: FLOAT_T */ -#line 340 "yacc_sql.y" +#line 343 "yacc_sql.y" { (yyval.number)=FLOATS; } -#line 1888 "yacc_sql.cpp" +#line 1909 "yacc_sql.cpp" break; case 43: /* insert_stmt: INSERT INTO ID VALUES LBRACE value value_list RBRACE */ -#line 344 "yacc_sql.y" +#line 347 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_INSERT); (yyval.sql_node)->insertion.relation_name = (yyvsp[-5].string); if ((yyvsp[-1].value_list) != nullptr) { (yyval.sql_node)->insertion.values.swap(*(yyvsp[-1].value_list)); + delete (yyvsp[-1].value_list); } (yyval.sql_node)->insertion.values.emplace_back(*(yyvsp[-2].value)); std::reverse((yyval.sql_node)->insertion.values.begin(), (yyval.sql_node)->insertion.values.end()); delete (yyvsp[-2].value); free((yyvsp[-5].string)); } -#line 1904 "yacc_sql.cpp" +#line 1926 "yacc_sql.cpp" break; case 44: /* value_list: %empty */ -#line 359 "yacc_sql.y" +#line 363 "yacc_sql.y" { (yyval.value_list) = nullptr; } -#line 1912 "yacc_sql.cpp" +#line 1934 "yacc_sql.cpp" break; case 45: /* value_list: COMMA value value_list */ -#line 362 "yacc_sql.y" +#line 366 "yacc_sql.y" { if ((yyvsp[0].value_list) != nullptr) { (yyval.value_list) = (yyvsp[0].value_list); @@ -1922,38 +1944,40 @@ YYLTYPE yylloc = yyloc_default; (yyval.value_list)->emplace_back(*(yyvsp[-1].value)); delete (yyvsp[-1].value); } -#line 1926 "yacc_sql.cpp" +#line 1948 "yacc_sql.cpp" break; case 46: /* value: NUMBER */ -#line 373 "yacc_sql.y" +#line 377 "yacc_sql.y" { (yyval.value) = new Value((int)(yyvsp[0].number)); (yyloc) = (yylsp[0]); } -#line 1935 "yacc_sql.cpp" +#line 1957 "yacc_sql.cpp" break; case 47: /* value: FLOAT */ -#line 377 "yacc_sql.y" +#line 381 "yacc_sql.y" { (yyval.value) = new Value((float)(yyvsp[0].floats)); + (yyloc) = (yylsp[0]); } -#line 1943 "yacc_sql.cpp" +#line 1966 "yacc_sql.cpp" break; case 48: /* value: SSS */ -#line 380 "yacc_sql.y" +#line 385 "yacc_sql.y" { char *tmp = common::substr((yyvsp[0].string),1,strlen((yyvsp[0].string))-2); (yyval.value) = new Value(tmp); free(tmp); + free((yyvsp[0].string)); } -#line 1953 "yacc_sql.cpp" +#line 1977 "yacc_sql.cpp" break; case 49: /* delete_stmt: DELETE FROM ID where */ -#line 389 "yacc_sql.y" +#line 395 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_DELETE); (yyval.sql_node)->deletion.relation_name = (yyvsp[-1].string); @@ -1963,11 +1987,11 @@ YYLTYPE yylloc = yyloc_default; } free((yyvsp[-1].string)); } -#line 1967 "yacc_sql.cpp" +#line 1991 "yacc_sql.cpp" break; case 50: /* update_stmt: UPDATE ID SET ID EQ value where */ -#line 401 "yacc_sql.y" +#line 407 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_UPDATE); (yyval.sql_node)->update.relation_name = (yyvsp[-5].string); @@ -1980,11 +2004,11 @@ YYLTYPE yylloc = yyloc_default; free((yyvsp[-5].string)); free((yyvsp[-3].string)); } -#line 1984 "yacc_sql.cpp" +#line 2008 "yacc_sql.cpp" break; case 51: /* select_stmt: SELECT select_attr FROM ID rel_list where */ -#line 416 "yacc_sql.y" +#line 422 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_SELECT); if ((yyvsp[-4].rel_attr_list) != nullptr) { @@ -2004,32 +2028,32 @@ YYLTYPE yylloc = yyloc_default; } free((yyvsp[-2].string)); } -#line 2008 "yacc_sql.cpp" +#line 2032 "yacc_sql.cpp" break; case 52: /* calc_stmt: CALC expression_list */ -#line 438 "yacc_sql.y" +#line 444 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_CALC); std::reverse((yyvsp[0].expression_list)->begin(), (yyvsp[0].expression_list)->end()); (yyval.sql_node)->calc.expressions.swap(*(yyvsp[0].expression_list)); delete (yyvsp[0].expression_list); } -#line 2019 "yacc_sql.cpp" +#line 2043 "yacc_sql.cpp" break; case 53: /* expression_list: expression */ -#line 448 "yacc_sql.y" +#line 454 "yacc_sql.y" { (yyval.expression_list) = new std::vector; (yyval.expression_list)->emplace_back((yyvsp[0].expression)); } -#line 2028 "yacc_sql.cpp" +#line 2052 "yacc_sql.cpp" break; case 54: /* expression_list: expression COMMA expression_list */ -#line 452 "yacc_sql.y" - { +#line 459 "yacc_sql.y" + { if ((yyvsp[0].expression_list) != nullptr) { (yyval.expression_list) = (yyvsp[0].expression_list); } else { @@ -2037,70 +2061,70 @@ YYLTYPE yylloc = yyloc_default; } (yyval.expression_list)->emplace_back((yyvsp[-2].expression)); } -#line 2041 "yacc_sql.cpp" +#line 2065 "yacc_sql.cpp" break; case 55: /* expression: expression '+' expression */ -#line 462 "yacc_sql.y" +#line 469 "yacc_sql.y" { (yyval.expression) = create_arithmetic_expression(ArithmeticExpr::Type::ADD, (yyvsp[-2].expression), (yyvsp[0].expression), sql_string, &(yyloc)); } -#line 2049 "yacc_sql.cpp" +#line 2073 "yacc_sql.cpp" break; case 56: /* expression: expression '-' expression */ -#line 465 "yacc_sql.y" +#line 472 "yacc_sql.y" { (yyval.expression) = create_arithmetic_expression(ArithmeticExpr::Type::SUB, (yyvsp[-2].expression), (yyvsp[0].expression), sql_string, &(yyloc)); } -#line 2057 "yacc_sql.cpp" +#line 2081 "yacc_sql.cpp" break; case 57: /* expression: expression '*' expression */ -#line 468 "yacc_sql.y" +#line 475 "yacc_sql.y" { (yyval.expression) = create_arithmetic_expression(ArithmeticExpr::Type::MUL, (yyvsp[-2].expression), (yyvsp[0].expression), sql_string, &(yyloc)); } -#line 2065 "yacc_sql.cpp" +#line 2089 "yacc_sql.cpp" break; case 58: /* expression: expression '/' expression */ -#line 471 "yacc_sql.y" +#line 478 "yacc_sql.y" { (yyval.expression) = create_arithmetic_expression(ArithmeticExpr::Type::DIV, (yyvsp[-2].expression), (yyvsp[0].expression), sql_string, &(yyloc)); } -#line 2073 "yacc_sql.cpp" +#line 2097 "yacc_sql.cpp" break; case 59: /* expression: LBRACE expression RBRACE */ -#line 474 "yacc_sql.y" +#line 481 "yacc_sql.y" { (yyval.expression) = (yyvsp[-1].expression); (yyval.expression)->set_name(token_name(sql_string, &(yyloc))); } -#line 2082 "yacc_sql.cpp" +#line 2106 "yacc_sql.cpp" break; case 60: /* expression: '-' expression */ -#line 478 "yacc_sql.y" +#line 485 "yacc_sql.y" { (yyval.expression) = create_arithmetic_expression(ArithmeticExpr::Type::NEGATIVE, (yyvsp[0].expression), nullptr, sql_string, &(yyloc)); } -#line 2090 "yacc_sql.cpp" +#line 2114 "yacc_sql.cpp" break; case 61: /* expression: value */ -#line 481 "yacc_sql.y" +#line 488 "yacc_sql.y" { (yyval.expression) = new ValueExpr(*(yyvsp[0].value)); (yyval.expression)->set_name(token_name(sql_string, &(yyloc))); delete (yyvsp[0].value); } -#line 2100 "yacc_sql.cpp" +#line 2124 "yacc_sql.cpp" break; case 62: /* select_attr: '*' */ -#line 489 "yacc_sql.y" +#line 496 "yacc_sql.y" { (yyval.rel_attr_list) = new std::vector; RelAttrSqlNode attr; @@ -2108,11 +2132,11 @@ YYLTYPE yylloc = yyloc_default; attr.attribute_name = "*"; (yyval.rel_attr_list)->emplace_back(attr); } -#line 2112 "yacc_sql.cpp" +#line 2136 "yacc_sql.cpp" break; case 63: /* select_attr: rel_attr attr_list */ -#line 496 "yacc_sql.y" +#line 503 "yacc_sql.y" { if ((yyvsp[0].rel_attr_list) != nullptr) { (yyval.rel_attr_list) = (yyvsp[0].rel_attr_list); @@ -2122,21 +2146,21 @@ YYLTYPE yylloc = yyloc_default; (yyval.rel_attr_list)->emplace_back(*(yyvsp[-1].rel_attr)); delete (yyvsp[-1].rel_attr); } -#line 2126 "yacc_sql.cpp" +#line 2150 "yacc_sql.cpp" break; case 64: /* rel_attr: ID */ -#line 508 "yacc_sql.y" +#line 515 "yacc_sql.y" { (yyval.rel_attr) = new RelAttrSqlNode; (yyval.rel_attr)->attribute_name = (yyvsp[0].string); free((yyvsp[0].string)); } -#line 2136 "yacc_sql.cpp" +#line 2160 "yacc_sql.cpp" break; case 65: /* rel_attr: ID DOT ID */ -#line 513 "yacc_sql.y" +#line 520 "yacc_sql.y" { (yyval.rel_attr) = new RelAttrSqlNode; (yyval.rel_attr)->relation_name = (yyvsp[-2].string); @@ -2144,19 +2168,19 @@ YYLTYPE yylloc = yyloc_default; free((yyvsp[-2].string)); free((yyvsp[0].string)); } -#line 2148 "yacc_sql.cpp" +#line 2172 "yacc_sql.cpp" break; case 66: /* attr_list: %empty */ -#line 524 "yacc_sql.y" +#line 531 "yacc_sql.y" { (yyval.rel_attr_list) = nullptr; } -#line 2156 "yacc_sql.cpp" +#line 2180 "yacc_sql.cpp" break; case 67: /* attr_list: COMMA rel_attr attr_list */ -#line 527 "yacc_sql.y" +#line 534 "yacc_sql.y" { if ((yyvsp[0].rel_attr_list) != nullptr) { (yyval.rel_attr_list) = (yyvsp[0].rel_attr_list); @@ -2167,19 +2191,19 @@ YYLTYPE yylloc = yyloc_default; (yyval.rel_attr_list)->emplace_back(*(yyvsp[-1].rel_attr)); delete (yyvsp[-1].rel_attr); } -#line 2171 "yacc_sql.cpp" +#line 2195 "yacc_sql.cpp" break; case 68: /* rel_list: %empty */ -#line 541 "yacc_sql.y" +#line 548 "yacc_sql.y" { (yyval.relation_list) = nullptr; } -#line 2179 "yacc_sql.cpp" +#line 2203 "yacc_sql.cpp" break; case 69: /* rel_list: COMMA ID rel_list */ -#line 544 "yacc_sql.y" +#line 551 "yacc_sql.y" { if ((yyvsp[0].relation_list) != nullptr) { (yyval.relation_list) = (yyvsp[0].relation_list); @@ -2190,55 +2214,55 @@ YYLTYPE yylloc = yyloc_default; (yyval.relation_list)->push_back((yyvsp[-1].string)); free((yyvsp[-1].string)); } -#line 2194 "yacc_sql.cpp" +#line 2218 "yacc_sql.cpp" break; case 70: /* where: %empty */ -#line 557 "yacc_sql.y" +#line 564 "yacc_sql.y" { (yyval.condition_list) = nullptr; } -#line 2202 "yacc_sql.cpp" +#line 2226 "yacc_sql.cpp" break; case 71: /* where: WHERE condition_list */ -#line 560 "yacc_sql.y" +#line 567 "yacc_sql.y" { (yyval.condition_list) = (yyvsp[0].condition_list); } -#line 2210 "yacc_sql.cpp" +#line 2234 "yacc_sql.cpp" break; case 72: /* condition_list: %empty */ -#line 566 "yacc_sql.y" +#line 573 "yacc_sql.y" { (yyval.condition_list) = nullptr; } -#line 2218 "yacc_sql.cpp" +#line 2242 "yacc_sql.cpp" break; case 73: /* condition_list: condition */ -#line 569 "yacc_sql.y" +#line 576 "yacc_sql.y" { (yyval.condition_list) = new std::vector; (yyval.condition_list)->emplace_back(*(yyvsp[0].condition)); delete (yyvsp[0].condition); } -#line 2228 "yacc_sql.cpp" +#line 2252 "yacc_sql.cpp" break; case 74: /* condition_list: condition AND condition_list */ -#line 574 "yacc_sql.y" +#line 581 "yacc_sql.y" { (yyval.condition_list) = (yyvsp[0].condition_list); (yyval.condition_list)->emplace_back(*(yyvsp[-2].condition)); delete (yyvsp[-2].condition); } -#line 2238 "yacc_sql.cpp" +#line 2262 "yacc_sql.cpp" break; case 75: /* condition: rel_attr comp_op value */ -#line 582 "yacc_sql.y" +#line 589 "yacc_sql.y" { (yyval.condition) = new ConditionSqlNode; (yyval.condition)->left_is_attr = 1; @@ -2250,11 +2274,11 @@ YYLTYPE yylloc = yyloc_default; delete (yyvsp[-2].rel_attr); delete (yyvsp[0].value); } -#line 2254 "yacc_sql.cpp" +#line 2278 "yacc_sql.cpp" break; case 76: /* condition: value comp_op value */ -#line 594 "yacc_sql.y" +#line 601 "yacc_sql.y" { (yyval.condition) = new ConditionSqlNode; (yyval.condition)->left_is_attr = 0; @@ -2266,11 +2290,11 @@ YYLTYPE yylloc = yyloc_default; delete (yyvsp[-2].value); delete (yyvsp[0].value); } -#line 2270 "yacc_sql.cpp" +#line 2294 "yacc_sql.cpp" break; case 77: /* condition: rel_attr comp_op rel_attr */ -#line 606 "yacc_sql.y" +#line 613 "yacc_sql.y" { (yyval.condition) = new ConditionSqlNode; (yyval.condition)->left_is_attr = 1; @@ -2282,11 +2306,11 @@ YYLTYPE yylloc = yyloc_default; delete (yyvsp[-2].rel_attr); delete (yyvsp[0].rel_attr); } -#line 2286 "yacc_sql.cpp" +#line 2310 "yacc_sql.cpp" break; case 78: /* condition: value comp_op rel_attr */ -#line 618 "yacc_sql.y" +#line 625 "yacc_sql.y" { (yyval.condition) = new ConditionSqlNode; (yyval.condition)->left_is_attr = 0; @@ -2298,47 +2322,47 @@ YYLTYPE yylloc = yyloc_default; delete (yyvsp[-2].value); delete (yyvsp[0].rel_attr); } -#line 2302 "yacc_sql.cpp" +#line 2326 "yacc_sql.cpp" break; case 79: /* comp_op: EQ */ -#line 632 "yacc_sql.y" +#line 639 "yacc_sql.y" { (yyval.comp) = EQUAL_TO; } -#line 2308 "yacc_sql.cpp" +#line 2332 "yacc_sql.cpp" break; case 80: /* comp_op: LT */ -#line 633 "yacc_sql.y" +#line 640 "yacc_sql.y" { (yyval.comp) = LESS_THAN; } -#line 2314 "yacc_sql.cpp" +#line 2338 "yacc_sql.cpp" break; case 81: /* comp_op: GT */ -#line 634 "yacc_sql.y" +#line 641 "yacc_sql.y" { (yyval.comp) = GREAT_THAN; } -#line 2320 "yacc_sql.cpp" +#line 2344 "yacc_sql.cpp" break; case 82: /* comp_op: LE */ -#line 635 "yacc_sql.y" +#line 642 "yacc_sql.y" { (yyval.comp) = LESS_EQUAL; } -#line 2326 "yacc_sql.cpp" +#line 2350 "yacc_sql.cpp" break; case 83: /* comp_op: GE */ -#line 636 "yacc_sql.y" +#line 643 "yacc_sql.y" { (yyval.comp) = GREAT_EQUAL; } -#line 2332 "yacc_sql.cpp" +#line 2356 "yacc_sql.cpp" break; case 84: /* comp_op: NE */ -#line 637 "yacc_sql.y" +#line 644 "yacc_sql.y" { (yyval.comp) = NOT_EQUAL; } -#line 2338 "yacc_sql.cpp" +#line 2362 "yacc_sql.cpp" break; case 85: /* load_data_stmt: LOAD DATA INFILE SSS INTO TABLE ID */ -#line 642 "yacc_sql.y" +#line 649 "yacc_sql.y" { char *tmp_file_name = common::substr((yyvsp[-3].string), 1, strlen((yyvsp[-3].string)) - 2); @@ -2348,20 +2372,20 @@ YYLTYPE yylloc = yyloc_default; free((yyvsp[0].string)); free(tmp_file_name); } -#line 2352 "yacc_sql.cpp" +#line 2376 "yacc_sql.cpp" break; case 86: /* explain_stmt: EXPLAIN command_wrapper */ -#line 655 "yacc_sql.y" +#line 662 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_EXPLAIN); (yyval.sql_node)->explain.sql_node = std::unique_ptr((yyvsp[0].sql_node)); } -#line 2361 "yacc_sql.cpp" +#line 2385 "yacc_sql.cpp" break; case 87: /* set_variable_stmt: SET ID EQ value */ -#line 663 "yacc_sql.y" +#line 670 "yacc_sql.y" { (yyval.sql_node) = new ParsedSqlNode(SCF_SET_VARIABLE); (yyval.sql_node)->set_variable.name = (yyvsp[-2].string); @@ -2369,11 +2393,11 @@ YYLTYPE yylloc = yyloc_default; free((yyvsp[-2].string)); delete (yyvsp[0].value); } -#line 2373 "yacc_sql.cpp" +#line 2397 "yacc_sql.cpp" break; -#line 2377 "yacc_sql.cpp" +#line 2401 "yacc_sql.cpp" default: break; } @@ -2450,7 +2474,7 @@ YYLTYPE yylloc = yyloc_default; } yyerror (&yylloc, sql_string, sql_result, scanner, yymsgp); if (yysyntax_error_status == YYENOMEM) - goto yyexhaustedlab; + YYNOMEM; } } @@ -2487,6 +2511,7 @@ YYLTYPE yylloc = yyloc_default; label yyerrorlab therefore never appears in user code. */ if (0) YYERROR; + ++yynerrs; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ @@ -2550,7 +2575,7 @@ YYLTYPE yylloc = yyloc_default; `-------------------------------------*/ yyacceptlab: yyresult = 0; - goto yyreturn; + goto yyreturnlab; /*-----------------------------------. @@ -2558,24 +2583,22 @@ YYLTYPE yylloc = yyloc_default; `-----------------------------------*/ yyabortlab: yyresult = 1; - goto yyreturn; + goto yyreturnlab; -#if 1 -/*-------------------------------------------------. -| yyexhaustedlab -- memory exhaustion comes here. | -`-------------------------------------------------*/ +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ yyexhaustedlab: yyerror (&yylloc, sql_string, sql_result, scanner, YY_("memory exhausted")); yyresult = 2; - goto yyreturn; -#endif + goto yyreturnlab; -/*-------------------------------------------------------. -| yyreturn -- parsing is finished, clean up and return. | -`-------------------------------------------------------*/ -yyreturn: +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at @@ -2603,7 +2626,7 @@ YYLTYPE yylloc = yyloc_default; return yyresult; } -#line 675 "yacc_sql.y" +#line 682 "yacc_sql.y" //_____________________________________________________________________ extern void scan_string(const char *str, yyscan_t scanner); diff --git a/src/observer/sql/parser/yacc_sql.hpp b/src/observer/sql/parser/yacc_sql.hpp index f128cc724..af42fa7a9 100644 --- a/src/observer/sql/parser/yacc_sql.hpp +++ b/src/observer/sql/parser/yacc_sql.hpp @@ -1,8 +1,8 @@ -/* A Bison parser, made by GNU Bison 3.7. */ +/* A Bison parser, made by GNU Bison 3.8.2. */ /* Bison interface for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . */ + along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -100,10 +100,8 @@ extern int yydebug; NUMBER = 301, /* NUMBER */ FLOAT = 302, /* FLOAT */ ID = 303, /* ID */ - PATH = 304, /* PATH */ - SSS = 305, /* SSS */ - STRING_V = 306, /* STRING_V */ - UMINUS = 307 /* UMINUS */ + SSS = 304, /* SSS */ + UMINUS = 305 /* UMINUS */ }; typedef enum yytokentype yytoken_kind_t; #endif @@ -112,7 +110,7 @@ extern int yydebug; #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { -#line 99 "yacc_sql.y" +#line 102 "yacc_sql.y" ParsedSqlNode * sql_node; ConditionSqlNode * condition; @@ -131,7 +129,7 @@ union YYSTYPE int number; float floats; -#line 135 "yacc_sql.hpp" +#line 133 "yacc_sql.hpp" }; typedef union YYSTYPE YYSTYPE; @@ -155,6 +153,8 @@ struct YYLTYPE + int yyparse (const char * sql_string, ParsedSqlResult * sql_result, void * scanner); + #endif /* !YY_YY_YACC_SQL_HPP_INCLUDED */ diff --git a/src/observer/sql/parser/yacc_sql.y b/src/observer/sql/parser/yacc_sql.y index 3199a8f54..548bee449 100644 --- a/src/observer/sql/parser/yacc_sql.y +++ b/src/observer/sql/parser/yacc_sql.y @@ -292,6 +292,7 @@ create_table_stmt: /*create table 语句的语法解析树*/ if (src_attrs != nullptr) { create_table.attr_infos.swap(*src_attrs); + delete src_attrs; } create_table.attr_infos.emplace_back(*$5); std::reverse(create_table.attr_infos.begin(), create_table.attr_infos.end()); @@ -348,6 +349,7 @@ insert_stmt: /*insert 语句的语法解析树*/ $$->insertion.relation_name = $3; if ($7 != nullptr) { $$->insertion.values.swap(*$7); + delete $7; } $$->insertion.values.emplace_back(*$6); std::reverse($$->insertion.values.begin(), $$->insertion.values.end()); @@ -384,6 +386,7 @@ value: char *tmp = common::substr($1,1,strlen($1)-2); $$ = new Value(tmp); free(tmp); + free($1); } ; diff --git a/src/observer/storage/buffer/frame.cpp b/src/observer/storage/buffer/frame.cpp index 157611489..b720eb1d1 100644 --- a/src/observer/storage/buffer/frame.cpp +++ b/src/observer/storage/buffer/frame.cpp @@ -77,12 +77,14 @@ void Frame::write_latch(intptr_t xid) } lock_.lock(); - write_locker_ = xid; - write_recursive_count_++; - LOG_DEBUG("frame write lock success." - "this=%p, pin=%d, pageNum=%d, write locker=%lx(recursive=%d), fd=%d, xid=%lx, lbt=%s", - this, pin_count_.load(), page_.page_num, write_locker_, write_recursive_count_, file_desc_, xid, lbt()); +#ifdef DEBUG + write_locker_ = xid; + ++write_recursive_count_; + TRACE("frame write lock success." + "this=%p, pin=%d, pageNum=%d, write locker=%lx(recursive=%d), fd=%d, xid=%lx, lbt=%s", + this, pin_count_.load(), page_.page_num, write_locker_, write_recursive_count_, file_desc_, xid, lbt()); +#endif } void Frame::write_unlatch() { write_unlatch(get_default_debug_xid()); } @@ -102,8 +104,8 @@ void Frame::write_unlatch(intptr_t xid) "write_locker=%lx, this=%p, pin=%d, pageNum=%d, fd=%d, xid=%lx, lbt=%s", write_locker_, this, pin_count_.load(), page_.page_num, file_desc_, xid, lbt()); - LOG_DEBUG("frame write unlock success. this=%p, pin=%d, pageNum=%d, fd=%d, xid=%lx, lbt=%s", - this, pin_count_.load(), page_.page_num, file_desc_, xid, lbt()); + TRACE("frame write unlock success. this=%p, pin=%d, pageNum=%d, fd=%d, xid=%lx, lbt=%s", + this, pin_count_.load(), page_.page_num, file_desc_, xid, lbt()); if (--write_recursive_count_ == 0) { write_locker_ = 0; @@ -133,11 +135,13 @@ void Frame::read_latch(intptr_t xid) lock_.lock_shared(); { +#ifdef DEBUG scoped_lock debug_lock(debug_lock_); - int recursive_count = ++read_lockers_[xid]; - LOG_DEBUG("frame read lock success." - "this=%p, pin=%d, pageNum=%d, fd=%d, xid=%lx, recursive=%d, lbt=%s", - this, pin_count_.load(), page_.page_num, file_desc_, xid, recursive_count, lbt()); + ++read_lockers_[xid]; + TRACE("frame read lock success." + "this=%p, pin=%d, pageNum=%d, fd=%d, xid=%lx, recursive=%d, lbt=%s", + this, pin_count_.load(), page_.page_num, file_desc_, xid, read_lockers_[xid], lbt()); +#endif } } @@ -159,12 +163,14 @@ bool Frame::try_read_latch() bool ret = lock_.try_lock_shared(); if (ret) { +#ifdef DEBUG debug_lock_.lock(); - int recursive_count = ++read_lockers_[xid]; - LOG_DEBUG("frame read lock success." - "this=%p, pin=%d, pageNum=%d, fd=%d, xid=%lx, recursive=%d, lbt=%s", - this, pin_count_.load(), page_.page_num, file_desc_, xid, recursive_count, lbt()); + ++read_lockers_[xid]; + TRACE("frame read lock success." + "this=%p, pin=%d, pageNum=%d, fd=%d, xid=%lx, recursive=%d, lbt=%s", + this, pin_count_.load(), page_.page_num, file_desc_, xid, read_lockers_[xid], lbt()); debug_lock_.unlock(); +#endif } return ret; @@ -181,7 +187,7 @@ void Frame::read_unlatch(intptr_t xid) "this=%p, pin=%d, pageNum=%d, fd=%d, xid=%lx, lbt=%s", this, pin_count_.load(), page_.page_num, file_desc_, xid, lbt()); -#if DEBUG +#ifdef DEBUG auto read_lock_iter = read_lockers_.find(xid); int recursive_count = read_lock_iter != read_lockers_.end() ? read_lock_iter->second : 0; ASSERT(recursive_count > 0, @@ -192,13 +198,12 @@ void Frame::read_unlatch(intptr_t xid) if (1 == recursive_count) { read_lockers_.erase(xid); } - -#endif // DEBUG +#endif } - LOG_DEBUG("frame read unlock success." - "this=%p, pin=%d, pageNum=%d, fd=%d, xid=%lx, lbt=%s", - this, pin_count_.load(), page_.page_num, file_desc_, xid, lbt()); + TRACE("frame read unlock success." + "this=%p, pin=%d, pageNum=%d, fd=%d, xid=%lx, lbt=%s", + this, pin_count_.load(), page_.page_num, file_desc_, xid, lbt()); lock_.unlock_shared(); } @@ -207,18 +212,18 @@ void Frame::pin() { std::scoped_lock debug_lock(debug_lock_); - intptr_t xid = get_default_debug_xid(); - int pin_count = ++pin_count_; + [[maybe_unused]] intptr_t xid = get_default_debug_xid(); + [[maybe_unused]] int pin_count = ++pin_count_; - LOG_DEBUG("after frame pin. " - "this=%p, write locker=%lx, read locker has xid %d? pin=%d, fd=%d, pageNum=%d, xid=%lx, lbt=%s", - this, write_locker_, read_lockers_.find(xid) != read_lockers_.end(), - pin_count, file_desc_, page_.page_num, xid, lbt()); + TRACE("after frame pin. " + "this=%p, write locker=%lx, read locker has xid %d? pin=%d, fd=%d, pageNum=%d, xid=%lx, lbt=%s", + this, write_locker_, read_lockers_.find(xid) != read_lockers_.end(), + pin_count, file_desc_, page_.page_num, xid, lbt()); } int Frame::unpin() { - intptr_t xid = get_default_debug_xid(); + [[maybe_unused]] intptr_t xid = get_default_debug_xid(); ASSERT(pin_count_.load() > 0, "try to unpin a frame that pin count <= 0." @@ -228,19 +233,18 @@ int Frame::unpin() std::scoped_lock debug_lock(debug_lock_); int pin_count = --pin_count_; - - LOG_DEBUG("after frame unpin. " - "this=%p, write locker=%lx, read locker has xid? %d, pin=%d, fd=%d, pageNum=%d, xid=%lx, lbt=%s", - this, write_locker_, read_lockers_.find(xid) != read_lockers_.end(), - pin_count, file_desc_, page_.page_num, xid, lbt()); + TRACE("after frame unpin. " + "this=%p, write locker=%lx, read locker has xid? %d, pin=%d, fd=%d, pageNum=%d, xid=%lx, lbt=%s", + this, write_locker_, read_lockers_.find(xid) != read_lockers_.end(), + pin_count, file_desc_, page_.page_num, xid, lbt()); if (0 == pin_count) { ASSERT(write_locker_ == 0, - "frame unpin to 0 failed while someone hold the write lock. write locker=%lx, pageNum=%d, fd=%d, xid=%lx", - write_locker_, page_.page_num, file_desc_, xid); + "frame unpin to 0 failed while someone hold the write lock. write locker=%lx, pageNum=%d, fd=%d, xid=%lx", + write_locker_, page_.page_num, file_desc_, xid); ASSERT(read_lockers_.empty(), - "frame unpin to 0 failed while someone hold the read locks. reader num=%d, pageNum=%d, fd=%d, xid=%lx", - read_lockers_.size(), page_.page_num, file_desc_, xid); + "frame unpin to 0 failed while someone hold the read locks. reader num=%d, pageNum=%d, fd=%d, xid=%lx", + read_lockers_.size(), page_.page_num, file_desc_, xid); } return pin_count; } diff --git a/src/observer/storage/clog/clog.cpp b/src/observer/storage/clog/clog.cpp index 0418fe310..7912cb699 100644 --- a/src/observer/storage/clog/clog.cpp +++ b/src/observer/storage/clog/clog.cpp @@ -69,7 +69,7 @@ const int32_t CLogRecordData::HEADER_SIZE = sizeof(CLogRecordData) - sizeof(CLog CLogRecordData::~CLogRecordData() { - if (data_ == nullptr) { + if (data_ != nullptr) { delete[] data_; } } diff --git a/test/sysbench/miniob_delete.lua b/test/sysbench/miniob_delete.lua new file mode 100644 index 000000000..1757877bb --- /dev/null +++ b/test/sysbench/miniob_delete.lua @@ -0,0 +1,42 @@ +#!/usr/bin/env sysbench +-- Copyright (C) 2006-2017 Alexey Kopytov + +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. + +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. + +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, write to the Free Software +-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +-- ---------------------------------------------------------------------- +-- Insert-Only OLTP benchmark +-- ---------------------------------------------------------------------- + +require("miniob_common") + +sysbench.cmdline.commands.prepare = { + function() + cmd_prepare() + end, + sysbench.cmdline.PARALLEL_COMMAND +} + +function prepare_statements() + +end + +function event() + local table_name = "sbtest" .. sysbench.rand.uniform(1, sysbench.opt.tables) + local k = sysbench.rand.default(1, sysbench.opt.table_size) + + con:query(string.format("DELETE FROM %s WHERE k=%d", table_name, k)) + + check_reconnect() +end \ No newline at end of file diff --git a/test/sysbench/miniob_select.lua b/test/sysbench/miniob_select.lua new file mode 100644 index 000000000..6741036ba --- /dev/null +++ b/test/sysbench/miniob_select.lua @@ -0,0 +1,35 @@ +#!/usr/bin/env sysbench +-- Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +-- miniob is licensed under Mulan PSL v2. +-- You can use this software according to the terms and conditions of the Mulan PSL v2. +-- You may obtain a copy of Mulan PSL v2 at: +-- http://license.coscl.org.cn/MulanPSL2 +-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +-- See the Mulan PSL v2 for more details. + + +require("miniob_common") + +sysbench.cmdline.commands.prepare = { + function () + cmd_prepare() + end, + sysbench.cmdline.PARALLEL_COMMAND +} + +function prepare_statements() + +end + +function event() + local table_name = "sbtest" .. sysbench.rand.uniform(1, sysbench.opt.tables) + local k_val = sysbench.rand.default(1, sysbench.opt.table_size) + + local query = "SELECT * FROM %s WHERE k=%d" + con:query(string.format(query, table_name, k_val)) + + check_reconnect() + +end diff --git a/deps/common/seda/seda_defs.h b/unittest/integer_generator_test.cpp similarity index 51% rename from deps/common/seda/seda_defs.h rename to unittest/integer_generator_test.cpp index 1e1c9aca4..e88993431 100644 --- a/deps/common/seda/seda_defs.h +++ b/unittest/integer_generator_test.cpp @@ -9,22 +9,29 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. */ // -// Created by Longda on 2021/4/21. +// Created by wangyunlai.wyl on 2024/01/15 // -#pragma once +#include "gtest/gtest.h" +#include "common/math/integer_generator.h" -#define SEDA_BASE_NAME "SEDA_BASE" -#define THREAD_POOLS_NAME "ThreadPools" -#define STAGES "STAGES" +using namespace std; +using namespace common; -#define EVENT_HISTORY "EventHistory" -#define MAX_EVENT_HISTORY_NUM "MaxEventHistoryNum" +TEST(IntegerGenerator, test) +{ + const int min = 1; + const int max = 120000; + IntegerGenerator generator(min, max); + for (int i = 0; i < 1000000; i++) { + int value = generator.next(); + ASSERT_GE(value, min); + ASSERT_LE(value, max); + } +} -#define COUNT "count" - -#define THREAD_POOL_ID "ThreadId" - -#define NEXT_STAGES "NextStages" -#define DEFAULT_THREAD_POOL "DefaultThreads" -#define METRCS_REPORT_INTERVAL "MetricsReportInterval" +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/unittest/simple_queue_test.cpp b/unittest/simple_queue_test.cpp new file mode 100644 index 000000000..9bccaf095 --- /dev/null +++ b/unittest/simple_queue_test.cpp @@ -0,0 +1,104 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2024/01/11. +// + +#include +#include "gtest/gtest.h" +#include "common/queue/simple_queue.h" + +using namespace std; +using namespace common; + +TEST(SimpleQueue, test) +{ + SimpleQueue queue; + EXPECT_EQ(0, queue.size()); + + int ret = queue.push(1); + EXPECT_EQ(0, ret); + EXPECT_EQ(1, queue.size()); + + int value; + ret = queue.pop(value); + EXPECT_EQ(0, ret); + EXPECT_EQ(1, value); + EXPECT_EQ(0, queue.size()); + + ret = queue.pop(value); + EXPECT_EQ(-1, ret); + EXPECT_EQ(0, queue.size()); + + ret = queue.push(2); + EXPECT_EQ(0, ret); + EXPECT_EQ(1, queue.size()); + + ret = queue.pop(value); + EXPECT_EQ(0, ret); + EXPECT_EQ(2, value); + EXPECT_EQ(0, queue.size()); +} + +#if 0 +TEST(SimpleQueue, test_big) +{ + SimpleQueue queue; + EXPECT_EQ(0, queue.size()); + + for (int i = 0; i < 1000000; ++i) { + int ret = queue.push(tmp); + EXPECT_EQ(0, ret); + EXPECT_EQ(i + 1, queue.size()); + } + + for (int i = 0; i < 1000000; ++i) { + int value; + int ret = queue.pop(value); + EXPECT_EQ(0, ret); + EXPECT_EQ(i, value); + EXPECT_EQ(1000000 - i - 1, queue.size()); + } +} +#endif + +TEST(SimpleQueue, test_unique_ptr) +{ + SimpleQueue> queue; + EXPECT_EQ(0, queue.size()); + + unique_ptr ptr(new int(1)); + int ret = queue.push(std::move(ptr)); + EXPECT_EQ(0, ret); + EXPECT_EQ(1, queue.size()); + EXPECT_EQ(nullptr, ptr.get()); + + unique_ptr value; + ret = queue.pop(value); + EXPECT_EQ(0, ret); + EXPECT_EQ(1, *value); + EXPECT_EQ(0, queue.size()); + + for (int i = 0; i < 1000000; ++i) { + unique_ptr ptr(new int(i)); + int ret = queue.push(std::move(ptr)); + EXPECT_EQ(0, ret); + EXPECT_EQ(i + 1, queue.size()); + EXPECT_EQ(nullptr, ptr.get()); + } + // 不做pop,检查内存是否释放(ASAN会检查) +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/unittest/thread_pool_executor_test.cpp b/unittest/thread_pool_executor_test.cpp new file mode 100644 index 000000000..777ac968d --- /dev/null +++ b/unittest/thread_pool_executor_test.cpp @@ -0,0 +1,101 @@ +/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved. +miniob is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. */ + +// +// Created by Wangyunlai on 2024/01/11. +// + +#include +#include + +#include "gtest/gtest.h" +#include "common/queue/queue.h" +#include "common/queue/simple_queue.h" +#include "common/thread/runnable.h" +#include "common/thread/thread_pool_executor.h" + +using namespace std; +using namespace common; + +class TestRunnable : public Runnable +{ +public: + TestRunnable(atomic &counter) : counter_(counter) {} + + virtual ~TestRunnable() = default; + + virtual void run() override { ++counter_; } + +private: + atomic &counter_; +}; + +class RandomSleepRunnable : public Runnable +{ +public: + RandomSleepRunnable(int min_ms, int max_ms) : min_ms_(min_ms), max_ms_(max_ms) {} + + virtual ~RandomSleepRunnable() = default; + + virtual void run() override + { + int sleep_ms = min_ms_ + rand() % (max_ms_ - min_ms_); + this_thread::sleep_for(chrono::milliseconds(sleep_ms)); + } + +private: + int min_ms_ = 0; + int max_ms_ = 0; +}; + +void test(int core_size, int max_pool_size, int keep_alive_time_ms, int test_num, function task_factory) +{ + ThreadPoolExecutor executor; + + int ret = executor.init("test", + core_size, // core_size + max_pool_size, // max_size + keep_alive_time_ms, // keep_alive_time_ms + make_unique>>()); + EXPECT_EQ(0, ret); + EXPECT_EQ(core_size, executor.pool_size()); + + for (int i = 0; i < test_num; ++i) { + ret = executor.execute(unique_ptr(task_factory())); + EXPECT_EQ(0, ret); + } + + executor.shutdown(); + executor.await_termination(); + EXPECT_EQ(executor.task_count(), test_num); +} + +TEST(ThreadPoolExecutor, test1) +{ + atomic counter(0); + test(1, 1, 60 * 1000, 1000000, [&counter]() { return new TestRunnable(counter); }); +} + +TEST(ThreadPoolExecutor, test2) +{ + atomic counter(0); + test(2, 8, 60 * 1000, 1000000, [&counter]() { return new TestRunnable(counter); }); +} + +TEST(ThreadPoolExecutor, test3) +{ + test(2, 8, 60 * 1000, 1000, []() { return new RandomSleepRunnable(10, 100); }); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}