// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). // // The implementation of ThreadStatus. // // Note that we make get and set access to ThreadStatusData lockless. // As a result, ThreadStatusData as a whole is not atomic. However, // we guarantee consistent ThreadStatusData all the time whenever // user call GetThreadList(). This consistency guarantee is done // by having the following constraint in the internal implementation // of set and get order: // // 1. When reset any information in ThreadStatusData, always start from // clearing up the lower-level information first. // 2. When setting any information in ThreadStatusData, always start from // setting the higher-level information. // 3. When returning ThreadStatusData to the user, fields are fetched from // higher-level to lower-level. In addition, where there's a nullptr // in one field, then all fields that has lower-level than that field // should be ignored. // // The high to low level information would be: // thread_id > thread_type > db > cf > operation > state // // This means user might not always get full information, but whenever // returned by the GetThreadList() is guaranteed to be consistent. #pragma once #include #include #include #include #include #include #include #include #include "rocksdb/status.h" #include "rocksdb/thread_status.h" #include "port/port.h" #include "util/thread_operation.h" namespace rocksdb { class ColumnFamilyHandle; // The structure that keeps constant information about a column family. struct ConstantColumnFamilyInfo { #ifdef ROCKSDB_USING_THREAD_STATUS public: ConstantColumnFamilyInfo( const void* _db_key, const std::string& _db_name, const std::string& _cf_name) : db_key(_db_key), db_name(_db_name), cf_name(_cf_name) {} const void* db_key; const std::string db_name; const std::string cf_name; #endif // ROCKSDB_USING_THREAD_STATUS }; // the internal data-structure that is used to reflect the current // status of a thread using a set of atomic pointers. struct ThreadStatusData { #ifdef ROCKSDB_USING_THREAD_STATUS explicit ThreadStatusData() : enable_tracking(false) { thread_id.store(0); thread_type.store(ThreadStatus::USER); cf_key.store(nullptr); operation_type.store(ThreadStatus::OP_UNKNOWN); op_start_time.store(0); state_type.store(ThreadStatus::STATE_UNKNOWN); } // A flag to indicate whether the thread tracking is enabled // in the current thread. This value will be updated based on whether // the associated Options::enable_thread_tracking is set to true // in ThreadStatusUtil::SetColumnFamily(). // // If set to false, then SetThreadOperation and SetThreadState // will be no-op. bool enable_tracking; std::atomic thread_id; std::atomic thread_type; std::atomic cf_key; std::atomic operation_type; std::atomic op_start_time; std::atomic operation_stage; std::atomic op_properties[ThreadStatus::kNumOperationProperties]; std::atomic state_type; #endif // ROCKSDB_USING_THREAD_STATUS }; // The class that stores and updates the status of the current thread // using a thread-local ThreadStatusData. // // In most of the case, you should use ThreadStatusUtil to update // the status of the current thread instead of using ThreadSatusUpdater // directly. // // @see ThreadStatusUtil class ThreadStatusUpdater { public: ThreadStatusUpdater() {} // Releases all ThreadStatusData of all active threads. virtual ~ThreadStatusUpdater() {} // Unregister the current thread. void UnregisterThread(); // Reset the status of the current thread. This includes resetting // ColumnFamilyInfoKey, ThreadOperation, and ThreadState. void ResetThreadStatus(); // Set the id of the current thread. void SetThreadID(uint64_t thread_id); // Register the current thread for tracking. void RegisterThread(ThreadStatus::ThreadType ttype, uint64_t thread_id); // Update the column-family info of the current thread by setting // its thread-local pointer of ThreadStateInfo to the correct entry. void SetColumnFamilyInfoKey(const void* cf_key); // returns the column family info key. const void* GetColumnFamilyInfoKey(); // Update the thread operation of the current thread. void SetThreadOperation(const ThreadStatus::OperationType type); // The start time of the current thread operation. It is in the format // of micro-seconds since some fixed point in time. void SetOperationStartTime(const uint64_t start_time); // Set the "i"th property of the current operation. // // NOTE: Our practice here is to set all the thread operation properties // and stage before we set thread operation, and thread operation // will be set in std::memory_order_release. This is to ensure // whenever a thread operation is not OP_UNKNOWN, we will always // have a consistent information on its properties. void SetThreadOperationProperty( int i, uint64_t value); // Increase the "i"th property of the current operation with // the specified delta. void IncreaseThreadOperationProperty( int i, uint64_t delta); // Update the thread operation stage of the current thread. ThreadStatus::OperationStage SetThreadOperationStage( const ThreadStatus::OperationStage stage); // Clear thread operation of the current thread. void ClearThreadOperation(); // Reset all thread-operation-properties to 0. void ClearThreadOperationProperties(); // Update the thread state of the current thread. void SetThreadState(const ThreadStatus::StateType type); // Clear the thread state of the current thread. void ClearThreadState(); // Obtain the status of all active registered threads. Status GetThreadList( std::vector* thread_list); // Create an entry in the global ColumnFamilyInfo table for the // specified column family. This function should be called only // when the current thread does not hold db_mutex. void NewColumnFamilyInfo( const void* db_key, const std::string& db_name, const void* cf_key, const std::string& cf_name); // Erase all ConstantColumnFamilyInfo that is associated with the // specified db instance. This function should be called only when // the current thread does not hold db_mutex. void EraseDatabaseInfo(const void* db_key); // Erase the ConstantColumnFamilyInfo that is associated with the // specified ColumnFamilyData. This function should be called only // when the current thread does not hold db_mutex. void EraseColumnFamilyInfo(const void* cf_key); // Verifies whether the input ColumnFamilyHandles matches // the information stored in the current cf_info_map. void TEST_VerifyColumnFamilyInfoMap( const std::vector& handles, bool check_exist); protected: #ifdef ROCKSDB_USING_THREAD_STATUS // The thread-local variable for storing thread status. static __thread ThreadStatusData* thread_status_data_; // Returns the pointer to the thread status data only when the // thread status data is non-null and has enable_tracking == true. ThreadStatusData* GetLocalThreadStatus(); // Directly returns the pointer to thread_status_data_ without // checking whether enabling_tracking is true of not. ThreadStatusData* Get() { return thread_status_data_; } // The mutex that protects cf_info_map and db_key_map. std::mutex thread_list_mutex_; // The current status data of all active threads. std::unordered_set thread_data_set_; // A global map that keeps the column family information. It is stored // globally instead of inside DB is to avoid the situation where DB is // closing while GetThreadList function already get the pointer to its // CopnstantColumnFamilyInfo. std::unordered_map< const void*, std::unique_ptr> cf_info_map_; // A db_key to cf_key map that allows erasing elements in cf_info_map // associated to the same db_key faster. std::unordered_map< const void*, std::unordered_set> db_key_map_; #else static ThreadStatusData* thread_status_data_; #endif // ROCKSDB_USING_THREAD_STATUS }; } // namespace rocksdb