From f2fd305a47546c1b1e0848678e2d45894964a410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Wed, 10 Jan 2024 20:29:57 +0800 Subject: [PATCH 01/39] add thread handling and use one-thread-per-connection as default --- CMakeLists.txt | 5 +- deps/common/os/process_param.h | 10 + deps/common/seda/callback.cpp | 89 ---- deps/common/seda/callback.h | 120 ----- deps/common/seda/class_factory.h | 141 ----- deps/common/seda/event_dispatcher.cpp | 138 ----- deps/common/seda/event_dispatcher.h | 160 ------ deps/common/seda/example_stage.cpp | 95 ---- deps/common/seda/example_stage.h | 37 -- deps/common/seda/init.cpp | 85 --- deps/common/seda/init.h | 40 -- deps/common/seda/kill_thread.cpp | 50 -- deps/common/seda/kill_thread.h | 94 ---- deps/common/seda/metrics_report_event.cpp | 15 - deps/common/seda/metrics_report_event.h | 33 -- deps/common/seda/metrics_stage.cpp | 128 ----- deps/common/seda/metrics_stage.h | 45 -- deps/common/seda/seda_config.cpp | 490 ------------------ deps/common/seda/seda_config.h | 233 --------- deps/common/seda/seda_defs.h | 30 -- deps/common/seda/stage.cpp | 235 --------- deps/common/seda/stage.h | 329 ------------ deps/common/seda/stage_event.cpp | 170 ------ deps/common/seda/stage_event.h | 173 ------- deps/common/seda/thread_pool.cpp | 333 ------------ deps/common/seda/thread_pool.h | 170 ------ deps/common/seda/timer_stage.cpp | 483 ----------------- deps/common/seda/timer_stage.h | 321 ------------ src/observer/common/init.cpp | 13 - src/observer/event/session_event.h | 3 +- src/observer/event/sql_event.h | 3 +- src/observer/main.cpp | 32 +- src/observer/net/cli_communicator.cpp | 6 +- src/observer/net/communicator.h | 7 +- ...e_thread_per_connection_thread_handler.cpp | 136 +++++ ...one_thread_per_connection_thread_handler.h | 33 ++ src/observer/net/server.cpp | 136 ++--- src/observer/net/server.h | 18 +- src/observer/net/server_param.h | 2 + src/observer/net/sql_task_handler.cpp | 85 +++ src/observer/net/sql_task_handler.h | 51 ++ src/observer/net/thread_handler.cpp | 35 ++ .../observer/net/thread_handler.h | 22 +- src/observer/session/session_stage.cpp | 67 +-- src/observer/session/session_stage.h | 18 +- 45 files changed, 462 insertions(+), 4457 deletions(-) delete mode 100644 deps/common/seda/callback.cpp delete mode 100644 deps/common/seda/callback.h delete mode 100644 deps/common/seda/class_factory.h delete mode 100644 deps/common/seda/event_dispatcher.cpp delete mode 100644 deps/common/seda/event_dispatcher.h delete mode 100644 deps/common/seda/example_stage.cpp delete mode 100644 deps/common/seda/example_stage.h delete mode 100644 deps/common/seda/init.cpp delete mode 100644 deps/common/seda/init.h delete mode 100644 deps/common/seda/kill_thread.cpp delete mode 100644 deps/common/seda/kill_thread.h delete mode 100644 deps/common/seda/metrics_report_event.cpp delete mode 100644 deps/common/seda/metrics_report_event.h delete mode 100644 deps/common/seda/metrics_stage.cpp delete mode 100644 deps/common/seda/metrics_stage.h delete mode 100644 deps/common/seda/seda_config.cpp delete mode 100644 deps/common/seda/seda_config.h delete mode 100644 deps/common/seda/seda_defs.h delete mode 100644 deps/common/seda/stage.cpp delete mode 100644 deps/common/seda/stage.h delete mode 100644 deps/common/seda/stage_event.cpp delete mode 100644 deps/common/seda/stage_event.h delete mode 100644 deps/common/seda/thread_pool.cpp delete mode 100644 deps/common/seda/thread_pool.h delete mode 100644 deps/common/seda/timer_stage.cpp delete mode 100644 deps/common/seda/timer_stage.h create mode 100644 src/observer/net/one_thread_per_connection_thread_handler.cpp create mode 100644 src/observer/net/one_thread_per_connection_thread_handler.h create mode 100644 src/observer/net/sql_task_handler.cpp create mode 100644 src/observer/net/sql_task_handler.h create mode 100644 src/observer/net/thread_handler.cpp rename deps/common/seda/stage_factory.h => src/observer/net/thread_handler.h (58%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2568a2cb7..c574ee961 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ 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(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) @@ -127,7 +128,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) 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/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/example_stage.h b/deps/common/seda/example_stage.h deleted file mode 100644 index 6f0409497..000000000 --- a/deps/common/seda/example_stage.h +++ /dev/null @@ -1,37 +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. -// - -#pragma once - -#include "common/seda/stage.h" - -namespace common { - -class ExampleStage : public Stage -{ -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); -}; -} // namespace common 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/init.h b/deps/common/seda/init.h deleted file mode 100644 index a2ff7bf97..000000000 --- a/deps/common/seda/init.h +++ /dev/null @@ -1,40 +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_INIT_H__ -#define __COMMON_SEDA_INIT_H__ - -// 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 - */ -int init_seda(ProcessParam *process_cfg); - -void cleanup_seda(); - -} // namespace common -#endif // __COMMON_SEDA_INIT_H__ 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_report_event.cpp b/deps/common/seda/metrics_report_event.cpp deleted file mode 100644 index 45f3a114c..000000000 --- a/deps/common/seda/metrics_report_event.cpp +++ /dev/null @@ -1,15 +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/20. -// - -#include "metrics_report_event.h" diff --git a/deps/common/seda/metrics_report_event.h b/deps/common/seda/metrics_report_event.h deleted file mode 100644 index 085d44678..000000000 --- a/deps/common/seda/metrics_report_event.h +++ /dev/null @@ -1,33 +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/20. -// - -#ifndef __COMMON_SEDA_METRICS_REPORT_EVENT_H__ -#define __COMMON_SEDA_METRICS_REPORT_EVENT_H__ - -#include "common/seda/stage_event.h" - -namespace common { -class MetricsReportEvent : public StageEvent -{ -public: - MetricsReportEvent(){ - - }; - - ~MetricsReportEvent(){ - - }; -}; -} // namespace common -#endif //__COMMON_SEDA_METRICS_REPORT_EVENT_H__ 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/seda_defs.h b/deps/common/seda/seda_defs.h deleted file mode 100644 index 1e1c9aca4..000000000 --- a/deps/common/seda/seda_defs.h +++ /dev/null @@ -1,30 +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/21. -// - -#pragma once - -#define SEDA_BASE_NAME "SEDA_BASE" -#define THREAD_POOLS_NAME "ThreadPools" -#define STAGES "STAGES" - -#define EVENT_HISTORY "EventHistory" -#define MAX_EVENT_HISTORY_NUM "MaxEventHistoryNum" - -#define COUNT "count" - -#define THREAD_POOL_ID "ThreadId" - -#define NEXT_STAGES "NextStages" -#define DEFAULT_THREAD_POOL "DefaultThreads" -#define METRCS_REPORT_INTERVAL "MetricsReportInterval" 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: - *

- */ - -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/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..113c03710 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)}." << 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,6 +140,7 @@ 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 = new Server(server_param); return server; @@ -166,7 +182,6 @@ int main(int argc, char **argv) } g_server = init_server(); - Server::init(); g_server->serve(); LOG_INFO("Server stopped"); @@ -174,16 +189,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..e25e74f4b 100644 --- a/src/observer/net/cli_communicator.cpp +++ b/src/observer/net/cli_communicator.cpp @@ -89,8 +89,10 @@ 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() diff --git a/src/observer/net/communicator.h b/src/observer/net/communicator.h index c2d82fd33..cbcfcfafa 100644 --- a/src/observer/net/communicator.h +++ b/src/observer/net/communicator.h @@ -79,6 +79,11 @@ class Communicator */ const char *addr() const { return addr_.c_str(); } + /** + * @brief 关联的文件描述符 + */ + int fd() const { return fd_; } + protected: Session *session_ = nullptr; struct event read_event_; @@ -94,7 +99,7 @@ class Communicator enum class CommunicateProtocol { PLAIN, ///< 以'\0'结尾的协议 - CLI, ///< 与客户端进行交互的协议 + CLI, ///< 与客户端进行交互的协议。CLI 不应该是一种协议,只是一种通讯的方式而已 MYSQL, ///< mysql通讯协议。具体实现参考 MysqlCommunicator }; 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..f117184a3 --- /dev/null +++ b/src/observer/net/one_thread_per_connection_thread_handler.cpp @@ -0,0 +1,136 @@ +/* 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 "net/communicator.h" +#include "net/sql_task_handler.h" + +using namespace std; + +class Worker +{ +public: + Worker(ThreadHandler &host, Communicator *communicator) + : host_(host), communicator_(communicator) + {} + + 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(); + } else { + thread_->join(); + } + delete thread_; + thread_ = nullptr; + } + return RC::SUCCESS; + } + + void operator()() + { + LOG_INFO("worker thread start. communicator = %p", communicator_); + + 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_ERROR("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_(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_; + std::thread *thread_ = nullptr; + volatile bool running_ = true; +}; + +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; +} \ No newline at end of file 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..067b09c30 --- /dev/null +++ b/src/observer/net/one_thread_per_connection_thread_handler.h @@ -0,0 +1,33 @@ +/* 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; +class OneThreadPerConnectionThreadHandler : public ThreadHandler +{ +public: + OneThreadPerConnectionThreadHandler() = default; + virtual ~OneThreadPerConnectionThreadHandler() = default; + + virtual RC new_connection(Communicator *communicator) override; + virtual RC close_connection(Communicator *communicator) override; + +private: + 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..6508a0aa4 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,22 @@ 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 "session/session.h" +#include "net/thread_handler.h" using namespace common; -Stage *Server::session_stage_ = nullptr; - ServerParam::ServerParam() { listen_addr = INADDR_ANY; @@ -57,8 +58,6 @@ Server::~Server() } } -void Server::init() { session_stage_ = get_seda_config()->get_stage(SESSION_STAGE_NAME); } - int Server::set_non_block(int fd) { int flags = fcntl(fd, F_GETFL); @@ -75,32 +74,6 @@ 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) -{ - 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; @@ -152,19 +125,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 = instance->thread_handler_->new_connection(communicator); + if (OB_FAIL(rc)) { + LOG_WARN("failed to handle new connection. rc=%s", strrc(rc)); delete communicator; return; } @@ -229,20 +192,6 @@ 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; @@ -286,20 +235,6 @@ 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; @@ -307,7 +242,7 @@ int Server::start_unix_socket_server() int Server::start_stdin_server() { - Communicator *communicator = communicator_factory_.create(server_param_.protocol); + unique_ptr communicator = communicator_factory_.create(server_param_.protocol); RC rc = communicator->init(STDIN_FILENO, new Session(Session::default_session()), "stdin"); if (OB_FAIL(rc)) { @@ -317,6 +252,7 @@ int Server::start_stdin_server() started_ = true; + SessionStage session_stage; while (started_) { SessionEvent *event = nullptr; @@ -331,21 +267,18 @@ int Server::start_stdin_server() } /// 在当前线程立即处理对应的事件 - session_stage_->handle_event(event); + session_stage.handle_request(event); } - delete communicator; - communicator = nullptr; return 0; } int Server::serve() { - evthread_use_pthreads(); - event_base_ = event_base_new(); - if (event_base_ == nullptr) { - LOG_ERROR("Failed to create event base, %s.", strerror(errno)); - exit(-1); + 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; } int retval = start(); @@ -355,18 +288,28 @@ int Server::serve() } if (!server_param_.use_std_io) { - event_base_dispatch(event_base_); - } - - if (listen_ev_ != nullptr) { - event_del(listen_ev_); - event_free(listen_ev_); - listen_ev_ = nullptr; - } - - if (event_base_ != nullptr) { - event_base_free(event_base_); - event_base_ = nullptr; + 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_ERROR("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; + } + + accept(server_socket_, 0, this); + } } started_ = false; @@ -379,8 +322,5 @@ void Server::shutdown() LOG_INFO("Server 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..2c8caf918 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 负责接收客户端消息并创建任务 @@ -31,10 +31,6 @@ class Server Server(ServerParam input_server_param); ~Server(); -public: - static void init(); - static void close_connection(Communicator *comm); - public: int serve(); void shutdown(); @@ -48,14 +44,6 @@ class Server * @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); private: /** @@ -83,12 +71,10 @@ class Server volatile bool started_ = false; int server_socket_ = -1; ///< 监听套接字,是一个描述符 - struct event_base *event_base_ = nullptr; ///< libevent对象 - struct event *listen_ev_ = nullptr; ///< libevent监听套接字事件 ServerParam server_param_; ///< 服务启动参数 CommunicatorFactory communicator_factory_; ///< 通过这个对象创建新的Communicator对象 - static common::Stage *session_stage_; ///< 通过这个对象创建新的请求任务 + ThreadHandler * thread_handler_ = nullptr; }; 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..7aac1cbdf --- /dev/null +++ b/src/observer/net/sql_task_handler.cpp @@ -0,0 +1,85 @@ +/* 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::operator()(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); + + 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..61f7e132c --- /dev/null +++ b/src/observer/net/sql_task_handler.h @@ -0,0 +1,51 @@ +/* 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; + +class SqlTaskHandler +{ +public: + SqlTaskHandler() = default; + virtual ~SqlTaskHandler() = default; + + /** + * @brief 指定连接上有数据可读时就读取消息然后处理 + * + * @param communicator + * @return RC 如果返回失败,就会断开连接 + */ + RC operator()(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..cd0f97f37 --- /dev/null +++ b/src/observer/net/thread_handler.cpp @@ -0,0 +1,35 @@ +/* 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 "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 { + LOG_ERROR("unknown thread handler: %s", name); + return nullptr; + } +} \ No newline at end of file diff --git a/deps/common/seda/stage_factory.h b/src/observer/net/thread_handler.h similarity index 58% rename from deps/common/seda/stage_factory.h rename to src/observer/net/thread_handler.h index 4d95e9b4e..9c6affcf5 100644 --- a/deps/common/seda/stage_factory.h +++ b/src/observer/net/thread_handler.h @@ -9,17 +9,25 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. */ // -// Created by Longda on 2010 +// Created by Wangyunlai on 2024/01/10. // #pragma once -#include "common/seda/class_factory.h" -#include "common/seda/stage.h" -namespace common { +#include +#include "common/rc.h" -class Stage; +class Communicator; -typedef ClassFactory StageFactory; +class ThreadHandler +{ +public: + ThreadHandler() = default; + virtual ~ThreadHandler() = default; -} // namespace common + virtual RC new_connection(Communicator *communicator) = 0; + virtual RC close_connection(Communicator *communicator) = 0; + +public: + static ThreadHandler * create(const char *name); +}; \ No newline at end of file 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..f1ed4c5a2 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,18 @@ 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: From 51b1c15131966c6ab205775b7bf17c8d180de419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Thu, 11 Jan 2024 09:08:22 +0800 Subject: [PATCH 02/39] split server to cliserver and netserver --- src/observer/main.cpp | 8 +- src/observer/net/cli_communicator.cpp | 9 +-- src/observer/net/communicator.h | 6 -- src/observer/net/server.cpp | 108 ++++++++++++++------------ src/observer/net/server.h | 38 ++++++--- 5 files changed, 100 insertions(+), 69 deletions(-) diff --git a/src/observer/main.cpp b/src/observer/main.cpp index 113c03710..3782b8982 100644 --- a/src/observer/main.cpp +++ b/src/observer/main.cpp @@ -142,7 +142,13 @@ Server *init_server() } server_param.thread_handling = process_param->thread_handling_name(); - Server *server = new Server(server_param); + Server *server = nullptr; + if (server_param.use_std_io) { + server = new CliServer(server_param); + } else { + server = new NetServer(server_param); + } + return server; } diff --git a/src/observer/net/cli_communicator.cpp b/src/observer/net/cli_communicator.cpp index e25e74f4b..0ca955625 100644 --- a/src/observer/net/cli_communicator.cpp +++ b/src/observer/net/cli_communicator.cpp @@ -98,11 +98,7 @@ bool is_exit_command(const char *cmd) 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; } @@ -134,6 +130,9 @@ RC CliCommunicator::read_event(SessionEvent *&event) { event = nullptr; char *command = read_command(); + if (nullptr == command || is_blank(command)) { + return RC::SUCCESS; + } if (is_exit_command(command)) { free(command); diff --git a/src/observer/net/communicator.h b/src/observer/net/communicator.h index cbcfcfafa..377627061 100644 --- a/src/observer/net/communicator.h +++ b/src/observer/net/communicator.h @@ -68,11 +68,6 @@ class Communicator */ Session *session() const { return session_; } - /** - * @brief libevent使用的数据,参考server.cpp - */ - struct event &read_event() { return read_event_; } - /** * @brief 对端地址 * 如果是unix socket,可能没有意义 @@ -86,7 +81,6 @@ class Communicator protected: Session *session_ = nullptr; - struct event read_event_; std::string addr_; BufferedWriter *writer_ = nullptr; int fd_ = -1; diff --git a/src/observer/net/server.cpp b/src/observer/net/server.cpp index 6508a0aa4..d67e7d9ce 100644 --- a/src/observer/net/server.cpp +++ b/src/observer/net/server.cpp @@ -37,8 +37,10 @@ See the Mulan PSL v2 for more details. */ #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; @@ -49,16 +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(); } } -int Server::set_non_block(int fd) +int NetServer::set_non_block(int fd) { int flags = fcntl(fd, F_GETFL); if (flags == -1) { @@ -74,9 +76,9 @@ int Server::set_non_block(int fd) return 0; } -void Server::accept(int fd, short ev, void *arg) +void NetServer::accept(int fd, short ev, void *arg) { - Server *instance = (Server *)arg; + NetServer *instance = (NetServer *)arg; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); @@ -135,10 +137,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 { @@ -146,7 +148,7 @@ int Server::start() } } -int Server::start_tcp_server() +int NetServer::start_tcp_server() { int ret = 0; struct sockaddr_in sa; @@ -197,7 +199,7 @@ int Server::start_tcp_server() 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); @@ -240,40 +242,7 @@ int Server::start_unix_socket_server() return 0; } -int Server::start_stdin_server() -{ - unique_ptr communicator = communicator_factory_.create(server_param_.protocol); - - 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; - } - - started_ = true; - - SessionStage session_stage; - 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; - } - - if (event == nullptr) { - break; - } - - /// 在当前线程立即处理对应的事件 - session_stage.handle_request(event); - } - - return 0; -} - -int Server::serve() +int NetServer::serve() { thread_handler_ = ThreadHandler::create(server_param_.thread_handling.c_str()); if (thread_handler_ == nullptr) { @@ -289,8 +258,8 @@ int Server::serve() if (!server_param_.use_std_io) { struct pollfd poll_fd; - poll_fd.fd = server_socket_; - poll_fd.events = POLLIN; + poll_fd.fd = server_socket_; + poll_fd.events = POLLIN; poll_fd.revents = 0; while (started_) { @@ -313,14 +282,57 @@ int Server::serve() } started_ = false; - LOG_INFO("Server quit"); + LOG_INFO("NetServer quit"); return 0; } -void Server::shutdown() +void NetServer::shutdown() { - LOG_INFO("Server shutting down"); + LOG_INFO("NetServer shutting down"); // cleanup started_ = false; } + +//////////////////////////////////////////////////////////////////////////////// + +CliServer::CliServer(const ServerParam &input_server_param) : Server(input_server_param) {} + +CliServer::~CliServer() +{ + if (started_) { + shutdown(); + } +} + +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; + } + + started_ = true; + + SqlTaskHandler task_handler; + while (started_) { + rc = task_handler(&communicator); + if (OB_FAIL(rc)) { + started_ = false; + } + } + + started_ = false; + return 0; +} + +void CliServer::shutdown() +{ + LOG_INFO("CliServer shutting down"); + + // cleanup + started_ = false; +} \ No newline at end of file diff --git a/src/observer/net/server.h b/src/observer/net/server.h index 2c8caf918..de0d0c5be 100644 --- a/src/observer/net/server.h +++ b/src/observer/net/server.h @@ -28,12 +28,24 @@ class ThreadHandler; 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: - int serve(); - void shutdown(); + NetServer(const ServerParam &input_server_param); + virtual ~NetServer(); + +public: + int serve() override; + void shutdown() override; private: /** @@ -65,16 +77,24 @@ class Server */ int start_unix_socket_server(); - int start_stdin_server(); - private: volatile bool started_ = false; int server_socket_ = -1; ///< 监听套接字,是一个描述符 - ServerParam server_param_; ///< 服务启动参数 - CommunicatorFactory communicator_factory_; ///< 通过这个对象创建新的Communicator对象 - 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 From 7572b3705a57ccc52331f41df784093767881f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Thu, 11 Jan 2024 09:15:27 +0800 Subject: [PATCH 03/39] format function name --- deps/common/os/signal.cpp | 22 +++++++++++----------- deps/common/os/signal.h | 12 ++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) 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 From b9bb0a54cc4ac85fbda717f5f427cf13c800c7b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Thu, 11 Jan 2024 09:15:44 +0800 Subject: [PATCH 04/39] format function name --- src/observer/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/observer/main.cpp b/src/observer/main.cpp index 3782b8982..b1833a21c 100644 --- a/src/observer/main.cpp +++ b/src/observer/main.cpp @@ -176,7 +176,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); From 2b14f13064c49a91f7392ab42ab73ac34ae8cf5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Thu, 11 Jan 2024 10:17:13 +0800 Subject: [PATCH 05/39] quit when type Ctrl C --- src/observer/net/cli_communicator.cpp | 34 +++++++++++++++++++++++++-- src/observer/net/cli_communicator.h | 2 ++ src/observer/net/server.cpp | 2 +- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/observer/net/cli_communicator.cpp b/src/observer/net/cli_communicator.cpp index 0ca955625..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); @@ -130,13 +155,18 @@ RC CliCommunicator::read_event(SessionEvent *&event) { event = nullptr; char *command = read_command(); - if (nullptr == command || is_blank(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..47ccb7f81 100644 --- a/src/observer/net/cli_communicator.h +++ b/src/observer/net/cli_communicator.h @@ -32,6 +32,8 @@ 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: int write_fd_ = -1; ///< 与使用远程通讯模式不同,如果读数据使用标准输入,那么输出应该是标准输出 + bool exit_ = false; }; diff --git a/src/observer/net/server.cpp b/src/observer/net/server.cpp index d67e7d9ce..bae085331 100644 --- a/src/observer/net/server.cpp +++ b/src/observer/net/server.cpp @@ -318,7 +318,7 @@ int CliServer::serve() started_ = true; SqlTaskHandler task_handler; - while (started_) { + while (started_ && !communicator.exit()) { rc = task_handler(&communicator); if (OB_FAIL(rc)) { started_ = false; From e7e5ec624504cbbba22966bf20f4b1d20bf7981c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Thu, 11 Jan 2024 20:29:07 +0800 Subject: [PATCH 06/39] add java thread pool --- deps/common/queue/queue.h | 58 ++++++ deps/common/queue/simple_queue.h | 53 ++++++ deps/common/queue/simple_queue.ipp | 44 +++++ deps/common/thread/runnable.h | 28 +++ deps/common/thread/thread_pool.h | 23 +++ deps/common/thread/thread_pool_executor.cpp | 197 ++++++++++++++++++++ deps/common/thread/thread_pool_executor.h | 99 ++++++++++ unittest/simple_queue_test.cpp | 104 +++++++++++ unittest/thread_pool_executor_test.cpp | 103 ++++++++++ 9 files changed, 709 insertions(+) create mode 100644 deps/common/queue/queue.h create mode 100644 deps/common/queue/simple_queue.h create mode 100644 deps/common/queue/simple_queue.ipp create mode 100644 deps/common/thread/runnable.h create mode 100644 deps/common/thread/thread_pool.h create mode 100644 deps/common/thread/thread_pool_executor.cpp create mode 100644 deps/common/thread/thread_pool_executor.h create mode 100644 unittest/simple_queue_test.cpp create mode 100644 unittest/thread_pool_executor_test.cpp diff --git a/deps/common/queue/queue.h b/deps/common/queue/queue.h new file mode 100644 index 000000000..a4b250279 --- /dev/null +++ b/deps/common/queue/queue.h @@ -0,0 +1,58 @@ +/* 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 任务队列接口 + * + * @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..9fcdc6a6f --- /dev/null +++ b/deps/common/queue/simple_queue.h @@ -0,0 +1,53 @@ +/* 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 任务数据类型。 + */ +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/queue/simple_queue.ipp b/deps/common/queue/simple_queue.ipp new file mode 100644 index 000000000..4a5a40b2f --- /dev/null +++ b/deps/common/queue/simple_queue.ipp @@ -0,0 +1,44 @@ +/* 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. +// + +namespace common { + +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; +} + +template +int SimpleQueue::size() const +{ + return queue_.size(); +} + +} // namespace common \ No newline at end of file diff --git a/deps/common/thread/runnable.h b/deps/common/thread/runnable.h new file mode 100644 index 000000000..7e37b5b63 --- /dev/null +++ b/deps/common/thread/runnable.h @@ -0,0 +1,28 @@ +/* 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 { + +class Runnable +{ +public: + Runnable() = default; + virtual ~Runnable() = default; + + virtual void run() = 0; +}; + +} // namespace common \ No newline at end of file diff --git a/deps/common/thread/thread_pool.h b/deps/common/thread/thread_pool.h new file mode 100644 index 000000000..ddbda566f --- /dev/null +++ b/deps/common/thread/thread_pool.h @@ -0,0 +1,23 @@ +/* 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 { +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..91c663bcb --- /dev/null +++ b/deps/common/thread/thread_pool_executor.cpp @@ -0,0 +1,197 @@ +/* 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" + +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>> &&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(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()) { + 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()); + + 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; + } + ThreadState &thread_state = iter->second; + lock_.unlock(); + + using Clock = chrono::steady_clock; + chrono::time_point idle_deadline = Clock::now(); + if (!thread_state.core_thread && keep_alive_time_ms_.count() > 0) { + idle_deadline += keep_alive_time_ms_; + } else { + idle_deadline += chrono::hours(1); + } + + /// 这里使用最粗暴的方式检测线程是否可以退出了 + /// 但是实际上,如果当前的线程个数比任务数要多,或者差不多,而且任务执行都很快的时候, + /// 并不需要保留这么多线程 + while (Clock::now() < idle_deadline) { + unique_ptr task; + int ret = work_queue_->pop(task); + if (0 == ret && task) { + thread_state.idle = false; + ++active_count_; + task->run(); + --active_count_; + thread_state.idle = true; + ++task_count_; + + if (!thread_state.core_thread && keep_alive_time_ms_.count() > 0) { + idle_deadline += keep_alive_time_ms_; + } else { + idle_deadline += chrono::hours(1); + } + } + if (state_ != State::RUNNING && work_queue_->size() == 0) { + break; + } + } + + thread_state.terminated = true; + thread_state.thread_ptr->detach(); + delete thread_state.thread_ptr; + thread_state.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_); + + thread *thread_ptr = new (nothrow) thread(&ThreadPoolExecutor::thread_func, this); + if (thread_ptr == nullptr) { + LOG_ERROR("create thread failed"); + return -1; + } + + ThreadState thread_state; + thread_state.core_thread = core_thread; + thread_state.idle = true; + thread_state.terminated = false; + thread_state.thread_ptr = thread_ptr; + threads_[thread_ptr->get_id()] = thread_state; + + if (threads_.size() > largest_pool_size_) { + largest_pool_size_ = static_cast(threads_.size()); + } + return 0; +} + +int ThreadPoolExecutor::extend_thread() +{ + lock_guard guard(lock_); + + if (static_cast(threads_.size()) >= max_pool_size_) { + return 0; + } + + return create_thread(false/*core_thread*/); +} + +} // end namespace common \ No newline at end of file diff --git a/deps/common/thread/thread_pool_executor.h b/deps/common/thread/thread_pool_executor.h new file mode 100644 index 000000000..2b602d58d --- /dev/null +++ b/deps/common/thread/thread_pool_executor.h @@ -0,0 +1,99 @@ +/* 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 做一个简化的线程池 + * + */ +class ThreadPoolExecutor +{ +public: + ThreadPoolExecutor() = default; + virtual ~ThreadPoolExecutor(); + + int init(const char *name, + int core_pool_size, + int max_pool_size, + long keep_alive_time_ms, + std::unique_ptr>> &&work_queue); + + int execute(std::unique_ptr &&task); + int shutdown(); + int await_termination(); + +public: + int active_count() const { return active_count_.load(); } + int core_pool_size() const { return core_pool_size_; } + int pool_size() const { return static_cast(threads_.size()); } + int largest_pool_size() const { return largest_pool_size_; } + int64_t task_count() const { return task_count_.load();} + +private: + int create_thread(bool core_thread); + int extend_thread(); + +private: + void thread_func(); + +private: + enum class State + { + NEW, + RUNNING, + TERMINATING, + TERMINATED + }; + + struct ThreadState + { + 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 \ No newline at end of file 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..c59458744 --- /dev/null +++ b/unittest/thread_pool_executor_test.cpp @@ -0,0 +1,103 @@ +/* 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); + } + EXPECT_GT(executor.task_count(), 0); + + executor.shutdown(); + executor.await_termination(); +} + +TEST(ThreadPoolExecutor, test) +{ + atomic counter(0); + test(1, 1, 60*1000, 1000000, [&counter](){ return new TestRunnable(counter); }); + printf("test 1 passed\n"); + + counter.store(0); + test(2, 8, 60*1000, 1000000, [&counter](){ return new TestRunnable(counter); }); + printf("test 2 passed\n"); + + test(2, 8, 60*1000, 1000, [](){ return new RandomSleepRunnable(10, 100); }); + printf("test 3 passed\n"); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file From ba732ae26d549bdff365ace936b3fccf4638c517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Thu, 11 Jan 2024 20:55:10 +0800 Subject: [PATCH 07/39] test thread pool passed --- deps/common/thread/thread_pool_executor.cpp | 10 +++++++--- deps/common/thread/thread_pool_executor.h | 3 ++- unittest/thread_pool_executor_test.cpp | 17 ++++++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/deps/common/thread/thread_pool_executor.cpp b/deps/common/thread/thread_pool_executor.cpp index 91c663bcb..db4fadd1c 100644 --- a/deps/common/thread/thread_pool_executor.cpp +++ b/deps/common/thread/thread_pool_executor.cpp @@ -163,7 +163,11 @@ void ThreadPoolExecutor::thread_func() 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"); @@ -177,7 +181,7 @@ int ThreadPoolExecutor::create_thread(bool core_thread) thread_state.thread_ptr = thread_ptr; threads_[thread_ptr->get_id()] = thread_state; - if (threads_.size() > largest_pool_size_) { + if (static_cast(threads_.size()) > largest_pool_size_) { largest_pool_size_ = static_cast(threads_.size()); } return 0; @@ -191,7 +195,7 @@ int ThreadPoolExecutor::extend_thread() return 0; } - return create_thread(false/*core_thread*/); + return create_thread_locked(false/*core_thread*/); } -} // end namespace common \ No newline at end of file +} // end namespace common diff --git a/deps/common/thread/thread_pool_executor.h b/deps/common/thread/thread_pool_executor.h index 2b602d58d..d534439a7 100644 --- a/deps/common/thread/thread_pool_executor.h +++ b/deps/common/thread/thread_pool_executor.h @@ -56,6 +56,7 @@ class ThreadPoolExecutor private: int create_thread(bool core_thread); + int create_thread_locked(bool core_thread); int extend_thread(); private: @@ -96,4 +97,4 @@ class ThreadPoolExecutor std::string pool_name_; }; -} // namespace common \ No newline at end of file +} // namespace common diff --git a/unittest/thread_pool_executor_test.cpp b/unittest/thread_pool_executor_test.cpp index c59458744..4cd69a6ef 100644 --- a/unittest/thread_pool_executor_test.cpp +++ b/unittest/thread_pool_executor_test.cpp @@ -76,28 +76,31 @@ void test(int core_size, int max_pool_size, int keep_alive_time_ms, ret = executor.execute(unique_ptr(task_factory())); EXPECT_EQ(0, ret); } - EXPECT_GT(executor.task_count(), 0); executor.shutdown(); executor.await_termination(); + EXPECT_EQ(executor.task_count(), test_num); } -TEST(ThreadPoolExecutor, test) +TEST(ThreadPoolExecutor, test1) { atomic counter(0); test(1, 1, 60*1000, 1000000, [&counter](){ return new TestRunnable(counter); }); - printf("test 1 passed\n"); +} - counter.store(0); +TEST(ThreadPoolExecutor, test2) +{ + atomic counter(0); test(2, 8, 60*1000, 1000000, [&counter](){ return new TestRunnable(counter); }); - printf("test 2 passed\n"); +} +TEST(ThreadPoolExecutor, test3) +{ test(2, 8, 60*1000, 1000, [](){ return new RandomSleepRunnable(10, 100); }); - printf("test 3 passed\n"); } int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} From b7cfeccac9ea844480085e3fc7c088c19c03b171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Fri, 12 Jan 2024 16:50:55 +0800 Subject: [PATCH 08/39] java thread pool handler --- src/observer/main.cpp | 2 +- .../net/java_thread_pool_thread_handler.cpp | 225 ++++++++++++++++++ .../net/java_thread_pool_thread_handler.h | 45 ++++ ...e_thread_per_connection_thread_handler.cpp | 31 +++ ...one_thread_per_connection_thread_handler.h | 7 +- src/observer/net/server.cpp | 18 +- src/observer/net/server.h | 3 +- src/observer/net/thread_handler.cpp | 3 + src/observer/net/thread_handler.h | 6 +- 9 files changed, 327 insertions(+), 13 deletions(-) create mode 100644 src/observer/net/java_thread_pool_thread_handler.cpp create mode 100644 src/observer/net/java_thread_pool_thread_handler.h diff --git a/src/observer/main.cpp b/src/observer/main.cpp index b1833a21c..37b87cfe4 100644 --- a/src/observer/main.cpp +++ b/src/observer/main.cpp @@ -55,7 +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)}." << endl; + cout << "-T: thread handling model. {one-thread-per-connection(default),java-thread-pool}." << endl; cout << "-n: buffer pool memory size in byte" << endl; } 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..6e5218055 --- /dev/null +++ b/src/observer/net/java_thread_pool_thread_handler.cpp @@ -0,0 +1,225 @@ +/* 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; + +struct EventCallbackAg +{ + JavaThreadPoolThreadHandler *host = nullptr; + Communicator *communicator = nullptr; + struct event *ev = nullptr; +}; + +class EventWorker : public Runnable +{ +public: + EventWorker(JavaThreadPoolThreadHandler &host) : host_(host) + {} + +public: + void run() + { + host_.event_loop_thread(); + } +private: + JavaThreadPoolThreadHandler &host_; +}; + +class SqlTaskRunner : public Runnable +{ +public: + SqlTaskRunner(JavaThreadPoolThreadHandler &host, Communicator *communicator, struct event *ev) + : host_(host), communicator_(communicator), ev_(ev) + {} + + virtual ~SqlTaskRunner() = default; + + void run() override + { + SqlTaskHandler handler; + RC rc = handler(communicator_); + if (RC::SUCCESS != rc) { + LOG_ERROR("failed to handle sql task. rc=%d", rc); + host_.close_connection(communicator_); + } else if (0 != event_add(ev_, nullptr)) { + LOG_ERROR("failed to add event"); + host_.close_connection(communicator_); + } + } +private: + JavaThreadPoolThreadHandler &host_; + 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; + } + + evthread_use_pthreads(); + 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 + unique_ptr>>(new SimpleQueue>)); + if (0 != ret) { + LOG_ERROR("failed to init thread pool executor"); + return RC::INTERNAL; + } + + ret = executor_.execute(unique_ptr(new EventWorker(*this))); + if (0 != ret) { + LOG_ERROR("failed to execute event worker"); + return RC::INTERNAL; + } + + return RC::SUCCESS; +} + +static void event_callback(evutil_socket_t fd, short event, void *arg) +{ + if (event & EV_READ) { + 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) +{ + executor_.execute(unique_ptr(new SqlTaskRunner(*this, ag->communicator, ag->ev))); +} + +void JavaThreadPoolThreadHandler::event_loop_thread() +{ + event_base_dispatch(event_base_); +} + +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; + struct event *ev = event_new(event_base_, fd, EV_READ | EV_ET, 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"); + event_free(ev); + return RC::INTERNAL; + } + + 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_free(ag->ev); + 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_) { + event_base_loopexit(event_base_, nullptr); + 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; +} \ No newline at end of file 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..f12be6fef --- /dev/null +++ b/src/observer/net/java_thread_pool_thread_handler.h @@ -0,0 +1,45 @@ +/* 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 "common/thread/thread_pool_executor.h" + +struct EventCallbackAg; + +class JavaThreadPoolThreadHandler : public ThreadHandler +{ +public: + JavaThreadPoolThreadHandler() = default; + virtual ~JavaThreadPoolThreadHandler(); + + virtual RC start() override; + virtual RC stop() override; + virtual RC await_stop() override; + + virtual RC new_connection(Communicator *communicator) override; + virtual RC close_connection(Communicator *communicator) override; + +public: + void handle_event(EventCallbackAg *ag); + void event_loop_thread(); +private: + struct event_base * event_base_ = nullptr; + common::ThreadPoolExecutor executor_; + std::map event_map_; + std::mutex lock_; +}; \ No newline at end of file diff --git a/src/observer/net/one_thread_per_connection_thread_handler.cpp b/src/observer/net/one_thread_per_connection_thread_handler.cpp index f117184a3..0b672f6d8 100644 --- a/src/observer/net/one_thread_per_connection_thread_handler.cpp +++ b/src/observer/net/one_thread_per_connection_thread_handler.cpp @@ -28,6 +28,13 @@ class Worker Worker(ThreadHandler &host, Communicator *communicator) : host_(host), communicator_(communicator) {} + ~Worker() + { + if (thread_ != nullptr) { + stop(); + join(); + } + } RC start() { @@ -98,6 +105,12 @@ class Worker volatile bool running_ = true; }; +OneThreadPerConnectionThreadHandler::~OneThreadPerConnectionThreadHandler() +{ + stop(); + await_stop(); +} + RC OneThreadPerConnectionThreadHandler::new_connection(Communicator *communicator) { lock_guard guard(lock_); @@ -133,4 +146,22 @@ RC OneThreadPerConnectionThreadHandler::close_connection(Communicator *communica 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() +{ + while (!thread_map_.empty()) { + this_thread::sleep_for(chrono::milliseconds(100)); + } + return RC::SUCCESS; } \ No newline at end of file diff --git a/src/observer/net/one_thread_per_connection_thread_handler.h b/src/observer/net/one_thread_per_connection_thread_handler.h index 067b09c30..bae82f6e1 100644 --- a/src/observer/net/one_thread_per_connection_thread_handler.h +++ b/src/observer/net/one_thread_per_connection_thread_handler.h @@ -22,11 +22,14 @@ class OneThreadPerConnectionThreadHandler : public ThreadHandler { public: OneThreadPerConnectionThreadHandler() = default; - virtual ~OneThreadPerConnectionThreadHandler() = default; + virtual ~OneThreadPerConnectionThreadHandler(); + + virtual RC start() override { return RC::SUCCESS; } + virtual RC stop() override; + virtual RC await_stop() override; virtual RC new_connection(Communicator *communicator) override; virtual RC close_connection(Communicator *communicator) override; - private: std::unordered_map thread_map_; // 当前编译器没有支持jthread std::mutex lock_; diff --git a/src/observer/net/server.cpp b/src/observer/net/server.cpp index bae085331..c28c17fa2 100644 --- a/src/observer/net/server.cpp +++ b/src/observer/net/server.cpp @@ -76,9 +76,8 @@ int NetServer::set_non_block(int fd) return 0; } -void NetServer::accept(int fd, short ev, void *arg) +void NetServer::accept(int fd) { - NetServer *instance = (NetServer *)arg; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); @@ -100,14 +99,14 @@ void NetServer::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)); @@ -118,7 +117,7 @@ void NetServer::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) { @@ -127,7 +126,7 @@ void NetServer::accept(int fd, short ev, void *arg) return; } - rc = instance->thread_handler_->new_connection(communicator); + rc = thread_handler_->new_connection(communicator); if (OB_FAIL(rc)) { LOG_WARN("failed to handle new connection. rc=%s", strrc(rc)); delete communicator; @@ -277,10 +276,15 @@ int NetServer::serve() break; } - accept(server_socket_, 0, this); + this->accept(server_socket_); } } + thread_handler_->stop(); + thread_handler_->await_stop(); + delete thread_handler_; + thread_handler_ = nullptr; + started_ = false; LOG_INFO("NetServer quit"); return 0; diff --git a/src/observer/net/server.h b/src/observer/net/server.h index de0d0c5be..895aaff89 100644 --- a/src/observer/net/server.h +++ b/src/observer/net/server.h @@ -53,9 +53,8 @@ class NetServer : public Server * @details 此函数作为libevent中监听套接字对应的回调函数 * @param fd libevent回调函数传入的参数,即监听套接字 * @param ev 本次触发的事件,通常是EV_READ - * @param arg 在注册libevent回调函数时,传入的参数,即Server对象 */ - static void accept(int fd, short ev, void *arg); + void accept(int fd); private: /** diff --git a/src/observer/net/thread_handler.cpp b/src/observer/net/thread_handler.cpp index cd0f97f37..941fcdfd9 100644 --- a/src/observer/net/thread_handler.cpp +++ b/src/observer/net/thread_handler.cpp @@ -16,6 +16,7 @@ See the Mulan PSL v2 for more details. */ #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" @@ -28,6 +29,8 @@ ThreadHandler * ThreadHandler::create(const char *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; diff --git a/src/observer/net/thread_handler.h b/src/observer/net/thread_handler.h index 9c6affcf5..aad62818d 100644 --- a/src/observer/net/thread_handler.h +++ b/src/observer/net/thread_handler.h @@ -25,9 +25,13 @@ class ThreadHandler ThreadHandler() = default; virtual ~ThreadHandler() = default; + virtual RC start() = 0; + virtual RC stop() = 0; + virtual RC await_stop() = 0; + virtual RC new_connection(Communicator *communicator) = 0; virtual RC close_connection(Communicator *communicator) = 0; - + public: static ThreadHandler * create(const char *name); }; \ No newline at end of file From 2956958e3d588b7c3991499023cde60360d107c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 10:26:52 +0800 Subject: [PATCH 09/39] support std::function in runnable --- deps/common/thread/runnable.h | 20 ++++++++- deps/common/thread/thread_pool_executor.cpp | 10 ++++- deps/common/thread/thread_pool_executor.h | 1 + .../net/java_thread_pool_thread_handler.cpp | 45 ++++++++++--------- ...e_thread_per_connection_thread_handler.cpp | 4 +- src/observer/net/server.cpp | 10 ++++- src/observer/net/thread_handler.h | 5 ++- 7 files changed, 67 insertions(+), 28 deletions(-) diff --git a/deps/common/thread/runnable.h b/deps/common/thread/runnable.h index 7e37b5b63..3b9a5eb66 100644 --- a/deps/common/thread/runnable.h +++ b/deps/common/thread/runnable.h @@ -14,6 +14,8 @@ See the Mulan PSL v2 for more details. */ #pragma once +#include + namespace common { class Runnable @@ -25,4 +27,20 @@ class Runnable virtual void run() = 0; }; -} // namespace common \ No newline at end of file +class RunnableAdaptor : public Runnable +{ +public: + RunnableAdaptor(const std::function &callable) : callable_(callable) + { + } + + void run() override + { + callable_(); + } + +private: + const std::function &callable_; +}; + +} // namespace common diff --git a/deps/common/thread/thread_pool_executor.cpp b/deps/common/thread/thread_pool_executor.cpp index db4fadd1c..c59d7edb0 100644 --- a/deps/common/thread/thread_pool_executor.cpp +++ b/deps/common/thread/thread_pool_executor.cpp @@ -75,6 +75,12 @@ int ThreadPoolExecutor::shutdown() 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) { @@ -138,9 +144,9 @@ void ThreadPoolExecutor::thread_func() ++task_count_; if (!thread_state.core_thread && keep_alive_time_ms_.count() > 0) { - idle_deadline += keep_alive_time_ms_; + idle_deadline = Clock::now() + keep_alive_time_ms_; } else { - idle_deadline += chrono::hours(1); + idle_deadline = Clock::now() + chrono::hours(1); } } if (state_ != State::RUNNING && work_queue_->size() == 0) { diff --git a/deps/common/thread/thread_pool_executor.h b/deps/common/thread/thread_pool_executor.h index d534439a7..177fe08c1 100644 --- a/deps/common/thread/thread_pool_executor.h +++ b/deps/common/thread/thread_pool_executor.h @@ -44,6 +44,7 @@ class ThreadPoolExecutor std::unique_ptr>> &&work_queue); int execute(std::unique_ptr &&task); + int execute(const std::function &callable); int shutdown(); int await_termination(); diff --git a/src/observer/net/java_thread_pool_thread_handler.cpp b/src/observer/net/java_thread_pool_thread_handler.cpp index 6e5218055..b6bd2b582 100644 --- a/src/observer/net/java_thread_pool_thread_handler.cpp +++ b/src/observer/net/java_thread_pool_thread_handler.cpp @@ -32,21 +32,6 @@ struct EventCallbackAg struct event *ev = nullptr; }; -class EventWorker : public Runnable -{ -public: - EventWorker(JavaThreadPoolThreadHandler &host) : host_(host) - {} - -public: - void run() - { - host_.event_loop_thread(); - } -private: - JavaThreadPoolThreadHandler &host_; -}; - class SqlTaskRunner : public Runnable { public: @@ -104,7 +89,8 @@ RC JavaThreadPoolThreadHandler::start() return RC::INTERNAL; } - ret = executor_.execute(unique_ptr(new EventWorker(*this))); + 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; @@ -115,7 +101,7 @@ RC JavaThreadPoolThreadHandler::start() static void event_callback(evutil_socket_t fd, short event, void *arg) { - if (event & EV_READ) { + if (event & (EV_READ | EV_CLOSED)) { EventCallbackAg *ag = (EventCallbackAg *)arg; JavaThreadPoolThreadHandler *handler = ag->host; handler->handle_event(ag); @@ -126,12 +112,30 @@ static void event_callback(evutil_socket_t fd, short event, void *arg) void JavaThreadPoolThreadHandler::handle_event(EventCallbackAg *ag) { - executor_.execute(unique_ptr(new SqlTaskRunner(*this, ag->communicator, ag->ev))); + auto sql_handler = [this, ag]() { + SqlTaskHandler handler; + RC rc = handler(ag->communicator); + if (RC::SUCCESS != rc) { + LOG_ERROR("failed to handle sql task. rc=%d", rc); + this->close_connection(ag->communicator); + } else if (0 != event_add(ag->ev, nullptr)) { + LOG_ERROR("failed to add event"); + this->close_connection(ag->communicator); + } + }; + + executor_.execute(sql_handler); } void JavaThreadPoolThreadHandler::event_loop_thread() { - event_base_dispatch(event_base_); + 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) @@ -159,6 +163,7 @@ RC JavaThreadPoolThreadHandler::new_connection(Communicator *communicator) event_free(ev); return RC::INTERNAL; } + LOG_TRACE("add event success. fd=%d, communicator=%p", fd, communicator); return RC::SUCCESS; } @@ -222,4 +227,4 @@ RC JavaThreadPoolThreadHandler::await_stop() LOG_INFO("end to await event base stopped"); return RC::SUCCESS; -} \ No newline at end of file +} diff --git a/src/observer/net/one_thread_per_connection_thread_handler.cpp b/src/observer/net/one_thread_per_connection_thread_handler.cpp index 0b672f6d8..7b3b45292 100644 --- a/src/observer/net/one_thread_per_connection_thread_handler.cpp +++ b/src/observer/net/one_thread_per_connection_thread_handler.cpp @@ -77,7 +77,7 @@ class Worker LOG_ERROR("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); + // LOG_TRACE("poll timeout. fd = %d", poll_fd.fd); continue; } @@ -164,4 +164,4 @@ RC OneThreadPerConnectionThreadHandler::await_stop() this_thread::sleep_for(chrono::milliseconds(100)); } return RC::SUCCESS; -} \ No newline at end of file +} diff --git a/src/observer/net/server.cpp b/src/observer/net/server.cpp index c28c17fa2..a336c32b4 100644 --- a/src/observer/net/server.cpp +++ b/src/observer/net/server.cpp @@ -249,6 +249,12 @@ int NetServer::serve() return -1; } + RC rc = thread_handler_->start(); + if (OB_FAIL(rc)) { + LOG_ERROR("failed to start thread handler: %s", strrc(rc)); + return -1; + } + int retval = start(); if (retval == -1) { LOG_PANIC("Failed to start network"); @@ -267,7 +273,7 @@ int NetServer::serve() LOG_ERROR("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); + // LOG_TRACE("poll timeout. fd = %d", poll_fd.fd); continue; } @@ -339,4 +345,4 @@ void CliServer::shutdown() // cleanup started_ = false; -} \ No newline at end of file +} diff --git a/src/observer/net/thread_handler.h b/src/observer/net/thread_handler.h index aad62818d..434d7ee4f 100644 --- a/src/observer/net/thread_handler.h +++ b/src/observer/net/thread_handler.h @@ -19,6 +19,9 @@ See the Mulan PSL v2 for more details. */ class Communicator; +/** + * @brief + */ class ThreadHandler { public: @@ -34,4 +37,4 @@ class ThreadHandler public: static ThreadHandler * create(const char *name); -}; \ No newline at end of file +}; From f53cb39314df1d25428753d762786a6598a9c3e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 12:16:04 +0800 Subject: [PATCH 10/39] add comments --- .../net/java_thread_pool_thread_handler.cpp | 77 +++++++++++-------- .../net/java_thread_pool_thread_handler.h | 29 ++++++- ...e_thread_per_connection_thread_handler.cpp | 10 ++- ...one_thread_per_connection_thread_handler.h | 13 ++++ src/observer/net/server.cpp | 2 +- src/observer/net/sql_task_handler.cpp | 2 +- src/observer/net/sql_task_handler.h | 11 ++- src/observer/net/thread_handler.h | 28 ++++++- 8 files changed, 126 insertions(+), 46 deletions(-) diff --git a/src/observer/net/java_thread_pool_thread_handler.cpp b/src/observer/net/java_thread_pool_thread_handler.cpp index b6bd2b582..7efcf4e5d 100644 --- a/src/observer/net/java_thread_pool_thread_handler.cpp +++ b/src/observer/net/java_thread_pool_thread_handler.cpp @@ -21,10 +21,13 @@ See the Mulan PSL v2 for more details. */ #include "common/thread/runnable.h" #include "common/queue/simple_queue.h" - using namespace std; using namespace common; +/** + * @brief libevent 消息回调函数的参数 + * + */ struct EventCallbackAg { JavaThreadPoolThreadHandler *host = nullptr; @@ -32,33 +35,6 @@ struct EventCallbackAg struct event *ev = nullptr; }; -class SqlTaskRunner : public Runnable -{ -public: - SqlTaskRunner(JavaThreadPoolThreadHandler &host, Communicator *communicator, struct event *ev) - : host_(host), communicator_(communicator), ev_(ev) - {} - - virtual ~SqlTaskRunner() = default; - - void run() override - { - SqlTaskHandler handler; - RC rc = handler(communicator_); - if (RC::SUCCESS != rc) { - LOG_ERROR("failed to handle sql task. rc=%d", rc); - host_.close_connection(communicator_); - } else if (0 != event_add(ev_, nullptr)) { - LOG_ERROR("failed to add event"); - host_.close_connection(communicator_); - } - } -private: - JavaThreadPoolThreadHandler &host_; - Communicator *communicator_ = nullptr; - struct event *ev_ = nullptr; -}; - JavaThreadPoolThreadHandler::~JavaThreadPoolThreadHandler() { this->stop(); @@ -72,13 +48,17 @@ RC JavaThreadPoolThreadHandler::start() 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 @@ -89,6 +69,8 @@ RC JavaThreadPoolThreadHandler::start() 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) { @@ -99,6 +81,16 @@ RC JavaThreadPoolThreadHandler::start() 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)) { @@ -112,13 +104,22 @@ static void event_callback(evutil_socket_t fd, short event, void *arg) void JavaThreadPoolThreadHandler::handle_event(EventCallbackAg *ag) { + /* + 当前函数是一个libevent的回调函数。按照libevent的要求,我们不能在这个函数中执行比较耗时的操作, + 因为它是运行在libevent主消息循环处理函数中的。 + + 我们这里在收到消息时就把它放到线程池中处理。 + */ + + // sql_handler 是一个回调函数 auto sql_handler = [this, ag]() { - SqlTaskHandler handler; - RC rc = handler(ag->communicator); + RC rc = sql_task_handler_.handle_event(ag->communicator); // 这里会有接收消息、处理请求然后返回结果一条龙服务 if (RC::SUCCESS != rc) { LOG_ERROR("failed to handle sql task. rc=%d", 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"); this->close_connection(ag->communicator); } @@ -130,7 +131,7 @@ void JavaThreadPoolThreadHandler::handle_event(EventCallbackAg *ag) void JavaThreadPoolThreadHandler::event_loop_thread() { LOG_INFO("event base dispatch begin"); - // event_base_dispatch 仅调用一次事件循环。 + // event_base_dispatch 也可以完成这个事情。 // event_base_loop 会等待所有事件都结束 // 如果不增加 EVLOOP_NO_EXIT_ON_EMPTY 标识,当前事件都处理完成后,就会退出循环 // 加上这个标识就是即使没有事件,也等在这里 @@ -146,6 +147,10 @@ RC JavaThreadPoolThreadHandler::new_connection(Communicator *communicator) ag->host = this; ag->communicator = communicator; ag->ev = nullptr; + /// 创建一个libevent事件对象。其中EV_READ表示可读事件,就是客户端发消息时会触发事件。 + /// EV_ET 表示边缘触发,有消息时只会触发一次,不会重复触发。这个标识在Linux平台上是支持的,但是有些平台不支持。 + /// 注意这里没有加 EV_PERSIST,表示事件触发后会自动从event_base中删除,需要自己再手动加上这个标识。这是有必 + /// 要的,因为客户端发出一个请求后,我们再返回客户端消息之前,不再希望接收新的消息。 struct event *ev = event_new(event_base_, fd, EV_READ | EV_ET, event_callback, ag); if (nullptr == ev) { LOG_ERROR("failed to create event"); @@ -185,8 +190,8 @@ RC JavaThreadPoolThreadHandler::close_connection(Communicator *communicator) } if (ag->ev) { - event_del(ag->ev); - event_free(ag->ev); + event_del(ag->ev); // 把当前事件从event_base中删除 + event_free(ag->ev); // 释放event对象 ag->ev = nullptr; } delete ag; @@ -201,7 +206,12 @@ 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(); } @@ -213,6 +223,7 @@ 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; diff --git a/src/observer/net/java_thread_pool_thread_handler.h b/src/observer/net/java_thread_pool_thread_handler.h index f12be6fef..07fe3f948 100644 --- a/src/observer/net/java_thread_pool_thread_handler.h +++ b/src/observer/net/java_thread_pool_thread_handler.h @@ -17,29 +17,54 @@ See the Mulan PSL v2 for more details. */ #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: - struct event_base * event_base_ = nullptr; - common::ThreadPoolExecutor executor_; + struct event_base * event_base_ = nullptr; /// libevent 的event_base + common::ThreadPoolExecutor executor_; /// 线程池 std::map event_map_; std::mutex lock_; + SqlTaskHandler sql_task_handler_; /// SQL请求处理器 }; \ No newline at end of file diff --git a/src/observer/net/one_thread_per_connection_thread_handler.cpp b/src/observer/net/one_thread_per_connection_thread_handler.cpp index 7b3b45292..e68bbe5c3 100644 --- a/src/observer/net/one_thread_per_connection_thread_handler.cpp +++ b/src/observer/net/one_thread_per_connection_thread_handler.cpp @@ -52,7 +52,7 @@ class Worker { if (thread_) { if (thread_->get_id() == std::this_thread::get_id()) { - thread_->detach(); + thread_->detach(); // 如果当前线程join当前线程,就会卡死 } else { thread_->join(); } @@ -86,7 +86,7 @@ class Worker break; } - RC rc = task_handler_(communicator_); + RC rc = task_handler_.handle_event(communicator_); if (OB_FAIL(rc)) { LOG_ERROR("handle error. rc = %s", strrc(rc)); break; @@ -94,13 +94,13 @@ class Worker } LOG_INFO("worker thread stop. communicator = %p", communicator_); - host_.close_connection(communicator_); + host_.close_connection(communicator_); /// 连接关闭后,当前对象会被删除 } private: ThreadHandler &host_; SqlTaskHandler task_handler_; - Communicator *communicator_; + Communicator *communicator_ = nullptr; std::thread *thread_ = nullptr; volatile bool running_ = true; }; @@ -160,8 +160,10 @@ RC OneThreadPerConnectionThreadHandler::stop() 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 index bae82f6e1..de50eeee7 100644 --- a/src/observer/net/one_thread_per_connection_thread_handler.h +++ b/src/observer/net/one_thread_per_connection_thread_handler.h @@ -18,19 +18,32 @@ See the Mulan PSL v2 for more details. */ #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 a336c32b4..95bfb41e9 100644 --- a/src/observer/net/server.cpp +++ b/src/observer/net/server.cpp @@ -329,7 +329,7 @@ int CliServer::serve() SqlTaskHandler task_handler; while (started_ && !communicator.exit()) { - rc = task_handler(&communicator); + rc = task_handler.handle_event(&communicator); if (OB_FAIL(rc)) { started_ = false; } diff --git a/src/observer/net/sql_task_handler.cpp b/src/observer/net/sql_task_handler.cpp index 7aac1cbdf..736bee731 100644 --- a/src/observer/net/sql_task_handler.cpp +++ b/src/observer/net/sql_task_handler.cpp @@ -18,7 +18,7 @@ See the Mulan PSL v2 for more details. */ #include "event/sql_event.h" #include "session/session.h" -RC SqlTaskHandler::operator()(Communicator *communicator) +RC SqlTaskHandler::handle_event(Communicator *communicator) { SessionEvent *event = nullptr; RC rc = communicator->read_event(event); diff --git a/src/observer/net/sql_task_handler.h b/src/observer/net/sql_task_handler.h index 61f7e132c..c69db20cf 100644 --- a/src/observer/net/sql_task_handler.h +++ b/src/observer/net/sql_task_handler.h @@ -25,6 +25,9 @@ See the Mulan PSL v2 for more details. */ class Communicator; class SQLStageEvent; +/** + * @brief SQL请求的处理器 + */ class SqlTaskHandler { public: @@ -33,11 +36,11 @@ class SqlTaskHandler /** * @brief 指定连接上有数据可读时就读取消息然后处理 - * - * @param communicator - * @return RC 如果返回失败,就会断开连接 + * @details 步骤包含接收请求、处理请求,然后返回应答 + * @param communicator 连接对象 + * @return RC 如果返回失败,就要断开连接 */ - RC operator()(Communicator *communicator); + RC handle_event(Communicator *communicator); RC handle_sql(SQLStageEvent *sql_event); diff --git a/src/observer/net/thread_handler.h b/src/observer/net/thread_handler.h index 434d7ee4f..dab5eab12 100644 --- a/src/observer/net/thread_handler.h +++ b/src/observer/net/thread_handler.h @@ -20,7 +20,10 @@ See the Mulan PSL v2 for more details. */ class Communicator; /** - * @brief + * @defgroup ThreadHandler + * @brief 线程池处理模型接口 + * @details 处理连接上所有的消息。可以使用不同的模型来处理,当前有一个连接一个线程的模式和线程池模式。 + * 线程模型仅处理与客户端通讯的连接,不处理observer监听套接字。 */ class ThreadHandler { @@ -28,13 +31,36 @@ class ThreadHandler 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); }; From d5c507aa92d77b18fb435b41b5e11162cea7c12d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 12:19:17 +0800 Subject: [PATCH 11/39] remove seda configuration --- etc/observer.ini | 39 --------------------------------------- 1 file changed, 39 deletions(-) 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 From 397f90a1a176951e5349957c4ac16490371da114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 14:12:18 +0800 Subject: [PATCH 12/39] set thread name --- deps/common/queue/queue.h | 7 +++- deps/common/queue/simple_queue.h | 1 + deps/common/thread/runnable.h | 8 +++++ deps/common/thread/thread_pool_executor.cpp | 16 +++++++++ deps/common/thread/thread_pool_executor.h | 21 ++++++++++++ deps/common/thread/thread_util.cpp | 33 +++++++++++++++++++ deps/common/thread/thread_util.h | 28 ++++++++++++++++ .../net/java_thread_pool_thread_handler.cpp | 8 ++--- ...e_thread_per_connection_thread_handler.cpp | 6 ++++ 9 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 deps/common/thread/thread_util.cpp create mode 100644 deps/common/thread/thread_util.h diff --git a/deps/common/queue/queue.h b/deps/common/queue/queue.h index a4b250279..8510060af 100644 --- a/deps/common/queue/queue.h +++ b/deps/common/queue/queue.h @@ -16,9 +16,14 @@ See the Mulan PSL v2 for more details. */ namespace common { +/** + * @brief 任务队列 + * @defgroup Queue + */ + /** * @brief 任务队列接口 - * + * @ingroup Queue * @tparam T 任务数据类型。 */ template diff --git a/deps/common/queue/simple_queue.h b/deps/common/queue/simple_queue.h index 9fcdc6a6f..2d5834e04 100644 --- a/deps/common/queue/simple_queue.h +++ b/deps/common/queue/simple_queue.h @@ -26,6 +26,7 @@ namespace common { * 如果想了解更高效的队列实现,请参考 [Oceanbase](https://github.com/oceanbase/oceanbase) 中 * deps/oblib/src/lib/queue/ 的一些队列的实现 * @tparam T 任务数据类型。 + * @ingroup Queue */ template class SimpleQueue : public Queue diff --git a/deps/common/thread/runnable.h b/deps/common/thread/runnable.h index 3b9a5eb66..523f0a052 100644 --- a/deps/common/thread/runnable.h +++ b/deps/common/thread/runnable.h @@ -18,6 +18,10 @@ See the Mulan PSL v2 for more details. */ namespace common { +/** + * @brief 可执行对象接口 + * @ingroup ThreadPool + */ class Runnable { public: @@ -27,6 +31,10 @@ class Runnable virtual void run() = 0; }; +/** + * @brief 可执行对象适配器,方便使用lambda表达式 + * @ingroup ThreadPool + */ class RunnableAdaptor : public Runnable { public: diff --git a/deps/common/thread/thread_pool_executor.cpp b/deps/common/thread/thread_pool_executor.cpp index c59d7edb0..600dfaa21 100644 --- a/deps/common/thread/thread_pool_executor.cpp +++ b/deps/common/thread/thread_pool_executor.cpp @@ -16,11 +16,22 @@ See the Mulan PSL v2 for more details. */ #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, @@ -112,6 +123,11 @@ 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()) { diff --git a/deps/common/thread/thread_pool_executor.h b/deps/common/thread/thread_pool_executor.h index 177fe08c1..540b94bd8 100644 --- a/deps/common/thread/thread_pool_executor.h +++ b/deps/common/thread/thread_pool_executor.h @@ -29,7 +29,14 @@ namespace common { /** * @brief 模拟java ThreadPoolExecutor 做一个简化的线程池 + * @defgroup ThreadPool + * @details 一个线程池包含一个任务队列和一组线程,当有任务提交时,线程池会从任务队列中取出任务分配给一个线程执行。 + * 这里的接口设计参考了Java的线程池ThreadPoolExecutor,但是简化了很多。 * + * 这个线程池支持自动伸缩。 + * 线程分为两类,一类是核心线程,一类是普通线程。核心线程不会退出,普通线程会在空闲一段时间后退出。 + * 线程池有一个任务队列,收到的任务会放到任务队列中。当任务队列中任务的个数比当前线程个数多时,就会 + * 创建新的线程。 */ class ThreadPoolExecutor { @@ -37,6 +44,20 @@ class ThreadPoolExecutor ThreadPoolExecutor() = default; virtual ~ThreadPoolExecutor(); + /** + * @brief 初始化线程池 + * + * @param name 线程池名称 + * @param core_size + * @param max_size + * @param keep_alive_time_ms + * @return int + */ + int init(const char *name, + int core_size, + int max_size, + long keep_alive_time_ms); + int init(const char *name, int core_pool_size, int max_pool_size, diff --git a/deps/common/thread/thread_util.cpp b/deps/common/thread/thread_util.cpp new file mode 100644 index 000000000..9c45f3d8c --- /dev/null +++ b/deps/common/thread/thread_util.cpp @@ -0,0 +1,33 @@ +/* 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/15. +// + +#include +#include + +namespace common { + +int thread_set_name(const char *name) +{ + const int namelen = 16; + char buf[namelen]; + snprintf(buf, namelen, "%s", name); + +#ifdef __APPLE__ + return pthread_setname_np(buf); +#elif __linux__ + return pthread_setname_np(pthread_self(), buf); +#endif +} + +} // namespace common \ No newline at end of file diff --git a/deps/common/thread/thread_util.h b/deps/common/thread/thread_util.h new file mode 100644 index 000000000..064ab273f --- /dev/null +++ b/deps/common/thread/thread_util.h @@ -0,0 +1,28 @@ +/* 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/15. +// + +#pragma once + +namespace common { + +/** + * @brief 设置当前线程的名字 + * @details 设置当前线程的名字可以帮助调试多线程程序,比如在gdb或者 top -H命令可以看到线程名字。 + * pthread_setname_np在Linux和Mac上实现不同。Linux上可以指定线程号设置名称,但是Mac上不行。 + * @param name 线程的名字。按照linux手册中描述,包括\0在内,不要超过16个字符 + * @return int 设置成功返回0 + */ +int thread_set_name(const char *name); + +} // namespace common \ No newline at end of file diff --git a/src/observer/net/java_thread_pool_thread_handler.cpp b/src/observer/net/java_thread_pool_thread_handler.cpp index 7efcf4e5d..567b21c61 100644 --- a/src/observer/net/java_thread_pool_thread_handler.cpp +++ b/src/observer/net/java_thread_pool_thread_handler.cpp @@ -60,10 +60,10 @@ RC JavaThreadPoolThreadHandler::start() // 创建线程池 // 这里写死了线程池的大小,实际上可以从配置文件中读取 int ret = executor_.init("SQL", // name - 2, // core size - 8, // max size - 60*1000, // keep alive time - unique_ptr>>(new SimpleQueue>)); + 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; diff --git a/src/observer/net/one_thread_per_connection_thread_handler.cpp b/src/observer/net/one_thread_per_connection_thread_handler.cpp index e68bbe5c3..cc65fb59a 100644 --- a/src/observer/net/one_thread_per_connection_thread_handler.cpp +++ b/src/observer/net/one_thread_per_connection_thread_handler.cpp @@ -17,10 +17,12 @@ See the Mulan PSL v2 for more details. */ #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 { @@ -65,6 +67,10 @@ class Worker 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(); From 1846380c8e925cfcc1d749b6b47d6fc8414f060e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 15:20:34 +0800 Subject: [PATCH 13/39] add document for thread model --- deps/common/thread/thread_pool_executor.cpp | 39 ++++--- deps/common/thread/thread_pool_executor.h | 106 ++++++++++++++---- docs/src/SUMMARY.md | 1 + ...thread-model-one-thread-per-connection.png | Bin 0 -> 281948 bytes .../images/thread-model-thread-pool.png | Bin 0 -> 127287 bytes docs/src/design/introduction.md | 1 + docs/src/design/miniob-thread-model.md | 43 +++++++ docs/src/how_to_run.md | 1 + 8 files changed, 152 insertions(+), 39 deletions(-) create mode 100644 docs/src/design/images/thread-model-one-thread-per-connection.png create mode 100644 docs/src/design/images/thread-model-thread-pool.png create mode 100644 docs/src/design/miniob-thread-model.md diff --git a/deps/common/thread/thread_pool_executor.cpp b/deps/common/thread/thread_pool_executor.cpp index 600dfaa21..6c770690f 100644 --- a/deps/common/thread/thread_pool_executor.cpp +++ b/deps/common/thread/thread_pool_executor.cpp @@ -101,7 +101,7 @@ int ThreadPoolExecutor::execute(unique_ptr &&task) int ret = work_queue_->push(std::move(task)); int task_size = work_queue_->size(); - if (task_size > pool_size()) { + if (task_size > pool_size() - active_count()) { extend_thread(); } return ret; @@ -134,12 +134,12 @@ void ThreadPoolExecutor::thread_func() LOG_WARN("[%s] cannot find thread state of %lx", pool_name_.c_str(), this_thread::get_id()); return; } - ThreadState &thread_state = iter->second; + ThreadData &thread_data = iter->second; lock_.unlock(); using Clock = chrono::steady_clock; chrono::time_point idle_deadline = Clock::now(); - if (!thread_state.core_thread && keep_alive_time_ms_.count() > 0) { + if (!thread_data.core_thread && keep_alive_time_ms_.count() > 0) { idle_deadline += keep_alive_time_ms_; } else { idle_deadline += chrono::hours(1); @@ -152,14 +152,14 @@ void ThreadPoolExecutor::thread_func() unique_ptr task; int ret = work_queue_->pop(task); if (0 == ret && task) { - thread_state.idle = false; + thread_data.idle = false; ++active_count_; task->run(); --active_count_; - thread_state.idle = true; + thread_data.idle = true; ++task_count_; - if (!thread_state.core_thread && keep_alive_time_ms_.count() > 0) { + if (!thread_data.core_thread && keep_alive_time_ms_.count() > 0) { idle_deadline = Clock::now() + keep_alive_time_ms_; } else { idle_deadline = Clock::now() + chrono::hours(1); @@ -170,10 +170,10 @@ void ThreadPoolExecutor::thread_func() } } - thread_state.terminated = true; - thread_state.thread_ptr->detach(); - delete thread_state.thread_ptr; - thread_state.thread_ptr = nullptr; + 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()); @@ -196,12 +196,12 @@ int ThreadPoolExecutor::create_thread_locked(bool core_thread) return -1; } - ThreadState thread_state; - thread_state.core_thread = core_thread; - thread_state.idle = true; - thread_state.terminated = false; - thread_state.thread_ptr = thread_ptr; - threads_[thread_ptr->get_id()] = thread_state; + 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()); @@ -213,7 +213,12 @@ int ThreadPoolExecutor::extend_thread() { lock_guard guard(lock_); - if (static_cast(threads_.size()) >= max_pool_size_) { + // 超过最大线程数,不再创建 + if (pool_size() >= max_pool_size_) { + return 0; + } + // 任务数比空闲线程数少,不创建新线程 + if (work_queue_->size() <= pool_size() - active_count()) { return 0; } diff --git a/deps/common/thread/thread_pool_executor.h b/deps/common/thread/thread_pool_executor.h index 540b94bd8..8ea99bd88 100644 --- a/deps/common/thread/thread_pool_executor.h +++ b/deps/common/thread/thread_pool_executor.h @@ -48,75 +48,137 @@ class ThreadPoolExecutor * @brief 初始化线程池 * * @param name 线程池名称 - * @param core_size - * @param max_size - * @param keep_alive_time_ms - * @return int + * @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 + NEW, //! 新建状态 + RUNNING, //! 正在运行 + TERMINATING, //! 正在停止 + TERMINATED //! 已经停止 }; - struct ThreadState + struct ThreadData { - bool core_thread = false; - bool idle = false; - bool terminated = false; - std::thread *thread_ptr = nullptr; + bool core_thread = false; /// 是否是核心线程 + bool idle = false; /// 是否空闲 + bool terminated = false; /// 是否已经退出 + std::thread *thread_ptr = nullptr; /// 线程指针 }; private: - State state_ = State::NEW; + 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_; + 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_; + mutable std::mutex lock_; /// 保护线程池内部数据的锁 + std::map threads_; /// 线程列表 int largest_pool_size_ = 0; /// 历史上达到的最大的线程个数 - std::atomic task_count_ = 0; + std::atomic task_count_ = 0; /// 处理过的任务个数 std::atomic active_count_ = 0; /// 活跃线程个数 - std::string pool_name_; + std::string pool_name_; /// 线程池名称 }; } // namespace common 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 0000000000000000000000000000000000000000..8bd35b7bbd8b78c03687da65eee65f7332a3056c GIT binary patch literal 281948 zcmeFYWl)^ovM-E#aCdhnc*x-HuECuo1W%B`U4pwq@ZcI?2<~n{f)m^wX6Bvz_dfUC zx6a3Vzuc-*Q%}{JXZ7l3y?S;3x}PXbb%j?LzI%BkBh+!ApGKZCX( z(^f<#h{IMyE$Dv4f7|*OZY!Lm{+=5X8C(0GVYdQajhwz*2rz92X!0T6gEw5zr7|;V z+VEG7<@yI(4eWGFy+~g<$3!h$XQ>yd;q+&jSZ#sQLUat^d}R7ixFBK&uK0C(&=xL* z6eZ_qRIKKe<6*A_cE4*_!CdHLj!)z*-8Rh8D|ZRAZfTm z7_xNm82;6?X84p+5U2lB0+uN4J-1zYKejB0?@ z3G_=%gf?|a5-bKbTMpU!ICx4V)_(u# zP=DpuP#AwfbBS_ZU7hrKQgBjY(wxx$G1#%e`?ZAzu|_(Y0RJ@^=Yt$BuLynKoNIVY zV+;pe!+Fg1?JcuBWL?r@wcGs~3fEKSXkoE=;LTl)9tftsxCC6~Hrn=TL}0-EiN7dG zLO_lf!$x{Y(~Qu_g6D-N?jfEWLT*F+xQP=iNg9c8NlT_0`pdr8lN(;=rz$T_N+5q7 zRyE4;Pv$zJ6C~drcuk!6Ae~LlHZ<;#<4tZ;3A0V)1H|(ldn74Ev{!`;D3NSdVIs8L zXatvX!C%oBiIwEF6R~7N6%#Xu{YtQmf~pl-XcvY9cG280L=!w{TN5J`ajwW8L#@i$ zE91zE7Z1=+7;+bOE;041%H>TnqGIH`hw9F`A$K6hQtAY!b_w|BG5<4 z$2>S#R7c23m2A;iuU$=9)$3F*?~`9^m21dzhsUz*krO1|OGFvXviZgj<1K}GZ0(Tu zwxM&tIwCEC;LjN__l-|ZrYSAv$ z_0wNwZTdIwpljupe4jvgS6@@aXPli^98_Kw%Tx^W!@pS<9l~eyi-n2;hH_dB&Z?{%+mZ5N6bV)vQP(Rx+wRY1 z?~Z@(SRN)GX&rt_u9%3mI zyhh6=TEzcOPEFGN_C+hyKa{cP6P6gW%lIhYJ$((mT;i=3Z8SkM|4~4b+D*zqF<*bi zK}K{&Sv`Zb{b0-B%ntLwX|yj14KC<4VVsi!W ztQssEc-!g1Qj#(nBuas%(`^eo9fLpOc*ua(!4sj51d|%Cb20=j9~f_y)|60n&?}TG zerSVSi`(?pAg|SpZH;LXCQSN#f8PJ8-M&ZjL*hW1n)RPGDDk#`u&EwD7`YkaoH`+D z2s;j|ZIEshvhA9RAN`#u@ijT6Q8+y(JIhzLglUwxSyd?W&3m_qWhqnovz%=~+uM}4 zN^Lg2)Jej0wRGkbkl|LP_oX038sZ0HT|&Q%ERoyI*AtFKj_>DA%XqRN)x2#Ntz-{a zzhn0^?F9Zz_=&^_I*+;7c&(i5ixRs>|M8f3mHES++VkCA=SECUj6PvkZvJaf{c@Yn zhszyv>3|A0Esn&LHPg*5i#ch(LRZV@BCVK3t|$Fbo-WOOQ~h^$wtOEi!^{;R9d}DX zOou8f=^Z@lZLw{tO(%nHpgnN|hwrSWmp*$Lsh(A&RmuikW)hCKk8Vp>YR=`(CC*|? z^40GR?b|>6>fkvRSTe6gD_=Ho)NL=n0;52}IVixwiy!!Z>$V)u*=_Bcm`JzG>lqoO zeq{QM>6F-?-FK$vPQCa0pc4}-KvZ(0%#VcXM|Fgzs zUbykl3YV|vecCDFbIEhwxyCwhNAe+PKV&?lIiea{kNO)8ca6-^x24+!`$cadMqv+1 zbn)4>gLR*W^0^tRq49V<$|ce68>Cy=%c!7lz26|o+sR#J{OS@P^5*^SZYy@ei54)yh&=S1_}hbDb<0d7H`3n4|%106!GEq$OL;DEOEuCBABq0;cNymSh2 zdGDOIZSTGLg`O(=+E3rjI;lT*?a24WnFtSUoOBL=7W~Ek+*OGqd%xYt^-XZCz3CnV zU!lt5R6^>Wtv{Ce{<(|@!dayrkmz{MxPKT(?qo6Vh|ZtQO9Ke_>#v{A`d72G{kgjS zH1*?Mq`BW2*Q3t$)M$DO4-R+_YGAUl-2L4k$pEpI1jOglxKneyx(_*kpq{&J_y?Fj z{oL%EOc+n7$xnJK@%-i%^KfjX*2|m%6bAeu{8gNnc3-*e${Zd`=(Pk@;F3680TZHR z#IwMW$N)s+L!{LdyTT^Ga)4Uv!x`LsFZ4d+-GjF~L7@NY<{joyRRSt)7K&sii=;vv zxl@R*;A6M8oopM<^7vy`S~dzKj_oc7unWE>8`d@ZIrRq!uN3FUFVLV1^CaLzYz>v{ z)YRbEVP!Np_y|WhBv=U^_K?FKnBP%=@Lz9`C<_q(tBkP!w-Pb&z!45k5>820O4kSe zr0esVot!7Pu>?-+#5BRQhk&O*aXA`BaCu;~HDeSx6SG!wjw&;s=pV>rJ1}SKcd~EM zNY|g7Nb^R%2=4;j}C zqi=tdew)HLqgrhkw-dsoL)2g1O}!FU%#L5UJhTX0QrCnsY9U$qE!O zE>gOSB&cys$WY39hiNvClrw{`)}WB%5a!#j`gcf0|1+fjw^k{1h<|Vdg*wNjTqcgr zdcQF;G&wYj)qad;1iw&rtI}_BV8w%yne2&(v`@;qhXavucxyhYU`yeV>fiN54;Aa;J*5 zE#(}HnmZ%MDv~BtQ|I2;gaugW2H8jIRrdf^ldP=Kg4jw{wBynv|GWv)SKfP#BWekR zt4Elct}(O(T8V8z=Y__l;iA8;q&xZIwej2in`j0KXN8a6l&!m~;{ znOC05ZH{IT8MDE<+LeVOMiTjie;9~{uzVu`YrOB{L@Ge5@ULhg3F5HUcPQ0Wzy3-9 z#-Y{UBMxoq!=F&&^IHEJZKf3jEE8|IKhMf#gbb#wdi$b9z1qZfvYVEWl2Hn4OncAcwM!6lnDlb%~1R zgY(Iabuizl_b0WgyFtx&4Q{MYUZ;H4O(FJB13gA}x9+5OIE=Gb0i$wzJ_o|X$l@_@ zxji%%n}AQ~&vSWtPw8aJ`@0TG2@r!i*ChtohkWvc3y_*d0~##`6qENNAI$nv`z3i> zXIzt?MM34u!$z>&=|>9{HR*@^!6Z(6*ob*@c_58HLZNzPTBZ@6DdQtENz{;53%(l& z2?2H+t$Y||U@8qxc;WFL#vFBKprH)xSYN|=N=wLF%S6KBu`7L;N z_ICxA{_zk6q~BCgd26)8a|8{bD7?Pw^Mst@MTw! z`W4F{O%d+S$5@b1UH=2cJu)WkNTNYH7---i&T~fhrY9(u(b9JBtF^Gwu>k)pFol+}gpK?Rj+~Qj`ph_(TvEX}RS3B7 zRzZIr1He4P#QgB6}}-z3(nof)JIhh0(`=F)_IPEC^(}*8CY#dqj9u8 zJZz8x8}c>h%3}u=-Dtk+ON zRH>t=-I2s0e}MDS2NC-bcmJcZ?1rte@f|rBXT1uuKN=4vgBlO{Z~avkbe>Ysp>x8@ zNwcY@eE-tHb7dbZz9{GOZvmg=mHuJ$kbeRAu?e*4Ej$ZKMPE2c3!lbb^EZew@0hKk zLG_wV_|fd?zEZ?Q{DcUt104Y4k}Fiwc3lD{1!WUiux1iQ9$KajNDK{AK z(5U0d;^AKXl|2?h0$~}1lENx$bFAdN#AYVjV!U8=rfs;m(a-s9(8DQk0+2X4d@wU; zpIACm%#zj{&sIi>z0sysmYkkJAI-k&sBLi6$Q&)DO*uRu1G-t4I9- zFg#40kT=vmm-Qt<+S*4%GZPJZ99hma@GzGV?a7P&872o`L!oh(LoS70^u3MhgggJH zi@Y4d(=v&^EaM|Ow!puFy_e$G9?Jhtg#8=QE6n25k&amZv#~x=)v$6Wuq4&iQcroT z(u&9^>OP^VfwNlK(MYeFp5qddsGk<5$Hr9^t!KmZ8!$WCP{&?st5+Xg-T$m$Y(%VI z+EmT1pP1?cRpc1!txQSN8(?q1Kc{iDH8M<{ZKyPq)87U&)No=}Y5z}W|Cg3AO3=Il z_w%>rP>2&+9t+v~IW1x*d8PM8kw*JMbk|%n7fj;uLR-ub_>SMam;S4hZVPly`d@vk8xLI`2o*AE z(0t1K*23A(>7M=dG0g$lnM^;Y)cBwRCQ51WqsGKmY3*ug;NX}bM7Ug!UZb$&0W1|v zL}+Z!MKh@0EMN3$P@u@vuPbs=``1m2TWqslF>A#nV%3-hD^F2sXR>@~fDHWw53x-~ z=syOT3{RE9gZZ{*C!%#Qy5WE?q1a$Pb|5qty$S#4vBvrSKq3g&oS=MqZ|i z5T5rK#)2@EF7tRco)gvsN`nicM1wXbjQMJE=z~;j-9u6ub0k;JX!0mz|4~ELID2_? zLHGEKi+-bvj%nUYGE=YUpUiJy+G5fBC}6*<J412nRX9BgIz2vFA1LcYXXcH@9-gIebC`=iJ=NKvZ1Ucj45$q8I>sE zNVo&b%F+DKPh17Vo>S3BdYaPTXZohi=?EUOgZ@sLz@$#+P98rL%J#O{$8pc`%}edC zxL3l+^`t<4Aq1sZP^xYJ1IIUb*-iOqdpdz3Ml`62%eR7(H*pcriMJ?0+uFF8(Cm6V z#a^g>8Xm+z6*00Wj&MraE?$Y(4_vD%a!{(D&O?)9PqjS1h9f|;c$Kq~(nsn+9%hH1 zrd}SIn>gTxKIWmRPsThlt{f2{HS|e95fSRbfugt!TNEqI*YOtsE^3d4GX=tewTM{r z(r~Dri`9#z2D_IvGtrL5nK+7KEPxL}Xj1U0|6y($ZzmzH@t5Z^sfr1nek_QL%!``I z`i1D&b*zqCFnGuuSQ_kk8C~BlPD-VvT&|c9n$?U*fMY%AFd}GX|ZoItj>3|w?bos_kWRvZ~dva7Md zTvm0KWy=^l8)r}|G>6A{am21U4FwaaW~mc_YnxLq0Q)C>(7}hfHViM=%{h?Z^@J6~ zdyY-x4O zveKU$O%(Wb6F&oGhf#oep3MEND_}dX;EJ1G6d<>Tmazq#m&Rb{qR%qUMHb0{$wdJn z|I0u2Ml+7(M&zZ!n^2Ko+m0MIyPwUy#mnmjjNOjeUU*!W#dkEWmBWO?;bIMOPT1yB zRpkRV29+@G67!XE6?E(f4pXqeW)~s{?avjw8`*5d<(ui22!NM_AlMN;18ze!6tZDE$$>^1tr!cEltm$kr;=rPo)8*Fj42&_ z;P`4AixEJ^d|~|}=qi8raLhs3iF!k8sOQN-TmAc$%vvmXcu`L}8N_j0N~Q*xt_?(n z{BoTa+g&47uCIE#EVs#2&J199g8&?mqLjV?)+Y{X^1nfQvLFdcFUWd{y+C``q3nh9 zllIG#%Zyym9)y@7y$g1_66j?((fEY+Okj;m;fTf57gB)Zh3A7_IWmQoPzM;GJJE=h zaw*|U!NLjt|2yf05JuDi_)z{^C!~dUf3oGBxH_gv8x~x?8??;7sbxLphNB#JVx>n;GnW)Cz1kuIWJ zKr~s{1`___R>p-0|1X-y3lI|9AWeNPDX>~*H;-TkCy117N8_9byV+p=ZamIe$}0rz zK``bwy<$peUd6VLZ<(aRfpEby0?DBy&jrqN2-(evLe6e7ta1k0gqj+lud@ZjAeWek zfTN>>*^4w8QXdKay6EX2O3FN8N{5ACG4e7ZyjVfw8fMuKnv=?}0b|>q6Ml10ikS7@ zjpugV0k@Gk;SW19#Ka?vHNA8{Xw#)2H~A#&uQ9>=2jE{-cLCTYaBi)P#ZvuiA-kxS zTF!^#@9@AyJtwR%uE7p>OlvDP>d1cN^Hdu&G-%mceKo>@Ee+rBElxbk2rbPmaMa_PG(v7=agBuG7mT9H8uuL zkN4}L(KlSnxw9i|pUPtE>P%|f|F;I;YqB*HyODqBh>0?trQi{JRe8q$XM^cxPXhMY zRts-sma_g_{@&gX&68UH*`TB4gS6Ze@rnB3Zc3Yuyy0@qqUe7%kX~9;D_7hIUT4eA z!S^0T!~C%zrtqH)4putQi4INb#7qA*C(BmjKmS_;GZW#$|D4l_{xRi$HYjvH{+~rC zbasw%3PWW2yo}vXKIfhppvk5V^&HNM>{KmsG(y8eOW(e>k*8?{6oRZ9A`HpfP^>p; z?#lefx%whufCCCDmHM6syLKT4)cPx$DUrUN^5~y-*Vj1P(z@1RQK4YlWwft!v32xB zm34p+S@=dnTOG2II>521o%{^67h318>p!5%`10|N{Dx(qY~JRh&Cm3bky1A^yxk~I5cU?05{ED z23*)qYHx6gi=aQn;N%}@?vgG*LKm4+4q%5%Ha>_=C>DM_^))*d*H7u2dQzx>!zZY9 z8vP<3M0k*f4=JagcU)}QY#hkT>G7~}qKt(vHRNjTfKD6^^McBjBebljB4g*D%|Dm+ zj+@W*mW2Isg{_0GI?Qr72daULER|PulsYGjW0rn>BS{(tnRN5hhVZRP2r#LG>LT}PS#fR1UJ^XmrhcbZ z?LO+KfZ%1jZALmNr+${tfGi-vuif`mrK%0URF{)a1bP=uU~dB8m(+SrTm@`JYyMOqlHh0}z0Va=| z0Hl(!0B{rV5*djQ*1Oo^AnM@cD&h+XrwF$78Yl5Lh&|e1zDB^+$5~_&)1Iu;-Qm=d z&}4PRZZVG*3NK~1{Oeg^#Mw8 zPNj(wQF$)J81X*7m3uuRxtwu4@t}OClt|F$wSu#66w^;zOiMTJE|sU&zG9ahof=Bf zariP^pXozIMeu6M*&Bh8-`l#zOgUb`euNH}rY*|Z^l@>-V4OD>lE#q{7>>BQiT73~ z{jrUMj8tF8mn3z}P*lm_a_Gn;TGbz2#T4ELS>?EYC7Uq)et;ygcC+Eyr}3$|ZP_oE z+(GU()`nNX7(7(ZWteGN+Z6@65Svbbbu2U8WqCZjNbz}}_MiV#6WDVc^95&0Ns z@{#UB)pPrTo^uWd^)1b(Q_w|F*kMpu%)#k}@avtabH9VWp1467>oMIZj0DHY;EY-J zd}Ox|T$TmrAZdUJqzuZYsub;Yb**%@l~q-amN{&xIJMvwys8oz4tYJ=Tah7&PZiW%*dZ zqS5$u^r4Y+OeV0E061zVN;b);E-JxEh|SQa3tC|}h^}jZ(~VVS|4zhbLNFefyIjQ; zHEd{?DMoVKg{ZFfHiC}XOFfUy-JWV)qB{h4q07Ez~+Eko_E1|24i)~5iRmn zV^6+YJnutO%~R%!uR!2ws5kH)SXNcr&g>73J};Y7mk`YLTB%^w_pJ>)FeWsXwvCvb z9Ydl^m1O6Mf7R1*k=22X=g`Jp`dJ#dIYmu!XYdq%o6%eUn;5Lo6-$TIN?-DnbOC4{ z$QY}s8|6&Z9+z)`Qhn4bAxhaoC2~HBMf(UDmS?q2#nEL~WZk{+lh06U554VIrg1;> z2Y2#m+KSJTfG)yr3UPpj+%xK4VdN13!wDn8?VjL0uke@pf~&>?xc=?EpE#k@vB~ly zZc8fPnE6V&pU}X_6h#EEbJa!pyip_kdkj>E$-B!LT`sx2r!OK(w+mTWHu*$)~Xq!~ebgs%iOL*s-Y zE{eD5mj=hRT!6aMDXER4jf#ySCa)i6Mkumuin5Q|HL&dl))l<3`e8{G94<(dws^S#fy9F)pzpRnA*)E!RCX z@QP44YF`fxF~Oa|ed^3_sJh5jsK?9YP)xR!nRBb8O3zL_m+H@)vL}M}OJXG$D9HVI zEAq#Q`D&g?iTTMq-g*Vy(X;Y-?Rt{PSO>)%Xp+q5NRfAS)te(#0W5?390c)zaV)Hy z_Apdc3q4U9z|XT&YG*7p5&N?veP3|M=Q4>Hrj?kx2(6q*27aK#9p9>8oY9ro z?5@VE0S0%onHTInli6V}q-1F3?nI$&ro@cS@b725O-_F4{IHJ>mi;+RZ2Sr1Wgxx} z(ofkS7|4AObz<401mhUm_|jS0JkS(aT0|nlQ}io@KnFq*D=oP=Eo~8#zNOj)g0L|=CExwP-YcR% zCUXEAcQ44Nh6gmsZPw;0sUljSQAgUCj$7T$L;4^GG+1iL29soew&tO>Zum=b=Jp2F zr&D2iqgygKI%GO&SH92YiYjiC3y%e~VVX$F&2U=6l(z|E1Oq!IkalD3)(%ML3qPcU z>TBBWrUFR0?Bkm_S~i#s>70eaTsu8QjPbIdzHqQx zZL^%8f$~f`fs_~JJ<2g_Z?M5%zPLjb<5Ex<)Y##SCr(*^*-gJJ82W|F010J#)FdJbE;+as*oD5 zNtMAJ(VDdkOxgIof|nhi9`yxl(3lfsQ#^OOh1eN50l&aF+D2-%Al%Oloi!l)^{OYW z%8H+{!PPmNPS{&U!A{%{=__f@m3+fZ%^<>%H)(3pRe)O)<5Hd*#99oT9>NEEaSxfw zn%WGKb}K7O;KATU9nUqS)oEb#=H{yOA$axXweRAI3*{E@Zt=SIR@(i%uMiqni^{tK zFj?oBrmC8(ORly4s-lQ3Q9u-K_wFxKX71LUMM6f`;45(w=z+qVXHD5TjC&3da@L z`s(zslfo{v<$l|s+(0n1WHk;@af5(D^Mep>=>Fbe^f{0x_sfx^<36X~7h+TzNsR%N zibi_@^)>l}YlZHobQP#cy_25t(~UOt%i8Ed)$CPS`c`x6p3f_d`_OI1zS&ZhQh>|v zCbsv>b$tF0vpA)I=SIMJA5`u+f%%!HmH~mBC`5XPKpX`<*S8MUM`;`}6M5Y>cBVO? zU*(xn%);PLv4m|<*LRxy{#^j|mr2L6_s22IP=L z48NegL5hI1Z|b=SW4{xb)v|KFQTbS8@q1RnQVi84bKH!x1btDs=+yE99J0d9`^#ve z%?EL^bA4r6amcRzB;dI~IYjyDu*~o9J#>tl-2}Ha&>M<1`D` zCrcK3zm_5NVFis$LZq+`518$yxY6v$@R_~OD_4TzRKYtObO9w3LD}A3SQQX6e#v(s zH#%Y_JRS;Ir)+5WnZCRyD<>satA*C0UHLJy)iC6=I8AJykb%pT4YzmGy%3bIS zfxIWRL7h=ZkRWK!GiY!(3x?>!ej9Z(z+d*CfdkJxqHZG6XP}eHW0fhQM2qE}geuCB zdr%({)O`K1s>x;~=_c>Kn`J`O3OnF2z&KA_zs8Sw-FIr;=j#B^mI$*(X3mGU%fZ*4 z{LGA+nIp-}rEEIiN=A~&+_dQ}ui%q$eSG#9`#Gy)akCMinxD(%C{30x2s-?FPT$BE zgD+!*#sc#f;%?2sfR3?WM~Y-h~WAY?1lY^i{GU%|771;Q*b{ z8NgFN({%erREr7-ppRThUIa0)@DU28Y|VAQDW~JHgWo0SFp3}X;|d}`cJs+3-;2im ziEt%8C=JD4;PLa*|FH;i#MH;P2$qbm2F?+yVppT$JPE&F2?o7#v-JCFBOzpcWzi>p z;W``w&`(gz90opEMu?*DSGX^8)`B)el=4RU^9Ur{A<4}A@k+>B8fg*wr86jQb=%gn zx!%7s-(F`!eD`k!<#frO>-EokS*VceZ!lW?WX&#v=$ z{qp6BypS*`%_9W#$*gTeC>iUS0&<@}1MPU;0E4;#{*Nl=>cXZ#|1Z0V3UiH9Z?n(Z z?|(Nv{A6;x>W}iNv8sF{GhB?=@bPynsy%04dW44uN&ueS_29&I^5Zlc6~bB88}6lA zoI9!~XA&!`_|e+qjL2zUcmK_^()`v00v4-TD|Ti+$uSZ~n>AT;S<`&JjDGac@nUL9 z)m6G5OASl=49Dq#F8Ct#_BsJ$XWPH~vI1X%0V44;U zXGiUmbC!~GY?bW()X*+YQL^EmH(D~_oxwP@<2-YY+l_|Dft&FxdsFC2v-8}_pT*lM z$XxxC<=NWj!doN=BTyHAuXe5(nNX13*wNjKj^YGT*pOW$I6x((AFSDguUr!eOgK6PUEyh3mPD0zn0iS3W8;kbuU!ZmbEx@eR^ z6e0hK*pinMsXY9#!She(z_BZiz-|1z2Q%^&IL-(osy-j0nqa*7p`K1@q8 z(8;4I`*pW2_Q#j&sj?wz;2MUUVQ9*>QJRATMq|*xNPJv&J%16+ZN7lLdKRx`aEbzO zfl$ix4dc9`9LDY!IwO=Q_R@5GNUfAi0byX=`$DOcgNkwmIrT7(2J;7DSn7b_uSmCK zK~4<>1BV|LnAe{CdVdOI;o-;D*f{)Dnu0THFJc>}bznIgnY0QK0d~7I3F<~{P3*sv zSYDJdSj=-%Z3~@x+GPN##{23*hO$UjAs;$VT?Fqm%M0y=oUn6U7Ygp z4GCT{I8H}!O?nJ`K`bAsHf|=b)!;nWfzqudAk?~@sDam9b?%LZOTFgvt6oPEm@oEV z(gX{C79yL*1fC^-GIY-sgJ_3Pt=}%Ccsl260G?)24-G(1dSZ}=9rSl^uWsq=Qo;yv zSGWgzbCO}fEGJD#3LnAMpe9+wMFJ!(mF9o;M$TC0xfj^!LCIIed72r=8dqj!_?R}D zHOyZ;J{`keaXa8?#*2@t4XBmoz{a4qth=?B)#Kr&yD`8vS+TQN1zZ>)^`G@ZilBRM z6{>j6Uw?%d)bJc*JiCwoO@mrL1TH3|aT=wh?OeMF*iTlUEr*|j)6KkN0asWgU;qWu zX^;ld-5GSnlV`Iz>XpRCI!|73X3f%WFynzOlqvTazgpp!-(tG`VNMfzGQ~?}@SAm7 zwRDvfzmkWOo}mvvoT);}>^?8TM;H`ETn+AD##L30ITNRB&ZDp9yXhzGx7Lar2q@3= znSM6LWw2aC17y1IV3f}5qAtW2uC-J~zQiW8}dZ;3`) zAoEWT1R5nY=HQ(sy9x4CW*uw>`LpY?1=*v7BU0>L3b5M`DEY?;r%jKDW?8{KxPC0o z%(gMyS>`cg%(S#uC@=Ed9F;0l)$s*s%M#$uZmNu~lw(U;CTAU!3SuwTnxf|Zv%@9n z@b8eqd^98of59s$DCRTfbId<6YMe8=yME{uF_N&rnq!_fk6R$=*}b_};S9ug8PY!T z8FlO~U3?aA4bmC#wm@TWKVIiDHF#pV(SqUL_TaPPd-u_^CE)CClp}t(-3(lMKGK&; zUvxWRNE%(4;-h&+nWJK&hf@u!NSDI;~m!F3D?;mCJS-p`LXdk$4A zPK4b;IyAnEkbdzO9A3srgwmFRLMO{OAC)Ei9~)4y%k%`{3;f+^)ntDD829&knzx&Q zz9C8}{y;y$@EW3j_le!S-nk&Pl=6Q&8{TXEdT4*l=#Pg(zc0UfR zc0=*jpe0_GP=t_H_hrB(DIt`nCjyL))!*66a3QRVmA-WhY~IRROtx$?6gp0_#>Yy2 zt{wfYC?t3g{39k&p#Q;u&e~gpp%r{N-}mIQ@vhn+?d&>2O1!!kU;debGE_ke5mi>I zu7uSc=@vMyS-1Dd%UL^r!9k|KGQ4}*Qt)|#_%Sl`w_U?t}?tv)*ijR z)l6zv^f7z0K1v1wsU7JzDpBprUP9Og7GjDk$jXMLaTiY}p4DH7dbOYqK;eWsptRO4 zc4OQDG&qLAnmc<^7uy<31DemHjcD3n*34TSho=>g>f|^B365v5o}fCd71|S)MRbPh z4hjwLquDOfFVZ4bZ8UxMlz|qsK+AhK@-TUS7J%AjTP`*Cglxkm77957{wq@oc17bJK*6ZO zF>~2vFGk}qAdCpY^I{$W-3f`WZ)1s~vUOQRjqh@U(%cDKooJi}>f@ejaLQMKt$XRF zSQTTE7r&p|HwGQ0Tiuf;MO@?PhhzRB>}OIM+xx}Hwpn23sOCAb8GJ`ym9t;(*4=$; z9eC&RXn?RJNcxXW4s5O^dWzh5yU1!CUeG5>hGv>~TUV~TzdzRC$BJe8;R1TSt z2VQ6Bjk*vvITSg%ms%@2hm)Gi^f0BM z==!`0g@i1M2ZH2x_W;RoNuzAq*RvP%rn}gU&phu6OiSKXnDMl^&70Qf)IP~Hv4@Zq zLlE+N=`U~#5@#Tt+Upw=)oE2;4y`XFr##<&zu%l46&abyedD&Wanz$(YrLBONnKcR zy@Mm8l#K`sqR2~}gIM2gd$yb3%LWPY)&!pQU{fuUj+q`ZxoIcUv1#p(Jgx+0?rGk^ z9Y^42Bafw99$hcIsta(U7=4O>^Z-sYp8RfrK|eL36&?U&4dWtyFQdFcfTAIQKEu*P zr2|UAr2};9H{=VTv_@$A4D|PY%Y-JHm;GAp*#uUbZhB*c`@+8A$fv;NYK39vAAlW^ zTF);dqnY63@7T5=zd`c=Y?6pPuJQ}qB$a}zwPFN&=b}*OuTr|hrfIv3x%M?rnuRBC z@k_A?V|D|MX$17S$Mm;s)Y@&T9^sB?j-JkK!4&Td^FOrj-FZCjrI$UQfdKD+bt=`R zd^?|kWVd!^dHk} zJ6*c)@Kekz)+E{j=+n+Ok83r63iKEVJ@+`SY5n$R@ol!*ydTVEYzgB4zspa?+XIN{$xlDNe=BwZx(}7fUpN7HHuKA5o z%ll#ZFj`xta%~PbkvBm3XYW?v6gsL=N6o3y_yK(-=?K2C5eb{pIJ*~}ERl|HkVR%U zJAFa5YAb-}P0VQ#(jl&eF^jy00rGV z>?D+_aJMspFKM8kvc&Hd$JI5|S6e@7L{J6De({=cKlI%IzY}{*NuF~o%S{{9uhn^HJhygJ-v?bg{zTd5%iOgwEHwKsh;lsc+NurE9NHO-kM|^uf{Py*S_m${V;Xp zMIBbG9f_$mk`);nIj#|;ZKqc0P*%*=ps(8=-K)jcAGX{c0O9U2=On&;nn@_#*`G=uLD>cnZ&C|>{Lr!4i#ZFAO8xchWiR(I ziiLV?LcYhlL@itBo99%mGq;?Hz=d$KZV#vaFAw>K?)SPCW{psk8*RU3&?fnvJu@s% z6bUQt0C<$Yzu9qV(YS42ovh;xJ-B(ol4m>dA9_@)jrr$h@7uR?! z!-qr8NFfIP?-$Ba3n9#N#S_r0U)A9C$2~pZ$L&$6EDj#WFoD@sfQCpNxjq{vpQ-C< zGG$8%k4n&l5qnfr^m-JPRLLIk9lNGc7|F*=0_NXJN# z9x!06?q|RMb3Ct}ciWC*d$D`>uIqcApYy!3@AG9=SH9l?fn9>Kp-Z(p>D97wa+uYK zoiB?nqX(nBY!S0h&H9RZ|JngAr#EC~eo(hv?;%pjzoJx+rH&?05TgSyP^HKFbXCu= z$gfxs6ZZ*9C>l&fL5zq8Uri=fH+csh0(R|q!FRMqWwN%`!R5Q$zv`D z30V}KH(XVI!u|6mHb@zm?!Rdj%lznp!R26K@=Wr_>=YK4{jaGx0@8QgF5_*B4ML_I z*GRj{Wu@;hU3Pmc#E*FCwVen5tQI*7Kg{(a`fryWZ=AA7l_ktl^o8I+nY7QHe2i)> zm5tin++8f3p)!eVerGQ>d2PSFFd9*40#y4TRr4t3HG&|j8vBtsL_UTTrGb{+Zj^+ z&4Jwz3ktxjp;Py&HE@S*Ix+|8rNx8`BxI3odPux>xA^>2G(6iGW8;oU?s$HS; zVkf_IhG>q-xj3VWeM^E?ECX`r~Fpmgb0u#MH^u#bh&uI-E~A@@{mAx6qlR zvR2bfK+!+wzwVexEpQu8p5MCK$+PQ=+76+qGm)%v82>`T`J$k6?;iag#P5;wO8XP7 zY$>&CO|FYy8eJEEpe88olI z`^ONf9JZY?gVZO?R)lcL)%=^a2MDyPrTGC4Ja2!y;E?xM+ng@nuGYvNG#!QP%vmLC z(Q|9>e}{^m|Mp{sFs_Lsp6`~0Y#`z_I^3CKu@^lM#jlNx!~#glj{F6ZNCy2nKUMSJ zE|R7gSetuA5Yox~hF7bY<-x*{jt;3z3)JKPuM6O6(JlIcD_^}&!#aWY$_2bM@$>&CiFTIfV}~3l(lg2yw7^A19)FGRG3Y2DprK*! zi;8da^^C)f$Gnu~u>s3JI5{jnlvQR(DPX4?-XuSJ>Tp8^%Cz!xg+mee!f!meYpYjk zd+Z&zGexI1$QEeg!tGjFLg3cU&OD#D1+6*1T49`lKSUao{M-;6?=uR+n+^g|+q0#x zCAVPg4BRaLi+snut!kY`A)_%_uYgue-jV7N$^JS1+6n)6!h7wvI`4XWi$U|hF!YvS zu=MgrL0319(Msc{VXjONc9mj^1?eTflGG4KWd>14Y&3i>ufkfit_SKDAp-!{QY5hw zc}+4yA{NGz`pN{xrTURhg9Sr z2b?^Kdpt?UXOc$T2Jn!bQ1Wvf=w$|!CsmBEmLBjHtk9s7L%U@s3>VUCODQVjod|z4 zTo;i2ub*f9>q1%wOhf#hbiXYBJ#sdw1pY0I<5ghN)^s{RTHpHBwgMPrJ_cc^YOcKd z^wiDTkEWC~k+`~^fvXU|I=)00$oCL?fW-HJj^p2CY0(dwI`tnA+D$7S8b*zlSy?{G zzwr+4%@#mmA93Q-WNSObqVQby8)5q7Ofxp}*+fG2TIAE?w1>%T0x6V5v;(J2$)<(} zm~)Sj{vihAdj%uMPSXkoW2%PBdnajs?<)Kdr{sMxy4(Ix*Kqim72R3=Cx=_Q@gPlt zk}0q}1!t$ffvE6*)%i*|7@Ht=uo~eL50pwkj!e)u@BrP7Qeq`iV-_8qUAJHdVT5_&#D_6JtT|h%fyKglrMu9Y4 zPWsw->z>HtowXQ{npMrn20ipRE+{xVm3hYE^ZE+zP$^~cKa}q)JuMT!2w4hGXV)p& ztA+~`(4~?kD(wE(VB*2b*gZILRLu9V-KfKSG+?n|=V5mTOrXiJgBsvtnSdT*%sJ5tftt0cL(5fMm#chh0k!!_3ds(Zg~txWY+yzyG*(1U z%p73f-))+G*oaQFoDHa(;p3}hL}cIJpi|_lbdF^hM>#{CUpQ-+=2B-*r~K9H?Egh3 zGxd$2@&UJPdS$-i++4wk=Am(DkwwSHx;sDe$|>(rbOlNMI>}o#AMM?w`cC)wbL^DD zKihekxkcUB#vmT@YFNDH>FYmmC>GKsxLvMarTwl%Q+o>KNRSGMTB>>;@DmI&k#t>& zY&}%PJM@!m*{Clx99eYuTC2fg_l*JJ^%B%ujAwD5E*RT{-hl&w*33CXydaT`_r|zZ z46BEmQQNs;P~GYFi9?@6XWgUg8-IF<`F{9gbd+F_uxZHU*3Q=mT5VA8ww#Xa@k@VI z_z4BLD*`X>=e*hM_3Zpd<}ZCk1Ll#n(4n|ke$2?gWGPIWSFcJ_g*D`*5sp=ldCAQb zd+yuGk$No|OuW|FSIg6}M&qjLnI!eLYV&JEX#p%j4cv1EBVg;N)VN_z`g6KKI)kaB@0f+rl3eIB>+#g1lNOn9ijnzpR@8KN!{N&?QMvcS9 zE(mxqmzy^(bL!m^blvh~lV>NCaFIF&W)w8zOQl`Xw;KNZWGAODwD%@9d-ow73Dlos26DcCIBgxSLOc?F( z%`Mt5V#_635;Q(j8oZqB0m#JGB}VYB_RyUzJ4bfQhZDq5MLKlCn|0s^B?U|7&$aSl zQFrQuk4i&(&CJ^m%dE9;d@q`nynEwo^ugzvj%o=1eeBjk#~J?d4I{V%Yb*9bA2^+h zv3R0)-wkplSeMM1;{n+O`vD^6V7u0WGntZ}n^S(Xjc zFchMN|MpZ;zqNCgowvqt6aN{ZOU6=b+pPixE~Wo7p2J$$660JsBhz^iFz4)j) zEZMsK`F=0Tp(k$V6b3k94;l1}E}QkLUX{_+!)->tSQYn^+b>+Dc!=bi)tRrHb2}}8 z0gnmEz%eky+1<|wNh6|l0M_boK2_YhcYF+cfU2HU&`}^#3yfdW-9b-5qT)WQMZe09 zhuoG*Ii}WogTlgs4-GZ7P4YVNTY{yKf==Gw;}oEvSWPjLlg|SRV~CXj-p>A@TfHf(P$~I&4Tjt<&~rU~ z>IF;*#{)-t0;Fb0^3HYGKcM$JFpFTYnpgFxnNSC%H;x_*Z9V{0jxk3Eh_(Rd13M@> z`i%qNNA}{>h)TmCT1e}iRVmNY8)t@mFWLFdfX|pR_GP+06J2;u!AaI;~#om+Ay} z$yhRhB?CG6`Idy1Nh9#iE)?h93WVV%Z#i8dDMv9ylKB?{Bzg=nxEm^M;Qf^x@o_Hn zB#a^?&!ziiSGn2gE_&RzgDRCq^v|jrJ4K5Ut@KNx*OxR0Bix4dpmWpm7x{}8L;-v1 z>Rco-HyfRxG*o((Hsw!YS!a zZSTf=6jCYY)O_Da_0zT9nJnHx_0neOQkB5O*Lmsm&3o1}d{^RT`Ny?jm_A5xYOPU^ z-bc5$*1La#Cp{6B8W$U9w_P~>NdUkL3iWPiiF^7;N#dSxt;aho9k2$dO0F}+tFQ-3 zX?6GgUPKZl+Mk`UXW3;1ZZ3&2l^cz8H?p)4AS{!=?`^h`3IozOhrY))!Aq10&Zu{s zd-X=Qa7Z^aFhO*J)c{V#;Za`4O9%$$mz?q@IQSXAvl_8yxYsD%3k~iqV*?6-*)QK- z3EDoXzw_wvv0@!`cjSW0VdPKs#b03}8HbYv9?A~(4cX3{LlQ;YdZHrW!rA<1^TVB;mpQ@G@g#+JupE;1- z)k{tcEQ85Zc)aho5;`k?)kc<>c#UA<0jFVkmPDF|9}b*~$NuPus#sSIW@iyPDW{}Z zJ&VY~tVgirI?m-cJ^s6Q6M|W1w4nrg>A-1?me5GGxzp9dz{3F|MN@G(@jzuR?YL>G z4S6;$cB9%xwm!hy2-Ed=e#!kAdpMoUzlZ#dF~rV&*UR|yof%_S4V?||*o$_Y>CW;| zaI(|CA?$Any-Jhogo~o-x$5w01J*5&2H$8Ch2t`14>JUehLeW#$)&HZwIV~hyv z9>`6WJ-1s3pM6W47h)&#*DgqnEb29Qa83CmW3giuv9==9?;>y|Ra-tTmBwG-fp50S z8tN2=OxVlKr5*fnYmf&L3st<9c)Ul!e%rs3?9@mzB=IjVy>5*RMqQ_sJ~snK zuRFj-Q&P-{iNR(P2{}?ze0Bh&!%n;u69>UJ)lqq zf4w6|)_4049ERog6b0ey;K4XO*_-gE#RkUb4nTwBY)P?>TsmzZFO?^1ua&jz&InI3#t0&K%7mXd+51=^~%Z4IT6DsQ9s&b4a|(P1G^XkFz0I)gpWQ z;;^@R$cIK}AIoFH=!!VD#}6Dd-h8SYt4i$xqlc7sKC}zCquc#=zEKJGDB+wK`u#a1 za`5wrknB#}!I330&V5I`I-sFl`?tZZ{TO-*6UrLzJw5#ApB#C_ANs}MOJ6`B&vTr` zt+L)45#Nah1@BMnl|z zEkqfGy+MIq16Bbqz*p$-{iqYQ->Yg)O4=`8kzAAwx$7Z@{2ZRQ*DyI9`i-1a@ibYn z9-jmvGoCSNa_!T(4Pi>HOwjPK11SF1{iD)Gr}+w8$nl`_3T&#e-lnzqua8AeCjUh% zkmh9Av*|~_@$cr)XfW6h2tQH=cUc{-Vpu76Xn5zQ+z-=V`y|W+3A+&YF@T%LJX=s~PMi ztqYqPw_LlUW2Wfj?j02-rOQ#wy`mHFms<886r9qji4UcxokA`2+E+`-v3dRyK56Z> z0lW-bH_jI)_slM^UNeTamdZV9k5jc66&;oKj(2|Hj#?P2-Ij93<#GGyfS`O2zC`*1 zI9at)5DV-Je%}_H!yfdn+v{(ZZ3*%+k8XRK?((EQ&kY$D_kdIS_VHB$g0usfdcNW= zY}U{fA!p+jYZnu8LidWz%pc1;^+tv52_zmPFJJ9HP;M5XC2p8sMfBU8P?=;aySOvw zbka~Qt;7Z9yb~ploul7j1Oxur(bH_U?O5t|7vWT+8rQ!2{)7!t0G;JU)A`l?W(_{^ z!u0d_4T*G{!ZFmipq`TE5It>>+}}i3Suo*x=ZEfDfmk>@zfTD~M0Lp!ccBYBIsynk zfim#zPp|lA?j6u+UI|}G`rf~tgtP_da1`3;mR++ylHPOaBE7)x*xf+xJ?MT|WWqy% z9{mKp^*v3Ab?e?yMagIjs0FmQ5^z`j;`rjJj~021^XP|I&I6$q*5qRfg@m4CTN(Mp z5yH4sT~a?RlYCKj0fHNTS`WRXZ24LF+DO^eKXK&OH*LZ26#8i@yI(Gcq6&A-uT`r5 zNlWq=X0;=DguU;vx`~#2mJ@3>sW1zFGygJRZ+n)9Ru*+-{%J}z$Iuas$?G;BDEkS$e&qRBf3%I3d=8sFU19SSCmUheGGTFLQR5{TeL5^PF${n=LloF~6SZ zQ!T{|hsB^;`?-P`EJfHy5c0UQ9WbF3h(uTt9a{N|Vog1*r zlga1Db}lBx~BD zG{g6E%ZJ5<=|6O|sbu0lO#e!M?KvtcUfPdgtwCm9L0QlO*bp-89Q@_JdRe}IG$?l%wioiL0FAA1DIoc(+^^d$nPgQB_z$Mg$s>L%0c$lrks8f36v zlW#%>IJU0ikb^J#4^9{oHNbcaLxTINSdi&A`A~Spy%ZgPE=?0njcUJsd;67k|As%I z7yrS;v1S8q_0X0dNqelr#Om+r^pyBGg*jNWgVKw{Z@t<*#GS*d@vEP^{V4(04G6gn z{hKn#B$1P@*t{IuSHeW00X|!2j;w^J8{a){5;G5M|AW(5XhL7(+6pP*L|8xz z>j8Hq((Q8eth!zMvJde+A;^Z>pYWcDiKw6FV%NxCl+9;fte3 z1_xPeP@Tm6M)u4KH-|^0J_|8;WR6NL*{ETp7~R~Yy~ueQbIYjyt!H{eY}j2U`pDl^ zGmtuk5LFzM6v!C!VJX*h6jZYS;#J8%zr4mle(S{qKygfrNqh?;;VMYgRv@}#0V4?4 z_}fM;Z?2swrg%8qZ11xKZ8sSNu^E(T=0Afpmz(BI2oqH=UQ4~I0cJBc-_Ru4i(P!U ztC%I7a_952?)oPh;arvvpN=b=iItYV3G@)=eraGuUI6)W@2xa(Z@TSo#ZQw(g?}Xb ziZu0x%yAx<6((zomLfC}Yhf30gD_IJT<3l$iJEbq`{l6KLs6sBO|zp)S}bO=Y=^l% z-V!j+7_0huOxJ%aawAD@{Il3goab9BlEHi8J4wmC+7LRMPV->mDb7NN%q7ilJ&wDG zac09aFH^`MYV2W#fN}PHaPzfPO35M?muC6p=&?uASweZKbOyRLE(fhY@ABSkwD45d z7IIo>SCb!Y0j}b#()-({+Ey;L{qw8_eleXmC_)YpkhEsH>yi#jzVP79a=LbmX#>>d z?q#;983$wybKh*Ec_YKu9Q+Y7%KK#2B$)ra#vs=$LZ4>^|E0-1V!ZA}1fC7aU3C-c7ziiJy+<74;Uu!mJ8|=U-(c zeDZdq^bcS`rv3Dv{POb-Om`cy zv)oer4RFLI_yDC;U<-uktOuqQ+v+DZ^it>l|MBbt@V?P-*&iN5;a#-4y>e$!NXRqoWYQy2 z{1~qcl=+|5#Psk^av1bLXP14$(*s-O_pYqQV9fnY?paGJBNZL2tH*^ zpn1{~G~)bZ14jZNO7?18dnKGk{FdAYhd+E~`Z+28G!61=4hkpVJbIuR{hHXaJyRlS zp%V@z;u$Ax!BE52tMs6DZEY#dfe2!V!FR=|YPr3JomA+V+J3)~uvQQGb8e=jOwX6u zvAQ>W+EMt^ItUpzcqNXMKU^S3A9m?WG`cKiV;EwTX!Fg^4HO32zcu`!Jn<$}5|ZWr zmU&GmGZwSds%;T2R$!fp`Y)1Xux046Q8*8z(vOCulkD;Y<@I=!_Kl@W9V>ZCGoNia z(%V`jN0Y=q^WvVX_5$bTnG!URL-YGLCkEoS5VhOn7ae|bdUCJ9kg>V=F|fbpGN}e~ z>IY@aHvz}02fxhUP>R1zX)^)HZjZ2DJxBZ)y>nLWMnX;=SO7Q3YHB*29(C0Ua8%rp zA%3682$)Hfe}ACULCg=lA&o*f-#ag5qw`78Qi_>hTLA1v+h4aCm{fiqSFlVM*jIo2 zktU6>m#jJ0dHbSU`D}G(c7)E9Kw%1R;NDL+olpK%&0N4`g%^g~K0=8MMT%47pr`4# z?CshxyTe@u_up#srfKy$WEE;|v3`(NF#emZb@l(lkqc)8kJv>-leOm+e+#M~tgFNl zTU5r?szc7ot~NeHummmeBtY6d^D~W)Y0?M z8f&nh?1p+oQTiRHnNDClzg&2~FI{`Tn#Ik2B}t4WCz2hCKR#(4#MExol38gRh0Y4J z(%x&e?5{&7x|-z^w;)7iiP2nX&-+|A1w#Kl(3EiM{hMW6(`o^}bSmD;aI9@Lu#5W* zSp;Ro=lKsfPDQ!42nC;&L)0MsjFNuKI`Ws+0W=a3?WpjkqTus+70+$;!5XD%Tpb* zb@@4J^w)^fZDs!Yf1~Qdo|BZe4BGeW1B~r#eEvW=7ogW){sA3~a3!7Vv)cU)kDBj@ z()?6-09XK5h7wBYdDn~|cm3~8 z3?}iEd&a$A{PDaAob%yfCs{?P!EcF?7xW*Af(mhgqwi^b=l~JVh<)0+|5fjCu`&7c zp+EjJ2K-EQ^|st5V?Vcq#Pz)WZuDVAR7vnh(Ft=Th2HFSQ5yV!lwA28!YtyhfrQ4Y zQC{~9qV@E_4{Z54aR+=Go)16!)?|6TS8yu>;H_$Y{2z4kDEDWG#aeeiCU;98o_$}? zENul9O%k;-`M#3tCo~*T1N?$6T8>r8X z+37e6)(M!(S>c4}u6mz3W(O^=f{4dcnYRs6L&?#<#JgZj_r(+#IAug- z74z?;z0=X!E}$N9Q;Dzs4=ziHd@1gS)sA z1T7=fs$Lp|61X(!;5Tb2XWInj=>zP}WWr}|Tb3PH%>gMYV_J->d#pi(&Er9k?W-(n zssBuPeJhckWD}HB(D+GuX~$#JSXm@?rm1##D}65Q^qM*P{=91sy4vuRS_tz<&p$|j zzfa)rsqnA|@zQk!GNWqGTeIzBKqP^Lhl~RWY<>|-EgQ3D90aZ8<{)hBU&WAGou6kCXPuJGoS*9@yDZkyG^+NN zD6hM*NPdG7UCGVV2=hA7_~{~~+Qk~f%yhZFWw=8{P~3hbz4j+i(@H3)4pXRUO8`Of zwP>A;;QPFGv&D1}?4u>oogcr)w_&>FdTScC8O7T$2yBJe*t9M_+q9Oh8x&mm{>2Zm z6GC!h)HIBq7J|WVPbtyK<;tI=Ahj|~PK8Q8sb>eAc7@`CBxof-o%UHTR?K1aGy0zw%!JPOqi+0q+|VC{{6~81llpHrI%WfaPblhrtrVj znT?`=q9)Kt;1E8lL^$<3SoQj4SJg8T=~&$&VH$iOe!|=bBCHKg2@kS!?0RM8SUO>j zy1v$-zZdU(A(?S;VOk=TZc}5D7VidCtbbqf2X(9yjsWxZ59vN`^#i@zq&bK#Xm<*x zJ_~F{d+oaquIk208}Qfi%MpGauDO9bosMsg6cCf4_>G1mNr&Ns$u4T za&c)jbjxvPco5bzkj_|~ic-@$yz0(-9^8C2P=RkC0$UKHtpn?teYSEr?z={x-uRYd zm{Bwzx_0T+XW=lX%U)5+<@Z2Q(BV1V^goSD1#lLN6X1t+|ZEIvAQ1?)4RGmV^8wtIsy1N;%NZUAiUsMR!+00H{TJ@ zR#mvlESF+Zy@q5PRcr)l~V)djMv2t|E-&K}Gg!@em5V`fQS%UC4#eEN#yyk5MdevhBYxU>e zwqpan2f6cWCu{MkkzYGD7DKNlnmN?M++0Sce}#lL*5<^+V82qdt^5Dd%D77 zg>dc~QVWVAbub@{pANbmL+j*?pQrdfX~{}6d%{QSAu(vNe3P;{utKZ*ZJMF?FLiZs zJAtaQNtV~|O21oLy;7fp60S+>6b$Ry)kc&;#2M0FWfQ;qF|5+KN~xIfVz9HFUpi@3 z2zf_A6dL&1Qif5zRfdS2od^*A_W6B3HVNy#6EXI;rp^E}5O~=6oN$H=x(jc1?Y0$z zX*!~|J{Hy{U$s?5;#e|5^LgjDu6=C=y41LH(}@3)i9*4F&Qy)P-MTI#&^7ASCMvTjJ>|zRWsA zymgUtXP*xix}%b$_BxqM`Pn{rA6kY3Q*;~%{X5%}9CrK~RQZ12qvAnJb{(^W&s>`% z(YZH7SKrK^L2rPBnf`QpbV!NN-nN?F`#$G~ztXPVdNU^%k~C}UIzT6h9Q-k3b*wbK z-%G1F)NC(&Ri!aKR2 zlzqd(uutlNpnpSsWxntaG2c<_9&Vz{-N~6DnKuUzSqW45TVVAvh!1 z*Kln=-O*-eRQYX^7Lgy+a}wxxw*EKAMO)B=oHV~lW6P|plPPJRnmisny3o#YI{ICE zGsMs`cmMfuTjr-#uFN2Q>pOa)P6Op^^jt|A)q%@~WrNCo`bSIu?Uw+tl0~llWEeu0 zkY1hUnD=~JwL?>~#Im&Dog3nosp(Fs&9K9(&mY8rIhf8smxy>y7km|M1HL2w=U!;|Tkm2MW5 z$b;x-za3%#Dy$F++_U{rYT%7CgbK!BUqC5N7-XWgXyu-u^sQ)cqj@vbmVQa_JR;&i z3JbX4z`aR0F8)DlKodg@QqdHP*&^uwgf}oV-?U~}JUB-rRzxM31B2=0LB(DNt+vO* zN7Ky|fu^lHB{|lHjek-~qLy3OPYT#);oc^S^+0o9QAzZK1#k18)u1x3FEkdu-Kri`ev#TC0S7 zllotN6o`UQ^OH58lO5w2a}#Ld#_)C~dD!P%?IMMu=6=t7S@`!_Ai?8~mRP+_{3F{6 zb`1|bhV=pd^V{$D%C;!f!2~j}eY5rPI;n;xz9cE40J19{d4@E`wm`e%Fyh2aD`BNPw+=_*FDOw2Shx zbA@9L<~G)T{`{LY{aEM&AN;%qWCqLR;N>o>VijZUL!m-sxNZ{JXAv~dN>>-Q%nPKj zB+jS;bh(^d67^t*=v@?kQTXGB^^wTu|l`Bs!imu;E zh4u8Q37QIi&T%`TVno1EWL*Bul2Pp5fhqN(g#7$HXAU>$E+k$^y_y^SahmtRH3Jsm z!ROzYTDw4cxiqDXwo%%O%p=Dt*s-T5;4NM5iAf2I!yM9l9!9j}KIj@t{07j+ha|$8 zWEAYu?lEkD%%8@FGJieZPElb`*EfJHomk3jrKQ#iPOHhReI5IRAWXq219jcs9YTmF z<6c+gMTr?ug8tmYlb2ANaNYIt*6md)vml$y>THiFKDzH!uhq`m*X8a@p8QSL%Xhi5 z^&1a58Vv#Q+EYG+dEb*^V6NJr6Fh;`#w_TBcyu^Pj8Bi~$$gL2d(1IuL3`VTgy|c> zEeb2{|J`fzhAu2)PtEdKTn>{$oKw3`;LD_1kUr~+;|uPF$vH~_f3@5HWOMWY$4 zgND4kWI_i%jBoV@HJp4iuqAQC0EJ z5x?;xrV^vhTX`$hJ7NOt^mW3iTcg@T(-%7P$o|1uc@3q{X|R^ zkWTUnuxrUx>Z!-s+j>EdoaRTi?02Sg)C`ww)fc}(F`hl^q+#?%h(A;&7^MVKt?uDo zCS_T2bMzOz^%r4~H;YM$!=>vCaqb3`1<|6d3(!aBr3SU6&>bJLIxB6S0S9SwFU{Vj)$w=}O#-!%WnfCbNk zKXsdCgJPfQjfZ(pd>$IJq0YexntXcTU+Y!8`L^85!h7WVGD89p98Lh`oT|*BuyFh~ zkN}xJQX51-LT6tzm5}POx*e;kyZ(MO7*A#()3M6xWF^=EIZ*fd_H1Fgz`(pytYi`S z1x$}={%{@{ea5ooxt=N{EaGAk@^Y6PI(}AZ)N%_Jgv=CVYkj2vv|X&=NMwq94wr?M zU)B2SAq`-|Artwjwf-3-3NcaNX$=xPFxQlFiz3(f2LlQK%fmRoj)1#*n}0v1 zoNWFnfK)eVmnZ;d^_={vTYjXkQwcgn3=9YSK78;T{aSqIdUF?lWi7eJbuaUG{tP3= zx~k9Zt>Si?-Rl(CRYz@dZf&syK0H(60Jb4lZ$N<8vtTq`CE+@$jpj8L{$Dv@4ZNTY zn4=;j^RoVqY&A+PB8Z9HJ%pdXAov3kmPg=1B8dvsh5@93cp39{KFT%NaiLvNUlcX9 zvJHdEOPms277}a-IjHg$)|#`wesSl>uqO&g+{(okl^GP*1Wo-rp7W8C`b$?^FnilJ zfMIBKUTn0aKN)U~>D5Lf1ChZyi%6_q>ou4Uc&Cs4tOfrhABr)&5civdG&rR1n+mb8 zbYUH+XF3XrEVbB;wLy@Y$~r?D0hKNhEL-{-zyZs?$n^aFjCC%}%*?~i?JD%r!ZwN8 zIB0FagbxK&n9n^dXIwcwycqfs(F({#%AWG<=t>?QI zD%FVTsHdj&d+(o3`u+9c`pY}dK|skW{Q!C2B-m}sXZv-L$^qzbg)mASy(<=|zhDau zV9+IUcSx$z@n8RZMXl@``teP*EBc>0TOU4Cb)VKfua4C1^7g)N+C_V_HI3m;xuz`&Wx^=TUn5dB;&l#`9s^hr=|_zdu_gl^0)T{v;li&|-5arpVdv^_d)OjPZm@W+gK+CA zQc!UkUY$3bUW18yMlVJ1Zd}ZH>c~C+=GavAX8QCvV{{boGc*giv@vY;;^AUdM}PQe z&85Sv9XYV0b6(38-BVjvG62i^4zgKq*p;wtBN{7DL<1#7~*Wg(}UhS<&n)8YGL_TmpOgTVn3JY0~%(9HVyeTo<>qNKk?OV{hX<5s0wRqWd6N!(n4P_|-`<6mi@a5Cw%fjwkU?yO19GXV$c$z1`;-wyhx z7$@r#k;d)Ue;dBbtAS;NHyPynW9^w@a%TMa50P%lc#=RCNf~}OPi*y3l4uFylPh$4 zQKn#n!p1}Qq9RLlD^YUgQ}Gd8@JYFP(Y12=FIGJk6W^N9)Hf`%=!nPIi)D9qavKrr zxz|kIU11(Wk79;G6(gSgh6jjO_wh~d?Gv71u?c``;Rtg3jp6rIhsV$lm2@G4x>>L_ z*SYiR6G{pXX(?FNSDF3lXBw+OBRtD9Hmuai`Q>Jpb?s6zlQd~&L4Fp*I_ptNY^Mvb8G=dx<-RI z?ztzAB25dZ4f)zjNEN>OFv|t;19&bk3zt^fp1VsgYwn`knXdjYZ*}K4`rGYt>kYGz z3}W-ll2>HVH<76rPmtnKtFTqgw@+7f6oFFh94=4@x*+KA55i(~DdP60=$HJj1BtH4 zR<@W|U)0(p4vMtihK(F&xXeFZOu0yma=qEa!6`ro=2mv;IMDOQelK;?-6o1S{CY4S zSl!`e+#f+F{}hVpJ+cw2$_L}$#b`J#(tZ0HOC-JC#gLz4bN(Lh{QRm1WbuBViTDte z0FVZQEaH><8S^lYhxOoKMVkYi8RGkeTFSAP!5<&xl>!-c%S zm&$f5`w3`6Gja__o8cnY6R9LO0J7Oyw6NWpY&B)!bGvC=w-Nse+r&`*NU8?X;!*50 zV38X45q0YJ%h;t&i%UD`{XhachrWq>6sz~IiAlP0;pV6?`dcJZs7My*ITdk2TC`4f zIp>Z2nY_ptxuJP?$rI6Gz;+Ub-dWbKYCcilaLmvaxk4{_)QPK-Jwb7t=)eGOCL#0C zsuT0&i@}z=?aUpn$j(W}CbP-!O57DL2AoYu_clJWc7p*c-a!&(o!e9w9(%0wR3ZGaFlunt>ZE#* ztxoU)Qk|PM=nmvD5R|6)up)F@tFBMIbXxmImTXo>>rDU7eV@#;0Yq!4J8g_b>#N@C zIR+cdFe>#96A&RC^GbN;_Lk0X!`a0@Xr=MLme8`QDRXfI8nQ(tQKl(+Nf9-E##}+5 zn9~F-S3aDpxQLZrBzu>R*!0ETX{899$Z;JR(H$)YcYb)rzh*kYiG!4xe}FzKLDf6X zfm|h<&t{CR*9+<0RX3&Ne9ceNhNZ+j1lC>Cy_gkfA67|_}=5QG;mzn25{0@u=K5VD{byV2_?DVr}taE55i?Di!Kg3yjRD` z5VEHU+E?})afc3)W*1LBBBv_pt2-JM*8W=*J*f4bc`ej1o>Z1XV`S( zueUOMdrp^eEkdw;^@V*zTukHF{+&Q#6yqj9^Z-v#>5IEfEA9K76HPnGcm>?btdCYY z+|QM`d3U|qKv!gF&J9w4gzcHzBcF)z47}Di$Is2R1&!H&jw(vhf#JyAFGQJN)bQh? zN0*?3?1G|yLx({b0|o-Jjt@un484S`-Cl4-*RtMq~oKM3ruO5P{3 z7BLYMfK7!GXcpo7lY5Qz z!mSYYIzAFUhqlE;$V_wcm>-?-JqeBEe7Rydm!Q`^ zx0zTG>IQMc)L1W$TDFa!?{uc(Zp}pdQ-lt-^M@M?uLNH>8&=cHyaFTQ>}J_hX=oWH zSR^Va5}&7KvK!p7q|hp%jz8voEBKMZ<&j~NOJ2U*M5&G|YXB1$Z~JSu0caYiDS_l? z#+b`I?Hxmk4LHQv(!6Y4H{1NccnjsFjo2ZBS@d}44nWd9^^gw z!05WukQ|AK$Ekw3{^kn$cN-B|UMiSV=Y=O=rRWiBug0or4_xi>TagX+s# zq!N>hx2nF}`$U-1O%|0^#xs5$J$`0lsoMu~ZfaWT{nu;G_v8Z4-p6C=X%~48Nmg@( zdSWjM)OYRbo|5m#aOse|0nbEqxu7Zt<#}C+Zs|d{D5Kum zK+;>yk_QLrQ;6XkjgL&ayOjhCS5A60{Pd)O*-BGx<0d!D;&k*?#PgrqkrAUj03LCc+jy%L1PPZ>3L5_d@7#q7$sJLx za9_#}sO8g<2!TVzrwS^?16t<6`Nb@m8-LTe>F2-YGI^j*#+A?b>G~}aF_%u#{|)K*Sd-IfX5H)7gmRo#{mICWbwk9f zEKE9sltonOtQ>NnUw;Lm4974Lwhz-RuPha+`0^bM`jf>ia9@2Q1tGS#tTkM(9@eW+ zmpaF6oF$OY;+&U0gx!&*K(+Q1Fyr4>h_DgXe^HPQi3}ruR_AkI2Zag8n^6KwQX^j=wFi3ZHh@UyLKj`an5%Oc8ehT^iB=aH;XKOWlq$w zj9aSyQw%*HW#MWT|7LANsppE~i`(6?4Up9IAYBy2%p&*pL;V3D*-dKuOQmsWEo(WS zPexLn%(D<15$N5YY6_{Jv0O|{H3qPvM`pm(ALb$0!*;^k-;@iSUni#5F9ISTPfr|m z-SuN48XZHpS#$jbAQ$N^f4+WF+zNv|WYid_&ajPK$p5Qq#SeYDHL0myy2VzF`*MA2 zHx|Nh$7b}N!p4U0-H*!9Xz;|tYa*AtlFSoC5yB`snRRFZo<^nSI{97+O3>OllDpui zYStgTfE5m5bhO2g=Wtm*dVxar&;)c~x!T;B%J)AMli33wPV~ng)Yo0WjvryYwT^uT z30*ZW)88k*2fp>(tdp%yX=k9|kdM#d@iXpIqyUBUvLYvoiHy*#x0({H%w6ewkD~Kr zd?zb!w?@jBIqrSkZs(QR{~tFj(v2WUDS}FOmkdEly1TnuM1c_k(lNTb zOFD$njdY`QgES1-cHR5_?&o>V^B?DoowI9kecrEpw>bZ)sHB0-Lm9W@P$S$(!{qxn zl^~-K$G1bn|Jl+`3Eq1##3k?0$HUuKiB@S0TClu>J|s`8dVhiq;bQMCHkTxiXA7DB z{YtZ_$}m)>j{ON+>Bq0f-(r{jWU5bl6g{0~8Uprnlb9j*}lo3)7Gs zOD=VVv}8nX{_es%Bvmd4!^yM>ZwCMHz+B^S!^vAe1h0zC82FBrHVsLe3F3JVijX?5 zZk1LLuHoPet)-P>bN;7hVYu?+&lJyLo$&SOK3&jY?;;NM9?O% zy?)qH3@Fg^*FQrqP6SoJVrh4y!KEp2j28J^?sIwS0$~ny%&1(wZhMI^;eK0IBuMBnDEM&0ybZR1?8v@ZF-4t%NbB3w zCXoJPpG5S{SBG`vWhTiZDDVl%!+wjFofDkw&|oGvax1tU%C5 zrRz-da1N+q>86a0%CtH}tTVmmh2JM%?)>!P?JvGvJ zymC}SerDKJ?4G|)LdQ!9WF`ry$eSM{DYB!+28(uDc-g{ebdgkmX3U%Y*-|BdrVuf5 z%{AYu1n@p8HqkqR9Tt#oB$kLGD{q3YLleb7auc|Gu%-VtE<@P<@@*SR;dSHQF=~TH zx(<`leiz1u5L*6B-hy9o4U42owxR9r2Ig!lA`ax8RrcqHOT_oLzgsoNAAnbzor?vq zrgF^;dnuf%&tESkF&AP!d%kR!uS$1|W{d9^Q z;G!Eu3|KNEFpyrelAy);Bp>-J*fIK7;WxyecSfJM{=KTbLqO}_GB_I)yxsyb*&Ipq#IA7L8v4wbf}_c*W>u{iu&Ev6&bIPi(g4f{rY zJh{;JnQ&>P(L$9zx3mwhwQ^x&Wx<2YFA&!=? ztHQ&p`k?g;+EaG2xM>5+4)NXvuWCKvDbyVNTD7}f8}%%}>a7Vwn5}t57nE1J!bVWf zJ!GSmR_@`%%799u9B+;iME@?c@+oYv-_+@W5B}GxOhup_X$_x*opHS7=bnY0|Em)U zS+V-9+FwcH>KfbL45TBhZ(kg0@il+~?u)Um7QbpzI7lFB$B_mV2o68=eL?spQTlsq zG6Kt?(K%hyfoqDyX9Yl~Sx)2O2y zgVHW_BMU>!P?C?a4L8j-T{+DO27Lrn1ANDT;hAE(VSQWw4>j#apQr@AaALXTb%p9) zPljxKj%;>zxBN(HsTFFYu*NJapg{81g zdDPs$1iT`---_ZI33w!gm_PnwI}HTm>yRy_MC!~yfhUK7d}Ie%0bHvuTa+97KH~ln zOzB}IT$nhFyHO=xI$5GJG!T5F%6W<>=^M7nzTnzlgyGWm;f&XZjJKbPNhEb&14pkZO-v5`j5Fs=+FC2*R2TYGIk$x zBmeQbEEF zlb>4Nh<|5+q;89jKl4CUx+c<++PA0Ml~r&W8Xh(K&zIC}iHrmXR-UQsthcz*Wvx#= zgWxz`-1rOp_FRe$@@Xdw9!}W^9F#*MeeVCD+Vd4tIFsGCheqd@j(99~kV_gH(s_-D z{TO1@7`n(t-Ey`OK3Z`eX~1K-)t$`hi30N&$JuAvjQh2Q$q{_6uD}Liq7;aV z@GA&+XHw1lJGx*l!y;1E*(i;ix@dUO`hXO-bYI@wdwgHpvNT2r9v_gJ!GMp=ZRELQt97X zT%JuH<+!W8g?vVszSN^if1F#T8gnR$_N@750NcicUxE|#cn=<%L_Y4f$87cDbHzK>v|+WpkM1dw>QIyWdI+;rbusP^iahJQ z%W+z%`)L60!VA__+H zs*QY~%PGmEHMD?ZWBLM5@n6|%G2!8}wPhWiw^jJt8992Jc|t0$*B@?Zu5SrbnC@nN zQOtIJ^T~$6NLs$L>(xnKi6O+vwl|?5b`Z*~s9u_BwK7*;@9tTy6X>Q>YZAo4km>%; z27I^_RLNnahNk#M$z7-4wMkOJij{+700V2IZ`q80$EWT`tqSsHp!UZNQ!;eneP3=} z3H?2BHP*Kg^UN)c`{G|V)ELhg#-qU1Dp|#ih#yr3^2B@^ZFu?k>L*{BY-X!M`&^d- z!*B2nSxKDu;j+0GZ@8EBs*{<;Q8Uo_QreUCw44#pEcld9Na_)?Y-$76e}r@bc9`B^ zKr&OMRq|A0Mx_h7{ zG{E0_F#7OvMv)7*_OB6&XRFfdGK!Vhqofja(p#!|E$X&kgg&WiS1hDuN1lZ2yj#)i zx-RA#iA=7vdT7-3elnRU7AtYd>=a|iHI~Yen$BlCOcn>C*DO_rOc%`~*Qc|Ft~BO;h#_$EAOCsQn$LO(b9$H!WcNY@0g@3rXS8GIvx3y0;c>Xw zb&DcZ61Hi*jUQ(0(&{x@ZL+34i;Tj=&G_7IxGVFW?B0@Zq|MNcWZ1uYwp;;y$@n=!v*w|1A};)jr{(3=0X~} z&Q)7$uJ{jQj^99jT9a?b{c-=Xz0V!taWw&rUx(ic;aLtuYJc5BBUYs4{X*9h9ip*F zr>p&WkUZC~RHqK~T9h2c<0kjMYym!)^zRIys%JP=>AvDL^Lr&Fr@5u^fot3bdZ=u3 z=wFz)<61zAt8LwxlGgZr$1^K0I8ljXM$ZN*mAS(_N@B zE@fj9m$rVm3+2js#|6Ev7r*QEMCm4A`mEN??|rE4RPhpshcV2wwEN3cKt{s5zqBr< zkC9i<+`3P;3dY=bPvZY@oj}wYHhWQN8h*=^0-D@l9!So?^`7v9>%Rni=I^*TYcLJX z*!_$zQUeO|=>N`Ax{Ai{Op)F~dZ)DDdIb2JaXQaLCN5`(6AH-8ZF$0QgU@!J}aRpYz-O z+bZ#zDiT!tp9qFtuVW2|VGdX1$T(X|>L-lPn@@~_JB z8@07QvA49Y^}mfEva>>rT5~y_3Gc*97m5A0+dxZ4A=j|0z)^h8c6lrU>bFA{)>4cA z{>-%RT+!Xj#nRI28r>6S+kM#_*xwB)K4Xh{VRSv|(O)X@jw#zK@r1|DOp5IT$;Qti zIwpjh+1tZga&jf!!%*@QKZ5H%dn`H(bT3+uyi7^ErOTn8c zRdEjU6CxhnWX`}SH6+Rb{5i=vRUyqTP0*QDNAQFV@iQ9y@0SSWflq3!(Sv{pJf3?w zPZV#aG9DT*|CvK4bTE+gje^OP-NR{nws@#(fp#1yERK>z*MA%Dp1=hRqa-6*i{wUu z_eVOnCpxdabZBNA=Ia2cP_wbo=X-LG1M~1PLll&h4kv(+sfZI*zI-r`k}i8;cG@s$ za_Oz~+3oRZi$jrs%Aso%4Ogrf@=LaO+TF5my-VNMiJ^I_H*Vc~!wGMSJ(<>EBrk|= z*1oweY>^l*?Pq$rfV`dV($SeUZi3jB`F`)-buOirn8MVZ&s7?L)vA~)T+raqPqqk%Re$m(9CN9f1OGUg7c>DuF9E%T(75>baVmf8uG3nOz= zoiw!i0^b=O(V27>%mQ}>X58o0U?*XbO*PairAc$^-yzzs`_2fHJ$lm;hnTXL8xNI> z!uxFsz#>IE;#QYT*u03z>vd7qbVR9hW)mSYvkAO0UbBMS&NgSGJ=skl7^}ad8-L^0 zC${LqeLY?Fkt4F$>TEqtn!0A>F%>05z86)ffj-+89M39VeZDuI9pg;y>8?3Y?ePF} zSC~zHT^0=ZKVHv=ywcsClKQvY5*G|tmcZ8u)P~%~nf@J$k$%r0x8h;Mu>-OiHru3g z-L{*rXd?R2d0ftI4zA`okU-ZBWDI$v)PIGL4kJMOMej~TSn3_!C@=?ErHB>3l%L&x zYbih80Di^4T=~KU;+7h2TKMKcWRu71Kuw9CfY3??_EPqI1X!4n6-G-G9jkqPYzMA% z&pxc*9ect%ynehN@Ex4$42--yFMKyOdHNxNP#^uJRn~Sn+!!~{c18ZCZT2K+2;#N8(y znLws&0vLg%WtLl9br4*&&`RSi+lNK=YvSZAUhDcb6v^+xWf#+Px7~^`=Ol{Uc>=ik zY1b+PP|KlPVYfO+PDHmM4a|BK7RY8QpmibV*_tm{kMtCfbR_r-2`fmrpm9AneEWbO z0rgU3`R!b1$l5v>&TaQCxkK$C9}@8)lN`%`8m<>m%^t+dXog zw`9y{b3^84z-shW;APJZ)HfQ$iy(eF{;cBDLO?LwU)U(N&kcgc;%RkB?B`*_4@Ri& zIEp5eQX$JEz)NanL#C^`1A%N%}acd=Rz#VBu0oKX|HTAiE8yL0T z%*4N8WeHX~8m;S74Oah{Wg-VZta5cfm^|+g=R;~{&&yN|U$3WcDF+=Z{Q|h)t*AWs{ZkhtksxAg( z9uT^|h-=g(%1w%akNgGUvJDgNcRV(2fRH?YA+N613n87j=xxJAi)W(10d=QtKT+@==vH+Rj-U#FgaE9jSt37_ zf)!pze6o}h+%JA4YA8Q6WdyWp?&~%3WfPTA4?)0F$4PHcnhcjw>e}Q9XgbL#Wn(0n zNr=Ax8$%>h&)fKq#XRXq7Ot$+e=pPkR3AZC@&)wgz3TKohX*J+gfLDei+OR~Eo3}R zM6>y9i8G3VBhjTb=VD6{$`JlC=dXN1Sr=Bt8IakA;j+cnQaeAL_I*U*_2xHf$RYHy zclG5R8NJpd@RkN0k9o_9ROu<^9+(lxN_l7gjg*-iAbZE%BtOL%gf+0fqC+l6or&>2 zp0!qppensIQNPQBwfbvDMn664J9@$wq##Fg&jK8C4mrzhrc4CpdoxvQ}NR z*g0dPk%X+iEgyr`S}f2dd-uPTZKk}ZdH2cIau__i!{X6LxHxvA-%zJloGc#XNwc>> z5dO3E>D9S3@%-008GxCXPWzl&i?)-}tVa>&DsbN_Q6V+`eG?~4|1Z7u8Pv$7B-)$k zMFx-#w{&*#LkiWvSKAGqIq!1*HfOBmsnero!@h3zh+~Yd`${4BMN7}>qn1inAbZ%~ zDVRRg!{mDO)3hdrj?mC?z-}uQ&Ay$X;UDIp0Nz+Kod z-fsNxEbZ_o>mcPeV3(Xj2jJ>cC7L@$49ycl=uq=xYp8{(QBa#XLe@#qgysKIrjw~b z=vbkg_(hcu_JXF6J5a;pL?VHl!35sE2~{&!pF z;d9;BraiNaUzd@WG2PD0(Q!Mqhd;K-If}xwP8X`#%m(I~dtn*H8gg3Jf#t;{F7j~M zpv_BzIc#uC%^#;H#?lWo)KK1L3OJV_GKCS?8w4!IpsaB07}UlJv9*0d-A=@|pMz5W zd=sJ^4?MA&O91?afo!hr+F)Bz-ofUZ1)=r-Al^$Q!x8E}L90gsF?UcX{;x1UQ(x6! z$e+#R;=Nak!XMKNb~^!r5^|#F*yo!Hosz#26{zu7>|x&6@d2_fsFx$?`A71f>aMPsIh zI`PlCN0)!jw=xwA(`)Vt>I>B2U$iC^inie%6# z!nflJq`6yO=N@>eyq#ONV7(`lnIFT1Kt%pWTtaCfKVEqdhO2rt{e%jce4CK4*0!j< zKaKlvXG$?_Z&%?A)??jA^o?57s}bjqz6;Txw}yGHg7>35nQ{R|6p-)ong-$9(4qze zHOTQl4_MrA$vM&$@62?cn0(1ue%oOgFw=j}OB!^!qpgUfnTrOLYk_6d23bUF1mx9# zP9)vfxIUltQk{9d{VXF@xbyQxW3dlJyD`rrR^A1z-KH9}%_2cMU$H{+Qk4YXWL-}j zF9WJ1%t|fKCA#Nlkd4uhOaeynV0M(ULCvxti5%=mOq{Rx7>HbR4;*8JfXD^>U0d4) zED^J~*wm2Of-o6U0ft7LoPTjWx@7p6Z<@asq?FSV#(@#B?)~@pq3dEpCZ~ggwO-^2 z0RwThYDKxfmRw;tZK%YK+u7;f$6pc~1}y=df}}eM6x~MXHHJilXkuwepT~Rh7=Ey7 z(x!wD2++KjaoS*adPZ;1FbGokg;gfh1{0{&;pVh9KO#G(+^7GIVp> zF2(Y2gt}_)k;u`LfH@ABpc?R9UOYpBmCG^CEnmHIl@n&Cv0{1F9@WMp$};SLPoK;; znflhRVIVrlx7F#NY$yX7EBNSXQjU%WDw(voV@@T@zv5DkZCzz&SW{lqAfL?5Hw3(OXI;dr+L{< zoFqU=Ye2jEOIH1h+khX?LJK$Ik*5l72wML{)0Tr`zoA%-(2-W;Wf+ZXQo8WlL`L}S zs;lB^^XZP3k}z$Ck9<*=?NI@knXKhS$r!6F)@GpIrU6ZFTGkMWu`N83J~X@AR?;{O zl)4$Z4W!I)=(TAd8KE?htYLLn?*)ikJfQk@h1)}}l5o*pY0^qy^!!wU{u*VT1Z{DF zzznbb2Sz*VS!RCR{Oz7bxGZA{FQ>FqSaS}n&(1)sBa&`DDqCCE5-&JcI7$KB?V9a> z2{qC&J3E#2qLlM&^K_(n4nfOBoY)pSA2-9kSlbd%Iz|H*{pjXNhh}e6EwNvI{6br= zIVvHEOJJju99pdYBCP<#LMdW*{b2^LG*ZuZ{^?GSCjW&xd4RR&_df0<%MLl8B;`mE&CLI*ruM# z=|j^XwZGu2`61+U-&e?t0}zwM=xFoe2=Yi8a9R^EZ4RNS*SqSSa)k>m;Q|-JV5Hx2 zvt{OJ{?h=wAI|hgMW?mR_R$0uRgps<<5O*5oq54(ul;nBH`jl)HLoaVD!pxZi9!j5 zhqF95XaNhSfA0P!KvcxlbNu(H4riJi@i0Vw{5G2X0=)B%a42yfv?3hz))`@2jSq7E zju7I8zq9OSL#Zh_R!?Tj!t&N(uH1c1X&<;_Lg@5Ppk&L#C_@xY6yS7MqLj|wVDno8 zVFL>H?{^KjVimcH=A%|ZA|7s%gV0f#OD<>ug72x0@q%dsJzqzO8A^F3*8()E@eCyI zzcplzKw)ROVe`N>S!e_mwEU3Cm3kfxeoz*s&mKq7qxwJ|a#6yKg+1vv zuyp9R7EUy*F}c`-m*D58Jz9X2gz{@r;gsr6K$;a*8kq{8Avbd3oW}L?ivinbN z3L^1=Z>8AlCL{R}>CvSb@QOFg7E~FYNcR zL^+caWvU<0H_a@vnrEdY6*2iOmKy$%D34@(UK3dHapKAr^-i2|hP8M4LeN81WrS!{ z1IQr1cXAuQ-RwXwC@4ZDr&mq=g)qni;r{KS5Rh-dQwQ}^lm|?yaiyfA^|S{P?0P>O z7CEA1$v(e`-S&{CDHs~q7=I#<7fjcraC}tZ)JlU1!qq8=xs)xX!)Xd3-4V?-CH$H6 zm{u5@f=(o?Ci?j#p8L%%S%Jr^M0SNdDc&~)D%LY$J7igsWt6XRF#C%9Fz}c#b$HV4 zb=nmU=_<+R-xmjdY~M4inttxp$PlF9l2VzbL`j9%v10@n=a3p4+MUWnr>WFVoUs#& z;8$x7*6)3e_K%_EUB{qE?cw;8YStZ@cJDLw>F!HB-@o5q$y9^vwe=WB;<57*hiKD8 zh{^eM<#pyBG~M8+vN!g>EN`PzWaFGu-a|IVk<=^5*Sf~Slu5X8NsS7bPhT(cA;si) z5tW@tck8}FGGCXz7`;E-eA8tYwmF<+HJudUAGg-RhSl#5f@K#BfGU5%YGdtsS=~Rk zhpTcA#ElVOwEr1lJQIX?xp+ywe`wyPtl-`6Rrj|A3oLotmN82m>%7z1}Eo|!7o4O?JYJ`NPlcD`|+gqW4cFE|y zld}?OmqI_Z9E@&0ju)sUxBGeqNNUxj>IKROx!-RWD?h{p7Ob?cBz5a5H0Oh|F}eMCd?8`a#Dmc2B@bCoFj z6xS@&L943ywlw>a_f^Fkk2#*94M~%Z+s2VD6l_ImO1J$0$N9Wc9`rt$f51NsFjezM<@8HE{^i%gZ|kd3s^mP1Gn-8w4J%m} zF3EF#;j)obKUUxwKtl~CSSup8qKM)gP1-D_z}{2)LRb#qE#&$17m>BB>~ur}m^)D3 zj*IKusGAD!Ij%|-{!FSPTg=lBVhkb|EP9;`;c-tWgaqwlE2M7*ayK|h0}7On)ZRYa z&|=PoeEXn@)TL+F5%##*1$`igpgfs_fD#Zq{~{ly!q!?Zudm%-x!D!}87o^gkvhvO zM_p?N@?!kmY3!9Wn#r=m$VjPPaV)5-P`&RSWkO1A0#}gvoj`MilXOzp8X;3IR+3JBfy^UWnNDkFJ~NVqr%J0&xD0s~;B&OTzA0Y9^eG zD$xqg#ST<127FuRFGA=_Y)Z0(L$$CIMQY;hUNw{}@~!Zh#Bbi<#D;$SlEcAb+vW-G zWcHh!RHG&;-V9aC;=zWNI_krP3F0PYXmipik>X(E3$}iXSz*;*?V(`KL zKJg97XE&hj|6O{oA%O`_@tLwf{I6V;!<0~5O+#oepWu?GQWX1SQh5jA>g_1}5c5mY z`_0TG&Bqzvu)(2qqel>j43cmHY%(V9zJ zz?cfa|0x(ZX!dy}LXZ=|{ChlqFx8YLqCr%W7X{&E?7Lv0QDzi82Gw zH&ttkym~OcpHriQU-_WSyo*0zzMZtT^>UlkC^W(?(R9l9MBi&@Z4Y zQ@NEE9XW8mhc}&{LtQ^Uj=@k4i(SbpSMareeGJyzr@@Wp!e-|}&W}|m${UDv(!Zb+ z8&5Zzs(BOqzpFv}%D;elQkd#$%)Ei~ve@868h|4FQfO-KMzAtG*{Fm9vQV9bABe=@ zYLPW&2yk{(RnpH<;|-ff4Pp3m=5AevB?72No*rB`KM!l5m##l|lPe1$(}}q+cslhq zcNhd6DVD<6_Z7g$FH*?pGuQk4t^RhUTH;Bhc=)09RVjxSy;Q)YAxl8DqB!5eFic&p zw5MVf23tfmTcjs#kB+$1yZ6bmhjTz`{&XzMLDxp#k-Fu4^~kCdy7C#ux(7{WQ+0>mX3j0ucT|R^5>keJQ0y)za$)|M?pob!NaY>r*3h(UO0` zwzD?_*gbiOss25;(Smn8g|@yvpL`3ZegeAB?gX-d9V{chMUaWLnDwYty2v+1D@O`Z zi+-H8>&nmaMaN02F7!L9Y403ASq;>a&$PIjim^|%W*&IaU)Y|34jTD{&;N7|#zU{5 z$*O-vJYX>qAgN3YK6nXb%_~BCVETKAP{r?m-vJz92W_%%3n%I(lZpI;GB*_}^>MSzwaN5;U^}M64gyl@5hwtG@@3*qN$m;92T7 zy?uz08Wto!<9r812Y8?ez!Ja)cPFIDwL|vN6z6eq0RPGOYHV-$>~AfYx-<=&@$>A9 zT#fQhBq5!I(ZkId*MAIOGMmm**&9S|R;tM$nAcdpp6z%&_SBl!e&%Dkg!9)+1E-J> z)7tij%@oPbkYIFpr&Gm9;mUQsOZm_?aUkbsR(;!x`o zQ68=Z_2q^sJD1^VH?btNU?KPE3(j#|`-6`Z5wZ~=;V@bR&nRxQr#pU&^ z8*jp{ddAW2!MET?!XQw#Dhp9SDdu1@)`!Zr#xZr~hnMB3wd5_jW}m+iG;1}w%u>*D z@dGcxKMyC*U+4n7NL#on^?O%j{cUM_6Vwl!blS}m_Qg5+m1lgT{c24F6fhio3_r7P zru!O;US%^xBmohGAF{2jZ}V3t?FnZ!f%2E#Z<$N8T(95vAi@s7qE#gq$XRIyruP+w zEH)?=g}{Ecq$!$4Oos?`qDfAvHH<|455}2yR>CE#RuN`2NF#&yu`J_*LeB{wmQH<}%epkUA3< z`@)!*$=z|>I;%AMKu<2Ip%PpLcpwx$#~A-^I~qJVdHl5ZxswMmf zP+NU8V-U(Yyzlno<#Vy;gK+t zB7Y#QnlkzLhhNtTC4B3CJC(p4bCSkP;Q)Qe*zXQhDW_z-V98>iuMofl}yV?ptfPF6Rc*ZsbM9{ph-a zI^H=MGkyM%#;3{@Aok_zE)VT<5zjrPq#o>#C!zJy47JI9#P7#9_)C!JsmBgX1iWJ2 z|B`M%G!URfRODg%GzaDIUZFVBrb8$;HcJ6a%=~VRM5jjETdKoj*R{{CxvJa(9d=tR zBBbgmQp#?6RLgV|-w?u#=sn(|yslD_jU(QwC`+-X9R)*k`xf>;jMn*f_X|j{#SF1N z3q&8=xZ8@I5Y8?RM5%&*qCR)78q~La66VSQ)GQ-~%8((Uw+}W?;$w<8vlAB=n9i=0 zfN14g!$%}`hh08^PO<}L`>SET!SEOLtmzxn1mUtlYPhB$XutI~UDVltRERYEqlrfZ z9cjeDcT;-BE%oVOBv0%^YSAt>gQ4n%jQjAvhxLp@`nWy$$qy6g29)rRi8tVwdDgIx zCcY=S_J#$cok2UQ?5GyCsVQ7WBMF|r zLx^=WtLM}nC_TPOh-VP#zYZ>Ly{4K_8rGDD>kK@1q;{Z%-%og==-Is-5463&wO_^| z$nU5Ka?Awk5EyljeChl^Wu8(cHED{)GF3q`lBQOe*)iKS?|X1}+N zu%_9jYkysS64{QPotM4I&gx1NQ2iHocdQvYi+auVsMoAx3oyT^ou1ZsIOKcb_^{)q z`TeH1HqeLgeu#r;L}kFp@1e!1CqF+^uuvzSk#+F&ISRjN9PS{kE`=sskq^1FyxD9U zN!)QlGP||K<#6)KI#_d)6Nq*u5VRW zX9Q9fpg?^MpzhuG8%HSHf+6Dz*7hH|^wXDAla%v*C#WB_5>Wm#LB)}~0a#f9M6))# z9g}a^#9)V}Y?l89Pa{J49xH1R7v9dLc0cEvf}|FZ!IcU1ZQgR~^uCJ=&=CmJNLNi<6~`%ksXsDM6mS7pF^Ep0l~x^W)C;H!~nf`%q2!gQb-7y6zzKEv{9} z!PO&sjN-d%mJ2QN zMQ#9D3qpg&h@AJ~O^&1-o+UUP&WDJK=ESqPT^v6lE)4Nt-&cKj^6=`X&YS$V_8-OL zm%;ugrt51)zw;D7UBXli@v1)$f~~_E-fZHV-rj6NKfD8^nV02uXj?GAmwpOC3|srv z&oxWL8Bx5;)M!f&eS+msEChGam@WL(XXc8GIfHw0`Hz$rhan~Ro)(V^%k9%=orD!P zCf`n3P;&U^=P|)_@{ymwVe7D3wx-t+UH*^q02*S1qP6#!?&ot&z(*VY-()bJUmdZ+ zK&~_uq%&fc%wBjEoga!9Xa8+iIchf+j9x~fjRVi;J@S1&Oa1&6$ekP#(C2zfbJhE$ zu5v2(s9^dON`?51`w&_O>gDJ{+Fq8@vbHM$mN`}rXRh*1FThWt67N7?!>hgNWJGEP z#PwDhT|%@bDXZAUO9WGw!J|=n>)mZx-$!raPUe)!hsSA3vH%=RGb$kF8A>vw3l?=6 zvg-Q}ETF=80@FIxZiwAhcaJ~bWIJ_zz4QqJQ=nd@6S_^TQ@}A)21OBkFTT$9G$hqM zmio_=(jgr1XR=mw>XUaKzE_~ia^i@ouQ~h)-+8W|z1kyNFmkgRvr2H{`P=G7OpEjs ziAM>m1G^BF36dwemHGO#pF{7>-rvL{YxR6v5&|@dHM4CYjjG>00okGaY zAWhAXPNe?Ibqr+}$|94jxU2S8e$%0#Bq-cq3EVeD+z*>{I3K$WL?WKPnV5C3kN5VB z{%7crF;tm*2=;6MI~J5-?k?$skP{)W%jPtfZ7yByA5g}24t(QM-|{-VQu7QOr-j?Xb3w+Hz13*; z?40IS#B(3dwsR<&D#YI}zeB?eiJcgw^#4CH&N$SkLS?@K!V4+AfbZ4*X8*SU*Gccg zV=>Uc<-kws-!Rh-B=PSFdD_3rOLK|hyzv+7O9?g4M$A=XyZ+%l2-(j(Z$m@v2NGU> zvPf;nqVnH3)FT*K@&TOw+r5tSrMtXYDd(;AD(j-hAl;}RoO!ZOdUgZ69SwE(2%@g- zYR%sIp-M-OqPt`gl#mL?f5(T`lSiP{F}(@}lj(93f}&V9oGG%FJg@p0cg!>?ZL|c1 z^30V-IktWALc#xuVHWdvYP)>7-IUt-Q?Q7!HrpT^-;Ig|IlkgwmhT{0gs7xXvpE9m zlWM!qaTw93?w+FHZ68`(?H@P=!{&k)|KG>v_^A9H;O0F4E%rsAuB5l4l=0v zu0P+f*8L}ybFR-EkbuI5`x_9xIXDUSdxb7Q>#Ng$h=K+}$UN2TZHR)N8ChT?CNFcl zhgy>y+Ecp&Ta6Gs`B{ED(E(xNQv&GzoA|d?K^PT`FwNXQZM{9W|M(0GH+0!I<&JFx zCeDS}0o`iOgyb{uXPsxR6!8?SHb;5H3Q-oS!+T)~?G-c5nf2ROiZ>l_V#$k*E^O6cmIw!f%ouB2v-E zM7plDs_#u^a-pJ@{q!=@MgEVs6`3`86UhPEhh|nhgs5VXK|-3{TQQeZ{RSJV>i+p)ZWyN{16oYgQ!(`F_6aJo8i4snvf6x*q|L{QY(o%3iKM1WV;IWduLW z=34CxCtNN=R1#~_HX`e>q@6GxZ3&~6k#i&fN6TLZ@$us%2&fd>_Tss_w=cZ52JxrW z9s)nh1#~|mloA`~LAR-QH26Sn7Klb-)FL-ccl9CR!uiS~~zu}X_TKUwK=>cjf)~unF!HlvB zYyPndVS6$dOT$LSsMJlL@YyAVMbTkkU<11DB7I}r5|GVqaGf%q{$6+4EGF{s@<_n( zI|A2RbhZa;=%m|Mr@jQk_oUCF-l_LfAa*y&N`@J0#Kf1Ber!J*6dCzRCyy&V#af}2 z_56yNT&9q>e={6r05S`7CX_e-Xa|X;T-p)Z;98WO54=5HBt}&-MT+%6+lxNx1QIh& zK4~Jl9)iXA%+Erp;ifv6fZ{aQx{*OevU`0?lFNrRn`M;Mbr)rzPImQwG`uY40xR79 zb)-9_HH|38)OcTA16bB6+j^LAs6gLQIbLoUpz!eh=fur$N}m~Iysg+a*w8_m6`d^G zL7vsoU(0`yP9p3hM2f`fmEc6m?`YQeGP}!YN%Qlrxn5 z23g7uZ+_b>s`CmkK}LW0mkov&Mh|X?B!A-s0OTEX3~K60sM7b6`R?zxI072M=q=Za zOCS5mY$4s6u1i*5?<4-@Fmn8pU!GD&DED|GYAv?@zh75GRN#lh8QgH;ccO)~q=%q7 z=$g-ZC#tqWbr^F$m&>@_g4pNXEkG}d>+{DumHq26hY0ZNY6k?qgm?u9*Ztx}xk#;7VYEb|Ff&>LTd_YDYV%Fd)n=FEaJq*~@jLj{ z<&fA}k||uA0onSmJ)Z%AYiekLUKU`}$mml<^}UV&`C`%_}J)uY@S!Z-sA+k1Gtswxqf zhZ^f3%{0AFLHeU3dl4v`E_!WuRwx_y=HOQRup`fMw=~0urML1AU~cVh9c=3IIPZKA z_b2MemqWQ!vBk_S@&nr=!OE}KB2>Z5Ji8i^XihBHP5B6V-|xL3Vo%CEy5)O=chT`% z2)f%Kf;XGVPxmRrA4}VDNP@*YQE8SrX9n*9*YmcpMx&MOF)$NRZ)N^^+4Ief9JogY z%~OzhStSL&4tO!|lKhzRtWbAR^K*^eUtgn8;MP9g-{G>xfr}Y(p*%X_vDf=M1bcR@6X6>{RP*^4K5nFU7xxzx2A?@5y z1|b`uUz*|nTA<1Hv!!E6Bbruc{#axG^t=<#wBkAuPgl2YTWlwDJob&jN#OfS=&u_>UQXA zMa=)m%3ayK6U?5Ey;&z|bKfX0-o$?I0<00~=Txml+x>>+I_M}uK+K-^YzfH+z|PPY z7j!0?leMsByE|XZy-lNKNkoL!yIgg9jqPiI7XR^8vEe7{!>!_as|(Uo)sKCCZ!+qu z#`9-r0~zlXzV8tR?JVORTdEjkOm5aO9h_UJVF}feGXL8Zu6reHXBL^8?Iaju;se_O zF)4cCk4V-8z7D8bk__&n4{*`9j11Bo`k>H_W6=g&I(tG9v$%Y=M6tRUZ|D^f6JWNxUMzo(pX>!V3SC6D?WV>`yoRr z`|?X3Y9mlNtS%H64>Rqt&^<@;ArqBHI;z!{_0LR16lci{bUWL!^-)0f&ef#<3xMOy zk?bln2!Fo0@AE@hroPrQ>J?CH_pWgtQ>rIf%}eqd+t<;49p1y4+gIf{f78LqqP5c# z-Ag+4B-=poRVAMm;g4LkCF7j_*{X> zI1eyIjOy;r)cR}ZeBHt&_{uaU5pwqv;cfGL12X*imvHM^jk4iM@6++46JR#?zLE0+ z5OurU*}UEHo?%ww30akaojFHCvSE2p#eL2+7!{kVh0L(kIWicZQ=W;I0V&S3p3l+-~CD7<*-2^Bsi&SqY$ue{%v>n$TGi-4zVD-(k@TCnof?r&>J z_>gCb4?_ywLyVv>8WtyO^p*o-|M_{3y~Azg>7zn0yJGR`$Ybf}hy4}DI_1)curoSG z>E}$Ow4J@GuSg_Udq0%!Nf!`jJZ>!DgDQg&fJY4lLHZZ6-X}!2FFE~Of+&BG*YeBs z&f#_jhHrRBVYsSpgcQj9nRmfmEfBMyaotxjZgh@TqQ3XIAae~@(Q67MFG9=i-68fk zs`X&pSp3SW@~-X^T%P<-MW8m({3h3*=y@8$I*WdJ`ZnBvKcDLTd@&#NrxUf)%JhP& zISf+$9>%P1js)6^)6cMmObZOfQ;zYT?}LsND0B0m$b|w_diXCcN_8l_T|S%vVH;rHO|#(N&stcq6Ux4yG7(h}I(%otiWI|$`GiH4gH~5y zmbk3m{|wheHQIqP-i~ItuJ5DpTl=I_a%qbn{ol34#ox>x(55}yl3-?vBb>yO z%_`dkDlUo?f2bTBK?8G8gsM~^;RpoK0>j7YnO z=c&sqfcJqo@TH}GD=K~Cf5LNFY3KrZ_D1fY4K3)?VR&(y&ssnSeATC`@R4}633nox z3>`c>sI1qlU-Nik(DGBi@@NO#Ukc*jJ8ufj&RuB##cbwjaxuNh)Wgi6vS_Cm6^!{4 z+At|$IGNk}a8!9y!KhOxig3iahvZi0v-z1{31;_;h%potnqfXHr)R(p3i0HK@0>6Y z-?m1Wc1m*B*~yUG1|nOUa%M6zV$}hfGk{bxh>#x4JO1(XDKYg%T*4IUx%Xs0Ua;T* zH<+XC%iVd&P{p+p{FwFo=Rc}y5G*(dnf}^8PUofy*qhfr?;eaBVgg8Vr)w|QgV?y4 zw(!QwXG>XCVO=l7tDo<=Nj(2>J;_HS(QQO-P@Vg|CQZ=A1R_S;mx>FweXT*848_yu{d%qjwa3Owgk{7&&6g@wAhw;;&)t1L)f~ zr-~IW^e?8aBsit;=9;3OXEuO@G;g|@w?9lZtL$UARFwK8y7JvMBfc;3v;C-0ZJ7*U z^g3KMz)81z8>;#=jBaAG{5)?zm@Kk>Qg=<~155f{Jo(TT1+wpLZqtup&Xk8wzTis} zafcKie$|>S14Lnd`DlU2+fsjhtHTsBaQr_D02{2%61bQ3a>$@F^T8#gsC;{w2iyl^ z?lC<6Fzal3=H~>`&K<~fR>R&GrnpJZKWNy$c3@O{{az@>U8Obu7cDT(wHUJSORDuT z471IKNwm!m(r?8U20-^vkY1*P$ZMCy$7YA#+svd2O*2HHsh=WWZg#!(a+2{rtw?#4 zb@;bAUlmNDAhF!N3!U@Pc{A~UQSVZb>n8dY4Qyq|bF8bnKX*5y2AZOK^%?>MJ5@Ri zvYN2gER}jny$~yuu~vTztMC1hK6SWFsCCmrHip51#I;MuGfPLM)x(C{5B9;X1umET zsDQlCrMLZPrt~1bq!zK6V~@78Xu))Js+9y!*~x%Er)OjT+4b5>ce#VW1y{iJM zX9C)--hITu6NW@x`l<1O4c}89g%0Con@nj*1MG68MeSs04xT^6w;3@i8?FdxJj^5hn)`Jvyyxyj3ct8?H>w-C^H?b_vB2d&hqS1vP9` zkY#L&^iq3ZIBufGb7d)P366P8TRq;tOk8(dX&S-e)ehHdLo!{aXvoKNg1F*8nMT~F zWuy@`{RjvS^arD_9NoG?2s!zg_+~+-k0CZ?%dbdnKun^LcabO014KoE$(4*KeZcGB zDjIc6Txb>~U&ix~!(xqDD@1WjU&f_rM%%G)8GL0TA8K8@CI#7S`pC&#Wqb#1uO_?y z#QnH}@Xx@c3;=_2uX8$)ae(Y6Yro<0Z2I(q4!MvXR93}zSAV%zgpn=Dub=N;@={j# zxQ6yw*FsC!gHnM`UeRRJ=&^F>;8gm3hG^tOSBy=*un zifxNhwj$tQ#)yKkK{V;BiX#%}`|!`e#>{2)@a!ZVUk;ih=X|62z205eAZt#)|v*0jcv9{;K-%tKS&icEV^Eh+Puf6bINOSIxw54^{U%G@-S*E#54Vs(B*H4G~398S4A~R=k+P{hNDkyqf)upg(?jqzP+nrMe)GY zG)a|urvdH*q@mIMgPM`Un41j>^~pe zhdY}- zAcH;~R+xOxtLrK?@HA=v7p>dhQRbd_=SO*$1ARJ$ga%J;S)@^<0B)w58JC&jXtBWM z7rEhEzVZgZ`?uIGk@$nKy6eeomj(JiiTw}BG{pZQ?`;!Jqg}-$x!YW0UUav8Yl`B@=_7}S$glIlt2zTuGbEb7I zvT{sK%2&e?Z~*fA&PYq&%fmtp2@qsT3%f1B%#f|%e|+3Csl6|mTUeKQ z@3>m?Y6PYiVPM&qj>y3cqg%n4f?7%Z)d&BOCM!{T|HNQVTNz*X6|raRK=>_FH4zeM zdMA#T@>XZ7>$O>o2HiH)-Ek$~d)-$@;dcL7TK9jLDanmX66CElFl9Sl)+~~Y zsf=g3v&lJG&QSaJ=bNjUhf$c61VGQ#dvnc)O>WH&cI+6VdfQ>7)bnF!kiXhXr)5LI z`}SnJ;hBDHus~-ROR2W?#p*v5?*{kV3H1C1MQ4I<0YSY)5+S_*2vPjzQI0f6hhWM;62#+-VC)B2^n z!>Adr(g5d)!X&f;iG=7|I^XmDjGD=R++$R2uCCkGJtrF2bYg6K#EP=dm4VWxfeO4V z66O5-Ui_Adue^`#-}`As{v7tJSxnZJ&e&VqiQ!+SgaF@E7#pFK6>6hlN5_uWBS>=Hly+!D*EQgBKn=kA#LQ(bl!u$J;=8UYD;!lx#@!%-Sno(E^g3NMs_s2rs*mEE)fDdF zv|qTBaN>v&5m_{8RlZ*LcRrZvR7!(GH?1FBzasDA9VTS)NX+p+ZE-`L^9((tGc8`w z{3923*o=0cs(r|F)hcdWt92oI?)o1zcjc@-j_Hw{>K{-{Eqp6kQz&UNFQD!}dKh~c zF$pC25qsDVZt)!iL;UV=yRN7YFsggAj2x5dakd<1VIy`_orvF`{*U}Q^XeFX_!@s`YyuP;KCQ08#P->IFgi)j5YCrM2Oj8=Wfmim^>dS z@}GKodv$@(Na*eY7+F~%WK?N{9gxwRJm3HK``O`wijU?%PK*1c$N)1vB;?Y$rz?P5 z-@+w%8xVw0Vh(6F(ajJr{Z15>z2mjI+HG&J5qKlNO4q3zFON2qJ`blZ(KG-s_3eI4 z!rT(kPkGs3{z=NR6^-dj?H4AIFA$NpUkq*zqJRl{RLltNA7Vy?M|u#@cAVr+?CfOX zz3QoOS&rNH0OTleUOiOF6?j)aDOVJBI*S(H@fbB5+N>{tSKV_ssLe2tkui`L+rn1 z)$TRC_n-h_RW{GmDk;dTR1fLHN4J0wv$gATprsg$OWxf7nZC?DVi1rX)Vvo9hh9|z zzz5LH{=rWQA&>JcP^sMw)ZF{(``CbL_jSd^4q`h#1%!rz=84EAdUv)#mXK_>rQMB0 z65-}|&3=55w!UxG>|JpVdN-*J@>3%PO)FmK0KiUq=WG#UdK?&fvsjSrE#1?-zhrul zo9y6rs#>%J0k%U7ltVR#KGC{C$1XhE29Ul=lV8XqF2PKA-^6rdhEuqQ$t2CHQ^*{k z192=o)Kgw8L5mQlk;2M4m7nY<8wXxs`b+*KWjjoc(~bD8xBT7cB?a@ZsCzD^NIX(S zOZPYik2m{rtq+-_;7lD~I3tBrK8f&Vt8{a=Tz_C0pY2uu_yPH%1ou2j*z5KufoWyH z-pc}t;zQk6Fn;YQ6Z~7eprc=uATyTOx$$Il3%!=p4A&jU9Vu(Y4HH|qa5;8sWC$3w zf5e(5U0WUii|(#Y$}T(32aXI&YTjS3TIzJLsz2i|#L$6J5h%1bID-Ok`>y_Y-d+-N ze*wbW_4+wZdQw--F|n9ne822sRZGe(SB=J=U1RbHA=l*@VGvRiIf@5L!nWRqu*6>u zdB4xE`&eKXT|7Z0wjB-_ECd8VIC3Pi$RJ^t>T$TtK!0I1Df96)Saoj&U=Z`!q|_ED z6?k|THe-d;WML7-l1t|rHY5#{Q}w$#)Z-0WCmBED;VGn6HmVE6hCe8(r0)%N*4O=E zW^NNp(5>sp_Bn>&`*n*7wXOoM(ew|<>4c#0Zm1(1k|p8R_c(uGB))%{y$u#h0(*!7 zUB$>YEmNZZq?PZa8>3aRzIY;E7Re6|-7lF=Qfm)SDOp9nJe?q5;8r4_=*J%lY4x*# zDJUs|t4|-jHQ>4|d%ua)UIveE@;CjN@;r+aXI@@rc2U6SW)2yu-;JKUmTN#o^W>%4 z*UBh5e=Qt)hSHHorkY$|jf#8=((##F9L|^5tbP*+>n7a2=hAd7ISv%Fz$6WK?5v{! zm#~c;Y*#7;>$|fys%JD9wV(=?{E@0E{w257BRNpyhor+|r{QBUt#|;$M|v-_$dy&a zTYjbcQe#*P)CIN8_t%#lGg(taQj$0Q6)?FhHSyg|`&4G0sHAXz27)P^vJTD}mtn@1f#tL{K&1)Q~Q%-s#Q@Rb-$<`DywwH3A~g1;7l3dSuTqyUM1 zXh|vZ10V4N2rk*4FWe8}FSa*4`^1Vmn|e&`ym@lTFzc#{BxaDas&RsHD0bRNEqPAL zWB9SHkETX0!ecO44zamm1-`%5ET@EtbCgVS*z<$%?5r&fYf-}+%m(%-FTrOqNoo=4 zcy&_bHzsLvZ`kky(^E1*CWehv_8Ym0JnYCWxwG~<0TOPt10DkNude%x!S44t6o>ye)TBogjv@`e;1nYL& z1t00f<^#9E7agEpH#GPw64UKVt(o4=GrEpZ#5TsX@0U?+8z^o4s-EizjeXEvrFXHF zUNnloYGG^P+RbC2IX8F(f8F=YQ5w=my>rsEkrpK~bhkqtwEOZF&`pyH@<*_r{3Y7} zg~!UDe7x_O#I;-zS%lK!IH~&X9oX(%V*%>UX7|-~Yy>kWifxU7WQ%CWKE5XlWmTE< z@LoPWiQlUdsh|VyY|5=hO87?lVqwkqQWIJs6HWtjHz; z_5`uVc}(E!q}3|IHPhrM`js@#JHzPjw>O7>f5We^wehA8Iaui~{GYDQ&>tFp_NJ!Y zdY)X)3+Q+TRP<{DE}`$<5oX5a(edo0!ZX`I2#K97wko4gH5_PWmU~TH<uV=g54xISnx5#F@@OX|rZwbw@5 zZ@)bX*h=9(&j``D+|ZnwYyv;wCMHB;xwgyOQTh>lfo?=*c&a{4G3 zSnp|bF;m8->jfI0Q3kh_c@_2ppr$`>46R6*^O!zj$m~RK!49DRVDzOHOuWAxR!VbO zseI}6OReF~NO)9Y82o*u?uxAyHwgN6v;mXO0lFl3>&sgQ8}DKKn!zrSWyHbN)UG9B z>n^CTXkKHmwM)|uc_(7_D2VasxuD9ZpYPs6C_7n#L{BxHUYV@1q3S~=pKgXXcM_Sn zs>=A-o?I8;=;PKAuXY!du7zy%g0)lYrR^i5(cx>2nGw4VuUH=o&ws@1V$m%X#nIoXjo#SO@#*+n@b87UM<=Y zmm&-~UH3mtqax*a;4g6+d{2=`$maJ>-`s`y2yo-$9;|tTa-ZBjy46^_t9*=r;GiFp z?llYK-mS}e9@wd0{R97)b@kb7CClvEuK=3K{?#E&deuv#qXjM`{hhYC5nl}psC88| zA(#JtFxiBuELoh54||p&c&A8l7%N>#k7L^tezcpyCLsEhURival`)&(J_#c*8Pe`tq+jFVmd^xg}- z`#MX=Z61&tCp_1&BMb^uoUf>z&WFThR&3FotFq>DQlNHmk@HR9xadFY zBN?n~{uW$HvH|^r;atS$_Cc{=4*y3>QdFY=Yq8ll651KnIq((JOUP4=?5@MuO5wiW z1#S%Ox+r7U6aK4c!}-*JZ_#-oiu&P|JM+dq!TVxs9G5IwGo+y`e<4;lXoPb9C1|%C zNFv#RqZN)(l|((VEod)?(#V}7sNtZRG}+U3yp@#e*PZ&Au%iU~Dp)@diJxbFl(qKg z$+vic@dIc$1dJNZ*cH?w&IPR_-Ke?a;s?8{O}j2m^BQYQHNi{aRU}7w04HhKCXqX`wa~JF3X8blaRM2)8{*vBWU#RJ3%~7 z1;3UI^0<$aO0g_KL3M&SHahk;#`nyHi^!R|RGE6|?aM$6Api#r+D9_B%e)wHHoxN)l@SBxkZ0@s-l=A zof6r$$AT$p0Qbi9v8M2}m=ZdI@>9eWsz}Gh48Kzh_PLjA(W3B>rTD)J>t=h1j!1V% z2-%DYh;6VT_12R`tr-)@M;>JoHAjlw!8h`xkqe)vI#}>>@FJq9{*;%YTGk3YMs1{b z62oeX<+J*5T(+AQuw4fvm^Ot|_FY+D;nYj!+d*6vSy;T~R%VcY4SnwSsoRVrd{V-Q z!v_|A?lo>Z*y_Q%CT-Lq7AcydgRJlv7-x6FFpnKSDtBqV$-TRqtZEg{uT9;ns<) zA|RIe=hSOabfg1j-ZG;1nq^avdAHgi-Y)mWNYJ~3TnLmd!1MD~7R7JfaCN)UE9Nk| znh(Lo-V+K?=7Y7igk3V}%&3&4VZRzVl&P$@o`1n{%bWZQ;_{XT3nuG#LRh&Ni{JDs za~Vt=C=y6bA5N0D>c`Kcm_GVml_dgiDu~nnq{WsTolwhXElbY3JI-#eF~5&wCkXk; zyWq&as%@Skz_Ee81|J{=Du2KE1zd14{fPOEhXs#Dt2zjOKd+Z>K96K_+ zH;Db`h0DHoi-52_+&bvUrNv;PpLHeVrEP)FB!^nByJ`Np)dz*+qlEmUu3#rRK-bozGp z^4jk+eMuG+{Y0j1Pl+6Q>5?VVAe7np^mTJ&0DIElTZ>XcE{J9DL7sfnBC5|1Vr%|D zfEJsO6Z*=1e?6#Oq#k>quY{@WVjlgu<)i@zK?O;F)nf8JNH?5;9iFeh65o1LrCZ({ zw`7lHDk!aEJ~Lb!O?KIRBNKDmc|>~H-0{>ib!t`zP!z@F)xF@#`gxHlg{syc{-ewq zZL;bYLKJ^7DNix_`!3STt;B>}Y7^%GzZvxb4Y9k+nlPsGc?wZ6wR2?Nb33~--&De> zcdhSI`44&ceOHUnq_x25AcLpLodnkjuO=J`P==-M+MA?`4@0&vD` zuA9u4Kci^hanQ~ye;hG4>W zH`v;syRqEDmAKD~$#;Re>p#5LZP$4>a(HuaAkzGD-mhYnO+%QBeG+%r*ic{?<7#{D z%Bj>w@uWw*Tw>wI>OGS*e)@SHa?Cc8ZhLBur4$~J+LsR%a)`jQzyznU$Vex@##)`& zM!Hkva4xMXY8InC6_oqCGXbf+!fdL!pgCTisN6ielotc}0p49poN}|<3ahEE7&i3N zD6dXdgDH4{q0{3nU)q#{Flhk>5^i&W@g{|L&;O!#Z#RCUGu+f@e^a`+l%%9pjbxJW z`SM(K!6j=sB8onL-s!e*E(U45KqIMNWdLD=mdIQTzwI<-Qz-cPTJs!WeP9l>O9QcS zII_a)#EE*7hc2XuTB^5E64yY|-Lq}nPI|4c0G3OB&MIk5W6WkTAw|ag8X$EZo_L3s zgQF|Nxx(ZggiIoR{Cb4ncKM@h*nyr2^A^_-NR1bxSy{JsmhMBlp|1N208053*u zeE(c0M~-4V8FRX^IY7kRs9KLf6Nd9ZNFz)z@m-FDDC>M*%t6CKB&HLtA%Gk7ksK$- zL=w{V!siRCbGy&c$~q%9wvaHQx*>nF&NLx=BZn+lBurs)hfeld4SZA)pQW?eLPea2 z7@ZffZD+|H1E5I!b}`uF0Ik;Ju~87v)6*w$Mf6zd#1=4n@+6AIi#{l6eD?CM9`J7~ z84zPVhp$lM-A6dh8PMg&`Z5Y5DST(sOaP^m`%c3qSBX(Kd~muusq{?pb%;-7*toee z^6T)qE%RQ^Sv2aoYkwGaUkzpptfTfM2bJWxh*@_q{Un{7`~V-zeq2HY5~#b2vPLp! z_YaM01m5;|x5E61^w*>YO%v_OY~ts4b`I7?lQfSvt9>mi{v=7;_iZpl@oM)}&pW9B zKA_~Qz)?N8UWFE;40O z{^oP`lg9ZeIW}qoS+_d&lppAJ4sZUP9W)L%o|I8$27tn4r+C3wC(zz!fYpCrpH?Ll zLR^T=q0$+Lo>KT*iiPoXp9Kp(;Slnr&*xpKY3Kf)l9C4O_oc|cLqU6A5wa9R1J&%F zHr?i3G!=|@M)m}}w;2Kirf5lRH{7k`H;H=jLCeQZLOkq#q-4psG(y@h*!0|*D~ z)?}r51GF;F{d-TFG~ zc?<`*$)yA}sDxSY=?O_*0v@iEtkg$pzo{nOBp`ihm1ymuaI0*Qmlt70W@{s{J{C4R zn|@5oBwW86o|Slh;}RVcRR%#bELd3=+)LkoL2CXqPq-+ zSd(}n-8_+;LixfLLJ%gKA=s;cjC0fN+i~e1lrxg%QqG}s4*p*ND!h65N>LF%mLAwGjeJB z#`af=`s;Bxu#RUdV{i5N+lnH0cLLHlVvdSQgs2Z+E-v$oUOfV0#FK)smmz{dFQqZh z8&syBP!+aDEuqg1g3=yJvcX#Q8)*jo`%r=uVqcTaH@w03Jwzn5Lu9<*{_1n*z)|C# z$EvLCO-e4K4@TQ9RJ!ud^TO+S4$m`|$R6Y48{G|F@O1_vcQbwbMVLWy6UIqQTB)*4 z;C3p)4?)pyg?Xasq-v6}e;;a#ZkUXg^#ttdKTMLJAd?_&&^Yii2{f=o<*%N*KqIT6 z8XT%c`MwFMj!3EpP-)8@ylGkf_&^-xjx;5B`|3HNf=Vz*YmoZ-U)I>h5t#F=y{p|6 zWvqu`t{Q2JIfv@!ee>%EtsZWuIPe?|B40ycs{{@ui^M03{_BCCq#)tc8c6@&bobtz ze=SFuYPp**#(sCkJ38CdwU+?&L1H;yjq+O+@E*UxB1S)!FnA!tL(NC~Ig2IN6Hb+a zVO3i$N13did@vk2G(_>qjt2xkEk@~~`C8DQdK%RdMWsH4fzrV6FXU)2nuePhhTGk7 zr_z%wpY!<9^(zq9{jCT(pj?&OxbFxERdv`tqPhcpV$~Fgg z*fUX^PtElTV%PV|$Z2z^1>!6WiHsS?+S zsP&P7vKbefxpUykZ}Z#OgZEk%H}u@<&06_i2Lx$wYJWmFJ;JOhm+>FX>=~12-ewF{ zuc&TjIF1CcDFh2+Uu%$4C`$506_vg?OwNCfff&m$ZJ4L9bbvF{HI7DtvRF68B zHnRzq!VZyRUPiP-2BuOLNX{9Pj>>x)~ud0%F&TaWS!gUwcHv+0qJ>6`oO^)8}XVIZOe7E0It zblq-SfQ8;E7?egBK^bWtmG?l>4V` zvmZApLZV;j7%a|=U9;} z3jsDG1j5O@!LuJw?$NqtUzlQ0D5gDhCPIk|6K2o*%c43Q3KlB_W_H3>&lJjoM~NGR6IF zp$mObZf>N#{BDh6DVezbeMW*Iv)AAezORC!*q^u-6zSct4v3E}puNzN_4HbS91Po| ze)h)FIqSP& zU>o4-w@w5YxdQITpGtXE>3Vd8X}z-Fa%97bWAk$$?;*|tyVv2BO+@@I4ZH>cUkH)} z!!@&hphw>jubOm+Sau0E!9RQ&jUi~R2^-3f&M{(wgO1;uuoDMvhi9z%;qVRq+nyiY zP{ZK!XDj4oI{t>e;D*rEEkFWrvB|c1rl50o>9Wl~Tn3Oo3z+^Nco+_Tt>&$rwC101 z2b2ZIgLy}-nDKmrfLf+C`L!rD+A6&ur1vphdGl!}p{;PmLM0^+cyFf*4&^y;QHCT)!LPi}u0!qH=4+{9su8Nv2>P5gdPly%1|r zhj$GFEIp^+t1?HRKDQ;749cNc4pDa-m>V%%>G!GfnyEhx?=zycWhcWW?HwA(Jj(J? zFQ>nJeK7-8docQC1ORtr0PQ74&u)(23cf5anONAMlBq#)k$?VG4N?)RK1~IVYKtuv z+&j~tiOVcl&Y|I-yTL#CD|_;_7#l!UOI79LJCoVjRoKeWV*0RacYwq8&Io{&inlcG zc7X`VqFe2HG604xyzJO?o)gq}^7O~(WE^R5^4{l5#yvh39lC z@$fgUqN`L(OSK1#1YK1{0=0)y;7*3=RQ1nF^wE_RIwn8u5h92q+tPa(96$94=i0w{ zpb^^>ZX)`soU9vq3f(=L{R+g#?}j0yBO0wM==RJ_$S8C~SUgA)E88 z>n|YGk=cG>tBC%(mm-_GbZaciGT+~!nQVpEN2xd08!cmUv2DL2Ggg!!{i$m4A{EJ6 zj=o?Ht!&H-rx*onWms8AG^0&HYnV-e2+4?z&tl<{1ObP-`J|Iw)t!2?Wo&(jFT2DP z_fz-}YvO&!VbDQA(C0GL_c}N2@;azHA4L1YQp2l^8C^;GRd9>WasJHNU0HFX&!84sCCM&pI-XrT%_^>TXdfzgIC)r&yqdZ>l>T{LhOmV+Pcay-s> zNwrv+b#KS_HohWr0Th^R0c2&<1}&)Hz1IIddSm@YcJ*DiJTwr3qa`&&OGKwO@`)si zX9K&4JL}%$t+xTT8Kl(Q{E?KjVsg(hL3fZ(ZymY|T2OH3s1?@@RGic-BpK^6aThV_ zN|-XiWGGdmZ>xbcRjs#n%x|KiiQlt#tOZ8_oMI9ZRCf*Rs|5B_m;@Sg;l(a}@8yO* z0E!h5ArxDmTQ`zR1io&tYGNAN_qV@)FIN4HnRPwwo<~??L-+se@85jTncvvFjL8Qk z%;AHmPthA~yQQ%p%qlc^FbMI~;Aaj-H*cVvqG=|9@8e$KAsF??LGzY3=>qyE`as;p z9Vf_I;WiT*Sza5lU7DTF!3LnnN9~ls&YNwm>q1a1pLQCmrMXb>1#ogbd1ie>bKx5& zHAMy6&F`a*bxN$%K_(v(qUm;w4jfBVxv|eH@lrhT01vmiJ|1Q_q|w;w1=x=!h!;v& zT^tsb1}OQsO^VRI?p_9%Ne&(9doP-+8l%r#RrJx{Thd}2Y|Up+zxw*_n)!8bfl8qG zv%F>P1h#1?d}=bnL|4Qy81%Bju;sOKXyW<@jqlh-;t9Uoekwl+;(h+bj9T$=@8CVL zgJ6#aldP1-S>Scg;JsDx0v&oc9}sq-8^jLHHSwGSMP-0dyJ^D&Ta#(3XQ(#saN&T1 z7z^7!_I2=#Y(oNG(E7)1EGA%(Gb)vbYS9^3@x2?#`62^|7Bq_Uz-yE-^*bnm)bx@k zq>!1RM>EzK(6@8d#Bv)U<>`+IMpIVOrOq4fW<`^Te*6=OdJ({L_jy;iIMnH<9ylot zJUHyTcP)C6LE-BbA{31ps-EOiIhk@9AC*c0M$Aw(!07_a=^|=i%onQ)w5TO*-+&;8 zTE6qhM6dI*0FF|(C$18JAtk1rjSmRtK532Pvz=9f3a8J&fWzNqkxTWqG^om;_eVr3 z(|`x>4}o7n6|Z4jDt3G>r)<6qN0x3Ddl58ewg37-wt`kMu!^1?Z49AevfmcjNe!J^uS z@ug3vpAP!&wD;iBFnRpdqM85J_Cq#>DgcMx$2P`mAOJ`{Qj~gk5)&Qa=qM2$lkgEM z0Nt-)Atx?Ic2l_Mu~t{^5HewwqMD8{TgovmQ)`;8<^8)5emXNsG=(i!umC*YAGHeG zVQHpO6nDLxRvlJwD|MIReunxi%NjpOL)muH%FoS7{+$glK7*03(fG`SI-KPCf%OXRp;Uc#`OTt%e?inX7M=qUbY}Eq&)K2Nm0= zTat$0^kHGAzT9wN8%?VreG=HrsMWIhv^5{X93pG|!=Fv(RuvyQD%qsy;@_XWep%{r zz?BifpJ5WhugpeED*y^O81636t>q|je%;8yQCBhCNUHr578%D#;Eosf^R|_L_@BsL4i{KkR?dajw*Tf3pDjZch^lDf|p0p zfLhcP;9eKOKJf*X&GLdGi+@DNEa&gyV`C$3my(A+b?N#@XV4codh_5FCk$6ruHIOV zBNsGd&O{E7dhuACr@1$MyydTEBxwPD47{Q`5q zo)q>M96BVGys^RpGFZ;9Ko}vmYf+|7AUb4aY7Cku&{lrm{66DxiGf*cbsxgmw6<)P z{mCE$BV|5oKI;^j!g)qO+Pa0WBgYShGLKI5{O3OL2kKbQ(QuJ}O>;Tw@nej1!PYoI zxqd}>k%E^NE1tvKnGH)Qn{IGFqA?p_waoblH{y9K(VwLlM9V5VzD3e4I$l+)R7fFL z4`oSADykZIoCE<)I-mB3B-z)_LEZ@r4i0jQWoxEf01o#pt=b-pg0RjsVqFBk6$^Th z<)$5mg0k?8{sefTReQa`7i%>HlKPywDfS$`1z0Zbd7KpNvCJgm;jG zaTS&#U*SO|wkcF3N|`izzOx8IjRhW@hcxl1k-qU;8U;oYMVAdA;@iNl9)&GF>&rh- zz#bq-%3=0c;nwyABnbJgd{|Y^d2&;1{Knz{frof_=UMIVh!CoJloJ7b-`F!xkfnk` z7B&ki%$#-&a#fFy#f|C!wkEl*1EmPfjU7ZGA3%bxft^J3r_s?r6;O5P5Q?CuD6IM| zDdE+CXD4<{=&XlVs{O3jHqh{T9W#4?d4i^e%4j`|)QV;E)rw0|lXEY~15ZW-N`9Ns!`XL{jU>iJIIOv5n9s zGDszaHL*1_#Db&BcWx`780>RI+M!2y*91Ko@ql8poyY=?6&$m-pI%j`1hxtLKrVU* z0qAqsJ-Q#l1jJ7I!O;`}iW8+zDc~96&t1Im;FbgLBZ>3I5&xkc$`5#V- z4UugF?^5i0N%BAyLtvs&zu#wV@_`27PdB&=)PkebLy5rhyQtW+t%8qcqUybHXq>QZ z5JJGD8>s`qbpT4?q}VAeHC<@SLQGP_uJ~d?*5EQ<<^+Y@gZyBVxmSY3IiprwHqK5{l-^FF_odA<7}`{j($S;!Y&M$& zjHW~-a9Pk^l*+l-KZ0q%%r5FcKg=g=^Xv(&z(o7~1-16**^mq#3W*xo zo+$~4H9$)oHCvDT+nE%7j^Huf*Sp_tY%Q-cp>paxu(`ye-o;s=){zO3;NxpfZAk+q zOA*8vRF6!KdHdfPz*O03?pL;M+0OQ}3o&ALHm_&OcCn>t9MQ`s#JeQ6N@9h(PZJ9f zv(Kp{Z>J;l`?uYX`&CMQ*Ir|cStLiB4_Inn1Ig|{ccsF8z7~Nx2PEc^0H$r^&^Y%HM&HG?YwxF%!|4E7w zoQEb)p+bHB5te{Bsf|;->JS#_3h~L<-i|GQzVD4Xzu4)LO;-^=Vxt%gII%ZlQV8QZ z0`^4lPsVH_sYZQ1KMP8mOIZITbyEa|j7&5^Ea}R{IH~d8SWLhSNG3t(i`|?M9nh_A z9SU?bZ2WNDu1kpmaJMu$@`J8ov=3x!qC0Hg{^slrCbQ}%>|=mS5E|<^c7NtHxYMs` z2B!)z1hcmj6VfPLW&k4COzwMO*cc?xi3Gx)3`Ea7L@WY>>tzt-&D_tRE}wfWunwllND93@@y5{*;a&fQ-%iy^E+NO8h+7!Lf{O_>pYt6)r3xv-2iGh+Ns(0p!wdN z#kUgsJvtyiL4}_Sn;FNZwRO8cVI~fQ=M-Z@p9HWKOSip2=J^ItJ8hcg1=t9_IrJsos<$6$%%wtj3ok4 z5O0Wk{#^nrS|{LvWLtnF>fV2Y$!jIBsY{Oxh$VO#;GS2cr|2kf1zZ-9^s@RN|1PIY zB%nyy`SF<9#Jz_-O*vAcZcLADvd8F&w;-5*)q66~kfFg0abXjkkdLW_P35Cw9jOc= zbY1oP7r>D+n2Dw$K-P2PKb`Qj>LEw*K8%1?z z`jf^-?FT9jVp{v5R56k#1qYG0O}Hy{w{(K!;O29Jr3Wu50FQD)y__?Axc(A9Lk{#_ z=p6jvPHBkR$*34W>2rPf8iVR9Oa*up?Spg{K5&wBJl71)2pdcg{Cd1MC2-e6NQ$k zQdFsWZ4RTP!Z%X3JH((p2+NaazB^i_x&)T?HTub5r*(^_|%bkr39iL0&;0nj%B4K+~%Gq_bUf zQo&2#kth9r4@%{}wFUXjt-~TE|6#+V6O0NnT%Uw6Ezmq;e?Itm_YJ5Za)l6X#Sce4 zwEkXBtBer_qu`AC16F^1zaIu-8-xE`R@BH+-ror4dS#Dr`p zJ1Lf7x90g8xg0{sYKWaO->5R*a#Z|u*#-F`;n4EyZN8`yJeEc{4ZdMt-C+m3KQyqH z`l!1FMe!chCo1fAv0SBH>XOs2mWEuI6qNDI)yVJ`t#N(@-*o)Cko`Gfi+V^`99Z!W z$yPK`EiRyY_?N{!HGNGIjC|Xj9nJIokG!RWe#zJY;S6WKpPj4Zw;b$JOHE#R;qisB zta8pJg>3n+<3snu(dj{EUH{{x5bNx+j1^rCCng5Wf1m%H=)da)&05DU&GF5DRMoF` zuuHEjb+xomX|^UP*Pxe}@$2XkW=tN9V5|zO5^UM}9!Z>z{fb?PNIEQ{S@K9oJ_PtVbQ>9ms10f>>J@!`b~Gb>}rLWKLg2=C0 zalNTglN+-&T;!riuk*9NsJc$P4@yBO+uC_4hvJ7)O{|!`5ezS?XCf>O3!!3~Gz)(E z-tu`GQ|)%)2OF6=MGV6Jv`hgx4C|q5r{rfwi=z$-qbQT^mU$u#EYtA0D|$ZWygB0o zWTIJI5fDg)IDnb8mld;ie#C!}Ca!%k$m%N;2dzUGv~SUvFPM5KeSo1#z0(aYKDHqK z)pR5HS)6ee`EtBZTbjMxy%2lW8Bt&qQfG1Ee8=U`ScgoN%{O+HwBRdO)$n{K23rKi zUFvb^en_`j8YkOJr~so_wDn`ue{pQmjWMjikz$r&_LzEQPB5Y3g6vQ(AZ*eLI5N(7 zSb7!4=2C*+`1R?9>=s{dQ^$!QM6Ke1HZ866{mi zD*xp!mtMb3*R4I-C2DJ(^_AMo@*fD0Z!BT{&ToISYI)=Fw8ZM0u}~vPKKjKWio^&C zrbUMzz5dZttWMoiuA?BG*Ctg z*$997rdFQ2ix2!QBp8pp>wlPf>#!)^KWbQ|8%b&DX6e>N>F!3579<2gK!p(mk&(24p;r`EYWv`z8owkLEC{ue=H13uiT!@Go zF0540eA~fxW3RoCkZJw6RQUFI|8Sd0;O<1G7*qAO9l_J>OzY)dX!-$fqoT5gI75}9 zwl7jbe|hgwn8LbH=&O^do$A*35AZ2)OBNP)Sy9eaaDYC{?0-^hfT7lZ*r4RLdMB+V ztQOXV<*Z_9r$F`&wUbHRV1@5`XyZ7>q->fkSlSsE-f!#~+~(`c4e_{rI|n)6eE zI!th${m6$ORm0`M+#w5;ae)b4wCr0H)2XX`SbNzM(+(91s(!`N{MZj2NxPxA{t{=@ z4|+-E*IF6CTzc06)FbrW5tvVct7&2xA5EWP(cllVKm}D!P)D8TH5VN-*>>rK5F)UR z$L+z46GPA91CH`QQK^~NWHq=+@x5Z`S5}(82t?qh zM;-zjYd@}9*X>UBo|YP8fW`~h4TsiymKJ~Mq~xJ^vU);o@0&~QXIwP)4im!=`HaQV z(HO&CJ&KMfcUH5_ok~ehJB#;_=K?0#3L8mxqIJ*?z>4QS?-$m zeNYH;J>4`!Xnf!nB0qNT#f2Tb!T9UJ1KjEez(Rl$%0#9n4r8+~P4+N&H zIYIv5cqw^syO)UyIU`R6ebIEZRZMOl!Pv1cKO9!dg&6R!e>eJKo40*Lj1;`<5~<_& z)ak4dEj65LsIOzprUh=C=qHoCABV|J|DffPdktpg)o-l1gAzWhpHgZ^EJ+C1eD@PB z0WRdfFti6V-}e{Gz}R;oRQXTEi`2JrV$$|^()kr_HgdB*jD8>GBK(wg!t3f7 zZl3ptm(u6YhUIfwAbn*mi$17gKHp6W7$Krd0d9_3=?mC`kcPsQX`9EsH`WnprB*9k zz()MzNPeU`O*9y#_Ut>UsMRd^y8Ap}24TB^IGPSPdF@;)Z(PqZ7+0a+dWo$G0RP%! zs8{_TYqcsj99|@y99W+m*PNCfenupN&`3AG&`_MRYhELz;0y1>qk{jYOg&Z{KcC&3 zb>+U$MFUcJHghp2q{6?b~@N?*fPBhDYES;{2)%F9npbN|xTJ{;x=8amWq& zYicE99joomaB~=L(I7GzuFJavLz952*e{G@%7?(4WxGAAlK`&QUD*1tiSgv5;=i2_ zRdyzW2Y;1-Ylg=`U>m+Hf|yJe?du(G{3i_h>n?UXl~usB&jbhQ|*DqPm3ignCZk{$RGz4b8j zdRyuNKIF+w|Ci2jsOckWTmALKE1UC_4%vfu1xng3MvR+8@|U67LSm*A3r<`@gPyFw zHUmn3_8pLhSARAm4t`FvL!FDN*v6qoIAv4xwanMexqsf-rgu{)1b!NVsv(AN6dT5l zYYMR?U#g!0F`!_@mIhYmesIB=NrlNQyM~bcmR<;5@rcb`n*o2%c=7jK%Y#O7)X9mW z#oH2W^~gwuVNtCDw5Bn^*8wNBoKIXcB55y_GRHso zQ|ya_Y(26@*Kbr}Q9i+_co~!_>v!9VFMMh4=81?DU!Or5i`G{%zS3G zo#I52pLEKTRJ|#dy!ZJ7TZenp7o1s7et;Rhdmb?nm16hVMtBDT#(M3xhe<0w#znel zj*pGItfm3<>&N#h=ANbWrNka9NlTzLkViTqWI z^&K#u^{m(I))3B5xy4JOPhPh`3=}tp&2b5%Jaq~x zfH%yQo`)s?Lkea&~U#DLFI(U_GY3D9%DE!G$#xRXc8cb!)!YF&=K zRakm}ptYg@hfPvG#tL;Fq$|w*y1XX%x$gcA^H1TvPPVkJcy(*jGoYXX=S}d6xb1f2 zz=H?iV^Ho#&r2pqeUQlu8vZZcSpIpf%+ah^{O)k9VR45(6IqrVWBbGoo1wmI%{oy} zWVj3uvwvdl2G|JW(%de2xrRuJ6!E}1_pvwMCc7nm5k5pzVBO){^FZl~U4zz>H81Ju z!5q=zSw%4D)l%W8|KF%Y&0+@KU*ER8Nvp(%c*eR{#@P@mKm4y+`a*q;4}?`hK4y-| z!ooPsAg7P7K{Ld-yvq&^jALZRi zOQ60WJ$9Lq{Nl(itKRcAm1We&!0AUQp1mmBupwz@Ok4ie@CA= z8qAj8)-pUml&QL5!~F71$XGHiQMg}Q%ioPWPYCpH!?NjP;xD>->`tZ7ECB#Lx>FD9 zCpcL6qMuAA9D`2K_@(h^E0z2IeJ;MOT0*>;7v2A4?r<~PZ<;Q%x!;Y&F=Uhl13oiyNxRLm^~9H#q9l zzd2~X@nbK1r7m@|DFDTuSYK#?$T%=%=H=0DayB{GyPRAT`(cEwNa)#FH^m_a!(!fZ z#>KiBp1sUQXx@J{JiA7hTwn@oQ}mU}Ux&5H%94AiSWsRX0K*2*wa3-}_u3Isf7(>a zQlxNRSTpEM^)2fd^UYW{p~~lusH%_a;=J-m&By%BH1im>sXYGu9A@817FEG`*z;v8|#JYUPREzvy$8t$n1_hc%tp^r-p7WS4^ zRgBs`H&yUGGRwPm)ib=Xo1tz#h`OrX2hI<$>;zM!)S{z3jP;mTIU^<2Z*G9<(QnCl z4X;$NM2+Mw;fN-Uk&mrTKNZ0xqry3Sl9hk-<5CnyP2$;JlWAMWXa!R+0>Pett*5SjQJVtH z4TQl9mim_0e7;~6Z(Q$u_4*M%krSkm!6nFS(_Q^J(VYGQn_GSQ4$eewJ*yNL1?OeJ zJ$NlQ*tqVk%zB6ZuDkh7Cy}-J%}siEv5W{AqiiH^)rD$QIgy&QH1RX%d%^RC=^E-ry+w5TKfE3Q zY;pctA$!0{BSo5Bp;srSrOv*HRZvgy?9Ab|BonX)rCcOflZH{VF~mOLZmo@h9ohYo z)`;h4)l}b4If|-dhQz3sti*_d@fA$n^_w5elrgq8A@C_hwORyh2VfLTAK^9kxycCD z8SHxzofrD#Q8rDo(0F{`i$hP@R{PtNo*5B`uy0PP=0kS!0s+r_n?K~GSGKToQT2KQYAcyf$>s3MNt?}?8|RC!W;i(lfZ$=pXNbL zmUu-pVg6tcx#9C$eRTH$^~+k4F~rERR42}nK0kJ{1&fE}*{WR>_ktAW6(cR)P}SPb zmbz@+d6|ASe48=&hSOD)C^tm7M3xu~@7yQsu6`} z-M5?ibe{=4lyhy4PlG#X?{Wt{TowXwz>C*c>;#cS$$=fR_kZ?nyYic8b&lA@6RzIT zdi{wLO1kVW5OaWuj|8VT_S7=sYTdx5S@Ev9aOdwa&`vyZLM!xq1_cKA9Z{SKk26V1 zpSmXh(O)J8jYd9be)7E;!`3*D_v6vq(0FzHE)fj?Pd8#Eg1m5en7E$5y{G^UdsRkH zM8A`40i*6WV|I!5-~Pmcw4h4<`r6TTq#+K;=(6h4Aa~rCR5qMJ?4H_%?48y5?nKwEfj5zG?9LR&9l4z#gE!BS>bJR3ImzJzPp1qU zcj8H7;da|%sRRH6aZrubw>*?#NK-jKI_iC)!HKyLE*V~bL4Ck%ZfGxQk@vqQc#2xI zSwnS@K9xpUYS(qznRyRxTqvH$QvolW_Y9ROfb|8ND4Zx*Bg7^7Y8el^uufsqPCgGm zTj!d^13YbiaQInarR^)-v9uWb-?L24l{2g9lO&CKj^<~RIeX*o$dl_ws++!RbMU=gF0*HPZus6@-tda zO3`Mwq}bY;APd*>d{F%F-v^VYu(LOh($C_vYSUWgu!VpsHftKZey5p+w8n~zMn6&B z39zve?qUrbSF6HP#WwpAJCW-ROg-%*COAe65^OBT1)L+bcNmX%5cXR`b@#0KMZO|u zAtYZ##6nbNLXK3BDH>G1T{c}ZH=kZs{~S`&n*#>EUE0AbrCPWIEyi(v_o!2y&sD~L zGet7piB|7L?6XzGnSZhXl9D7VLESTV0nJKIGqu)z@rNWSI=v!#lftw8xeni`4d49z z94Q{`pY0u-@B9=x6wrgyjgL&;cBNxITh~~nJ(k|R{-8&De_Nr^Pc(%xK~!GVzUkBK zDD5|`2${ZZE}G|`e^3saiN=)HGbAc%e$RlPw?f{6nt3yax=^oASG-SYys2*J{Z7^I zXEn0D8dPq03?-p`YK7xCrx_*>Ubg+VgJEBw)_UK&&+Hn6$O#cf?Vux`Oy)7_XO?HVUAKQWS+O<;A*lEBjL*iCBI2Z$U zo#Zysti8!dc28IFyYCnmxrbG6POSzQfqjT|y9<5y0QKcO+bY0~vqY-q5*CsCRl13F zny)gFT>8MV(z@njePW&Tuejfgs811yfkDD_wA-HTNQy0|7Y>Fh_;~bqR%ejiPW<%F zt(Jb@rNj)c1nkuc((p>9Y~w`xX43es(91a`RKi`7KY`h%5B;3{$8TX?I+Xsqx9hU3 zLyx`AAg+6kkF`F%+d_nh{=!VmvS%JK*qHw!GWf+! zdYPky0~oixMv7f|VIMP^%7@|x`%eFuw*istS^ z_tjh6ke$MqzJcnNqzC5SqT*4;@sJ#We9g%eE@Mr<6e1I8n?@qu3GU^6UUt*tDz1@WUq|M)dYS}MKMSK^tE(tC>T?X?VTcf4vRX|2pXf;v<6s(4X+b;5GjnT7vv-NWqJHQ@QknD4 z)V!?B+S`{GoKMXEVNFC59|^gel2PCNw)nOo{Wn{1*P+X{inmg;a>zIxZ-h;&quO#W zocJd@sxt-a8_Nqrp5ggwaILhppYKAh7#|3@=hk;$%`GpZXjD?zS5fS|UAuD7!fj)|RTIH-GXv8kbE6LMt z&h@So*Br<3Y`L){g&+aN=Sm1lc@!sb&!ya+knO41C^FSyLMn{RcV$<8L-RLkvew-E z@DCNlzy4F9!!jf&1a7nbWE6GrLKU@B=oLv*0w}4^Ve9c;j23J|>O622jmqB|8gJ`X z+y^A6M0t{uxtP>jn232lDwFRZ`p+Usb>`p!C9f>w}er?;x%HZ(Z&F0SLK62c2JB zkJ}ik_8yw95}pWL>p##F&~5%yG6yI@tuk{8KpnGGxQxZ0{}Q?5ExXZUe|H!MU8Gr2 z>y<~DKknwZWmw9R`(el=eG~8T^AzEEIP7pKuGY8tV8*3Pm`14l`)k@>Z14T z+y4MPqJR2zTHxyM`sQViw^=0Q7_%g_HG>G#@#daeGtiG#^C3<%zT z$!TdL{6Mi9{4G$Sa({ysWaKPGo=U-!jw?uE8cuTbIWnf zl%ZP*>{^tQ5Z^|Q@g(_xjo#7CIWPzmJli{}y(^2oz6wZx;u8L!RH6M<&fZfgQl>W? zI2dDX0boi2E%Dq>leQ$q<0aEQ{qM?Ti7E^$*wZO^TB^n|oT(>0IZpIbsz6+ziBbS* z#FAFgy8cJCQow_g{vV8(9@t{?)!6O|TGH|v`fYzDRONMl$?7SwOfDXlP*tU9e{KI# ze|j}+89vs@-HP4P1wHzDt;d!kqPz|{rLwi~S}nw_c%xE7aV1ILJ+Wqh=A4Rb<0PG_ zmMGO64*RLo_Zh#+fg=RiL=x;N;$GKEA6)!T0-6Dfc(bQam!2Djb9?ihOm%~AsMjYJ zv3G{sakp&sv@4m6jzs+h`Z6@q?p2FMw@MhX74%1TpA9nw6p2>UXmvivIY7}<|qk(Ab(Coy8eix-t+>r>Ny?s0WoOAdqQ=F;}Qt~8ebAKJSckD5y-{c?PVKpW-% z@UBwXakX+>ruS8pK_bOncBgh-Y<3qZfz_aWCj!Z<8+;wI zwf%#@1^!9W`MH=;upnQhQ5pG|JOeJr?M87m{-VO@zltgzFYVdS6GxIqXYdwzbENF` z{b2yDt+0X}(=@1?I#oYAbfD-1A-j^r5dFM|%1`M>+|H$moRjaA*$KHKd#p})#jAc3 z6Fs>Tds}h|nVj4eLkA;gd$7m+L(2ym_wLJdb!cE;(=t3ENGk>_9~#zb-SAqZT=7=) zrLBU?;mW@*8p6sFyVt@#Q4Cdue!|c{<907X^0N&21}9&;{x0P~7{29-GfWi;Vi5)~ zv_mM86(Q^DJ@2qrIw3oVagoji|C(Ib@Xxiswa02mZNd@9Ag#Hf)AKn-UvID;+ENg1EG&#d$)j+LwF z(G9O=Sl5%w+OHTEek=}!BE%F7r80ut>{|^kq-J@t-%9BaC&Dq(r@G>scs6>nr_PLQ zniuwWk_A8GsuQ*NX?XFR23WHIw@tQAb7*t#Nzedt3aUv;tcOVLjREmg9rg4*_3>^2 zM4qWKqf6-+o7ldlr0j~4BB2J!b^r)X1y_0$jGVgKHT07!98|?nife!3e~_Wh2vj~e zc)ly_EbJGz*_biBjMY}-f_0CrO&h0%IX-4<;Vqabmz%q7TBU|`Rx);7r^%b>Tt99@ zS>#pHnTh;fX7OPZqBHe z**&|l|7d>wy;c}{8Mc47Z;oX)T#s8;zK}a+e(4|u`umB?M1|_}Mns=JJu9b@c_CMP z4uv59uzka3ldvLAtBL_K8@=CeF~RGNvXWIE&*J>|9>F9}>Aw3#ZLlwA*kF5t|kll?J=zL2V=6>~zAlgF|HduNwt6?9b-6 zmTd;)Q3LH{vdJ#JZ*x|9Qpq&>wzS9afcH{&*z00xv18LLQG*x65EhV6X2@2!tg1bO zSehlG`Jrrhel@=G0N6XSYdpvkx_#1jrh=)KQ|@KB9W#B?F0{xhWScWub^M1b_u><& zQ99*Hktv#p3;U9-vMTZZvGiF0g910OQTtz#`+mN&5Nj*E*=c$dFTr=M&IKXQ4=)Tb zE3DSbC3-&}dLa6*0`&(iWsLjhj>Kk+S?>FJV&S$2d3ip4Pc+e|k&X-tB}-oI`HT~; zbO%zkbMR0%noDoa$xX`!C(X9dNR>^Ua>$!95+b{DdKI7eKE3`a3DO2-^46!EncpKN zARzFD!MzvI)m-^9ccP-ygNa%5E{f53c#2d4(dh-!(bs#Ln#6$?T25R%J_5*qPH;5* zJ^dH@rl-#EzE8y_lgVADcG#ZCJk!FkSz}y1OnEd3D0FKp zf)2*&M6S&*^eIRD?)=qjMJxro6DzjSkwhGL&pA}z8Rah&E8dT|n6uTDZNC*h^U;pD zuIVhjl*yP)8i-(u(!M5t_}z;Fd%epZWr=Jpg5Yw^t+LBe(~H{uyq1|lYE7{+9)FjM zBkvj?9m#rr=wH?Dv#CoeFT!i4ny&XGzTE4aUgjl`+cup)EazD!+p6svk|^yq!S1LL z>qsNp$+|L~|E{*T6+17~4d=BFbo0Q^D~B%Kx3`WtfZ0HYOq*h?AZ9IX5sTWfTc|?{ zN(ucfqK(i$9y-x$9)2rmOIJ0HRm}autNF~zl4?@mY(!D0TI?*KUVd;5PXHaNLNJC{ zhn5#c@Y)>Wz}|ZQ(3@Jo`pw?ih@AUta`C|7^B*UprZHU5EXn3@L&=>7EhZK>Sy|c2CQj zN{RJn;PYgQl_KjOi%BEA*`R zB~{!%vX8w%%pb(Ee20!Z?W`w3b~OEiJ7H;LcC_i?gWTX(A%6i zw1G<;4%oXFrpNCh4?3~}G!0(Oa4 zZ}hRa_y}+^J%J(Boy#mr2UN@M1JsXd(9J z7wmp>0?QiHGdXSlID0p(N0{@{v~smQ4kqnezh1k__CJs!OD;{;q+}M}(4QP4go&ta zr-UIhWO77bG=(F^kG!3&kb>JPU)S|1qruecQqT7?=D!@uyuQ$xV9t3jhqG(Fo&!&a zf%Gf=F@c9q??PJ=b_qyavRU&YXO#U$p&tY9+;Xz)?*v$}TDpcG_4-Un; zSu~M7tC`G1)&up*=R2cgHjs*hx-j2mEyJaL;&$?fU`GZll2B_6_1)m0;EIi!oU-TN zooJ!V<^A19!hxx}G(D_;lU2<{9o|{`+SR{o$+gEfNqZL<-5UPhibEyu?&;#BWxE8P zuO}}MtyCm8^2amDPL2-50~T?=M-&2{ZUFOTA@E6M1~H3OqSv~SfP7ohK9fg<>STFL z`xr8Q-4WJ(@>#-l2GI)*Fxp*hC5$xE=d%1a@zQ?hB^ZXXCpVK;;Y<+x``7%$oNJcR zUVR9Y+_}$I$9J?gvJ5Xa;}*E2kiGD_c~2H9q)z)zKTosN+v;VSzt6IR#*53uSxR8K zoWjN-@uQfQj#{75>h7s}u3`NT!ZB8jkgGRS?`)vi|5Avyqfq(3)gu*fN2PNG@n7>Y z8YH^&04hTKKiS))qnHvv4uh4iP@H4GoHD5oT7%al)@(rw77FN1 zp%mWOiDh?8HYK8~Hv?T{r3Ll)c3GP$V=6qVE59l|cWbk_E~t%SELYY2-I9Xksd0N+ zQq{k^3>($RMjl#8+-~NCnvWIH6nL6UU1FtFue_8v#UgC~Qq=_f3V=d+f4SumB1bW0_S2wIwVV*|u1aZueZ7a9$eilV zr*=G|IAFh0JMU8R#1jwD{m#S4bvzM|ansvuq(yU|l8@OTaYDlo3)N-62e<;ty7v)V z92$Il{<4==H@uH&FtF>|<(ypq*Wcb5VQ+`6&lX>@1#P+OenLn_ntgnt2vS8HCSIvi zME#+V5$S%^@ZfGG)9$xyCYLEaYksYpsSkZj@r6Lenx*HThT}Zj3LvboEM>DiDBbmK z$~6Js&?8JT5rMbb(xHngD@it8fzM;g-~6P1AB9cia&vrr;aXtXp_S2>HN4iy>9&>r zjwnjg8mT~Szj7Oj(W?3D6cFK{xf{cO38fMo2Fq;k43_iiA@V3&%IKwLYs}cmH-W71 zGL{RkoQEWr_q$R>Bc#01J31Yo;ZvR#9FZ!v%w~#pM$k!{2iI>gY$JUe!%R-|^ziDm zcCUNoG=53zL#rYNs#@1R6r&qURx0b}NxOy~-B7;~qMb{R&8!mX?m|twq!pmPJ}ycF zwKX_J)82J*7Fce^#sviHZc{A${)4Z*)_zzukxNe_UE?ci?~s6auziC+@ebvfiJe7RzCr za$JmvkzwQ9vZDs~xfF|Z`p&iJ7!;aaScBRE&&9++w|Vs;$woR5*HX7PPnHHF;Xjhv z_Z0ddP4Hq&DqUX|(eZ*q9zH;F5Dm6o0I9Rn(w-?8#Iuk%buaeYG>XQ5&&LH7zZrr3 z3xFQW1?)EkiF1Q}dCIr9deZ9&BXW%ws~<8lXo~LF*lwmdl%=`#!XNVp{Ma}J+yEUz zM}ph_mn~sghrKgdLE9Uf-_(BtoHQ!^^%wsmjHqIX4DiSTpO%JlY<=wIwJPF%&CNz& zGONLtom?=f)xCZ;GJos*U(fC-;`xsT2&WpW!$LUKZv}nxopl(o=cb)API`<5*)yoM zDIEdy0tp=guYZsI=z@}{sgpEws8oUC%bqEFEkgx;F(xWQg|ATcexWvCx0vQnZTD;B zT0#5E0bV+4b42Ag@x6Pzig+$L*KWy!7uHxr;e0;~f&-}Pg=s(T}9-y3m zzJ+y9?AeL<@1{$*vFW1ArEd<$Q=*Q0+hWYxfNPZ5{~r8Z7-D^MUA7Ka9M%49O>7Q& zXPf~)Z%lT6<#hwIdZo3|?m3GztJc%EJ9N&b21d^=-Y?$j(SUtn2yW_d_Ct@x@Q>u! zuV@fF;g&;Q(co<&8V;XlZ_^4nxxHat@(mp79=EAN zI(++V&Wt*F9BJ`wvyx`q@==*Z8_zG$mYL5j0+xokKU##8k%TP|{|{eM$$$CZ7DUox z1TXbp*O6a9TJ&Ziv))x{24<)!`E-eJv ze7oj&?MXi?X)Mk6^crT?n{#E;e07L4mw2+gHVamKt>EcgsCc!#T>bEJDyMKCQCJ!% zpHvJQyONRxsiq9TYXRR6@9|w#yixBAUfl~^piQwP+8ywHr<hmx+5r6r_p8J9Q zb50FOLW{9f+Jju#YSote6m$7a+fy2M2OXpIzbYC$h^2(+1)RW!t4`*QR&EZ#kLHg6 zl}4t8*<9yI{0R-3NVSBehRJabOIXL9=cC~-d!{9x?!6skBHa{ znJ;`QbxTrsATW;UisBR@$D(zP;4dM%TDHdPKKeZWl zGo^sI)U%BG*A#Elo;vsUHD0O_b4a_*4v11f@3;|16Lh$~6w8Y;9I0n*On5TH7esep zuNv}*mt)Jc>H?U%e~;kHe}|cFhtjuuRoJLjlexrfk->_RiXFW-;|{_x8M@l{s;TB1 z%Kzl3>)Dw;8TB$oHa;oyY~Xfoa@F-xK*rdxq##bzGwIRg_zn|}>TB;^oB#MWwbvI@y}_;}$=Oe3w^=q-EKL~$tbZEW)f$Mwo!N5E{Ali(3h z11kmx-MKE^pWeVjmh&JBLeKJ;n&iMT4l`{dS!>rz8if11TWMX>+!p=);Yo;%toa_A zl3d-5_%9=NvyXG3d5$es=b}z-H2=&0%gT0eOg;#_5Z5lp5@qwWAc`^kNBVBixKZ+f z#Ra1o#I-OW;;tRsKG8lb`(mysc}{;V4z44gZ0v2bjrM6X zh=yiCL;oysP~-@3gUA*zVITFcKxZ9j-}i{gLN(%*=j*Lxy4S7_6OZKb8U}3 z*z=ROrG6@gcfqO#$QXK_+0 zK!Zt+W{*!>05%N%6FMm)tn&Zgv%^{t)7F;1@{5P1Uxvj3OD%5Rv#DFPB5r_;=h8ho zm@N&2pHs%-3|Da5R%4Rdt%JRgQ_qb|JL7#LPRS$3i(fVxoGk*kqyh0DN`Tp29onXY(Z)G= zUp`;Tm3lp;Bn@d~$?S&&_ot$cGp#Y#aF>)HRH57U+~00VwR`ge4uzbV_;^D0(N>+~ zY}}kU^uofsy1Q)1CJMW6{xk42yNBTR*oQ%kZ0knO%f4?ike0=&me?_}eMzf8kf|!n zA~C=b)p=lv>M!@7e>o}BuZ3DnNAz&i@xLN^R6W{W>tHozC(2Ovv?lz{YR}Ulf&V%V z%b;`xKy4DVq=QpqliVa79cGw*rYt6DiJw_J?T-6_TTN%0s`{(s&wI-PAp0N472SS$ z@_XOs@xP>k=GAAbO;P{9ngVFvkbwBjq_+o)Opk#7f^gjX>&W(qF(e6&lo#!~h4z=j zc!h4Dsesj>OcP|!lYF9>h%}u!9?|3!Xt6K-t-K` z6xIV)E`y>Gfry!tbd#Ht^LKueIywq;+Ge)Z+qJX)dSG8(vGvJRW$}EI1zt2rO{`qZ z%B);&zJf5qTILsn>DTZ;+T*LMc+~hx9_HU51)stSO=6*PBjDB z367}x2*-PpDrcuLhS@nQ_uYSXZ`JBG!#Ag}!Ny5n6hTe0T zxy~l+f88VabB*I*Y*4qzR^DF*>^f;A{?OTQg5N_u8^>FA&p(_dksA=AMo1|nld_^C zc^_9MS`b@?PmC!^u%?PV_*;kKB-0!-mu<>MIA!Pv??nJY{Vgd#FPjwP>7#MDCV=*B z^WD+%^bt+2P!2hIQEv450gH!$gC};tyr88;`yt`JC!;sb=C1d{)ks~$Hbo?pwz5@M zAHo?4Z+qRG(%3(FR>hd2mzQSJoTEX=h9aD}F3#aQ#-_;g=rWrpydj6;rTGCv;QJ>N;2<8jXX zey<&Dr|jkfaDxMp-hEC`zhQ7=)BCM5c@)>ZhExZUNzXba{(W^^$JvXKxPB6NWR2qv zn}nPqs_35eGZa0FEYt`z>;18B$z3?V(YtbF=(=ij0I|{vIIW0a&mWvJy8WkE+?yt7 z(SXC`y2pXHZ!+TOa+$Mhn|Ld?5NE!J?~51D_hJhg-&;TGkXyw3DlRsR!Pz9W3+IcQ z?CQ-yB9g|JXhbOa860~K*9`Bnzki!SE@xfU0`%!i*~L#@SE;OFC~1H;c`dnWC83 z?-b-S0J@gG+-S|#*R=TPN;7wZs|geZ4}$s2{5yp5BTxj;$_o6X1FE6TYi(G}9<)^y zpto!JxWNyM4Te)}O{7&4j;TNs+I%CH8uX7w^iR8ikPju%^Mxu$e=4c*L$>i3e3*Y{ zwTnQ>;PQCU^DU?F*BwizpSNM?9}`W)SpVmX5zU7SYO6nuPX2+V{?{@=W80$6XEdHd zEP-!isIfBLsJWA?*6wdcEyR)L!C33jJ2xif)mh!Q=jR0F7?&lr9;M_=GhVF1%_@vC(1kd)*^b@+vvFtnWE#tA2D;fQ#wuXG_JGQ2%!9!+Y;6BP{L*ciPH` z57gw$bkwaMpQt9^OGxrr)<_BqFDPXv*ET#PA*?o#6ngMLH{SZUiKes=sF5eMomX1T z-x7&9^JDNepTvGzIVLm*|Cw$ezW(cmY=Req)G~df1{y;%#hta**?4(mm8j9@Gjh)A zjM)j44}JQilf`;CyNkIq!t%YTc_b;AaL z$*yF|3L}uZh=j{hZ6QY%N1AW$>ibO!hiK6hkOU;#PM;Gz;|4U zhC`(uTRwmXxG^SGXkuPPeEGL@dE7gnu{uDUJTGV#Nn2^gSBw7rXT)7Y`7!5MRqW$E zk4^v9xt!^FcO&}|AMDJoYxgNgrgokBb^E+Ic$ju4?I0uE>Dcm>=|7>)CuIW*k-Q=^Qg82}r3lbDhS^{6o_8WkyWao4 zf3^nMVQ$PqnuDEx8?=!EX0qtBPb>Y4U#~6?jMhkrtTM5;*&?5c#j$C9FC_|#PBGX@ zJXd#G)$MbbwIkmsQN|Y?V0gtZF=To^K+Uu&P@w2$@)!SK!_~8Vt2kN`#R+$fEuo0A z&3ocmkZ|^>*TuVtA0I{=3R&ZFY13%cIS+1!bPwdoi?PTjUQnEo8-WTN(&@h~@Qm$1 zxm)2ub!U5?qTlP!O00;@I5NS;gnhwlUZTFxJM&q2zwKP`BJLUa%#Tj4OL=ze%v&V9 zt0w*)Mf|9fPhst1$dM8icZ=F%Wo(<7$_UoADhpJLsNMe^_Be+%%A1kIqX8WOFJNH; ztsSXeS*Q3HUDcjSX1XuFx4>(1z%z4+OT_M80REUXijO)`1eE|`Sc}v&Fcf{AegSaL z+C@|=R}?bVel>T=YA@eI5LeEb*`Fziub4*d|=hb10Q`yIRO(xHC8GLRNc3x*;5NN@Ga^BIyZRbv~82zaUDq_&mb8&-?7 z|Lgte|3WOpJh@TGpy!PKHfMfWX{nj6C zv`+z zih{_#RnzIwqZDQ0kNpH6Dnm?AMO_-o#34t7K$n>ND^Mp=J7v*9uH(yS*X!3*)bY;_ zkjZpzHAA;D#;-@+xzOe{Zz+={k;OU|V>mhJ&%V?@v@`VDMg1-hv^(E${$DKsf*pDp z_*0kr*l$}1;$-0lRh1XMIkZy)^wNPoWhL!Yk@(ccs3et-1e_{WGoHxr=4eH3?&(0i zReIJCy*KaDd->wC=xw?8vHdKF zF3#0ghesPfrXKP^lK>Mh2zX8)$#QFZEKV-%C*ZfXVBEU}sc-7U`PRBJu|Nj(uJL_Z zg$2RFA3R{;J0riK!(G(B)?y{!<}>~h`favJ3g zOzF=zMqyUFK8H1WVOG^&^N}T+r`c0^L1nwm-DplNqFOEdd*sa!=AMudno9oN^^ehD zC$Td(p1eTs%w1ue{lf=24Xjv=J=~ch_i|iy^(K=jrna0U^rC&A0~!7@ZE*50Dyy&; zPeAW$cQ-NcHR*)2!Y$_5aei@FP@zsb{>L~Ge(D3(@7pl1=Zg;6xD-*9i~sCTcp;@Y zQHr7sSObxr9;L%qZLUCdCKHw0gfCFX#76A`zhTG@wlF|1fnHUQPaC z|Cf*s=`InF4v{WFLQz6GWKz=IA)^H85)cqZigYL)qf=TsM@ZM`vF(2Lea`PW=l2JU zbM77PeLmOqj@Ky2DAj{sEz;b(Xz}(myfd^t$_!8&fkaDauABso`WjMxLaD&(3!T-ZJ$T_M>2%aWfn#?bu#x$N)<%Uyb6|*)e$qo3~em zNCBq_=ojiZc~h}haYzH)KoFpbVaA-6AcQgJ8ogKk>n%5*0+<-NL(f^VM;PA7%+^eS z4eR-+1p|7YNbz zsgQ9&r1Rq?9q*VfZZ5_3*~`ydSkasq^Q(29qy$f%N*zqM1{^b2rpFvrHi~5Y3T6f8 z%a?FT$Srk)8vmRr$^M(alsup3j&clVyM5=;qtX{gdGq<|_9_LR9lobaR&g~Sz$!B! zE)YOOia$Hia-K_|;b2+JQH8JhMPY8KIO(u;n=&SrKqJeHBiJYO%<^%M2G!O9*;qOk zA558-)v8y8`%5i#5DT}TTarw~OMvD;IKKEO%Q^e~x+^^)lu)gdQ zY}=Vo=8dI8S-aG|v*Q5!+lvQFxjXsGb9?LV=X9&|5$Fq?EHhEl1So6exA6&4AJ*UK z1pSc42Du{jKTogR#HK&UxIRfTJzp6$XMCLSq9G#2J3)|^JnRZSKcoF!oFa`bnUEjl zyXezYiz$YR=26@kwj^C9YAg|-R;u4l^~kgvU4M;L{ZEu0oTQB~j0bzCviRi2cZJ?1 zwEOTY+UMDmFuH;k~&raaAt)b!synC zl>gQ%e$(ZbewkLukVtl9gRL$<<@(u1<+^*|KI~l55{td!%PlNql zCR8j-8#CDNNx#O}$}#mEpMkSWsLZ*T{mxIl%y=PFKis$PD)-)2g-2M6pO4}kyz@aZ zZOC_VYCbV7QvK;QpD_ZV;zC{wV|PcAFEZL9UPva86i!LilH9SO>w{T-TYcAhT2IJ? z(5%ZTL8RvUUO?bakl6{KA}+I`wG31AmPyF0g~qpi(4}=&Q!TI?nQIltlb#dxI_l4! zZ`T~0P9Zho<4Dr5G#1q?!|(`OK=MO$IX9D&_XA*VNSnieo`Q~?t_{FQ>IK>op|Lwq z01H1-Py3VRH+iSV7ipF~Urm34k2BVB|5#*Inkn7ch37O3u`MsXL(l2h$Y2TbUab#g z_F>EPpkL~99cX^4vT~{jn?Bj(I1Lo@urnUK6jPjh6N`>0#TG-^-0;j~_=}2tvKJ#N zF72L;Oe1Cp`z;zno&U-pPm8WBx*wGGp@;X}#z`JfgI}mv_v4U9-k(B2AwB_T-9YcV zNgB_w9q+~Y_Y^D4MQKn9Kx-6q8+7NZ1`wfh2SGe(mzjru3j;bCq3J@pae~s;#h5xO z^>F*C$pVMRbqzEBfBjU+9=PIOv0ri4Um0S)MGn?@63C~ltTR$W*$d(1_lkK~&=Cf$ z`eTK`mcA5_G2WK5>A#i2%HswzTJXUzeN;RW&ICpR7U3$7-3G$Bh7t&W5H_c8zCR7| z`;Ub3azXGdV6s;?#?xYv#vTmkU|o5JzTvO@FHP+N->b9}?=AdFbKi|1?=YL(Et75YTG; zU)j9D6lf;;GH|cxV+-1qp9A(KNMH9RcrZUbydC7xeF{mU*Z-Klurn`G>TVU~8T%t; z*j~_hU!1HX>KS@kYW6(o3D2;T>1*pZn@;xV989_diaLZwo?6THE8HJNu9*)V+R~0&U3kF%r18X8;|a8jmXG zxVwL-tcWF;CdEfnnX)RYZLOL(cdQI7dePh^lV~ATxx%m3( zRw4_$y@f1N|9var++T}A$_j#EYz9~v@6Z-1*0az31M5*SsT zDO+rYZ!oH$Qt!C-37d^^pSGtYjz%;B3{?wjxpdkV0LuTALB z_fP7ht!HI0H7cBUuxNmYqFY4iXh*b@U{wZfwf62ZL@`xT8P&fW`N2d#xp#VY1~JI03p%{T z=vUZ)N@!QlPwP}rz=d=&SoTa9$%64raKaFnDAqx~jmBE- z+mIL2zX|eamLHPuwo4S&wv*t+iJXBhggb1X1>rbEZe!Hx@6b^ya|i05!noXMg6dHd zqius%Y)sfD;^keza87wz9doZt9}iQjczwHfjFb=luaL&@ZE`JjlLInbl^d{k5aWZK(|f?PybgKonYc{h|Qh@ zzFxbXp=%6|Yb=GF-g+N@+!)iIDSg{346EpkHQ?_yA6z-==Jvg8v9p5)YCND>_U zv{{3SZ`4N#OsDj1{ZxHI`&yx|Up;elq8+mt0-c)6xH(`764CQ9zJEAVA{37 z+{#`mxs{>amFPLYaHKI~yjB``kEyJIfOpmK4AkgsVjM%}0DQV3Zl`&S4j+1>p=OGS zTd^sJD884Ohz0nlyfW}#o4#p~JhRhskTF1ybYNs*O|J0A#to%be`hNUe`D4z)M5WQ znO;&Y=>tEG#YrV;-;iSjic~b@|1ArbT|6`{mTFzcmhvh9W^$c4?>MD3pm&~D;b#W# zP_f@LRt%2|v-`83+H@b56e3)9l~v_V^4IJ~-=ETHeVQrz=KQVXZR>M5CYzw2@q1@S zSc{N#Vn-NzvjF;u_Rav>(D(K{4vqPJn7DRp--j|Y@*ePKJIB7^y~tqF$}*YSPp7u$ zXBX$&oa={0hh#j~W6{8=$+(!fNMGhsaoKPS-cCQCm=iOO%PvTt61C9YQv8BES%t&> z)Pm<>uWNeNJ$Mo7pC0Lz@?gY*bPE@tN`on#AN3lAPf&Q;@g6AMi13C>qj)aZtQr4UVC z&pwW*(!GCTt;JN;;F!LmVQ$ehx^8)4k%zB4qE}HoWRf9#1K50h#3L4c1EIJGHh8m%a32|Gxd^pdnt> zl(>gH{eTpNeQEK4NJ^g~Bs8CWbWf5m;CWwcOf+pVl<@ZRgQX}m$+~p%owgSBnAjoI zA@We&LwFyWT=wsNJv6WcU6apus2GrPwf?;D;Sy9a8u!NmX|I5vv4pQrAy`RoYEbpm z8Sr0ldGsmxS^am{sCa>d8)RV9_u+=deF%0A>DE93u`kng`i(p8KkP~ihUcDNNtni6 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%0AtlngDy$SMGsy)gDR}y`xmW2RgP;gtPjuDng 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

|88Kly*@3Cgxe9%--;RW`E?3J52?q9IlR?Z-qjt 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e0ecf370f0701639767b8306f6867a15efb7d380 GIT binary patch literal 127287 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 Ue0k|L9{jt0Sy47m#=!so0B*i!iU0rr literal 0 HcmV?d00001 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..7afeb3281 --- /dev/null +++ b/docs/src/design/miniob-thread-model.md @@ -0,0 +1,43 @@ +> 本篇文档介绍 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 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 的内存大小,单位字节。 | **更多** From 915e141524812f9b6b3f4306e9c342e39f3f0761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 15:39:31 +0800 Subject: [PATCH 14/39] add reference --- docs/src/design/miniob-thread-model.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/src/design/miniob-thread-model.md b/docs/src/design/miniob-thread-model.md index 7afeb3281..c6851d5c0 100644 --- a/docs/src/design/miniob-thread-model.md +++ b/docs/src/design/miniob-thread-model.md @@ -41,3 +41,9 @@ JavaThreadPoolThreadHandler 会创建一个线程池,线程池中一个线程 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 From 70aef05dcd868d45836d0c968411e4ef310a903b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 15:52:01 +0800 Subject: [PATCH 15/39] clang-format --- deps/common/queue/queue.h | 10 ++-- deps/common/queue/simple_queue.h | 7 ++- deps/common/thread/runnable.h | 13 ++-- deps/common/thread/thread_pool.h | 2 +- deps/common/thread/thread_pool_executor.cpp | 36 +++++------ deps/common/thread/thread_pool_executor.h | 60 +++++++++---------- deps/common/thread/thread_util.cpp | 4 +- deps/common/thread/thread_util.h | 2 +- src/observer/main.cpp | 2 +- src/observer/net/cli_communicator.h | 3 +- .../net/java_thread_pool_thread_handler.h | 13 ++-- ...one_thread_per_connection_thread_handler.h | 5 +- src/observer/net/server.h | 9 +-- src/observer/net/thread_handler.h | 6 +- src/observer/session/session_stage.h | 1 - 15 files changed, 81 insertions(+), 92 deletions(-) diff --git a/deps/common/queue/queue.h b/deps/common/queue/queue.h index 8510060af..3744be5f2 100644 --- a/deps/common/queue/queue.h +++ b/deps/common/queue/queue.h @@ -33,12 +33,12 @@ class Queue using value_type = T; public: - Queue() = default; + Queue() = default; virtual ~Queue() = default; /** * @brief 在队列中放一个任务 - * + * * @param value 任务数据 * @return int 成功返回0 */ @@ -46,7 +46,7 @@ class Queue /** * @brief 从队列中取出一个任务 - * + * * @param value 任务数据 * @return int 成功返回0。如果队列为空,也不是成功的 */ @@ -54,10 +54,10 @@ class Queue /** * @brief 当前队列中任务的数量 - * + * * @return int 对列中任务的数量 */ virtual int size() const = 0; }; -} // namespace common \ No newline at end of file +} // namespace common \ No newline at end of file diff --git a/deps/common/queue/simple_queue.h b/deps/common/queue/simple_queue.h index 2d5834e04..7cb976876 100644 --- a/deps/common/queue/simple_queue.h +++ b/deps/common/queue/simple_queue.h @@ -23,7 +23,7 @@ namespace common { /** * @brief 一个十分简单的线程安全的任务队列 * @details 所有的接口都加了一个锁来保证线程安全。 - * 如果想了解更高效的队列实现,请参考 [Oceanbase](https://github.com/oceanbase/oceanbase) 中 + * 如果想了解更高效的队列实现,请参考 [Oceanbase](https://github.com/oceanbase/oceanbase) 中 * deps/oblib/src/lib/queue/ 的一些队列的实现 * @tparam T 任务数据类型。 * @ingroup Queue @@ -33,6 +33,7 @@ class SimpleQueue : public Queue { public: using value_type = T; + public: SimpleQueue() : Queue() {} virtual ~SimpleQueue() {} @@ -45,10 +46,10 @@ class SimpleQueue : public Queue int size() const override; private: - std::mutex mutex_; + std::mutex mutex_; std::queue queue_; }; -} // namespace common +} // namespace common #include "common/queue/simple_queue.ipp" \ No newline at end of file diff --git a/deps/common/thread/runnable.h b/deps/common/thread/runnable.h index 523f0a052..53fa04ed3 100644 --- a/deps/common/thread/runnable.h +++ b/deps/common/thread/runnable.h @@ -25,7 +25,7 @@ namespace common { class Runnable { public: - Runnable() = default; + Runnable() = default; virtual ~Runnable() = default; virtual void run() = 0; @@ -38,17 +38,12 @@ class Runnable class RunnableAdaptor : public Runnable { public: - RunnableAdaptor(const std::function &callable) : callable_(callable) - { - } + RunnableAdaptor(const std::function &callable) : callable_(callable) {} - void run() override - { - callable_(); - } + void run() override { callable_(); } private: const std::function &callable_; }; -} // namespace common +} // namespace common diff --git a/deps/common/thread/thread_pool.h b/deps/common/thread/thread_pool.h index ddbda566f..ef3716fb0 100644 --- a/deps/common/thread/thread_pool.h +++ b/deps/common/thread/thread_pool.h @@ -20,4 +20,4 @@ class ThreadPool public: }; -} // namespace common \ No newline at end of file +} // 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 index 6c770690f..1989c4007 100644 --- a/deps/common/thread/thread_pool_executor.cpp +++ b/deps/common/thread/thread_pool_executor.cpp @@ -23,20 +23,14 @@ using namespace std; namespace common { -int ThreadPoolExecutor::init(const char *name, - int core_pool_size, - int max_pool_size, - long keep_alive_time_ms) +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) +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_); @@ -52,13 +46,13 @@ int ThreadPoolExecutor::init(const char *name, pool_name_ = name; } - core_pool_size_ = core_pool_size; - max_pool_size_ = max_pool_size; + 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); + work_queue_ = std::move(work_queue); while (static_cast(threads_.size()) < core_pool_size_) { - if (create_thread(true/*core_thread*/) != 0) { + if (create_thread(true /*core_thread*/) != 0) { LOG_ERROR("create thread failed"); return -1; } @@ -99,7 +93,7 @@ int ThreadPoolExecutor::execute(unique_ptr &&task) return -1; } - int ret = work_queue_->push(std::move(task)); + int ret = work_queue_->push(std::move(task)); int task_size = work_queue_->size(); if (task_size > pool_size() - active_count()) { extend_thread(); @@ -138,6 +132,7 @@ void ThreadPoolExecutor::thread_func() 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_; @@ -150,6 +145,7 @@ void ThreadPoolExecutor::thread_func() /// 并不需要保留这么多线程 while (Clock::now() < idle_deadline) { unique_ptr task; + int ret = work_queue_->pop(task); if (0 == ret && task) { thread_data.idle = false; @@ -197,10 +193,10 @@ int ThreadPoolExecutor::create_thread_locked(bool core_thread) } ThreadData thread_data; - thread_data.core_thread = core_thread; - thread_data.idle = true; - thread_data.terminated = false; - thread_data.thread_ptr = thread_ptr; + 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_) { @@ -222,7 +218,7 @@ int ThreadPoolExecutor::extend_thread() return 0; } - return create_thread_locked(false/*core_thread*/); + return create_thread_locked(false /*core_thread*/); } -} // end namespace common +} // end namespace common diff --git a/deps/common/thread/thread_pool_executor.h b/deps/common/thread/thread_pool_executor.h index 8ea99bd88..659d7978e 100644 --- a/deps/common/thread/thread_pool_executor.h +++ b/deps/common/thread/thread_pool_executor.h @@ -32,7 +32,7 @@ namespace common { * @defgroup ThreadPool * @details 一个线程池包含一个任务队列和一组线程,当有任务提交时,线程池会从任务队列中取出任务分配给一个线程执行。 * 这里的接口设计参考了Java的线程池ThreadPoolExecutor,但是简化了很多。 - * + * * 这个线程池支持自动伸缩。 * 线程分为两类,一类是核心线程,一类是普通线程。核心线程不会退出,普通线程会在空闲一段时间后退出。 * 线程池有一个任务队列,收到的任务会放到任务队列中。当任务队列中任务的个数比当前线程个数多时,就会 @@ -46,35 +46,29 @@ class 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); + 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); + 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 */ @@ -82,7 +76,7 @@ class ThreadPoolExecutor /** * @brief 提交一个任务,不一定可以立即执行 - * + * * @param callable 任务 * @return int 成功放入队列返回0 */ @@ -117,18 +111,18 @@ class ThreadPoolExecutor /** * @brief 处理过的任务个数 */ - int64_t task_count() const { return task_count_.load();} + 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); @@ -142,7 +136,7 @@ class ThreadPoolExecutor * @brief 线程函数。从队列中拉任务并执行 */ void thread_func(); - + private: /** * @brief 线程池的状态 @@ -157,28 +151,28 @@ class ThreadPoolExecutor struct ThreadData { - bool core_thread = false; /// 是否是核心线程 - bool idle = false; /// 是否空闲 - bool terminated = false; /// 是否已经退出 - std::thread *thread_ptr = nullptr; /// 线程指针 + 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_; /// 任务队列 + int core_pool_size_ = 0; /// 核心线程个数 + int max_pool_size_ = 0; /// 最大线程个数 + std::chrono::milliseconds keep_alive_time_ms_; /// 非核心线程空闲多久后退出 - mutable std::mutex lock_; /// 保护线程池内部数据的锁 - std::map threads_; /// 线程列表 - int largest_pool_size_ = 0; /// 历史上达到的最大的线程个数 + std::unique_ptr>> work_queue_; /// 任务队列 - std::atomic task_count_ = 0; /// 处理过的任务个数 - std::atomic active_count_ = 0; /// 活跃线程个数 + mutable std::mutex lock_; /// 保护线程池内部数据的锁 + std::map threads_; /// 线程列表 - std::string pool_name_; /// 线程池名称 + int largest_pool_size_ = 0; /// 历史上达到的最大的线程个数 + std::atomic task_count_ = 0; /// 处理过的任务个数 + std::atomic active_count_ = 0; /// 活跃线程个数 + std::string pool_name_; /// 线程池名称 }; -} // namespace common +} // namespace common diff --git a/deps/common/thread/thread_util.cpp b/deps/common/thread/thread_util.cpp index 9c45f3d8c..b167bef0c 100644 --- a/deps/common/thread/thread_util.cpp +++ b/deps/common/thread/thread_util.cpp @@ -20,7 +20,7 @@ namespace common { int thread_set_name(const char *name) { const int namelen = 16; - char buf[namelen]; + char buf[namelen]; snprintf(buf, namelen, "%s", name); #ifdef __APPLE__ @@ -30,4 +30,4 @@ int thread_set_name(const char *name) #endif } -} // namespace common \ No newline at end of file +} // namespace common \ No newline at end of file diff --git a/deps/common/thread/thread_util.h b/deps/common/thread/thread_util.h index 064ab273f..e4ea458b2 100644 --- a/deps/common/thread/thread_util.h +++ b/deps/common/thread/thread_util.h @@ -25,4 +25,4 @@ namespace common { */ int thread_set_name(const char *name); -} // namespace common \ No newline at end of file +} // namespace common \ No newline at end of file diff --git a/src/observer/main.cpp b/src/observer/main.cpp index 37b87cfe4..054289516 100644 --- a/src/observer/main.cpp +++ b/src/observer/main.cpp @@ -144,7 +144,7 @@ Server *init_server() Server *server = nullptr; if (server_param.use_std_io) { - server = new CliServer(server_param); + server = new CliServer(server_param); } else { server = new NetServer(server_param); } diff --git a/src/observer/net/cli_communicator.h b/src/observer/net/cli_communicator.h index 47ccb7f81..df5d66062 100644 --- a/src/observer/net/cli_communicator.h +++ b/src/observer/net/cli_communicator.h @@ -33,7 +33,8 @@ class CliCommunicator : public PlainCommunicator RC write_result(SessionEvent *event, bool &need_disconnect) override; bool exit() const { return exit_; } + private: int write_fd_ = -1; ///< 与使用远程通讯模式不同,如果读数据使用标准输入,那么输出应该是标准输出 - bool exit_ = false; + bool exit_ = false; }; diff --git a/src/observer/net/java_thread_pool_thread_handler.h b/src/observer/net/java_thread_pool_thread_handler.h index 07fe3f948..37652fb6a 100644 --- a/src/observer/net/java_thread_pool_thread_handler.h +++ b/src/observer/net/java_thread_pool_thread_handler.h @@ -50,7 +50,7 @@ class JavaThreadPoolThreadHandler : public ThreadHandler public: /** * @brief 使用libevent处理消息时,需要有一个回调函数,这里就相当于libevent的回调函数 - * + * * @param ag 处理消息回调时的参数,比如libevent的event、连接等 */ void handle_event(EventCallbackAg *ag); @@ -62,9 +62,10 @@ class JavaThreadPoolThreadHandler : public ThreadHandler void event_loop_thread(); private: - struct event_base * event_base_ = nullptr; /// libevent 的event_base - common::ThreadPoolExecutor executor_; /// 线程池 - std::map event_map_; - std::mutex lock_; - SqlTaskHandler sql_task_handler_; /// SQL请求处理器 + 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/one_thread_per_connection_thread_handler.h b/src/observer/net/one_thread_per_connection_thread_handler.h index de50eeee7..9e27a3219 100644 --- a/src/observer/net/one_thread_per_connection_thread_handler.h +++ b/src/observer/net/one_thread_per_connection_thread_handler.h @@ -26,7 +26,7 @@ class Worker; class OneThreadPerConnectionThreadHandler : public ThreadHandler { public: - OneThreadPerConnectionThreadHandler() = default; + OneThreadPerConnectionThreadHandler() = default; virtual ~OneThreadPerConnectionThreadHandler(); //! @copydoc ThreadHandler::start @@ -41,9 +41,10 @@ class OneThreadPerConnectionThreadHandler : public ThreadHandler 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::unordered_map thread_map_; // 当前编译器没有支持jthread /// 保护线程安全的锁 std::mutex lock_; }; \ No newline at end of file diff --git a/src/observer/net/server.h b/src/observer/net/server.h index 895aaff89..2e39cb0ab 100644 --- a/src/observer/net/server.h +++ b/src/observer/net/server.h @@ -31,8 +31,9 @@ class Server Server(const ServerParam &input_server_param) : server_param_(input_server_param) {} virtual ~Server() {} - virtual int serve() = 0; + virtual int serve() = 0; virtual void shutdown() = 0; + protected: ServerParam server_param_; ///< 服务启动参数 }; @@ -79,10 +80,10 @@ class NetServer : public Server private: volatile bool started_ = false; - int server_socket_ = -1; ///< 监听套接字,是一个描述符 + int server_socket_ = -1; ///< 监听套接字,是一个描述符 CommunicatorFactory communicator_factory_; ///< 通过这个对象创建新的Communicator对象 - ThreadHandler * thread_handler_ = nullptr; + ThreadHandler *thread_handler_ = nullptr; }; class CliServer : public Server @@ -91,7 +92,7 @@ class CliServer : public Server CliServer(const ServerParam &input_server_param); virtual ~CliServer(); - int serve() override; + int serve() override; void shutdown() override; private: diff --git a/src/observer/net/thread_handler.h b/src/observer/net/thread_handler.h index dab5eab12..c5cf0e075 100644 --- a/src/observer/net/thread_handler.h +++ b/src/observer/net/thread_handler.h @@ -28,7 +28,7 @@ class Communicator; class ThreadHandler { public: - ThreadHandler() = default; + ThreadHandler() = default; virtual ~ThreadHandler() = default; /** @@ -57,10 +57,10 @@ class ThreadHandler * @param communicator 与客户端通讯的对象 */ virtual RC close_connection(Communicator *communicator) = 0; - + public: /** * @brief 创建一个线程模型 */ - static ThreadHandler * create(const char *name); + static ThreadHandler *create(const char *name); }; diff --git a/src/observer/session/session_stage.h b/src/observer/session/session_stage.h index f1ed4c5a2..2762c66ca 100644 --- a/src/observer/session/session_stage.h +++ b/src/observer/session/session_stage.h @@ -42,7 +42,6 @@ class SessionStage SessionStage() = default; virtual ~SessionStage(); - public: void handle_request2(SessionEvent *event); From a4762f9baeeec5a802580564a641db86dcc0d685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 15:55:01 +0800 Subject: [PATCH 16/39] clang-format --- unittest/thread_pool_executor_test.cpp | 33 +++++++++++--------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/unittest/thread_pool_executor_test.cpp b/unittest/thread_pool_executor_test.cpp index 4cd69a6ef..777ac968d 100644 --- a/unittest/thread_pool_executor_test.cpp +++ b/unittest/thread_pool_executor_test.cpp @@ -27,16 +27,11 @@ using namespace common; class TestRunnable : public Runnable { public: - TestRunnable(atomic &counter) : counter_(counter) - { - } + TestRunnable(atomic &counter) : counter_(counter) {} virtual ~TestRunnable() = default; - virtual void run() override - { - ++counter_; - } + virtual void run() override { ++counter_; } private: atomic &counter_; @@ -45,8 +40,7 @@ class TestRunnable : public Runnable class RandomSleepRunnable : public Runnable { public: - RandomSleepRunnable(int min_ms, int max_ms) : min_ms_(min_ms), max_ms_(max_ms) - {} + RandomSleepRunnable(int min_ms, int max_ms) : min_ms_(min_ms), max_ms_(max_ms) {} virtual ~RandomSleepRunnable() = default; @@ -55,20 +49,21 @@ class RandomSleepRunnable : public Runnable 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) +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>>()); + + 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()); @@ -85,18 +80,18 @@ void test(int core_size, int max_pool_size, int keep_alive_time_ms, TEST(ThreadPoolExecutor, test1) { atomic counter(0); - test(1, 1, 60*1000, 1000000, [&counter](){ return new TestRunnable(counter); }); + 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(2, 8, 60 * 1000, 1000000, [&counter]() { return new TestRunnable(counter); }); } TEST(ThreadPoolExecutor, test3) { - test(2, 8, 60*1000, 1000, [](){ return new RandomSleepRunnable(10, 100); }); + test(2, 8, 60 * 1000, 1000, []() { return new RandomSleepRunnable(10, 100); }); } int main(int argc, char **argv) From 18b9107cb83bde3205b4f319bb9f5fb13002d3a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 16:00:46 +0800 Subject: [PATCH 17/39] clang-format --- src/observer/net/cli_communicator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/observer/net/cli_communicator.h b/src/observer/net/cli_communicator.h index df5d66062..9f0c4a215 100644 --- a/src/observer/net/cli_communicator.h +++ b/src/observer/net/cli_communicator.h @@ -35,6 +35,6 @@ class CliCommunicator : public PlainCommunicator bool exit() const { return exit_; } private: + bool exit_ = false; ///< 是否需要退出 int write_fd_ = -1; ///< 与使用远程通讯模式不同,如果读数据使用标准输入,那么输出应该是标准输出 - bool exit_ = false; }; From 93d951b5cc1d363753d6299000a9e36516ba1fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 16:01:38 +0800 Subject: [PATCH 18/39] clang-format --- src/observer/net/cli_communicator.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/observer/net/cli_communicator.h b/src/observer/net/cli_communicator.h index 9f0c4a215..e5dbb7a2c 100644 --- a/src/observer/net/cli_communicator.h +++ b/src/observer/net/cli_communicator.h @@ -36,5 +36,6 @@ class CliCommunicator : public PlainCommunicator private: bool exit_ = false; ///< 是否需要退出 + int write_fd_ = -1; ///< 与使用远程通讯模式不同,如果读数据使用标准输入,那么输出应该是标准输出 }; From 189e3ec31d94b6f8085c2849ec755c01a0fc176b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 16:03:03 +0800 Subject: [PATCH 19/39] clang-format --- src/observer/net/cli_communicator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/observer/net/cli_communicator.h b/src/observer/net/cli_communicator.h index e5dbb7a2c..e0ce7f86d 100644 --- a/src/observer/net/cli_communicator.h +++ b/src/observer/net/cli_communicator.h @@ -36,6 +36,6 @@ class CliCommunicator : public PlainCommunicator private: bool exit_ = false; ///< 是否需要退出 - + int write_fd_ = -1; ///< 与使用远程通讯模式不同,如果读数据使用标准输入,那么输出应该是标准输出 }; From 0b534fd83387f4671ac4a5ff5c0e57b208cb1116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 16:31:50 +0800 Subject: [PATCH 20/39] uncomment sysbench --- .github/workflows/test.yml | 60 ++++++++++++++++++-------------------- CMakeLists.txt | 25 ++++++++-------- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ac3640809..b835b17a2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,34 +27,32 @@ 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: + 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 -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index c574ee961..896d4d57a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,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) @@ -88,18 +89,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) From 2be1487541f6857a1a53514c89af73a96d133efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 17:08:33 +0800 Subject: [PATCH 21/39] add benchmark into github action test --- .github/workflows/test.yml | 24 +++++++++++++++++++++++- CMakeLists.txt | 6 +++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b835b17a2..16ad880dc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,10 @@ jobs: # sysbench cannot work property on this platform. # I found that sysbench would send more request before receiving last response sysbench-test: + strategy: + matrix: + thread_model: ['one-thread-per-connection', 'java-thread-pool'] + runs-on: ubuntu-latest steps: - name: Checkout repository and submodules @@ -45,7 +49,7 @@ jobs: run: | sudo bash build.sh init bash build.sh release -DCONCURRENCY=ON -DWITH_UNIT_TESTS=OFF - nohup ./build_release/bin/observer -s /tmp/miniob.sock -f etc/observer.ini -P mysql -t mvcc & + 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" @@ -56,3 +60,21 @@ jobs: 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 + + 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/ + find ./ -name "*_concurrency_test" | xargs time \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 896d4d57a..fbbe9077f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ 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) @@ -144,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) From 323c43e55126d19b7cd2e1186dd3c3937eb0a8ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 17:16:38 +0800 Subject: [PATCH 22/39] build benchmark in release mode --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From c2b10bc4def08ccee49a066469d4d92cc549ceb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 18:29:56 +0800 Subject: [PATCH 23/39] debug integer generator --- benchmark/bplus_tree_concurrency_test.cpp | 2 +- benchmark/record_manager_concurrency_test.cpp | 18 +++++---- .../common/math}/integer_generator.h | 9 ++++- unittest/integer_generator_test.cpp | 37 +++++++++++++++++++ 4 files changed, 57 insertions(+), 9 deletions(-) rename {benchmark => deps/common/math}/integer_generator.h (93%) create mode 100644 unittest/integer_generator_test.cpp 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..148b47851 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; } @@ -270,6 +270,7 @@ class DeletionBenchmark : public BenchmarkBase 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()); } protected: @@ -278,11 +279,14 @@ class DeletionBenchmark : public BenchmarkBase BENCHMARK_DEFINE_F(DeletionBenchmark, Deletion)(State &state) { - IntegerGenerator generator(0, static_cast(rids_.size())); + IntegerGenerator generator(0, static_cast(rids_.size() - 1)); + LOG_INFO("rids size =%d", static_cast(rids_.size() - 1)); Stat stat; for (auto _ : state) { int32_t value = generator.next(); + LOG_WARN("value=%" PRIu32 " rids size =%ld", value, rids_.size()); + ASSERT(value >= 0 && value < static_cast(rids_.size()), "invalid value. value=%" PRIu32, value); RID rid = rids_[value]; Delete(rid, stat); } diff --git a/benchmark/integer_generator.h b/deps/common/math/integer_generator.h similarity index 93% rename from benchmark/integer_generator.h rename to deps/common/math/integer_generator.h index 26ae4904f..501258443 100644 --- a/benchmark/integer_generator.h +++ b/deps/common/math/integer_generator.h @@ -11,8 +11,13 @@ See the Mulan PSL v2 for more details. */ // // Created by Wangyunlai on 2023/05/04 // + +#pragma once + #include +namespace common { + class IntegerGenerator { public: @@ -23,4 +28,6 @@ class IntegerGenerator private: std::random_device rd_; std::uniform_int_distribution<> distrib_; -}; \ No newline at end of file +}; + +} // namespace common \ No newline at end of file diff --git a/unittest/integer_generator_test.cpp b/unittest/integer_generator_test.cpp new file mode 100644 index 000000000..1c6342124 --- /dev/null +++ b/unittest/integer_generator_test.cpp @@ -0,0 +1,37 @@ +/* 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.wyl on 2023/08/14 +// + +#include "gtest/gtest.h" +#include "common/math/integer_generator.h" + +using namespace std; +using namespace common; + +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); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file From 70c74dfc7e11d4d7d40a6d16a216408db67076bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 21:08:19 +0800 Subject: [PATCH 24/39] add sysbench delete test case --- test/sysbench/miniob_delete.lua | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/sysbench/miniob_delete.lua 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 From 4878ee228d4c4a6ded99661f191b4a648b024a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 21:12:06 +0800 Subject: [PATCH 25/39] test benchmark --- .github/workflows/test.yml | 7 +- benchmark/record_manager_concurrency_test.cpp | 4 +- deps/common/log/log.h | 10 +++ deps/common/math/integer_generator.h | 13 +++- src/observer/storage/buffer/frame.cpp | 68 +++++++++---------- unittest/integer_generator_test.cpp | 4 +- 6 files changed, 59 insertions(+), 47 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 16ad880dc..46325ea95 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,6 +31,7 @@ jobs: strategy: matrix: thread_model: ['one-thread-per-connection', 'java-thread-pool'] + test_case: ['miniob_insert', 'miniob_delete'] runs-on: ubuntu-latest steps: @@ -58,8 +59,8 @@ jobs: 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 --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 @@ -77,4 +78,4 @@ jobs: shell: bash run: | cd build_release/bin/ - find ./ -name "*_concurrency_test" | xargs time \ No newline at end of file + find ./ -name "*_concurrency_test" -executable -exec {} \ No newline at end of file diff --git a/benchmark/record_manager_concurrency_test.cpp b/benchmark/record_manager_concurrency_test.cpp index 148b47851..8f470f179 100644 --- a/benchmark/record_manager_concurrency_test.cpp +++ b/benchmark/record_manager_concurrency_test.cpp @@ -280,13 +280,11 @@ class DeletionBenchmark : public BenchmarkBase BENCHMARK_DEFINE_F(DeletionBenchmark, Deletion)(State &state) { IntegerGenerator generator(0, static_cast(rids_.size() - 1)); - LOG_INFO("rids size =%d", static_cast(rids_.size() - 1)); Stat stat; + for (auto _ : state) { int32_t value = generator.next(); - LOG_WARN("value=%" PRIu32 " rids size =%ld", value, rids_.size()); - ASSERT(value >= 0 && value < static_cast(rids_.size()), "invalid value. value=%" PRIu32, value); RID rid = rids_[value]; Delete(rid, stat); } diff --git a/deps/common/log/log.h b/deps/common/log/log.h index 33b931c50..2063712da 100644 --- a/deps/common/log/log.h +++ b/deps/common/log/log.h @@ -333,6 +333,16 @@ 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/deps/common/math/integer_generator.h b/deps/common/math/integer_generator.h index 501258443..8c9296ef3 100644 --- a/deps/common/math/integer_generator.h +++ b/deps/common/math/integer_generator.h @@ -15,19 +15,28 @@ See the Mulan PSL v2 for more details. */ #pragma once #include +#include "common/log/log.h" namespace common { class IntegerGenerator { public: - IntegerGenerator(int min, int max) : distrib_(min, max) {} + IntegerGenerator() = delete; + IntegerGenerator(int min, int max) : distrib_(min, max) { + LOG_INFO("IntegerGenerator: min=%d, max=%d", distrib_.min(), distrib_.max()); + } + + IntegerGenerator(const IntegerGenerator &other) = 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_; }; -} // namespace common \ No newline at end of file +} // namespace common diff --git a/src/observer/storage/buffer/frame.cpp b/src/observer/storage/buffer/frame.cpp index 157611489..cc35be084 100644 --- a/src/observer/storage/buffer/frame.cpp +++ b/src/observer/storage/buffer/frame.cpp @@ -77,12 +77,10 @@ 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()); + 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_=xid), ++write_recursive_count_, file_desc_, xid, lbt()); } void Frame::write_unlatch() { write_unlatch(get_default_debug_xid()); } @@ -102,8 +100,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; @@ -134,10 +132,9 @@ void Frame::read_latch(intptr_t xid) { 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()); + 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()); } } @@ -160,10 +157,9 @@ bool Frame::try_read_latch() bool ret = lock_.try_lock_shared(); if (ret) { 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()); + 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(); } @@ -181,7 +177,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 +188,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 +202,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 +223,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/unittest/integer_generator_test.cpp b/unittest/integer_generator_test.cpp index 1c6342124..69d689b19 100644 --- a/unittest/integer_generator_test.cpp +++ b/unittest/integer_generator_test.cpp @@ -9,7 +9,7 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. */ // -// Created by wangyunlai.wyl on 2023/08/14 +// Created by wangyunlai.wyl on 2024/01/15 // #include "gtest/gtest.h" @@ -34,4 +34,4 @@ int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} From 9257dbee1bc604fffaf2f74e3063f57deaa148bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 21:15:55 +0800 Subject: [PATCH 26/39] clang-format --- benchmark/record_manager_concurrency_test.cpp | 1 - deps/common/log/log.h | 9 ++++----- deps/common/math/integer_generator.h | 8 ++------ unittest/integer_generator_test.cpp | 4 ++-- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/benchmark/record_manager_concurrency_test.cpp b/benchmark/record_manager_concurrency_test.cpp index 8f470f179..5ae256d72 100644 --- a/benchmark/record_manager_concurrency_test.cpp +++ b/benchmark/record_manager_concurrency_test.cpp @@ -282,7 +282,6 @@ BENCHMARK_DEFINE_F(DeletionBenchmark, Deletion)(State &state) IntegerGenerator generator(0, static_cast(rids_.size() - 1)); Stat stat; - for (auto _ : state) { int32_t value = generator.next(); RID rid = rids_[value]; diff --git a/deps/common/log/log.h b/deps/common/log/log.h index 2063712da..0c98dac2e 100644 --- a/deps/common/log/log.h +++ b/deps/common/log/log.h @@ -335,13 +335,12 @@ int Log::out(const LOG_LEVEL console_level, const LOG_LEVEL log_level, T &msg) #ifndef TRACE #ifdef DEBUG -#define TRACE(format, ...) \ - LOG_TRACE(format, ##__VA_ARGS__) -#else // DEBUG +#define TRACE(format, ...) LOG_TRACE(format, ##__VA_ARGS__) +#else // DEBUG #define TRACE(...) -#endif // DEBUG +#endif // DEBUG -#endif // TRACE +#endif // TRACE #define SYS_OUTPUT_FILE_POS ", File:" << __FILE__ << ", line:" << __LINE__ << ",function:" << __FUNCTION__ #define SYS_OUTPUT_ERROR ",error:" << errno << ":" << strerror(errno) diff --git a/deps/common/math/integer_generator.h b/deps/common/math/integer_generator.h index 8c9296ef3..dc625be1f 100644 --- a/deps/common/math/integer_generator.h +++ b/deps/common/math/integer_generator.h @@ -15,17 +15,13 @@ See the Mulan PSL v2 for more details. */ #pragma once #include -#include "common/log/log.h" namespace common { class IntegerGenerator { public: - IntegerGenerator() = delete; - IntegerGenerator(int min, int max) : distrib_(min, max) { - LOG_INFO("IntegerGenerator: min=%d, max=%d", distrib_.min(), distrib_.max()); - } + IntegerGenerator(int min, int max) : distrib_(min, max) {} IntegerGenerator(const IntegerGenerator &other) = delete; IntegerGenerator &operator=(const IntegerGenerator &) = delete; @@ -39,4 +35,4 @@ class IntegerGenerator std::uniform_int_distribution<> distrib_; }; -} // namespace common +} // namespace common diff --git a/unittest/integer_generator_test.cpp b/unittest/integer_generator_test.cpp index 69d689b19..e88993431 100644 --- a/unittest/integer_generator_test.cpp +++ b/unittest/integer_generator_test.cpp @@ -20,8 +20,8 @@ using namespace common; TEST(IntegerGenerator, test) { - const int min = 1; - const int max = 120000; + const int min = 1; + const int max = 120000; IntegerGenerator generator(min, max); for (int i = 0; i < 1000000; i++) { int value = generator.next(); From 76739fddb66cf8a5392f6117868e8155ab90df6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 21:17:20 +0800 Subject: [PATCH 27/39] fix format of test.yml --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 46325ea95..b1e0f7430 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,7 +31,7 @@ jobs: strategy: matrix: thread_model: ['one-thread-per-connection', 'java-thread-pool'] - test_case: ['miniob_insert', 'miniob_delete'] + test_case: ['miniob_insert', 'miniob_delete'] runs-on: ubuntu-latest steps: @@ -78,4 +78,4 @@ jobs: shell: bash run: | cd build_release/bin/ - find ./ -name "*_concurrency_test" -executable -exec {} \ No newline at end of file + find ./ -name "*_concurrency_test" -executable -exec {} From 607b985d6545de00d0db4385f966ee2aada642cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 15 Jan 2024 21:19:12 +0800 Subject: [PATCH 28/39] clang-format --- deps/common/math/integer_generator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/common/math/integer_generator.h b/deps/common/math/integer_generator.h index dc625be1f..c2e206e9e 100644 --- a/deps/common/math/integer_generator.h +++ b/deps/common/math/integer_generator.h @@ -23,7 +23,7 @@ class IntegerGenerator public: IntegerGenerator(int min, int max) : distrib_(min, max) {} - IntegerGenerator(const IntegerGenerator &other) = delete; + IntegerGenerator(const IntegerGenerator &other) = delete; IntegerGenerator &operator=(const IntegerGenerator &) = delete; int next() { return distrib_(rd_); } From e2683e09a8c16c3000f78842a55031e44b4322f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Tue, 16 Jan 2024 10:04:25 +0800 Subject: [PATCH 29/39] fix compile bug in debug mode --- src/observer/storage/buffer/frame.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/observer/storage/buffer/frame.cpp b/src/observer/storage/buffer/frame.cpp index cc35be084..b720eb1d1 100644 --- a/src/observer/storage/buffer/frame.cpp +++ b/src/observer/storage/buffer/frame.cpp @@ -78,9 +78,13 @@ void Frame::write_latch(intptr_t xid) lock_.lock(); +#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_=xid), ++write_recursive_count_, file_desc_, xid, lbt()); + 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()); } @@ -131,10 +135,13 @@ void Frame::read_latch(intptr_t xid) lock_.lock_shared(); { +#ifdef DEBUG scoped_lock debug_lock(debug_lock_); + ++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()); + this, pin_count_.load(), page_.page_num, file_desc_, xid, read_lockers_[xid], lbt()); +#endif } } @@ -156,11 +163,14 @@ bool Frame::try_read_latch() bool ret = lock_.try_lock_shared(); if (ret) { +#ifdef DEBUG debug_lock_.lock(); + ++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()); + this, pin_count_.load(), page_.page_num, file_desc_, xid, read_lockers_[xid], lbt()); debug_lock_.unlock(); +#endif } return ret; From c31c3cb02304e033116c41d4b0d419ec4eefeec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Tue, 16 Jan 2024 10:40:20 +0800 Subject: [PATCH 30/39] fix benchmark concurrency setup failure --- benchmark/record_manager_concurrency_test.cpp | 12 ++++++++++-- deps/common/math/integer_generator.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/benchmark/record_manager_concurrency_test.cpp b/benchmark/record_manager_concurrency_test.cpp index 5ae256d72..7983ea033 100644 --- a/benchmark/record_manager_concurrency_test.cpp +++ b/benchmark/record_manager_concurrency_test.cpp @@ -261,19 +261,27 @@ 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: + // 从实际测试情况看,每个线程都会执行setup,但是它们操作的对象都是同一个 + // 但是每个线程set up结束后,就会执行测试了。如果不等待的话,就会导致有些 + // 线程访问的数据不是想要的结果 + volatile bool setup_done_ = false; vector rids_; }; diff --git a/deps/common/math/integer_generator.h b/deps/common/math/integer_generator.h index c2e206e9e..2ac182812 100644 --- a/deps/common/math/integer_generator.h +++ b/deps/common/math/integer_generator.h @@ -24,6 +24,7 @@ class IntegerGenerator 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_); } From 0e02bd92d58d695d0ac61119ec2a260da0b18997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Tue, 16 Jan 2024 10:47:57 +0800 Subject: [PATCH 31/39] fix benchmark test yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b1e0f7430..18288d0e3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -78,4 +78,4 @@ jobs: shell: bash run: | cd build_release/bin/ - find ./ -name "*_concurrency_test" -executable -exec {} + for file in `find ./ -name "*_concurrency_test" -executable`; do $file; if [ $? -ne 0 ]; then exit 1; fi; done From 11a72015cba26ba4cd2045323c52475159f4b983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Tue, 16 Jan 2024 10:49:23 +0800 Subject: [PATCH 32/39] clang-format --- benchmark/record_manager_concurrency_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/record_manager_concurrency_test.cpp b/benchmark/record_manager_concurrency_test.cpp index 7983ea033..546fd00c2 100644 --- a/benchmark/record_manager_concurrency_test.cpp +++ b/benchmark/record_manager_concurrency_test.cpp @@ -282,7 +282,7 @@ class DeletionBenchmark : public BenchmarkBase // 但是每个线程set up结束后,就会执行测试了。如果不等待的话,就会导致有些 // 线程访问的数据不是想要的结果 volatile bool setup_done_ = false; - vector rids_; + vector rids_; }; BENCHMARK_DEFINE_F(DeletionBenchmark, Deletion)(State &state) From 0a65f322bc9df16e7b81aa803050608a26521d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Tue, 16 Jan 2024 12:25:09 +0800 Subject: [PATCH 33/39] fix concurrency bug --- deps/common/thread/runnable.h | 4 ++-- src/observer/net/java_thread_pool_thread_handler.cpp | 11 ++++++++--- src/observer/net/mysql_communicator.cpp | 5 ++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/deps/common/thread/runnable.h b/deps/common/thread/runnable.h index 53fa04ed3..fe9ae1c22 100644 --- a/deps/common/thread/runnable.h +++ b/deps/common/thread/runnable.h @@ -38,12 +38,12 @@ class Runnable class RunnableAdaptor : public Runnable { public: - RunnableAdaptor(const std::function &callable) : callable_(callable) {} + RunnableAdaptor(std::function callable) : callable_(callable) {} void run() override { callable_(); } private: - const std::function &callable_; + std::function callable_; }; } // namespace common diff --git a/src/observer/net/java_thread_pool_thread_handler.cpp b/src/observer/net/java_thread_pool_thread_handler.cpp index 567b21c61..c2e411ad1 100644 --- a/src/observer/net/java_thread_pool_thread_handler.cpp +++ b/src/observer/net/java_thread_pool_thread_handler.cpp @@ -94,6 +94,7 @@ RC JavaThreadPoolThreadHandler::start() 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); @@ -115,13 +116,17 @@ void JavaThreadPoolThreadHandler::handle_event(EventCallbackAg *ag) auto sql_handler = [this, ag]() { RC rc = sql_task_handler_.handle_event(ag->communicator); // 这里会有接收消息、处理请求然后返回结果一条龙服务 if (RC::SUCCESS != rc) { - LOG_ERROR("failed to handle sql task. rc=%d", 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"); + 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); } }; @@ -164,7 +169,7 @@ RC JavaThreadPoolThreadHandler::new_connection(Communicator *communicator) int ret = event_add(ev, nullptr); if (0 != ret) { - LOG_ERROR("failed to add event"); + LOG_ERROR("failed to add event. fd=%d, communicator=%p, ret=%d", fd, communicator, ret); event_free(ev); return RC::INTERNAL; } diff --git a/src/observer/net/mysql_communicator.cpp b/src/observer/net/mysql_communicator.cpp index 3e56cc624..b8eeebfbd 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,8 +638,6 @@ 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_) { /// 还没有做过认证,就先需要完成握手阶段 From 8c9d80120d64c37e352d0baefff5d6d0d95a2aad Mon Sep 17 00:00:00 2001 From: wangyunlai Date: Tue, 16 Jan 2024 11:09:15 +0000 Subject: [PATCH 34/39] test sysbench miniob_delete --- src/observer/net/sql_task_handler.cpp | 2 + src/observer/sql/parser/lex_sql.cpp | 37 +- src/observer/sql/parser/lex_sql.h | 11 +- src/observer/sql/parser/yacc_sql.cpp | 743 +++++++++++++------------- src/observer/sql/parser/yacc_sql.hpp | 18 +- src/observer/sql/parser/yacc_sql.y | 3 + 6 files changed, 420 insertions(+), 394 deletions(-) diff --git a/src/observer/net/sql_task_handler.cpp b/src/observer/net/sql_task_handler.cpp index 736bee731..d4d61f5af 100644 --- a/src/observer/net/sql_task_handler.cpp +++ b/src/observer/net/sql_task_handler.cpp @@ -43,6 +43,8 @@ RC SqlTaskHandler::handle_event(Communicator *communicator) event->session()->set_current_request(nullptr); Session::set_current_session(nullptr); + delete event; + if (need_disconnect) { return RC::INTERNAL; } 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); } ; From a992b9887e29b6b9d7068cf9dbe8ee3f8b507947 Mon Sep 17 00:00:00 2001 From: wangyunlai Date: Wed, 17 Jan 2024 09:37:56 +0000 Subject: [PATCH 35/39] remove EV_ET in java_thread_pool --- deps/common/thread/thread_pool_executor.cpp | 8 ++------ src/observer/net/java_thread_pool_thread_handler.cpp | 4 +++- .../net/one_thread_per_connection_thread_handler.cpp | 2 +- src/observer/net/server.cpp | 2 +- src/observer/storage/clog/clog.cpp | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/deps/common/thread/thread_pool_executor.cpp b/deps/common/thread/thread_pool_executor.cpp index 1989c4007..de6b40180 100644 --- a/deps/common/thread/thread_pool_executor.cpp +++ b/deps/common/thread/thread_pool_executor.cpp @@ -136,14 +136,12 @@ void ThreadPoolExecutor::thread_func() chrono::time_point idle_deadline = Clock::now(); if (!thread_data.core_thread && keep_alive_time_ms_.count() > 0) { idle_deadline += keep_alive_time_ms_; - } else { - idle_deadline += chrono::hours(1); } /// 这里使用最粗暴的方式检测线程是否可以退出了 /// 但是实际上,如果当前的线程个数比任务数要多,或者差不多,而且任务执行都很快的时候, /// 并不需要保留这么多线程 - while (Clock::now() < idle_deadline) { + while (thread_data.core_thread || Clock::now() < idle_deadline) { unique_ptr task; int ret = work_queue_->pop(task); @@ -155,10 +153,8 @@ void ThreadPoolExecutor::thread_func() thread_data.idle = true; ++task_count_; - if (!thread_data.core_thread && keep_alive_time_ms_.count() > 0) { + if (keep_alive_time_ms_.count() > 0) { idle_deadline = Clock::now() + keep_alive_time_ms_; - } else { - idle_deadline = Clock::now() + chrono::hours(1); } } if (state_ != State::RUNNING && work_queue_->size() == 0) { diff --git a/src/observer/net/java_thread_pool_thread_handler.cpp b/src/observer/net/java_thread_pool_thread_handler.cpp index c2e411ad1..9dd1957e8 100644 --- a/src/observer/net/java_thread_pool_thread_handler.cpp +++ b/src/observer/net/java_thread_pool_thread_handler.cpp @@ -154,9 +154,11 @@ RC JavaThreadPoolThreadHandler::new_connection(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 | EV_ET, event_callback, ag); + 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; diff --git a/src/observer/net/one_thread_per_connection_thread_handler.cpp b/src/observer/net/one_thread_per_connection_thread_handler.cpp index cc65fb59a..1cc5e7a41 100644 --- a/src/observer/net/one_thread_per_connection_thread_handler.cpp +++ b/src/observer/net/one_thread_per_connection_thread_handler.cpp @@ -80,7 +80,7 @@ class Worker while (running_) { int ret = poll(&poll_fd, 1, 500); if (ret < 0) { - LOG_ERROR("poll error. fd = %d, ret = %d, error=%s", poll_fd.fd, ret, strerror(errno)); + 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); diff --git a/src/observer/net/server.cpp b/src/observer/net/server.cpp index 95bfb41e9..15d5064ea 100644 --- a/src/observer/net/server.cpp +++ b/src/observer/net/server.cpp @@ -270,7 +270,7 @@ int NetServer::serve() while (started_) { int ret = poll(&poll_fd, 1, 500); if (ret < 0) { - LOG_ERROR("poll error. fd = %d, ret = %d, error=%s", poll_fd.fd, ret, strerror(errno)); + 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); 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_; } } From e06ac3de5c56875fc47d1afc6ad27433617471a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Fri, 19 Jan 2024 10:17:10 +0800 Subject: [PATCH 36/39] add sysbench select test case --- test/sysbench/miniob_select.lua | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 test/sysbench/miniob_select.lua 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 From fc36f98f09f72f27934331752aeff8b8dd96dfd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Fri, 19 Jan 2024 10:17:51 +0800 Subject: [PATCH 37/39] test miniob_select --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 18288d0e3..54659db9f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,7 +31,7 @@ jobs: strategy: matrix: thread_model: ['one-thread-per-connection', 'java-thread-pool'] - test_case: ['miniob_insert', 'miniob_delete'] + test_case: ['miniob_insert', 'miniob_delete', 'miniob_select'] runs-on: ubuntu-latest steps: From c4d1b5f863ca553c44bf38c54e4f238a46bfc0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 22 Jan 2024 09:21:26 +0800 Subject: [PATCH 38/39] reset deprecret eof flag --- src/observer/net/mysql_communicator.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/observer/net/mysql_communicator.cpp b/src/observer/net/mysql_communicator.cpp index b8eeebfbd..247bb90aa 100644 --- a/src/observer/net/mysql_communicator.cpp +++ b/src/observer/net/mysql_communicator.cpp @@ -643,7 +643,9 @@ RC MysqlCommunicator::read_event(SessionEvent *&event) /// 还没有做过认证,就先需要完成握手阶段 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_; From 73be9f44073551f7920d09bccb2da700863c0979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=BF=90=E6=9D=A5?= Date: Mon, 22 Jan 2024 09:46:21 +0800 Subject: [PATCH 39/39] reset signal handler --- src/observer/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/observer/main.cpp b/src/observer/main.cpp index 054289516..091f51637 100644 --- a/src/observer/main.cpp +++ b/src/observer/main.cpp @@ -168,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); }