├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── config.mk ├── include ├── atomic.h ├── cache.h ├── counter.h ├── hash.h ├── logging.h ├── mutex.h ├── slice.h ├── sliding_window.h ├── spin_lock.h ├── string_util.h ├── thread.h ├── thread_pool.h ├── timer.h ├── tprinter.h └── util.h ├── src ├── cache.cc ├── hash.cc ├── logging.cc └── tprinter.cc └── test ├── Makefile ├── build_test.sh ├── perf_test_condvar.cc ├── perf_test_counter.cc ├── perf_test_framework.cc ├── perf_test_mutex.cc ├── perf_test_thread_pool.cc ├── test_logging.cc ├── test_mutex.cc ├── test_thread.cc ├── test_thread_pool.cc └── test_util.cc /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Baidu 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of common nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include config.mk 2 | 3 | LIB=libcommon.a 4 | INC=include 5 | INCLUDE_PATH=-Iinclude/ 6 | CXXFLAGS=-fPIC -Wall -g 7 | COMMON_OBJ = $(patsubst %.cc, %.o, $(wildcard src/*.cc)) 8 | 9 | .PHONY: install clean 10 | 11 | $(LIB): $(COMMON_OBJ) $(COMMON_HEADER) 12 | $(AR) -rs $@ $(COMMON_OBJ) 13 | 14 | %.o: %.cc 15 | $(CXX) $(CXXFLAGS) $(INCLUDE_PATH) -c $< -o $@ 16 | 17 | clean: 18 | rm -rf $(LIB) 19 | rm -rf $(COMMON_OBJ) 20 | 21 | install: $(LIB) $(INC) 22 | @mkdir -p $(PREFIX) 23 | @mkdir -p $(PREFIX)/lib 24 | @cp $(LIB) $(PREFIX)/lib 25 | @mkdir -p $(PREFIX)/include/common 26 | @cp $(INC)/* $(PREFIX)/include/common 27 | @echo 'Install succeed, target directory is "'$(PREFIX)'".' 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Common 2 | Baidu common library 3 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # install path 2 | PREFIX=./output 3 | -------------------------------------------------------------------------------- /include/atomic.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/common/f550db760ed4a339e5cb5641cc31eb28d744ebce/include/atomic.h -------------------------------------------------------------------------------- /include/cache.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 6 | // Use of this source code is governed by a BSD-style license that can be 7 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 8 | // 9 | // A Cache is an interface that maps keys to values. It has internal 10 | // synchronization and may be safely accessed concurrently from 11 | // multiple threads. It may automatically evict entries to make room 12 | // for new entries. Values have a specified charge against the cache 13 | // capacity. For example, a cache where the values are variable 14 | // length strings, may use the length of the string as the charge for 15 | // the string. 16 | // 17 | // A builtin cache implementation with a least-recently-used eviction 18 | // policy is provided. Clients may use their own implementations if 19 | // they want something more sophisticated (like scan-resistance, a 20 | // custom eviction policy, variable cache sizing, etc.) 21 | 22 | #ifndef BAIDU_COMMON_CACHE_H_ 23 | #define BAIDU_COMMON_CACHE_H_ 24 | 25 | #include 26 | #include "slice.h" 27 | 28 | namespace baidu { 29 | namespace common { 30 | 31 | class Cache; 32 | 33 | // Create a new cache with a fixed size capacity. This implementation 34 | // of Cache uses a least-recently-used eviction policy. 35 | extern Cache* NewLRUCache(size_t capacity); 36 | 37 | class Cache { 38 | public: 39 | Cache() { } 40 | 41 | // Destroys all existing entries by calling the "deleter" 42 | // function that was passed to the constructor. 43 | virtual ~Cache(); 44 | 45 | // Opaque handle to an entry stored in the cache. 46 | struct Handle { }; 47 | 48 | // Insert a mapping from key->value into the cache and assign it 49 | // the specified charge against the total cache capacity. 50 | // 51 | // Returns a handle that corresponds to the mapping. The caller 52 | // must call this->Release(handle) when the returned mapping is no 53 | // longer needed. 54 | // 55 | // When the inserted entry is no longer needed, the key and 56 | // value will be passed to "deleter". 57 | virtual Handle* Insert(const Slice& key, void* value, size_t charge, 58 | void (*deleter)(const Slice& key, void* value)) = 0; 59 | 60 | // If the cache has no mapping for "key", returns NULL. 61 | // 62 | // Else return a handle that corresponds to the mapping. The caller 63 | // must call this->Release(handle) when the returned mapping is no 64 | // longer needed. 65 | virtual Handle* Lookup(const Slice& key) = 0; 66 | 67 | // Release a mapping returned by a previous Lookup(). 68 | // REQUIRES: handle must not have been released yet. 69 | // REQUIRES: handle must have been returned by a method on *this. 70 | virtual void Release(Handle* handle) = 0; 71 | 72 | // Return the value encapsulated in a handle returned by a 73 | // successful Lookup(). 74 | // REQUIRES: handle must not have been released yet. 75 | // REQUIRES: handle must have been returned by a method on *this. 76 | virtual void* Value(Handle* handle) = 0; 77 | 78 | // If the cache contains entry for key, erase it. Note that the 79 | // underlying entry will be kept around until all existing handles 80 | // to it have been released. 81 | virtual void Erase(const Slice& key) = 0; 82 | 83 | // Return a new numeric id. May be used by multiple clients who are 84 | // sharing the same cache to partition the key space. Typically the 85 | // client will allocate a new id at startup and prepend the id to 86 | // its cache keys. 87 | virtual uint64_t NewId() = 0; 88 | 89 | private: 90 | void LRU_Remove(Handle* e); 91 | void LRU_Append(Handle* e); 92 | void Unref(Handle* e); 93 | 94 | struct Rep; 95 | Rep* rep_; 96 | 97 | // No copying allowed 98 | Cache(const Cache&); 99 | void operator=(const Cache&); 100 | }; 101 | 102 | } // namespace common 103 | } // namespace baidu 104 | 105 | #endif // BAIDU_COMMON_CACHE_H_ 106 | -------------------------------------------------------------------------------- /include/counter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #ifndef BAIDU_COMMON_COUNTER_H_ 8 | #define BAIDU_COMMON_COUNTER_H_ 9 | 10 | #include "atomic.h" 11 | #include "timer.h" 12 | 13 | namespace baidu { 14 | namespace common { 15 | 16 | class Counter { 17 | volatile int64_t val_; 18 | public: 19 | Counter(int64_t val = 0) : val_(val) {} 20 | int64_t Add(int64_t v) { 21 | return atomic_add64(&val_, v) + v; 22 | } 23 | int64_t Sub(int64_t v) { 24 | return atomic_add64(&val_, -v) - v; 25 | } 26 | int64_t Inc() { 27 | return atomic_add64(&val_, 1) + 1; 28 | } 29 | int64_t Dec() { 30 | return atomic_add64(&val_,-1) - 1; 31 | } 32 | int64_t Get() const { 33 | return val_; 34 | } 35 | int64_t Set(int64_t v) { 36 | return atomic_swap64(&val_, v); 37 | } 38 | int64_t Clear() { 39 | return atomic_swap64(&val_, 0); 40 | } 41 | }; 42 | 43 | } // namespace common 44 | } // namespace baidu 45 | 46 | #endif // BAIDU_COMMON_COUNTER_H_ 47 | 48 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 49 | -------------------------------------------------------------------------------- /include/hash.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 6 | // Use of this source code is governed by a BSD-style license that can be 7 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 8 | // 9 | // Simple hash function used for internal data structures 10 | 11 | #ifndef BAIDU_COMMON_HASH_H_ 12 | #define BAIDU_COMMON_HASH_H_ 13 | 14 | #include 15 | #include 16 | 17 | namespace baidu { 18 | namespace common { 19 | 20 | uint32_t Hash(const char* data, size_t n, uint32_t seed); 21 | 22 | } // common 23 | } // baidu 24 | 25 | #endif // BAIDU_COMMON_HASH_H_ 26 | -------------------------------------------------------------------------------- /include/logging.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #ifndef BAIDU_COMMON_LOGGING_H_ 8 | #define BAIDU_COMMON_LOGGING_H_ 9 | 10 | #include 11 | 12 | namespace baidu { 13 | namespace common { 14 | 15 | enum LogLevel { 16 | DEBUG = 2, 17 | INFO = 4, 18 | WARNING = 8, 19 | ERROR = 16, 20 | FATAL = 32, 21 | }; 22 | 23 | void SetLogLevel(int level); 24 | bool SetLogFile(const char* path, bool append = false); 25 | bool SetWarningFile(const char* path, bool append = false); 26 | bool SetLogSize(int size); // in MB 27 | bool SetLogCount(int count); 28 | bool SetLogSizeLimit(int size); // in MB 29 | 30 | void Log(int level, const char* fmt, ...); 31 | 32 | class LogStream { 33 | public: 34 | LogStream(int level); 35 | template 36 | LogStream& operator<<(const T& t) { 37 | oss_ << t; 38 | return *this; 39 | } 40 | ~LogStream(); 41 | private: 42 | int level_; 43 | std::ostringstream oss_; 44 | }; 45 | 46 | } // namespace common 47 | 48 | using common::DEBUG; 49 | using common::INFO; 50 | using common::WARNING; 51 | using common::ERROR; 52 | using common::FATAL; 53 | 54 | } // namespace baidu 55 | 56 | #define LOG(level, fmt, args...) ::baidu::common::Log(level, "[%s:%d] " fmt, __FILE__, __LINE__, ##args) 57 | #define LOGS(level) ::baidu::common::LogStream(level) 58 | 59 | #endif // BAIDU_COMMON_LOGGING_H_ 60 | 61 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 62 | -------------------------------------------------------------------------------- /include/mutex.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #ifndef BAIDU_COMMON_LOCK_MUTEX_H_ 8 | #define BAIDU_COMMON_LOCK_MUTEX_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "timer.h" 16 | 17 | namespace baidu { 18 | namespace common { 19 | 20 | // #define MUTEX_DEBUG 21 | 22 | static void PthreadCall(const char* label, int result) { 23 | if (result != 0) { 24 | fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); 25 | abort(); 26 | } 27 | } 28 | 29 | // A Mutex represents an exclusive lock. 30 | class Mutex { 31 | public: 32 | Mutex() 33 | : owner_(0), msg_(NULL), msg_threshold_(0), lock_time_(0) { 34 | pthread_mutexattr_t attr; 35 | PthreadCall("init mutexattr", pthread_mutexattr_init(&attr)); 36 | PthreadCall("set mutexattr", pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)); 37 | PthreadCall("init mutex", pthread_mutex_init(&mu_, &attr)); 38 | PthreadCall("destroy mutexattr", pthread_mutexattr_destroy(&attr)); 39 | } 40 | ~Mutex() { 41 | PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); 42 | } 43 | // Lock the mutex. 44 | // Will deadlock if the mutex is already locked by this thread. 45 | void Lock(const char* msg = NULL, int64_t msg_threshold = 5000) { 46 | #ifdef MUTEX_DEBUG 47 | int64_t s = (msg) ? timer::get_micros() : 0; 48 | #endif 49 | PthreadCall("mutex lock", pthread_mutex_lock(&mu_)); 50 | AfterLock(msg, msg_threshold); 51 | #ifdef MUTEX_DEBUG 52 | if (msg && lock_time_ - s > msg_threshold) { 53 | char buf[32]; 54 | common::timer::now_time_str(buf, sizeof(buf)); 55 | printf("%s [Mutex] %s wait lock %.3f ms\n", buf, msg, (lock_time_ -s) / 1000.0); 56 | } 57 | #endif 58 | } 59 | // Unlock the mutex. 60 | void Unlock() { 61 | BeforeUnlock(); 62 | PthreadCall("mutex unlock", pthread_mutex_unlock(&mu_)); 63 | } 64 | // Crash if this thread does not hold this mutex. 65 | void AssertHeld() { 66 | if (0 == pthread_equal(owner_, pthread_self())) { 67 | abort(); 68 | } 69 | } 70 | private: 71 | void AfterLock(const char* msg, int64_t msg_threshold) { 72 | #ifdef MUTEX_DEBUG 73 | msg_ = msg; 74 | msg_threshold_ = msg_threshold; 75 | if (msg_) { 76 | lock_time_ = timer::get_micros(); 77 | } 78 | #endif 79 | (void)msg; 80 | (void)msg_threshold; 81 | owner_ = pthread_self(); 82 | } 83 | void BeforeUnlock() { 84 | #ifdef MUTEX_DEBUG 85 | if (msg_ && timer::get_micros() - lock_time_ > msg_threshold_) { 86 | char buf[32]; 87 | common::timer::now_time_str(buf, sizeof(buf)); 88 | printf("%s [Mutex] %s locked %.3f ms\n", 89 | buf, msg_, (timer::get_micros() - lock_time_) / 1000.0); 90 | } 91 | msg_ = NULL; 92 | #endif 93 | owner_ = 0; 94 | } 95 | private: 96 | friend class CondVar; 97 | Mutex(const Mutex&); 98 | void operator=(const Mutex&); 99 | pthread_mutex_t mu_; 100 | pthread_t owner_; 101 | const char* msg_; 102 | int64_t msg_threshold_; 103 | int64_t lock_time_; 104 | }; 105 | 106 | // Mutex lock guard 107 | class MutexLock { 108 | public: 109 | explicit MutexLock(Mutex *mu, const char* msg = NULL, int64_t msg_threshold = 5000) 110 | : mu_(mu) { 111 | mu_->Lock(msg, msg_threshold); 112 | } 113 | ~MutexLock() { 114 | mu_->Unlock(); 115 | } 116 | private: 117 | Mutex *const mu_; 118 | MutexLock(const MutexLock&); 119 | void operator=(const MutexLock&); 120 | }; 121 | 122 | // Conditional variable 123 | class CondVar { 124 | public: 125 | explicit CondVar(Mutex* mu) : mu_(mu) { 126 | PthreadCall("init condvar", pthread_cond_init(&cond_, NULL)); 127 | } 128 | ~CondVar() { 129 | PthreadCall("destroy condvar", pthread_cond_destroy(&cond_)); 130 | } 131 | void Wait(const char* msg = NULL) { 132 | int64_t msg_threshold = mu_->msg_threshold_; 133 | mu_->BeforeUnlock(); 134 | PthreadCall("condvar wait", pthread_cond_wait(&cond_, &mu_->mu_)); 135 | mu_->AfterLock(msg, msg_threshold); 136 | } 137 | // Time wait in ms, return true iff signalled 138 | bool TimeWait(int timeout, const char* msg = NULL) { 139 | timespec ts; 140 | struct timeval tv; 141 | gettimeofday(&tv, NULL); 142 | int64_t usec = tv.tv_usec + timeout * 1000LL; 143 | ts.tv_sec = tv.tv_sec + usec / 1000000; 144 | ts.tv_nsec = (usec % 1000000) * 1000; 145 | int64_t msg_threshold = mu_->msg_threshold_; 146 | mu_->BeforeUnlock(); 147 | int ret = pthread_cond_timedwait(&cond_, &mu_->mu_, &ts); 148 | mu_->AfterLock(msg, msg_threshold); 149 | return (ret == 0); 150 | } 151 | void Signal() { 152 | PthreadCall("signal", pthread_cond_signal(&cond_)); 153 | } 154 | void Broadcast() { 155 | PthreadCall("broadcast", pthread_cond_broadcast(&cond_)); 156 | } 157 | private: 158 | CondVar(const CondVar&); 159 | void operator=(const CondVar&); 160 | Mutex* mu_; 161 | pthread_cond_t cond_; 162 | }; 163 | 164 | } // namespace common 165 | 166 | using common::Mutex; 167 | using common::MutexLock; 168 | using common::CondVar; 169 | 170 | } // namespace baidu 171 | 172 | #endif // COMMON_LOCK_MUTEX_H_ 173 | 174 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 175 | -------------------------------------------------------------------------------- /include/slice.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 6 | // Use of this source code is governed by a BSD-style license that can be 7 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 8 | // 9 | // Slice is a simple structure containing a pointer into some external 10 | // storage and a size. The user of a Slice must ensure that the slice 11 | // is not used after the corresponding external storage has been 12 | // deallocated. 13 | // 14 | // Multiple threads can invoke const methods on a Slice without 15 | // external synchronization, but if any of the threads may call a 16 | // non-const method, all threads accessing the same Slice must use 17 | // external synchronization. 18 | 19 | #ifndef BAIDU_COMMON_SLICE_H_ 20 | #define BAIDU_COMMON_SLICE_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace baidu { 28 | namespace common { 29 | 30 | class Slice { 31 | public: 32 | // Create an empty slice. 33 | Slice() : data_(""), size_(0) { } 34 | 35 | // Create a slice that refers to d[0,n-1]. 36 | Slice(const char* d, size_t n) : data_(d), size_(n) { } 37 | 38 | // Create a slice that refers to the contents of "s" 39 | Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } 40 | 41 | // Create a slice that refers to s[0,strlen(s)-1] 42 | Slice(const char* s) : data_(s), size_(strlen(s)) { } 43 | 44 | // Return a pointer to the beginning of the referenced data 45 | const char* data() const { return data_; } 46 | 47 | // Return the length (in bytes) of the referenced data 48 | size_t size() const { return size_; } 49 | 50 | // Return true iff the length of the referenced data is zero 51 | bool empty() const { return size_ == 0; } 52 | 53 | // works as basic_string 54 | const char* begin() const { return data_; } 55 | 56 | const char* end() const { return data_ + size_; } 57 | 58 | // Return the ith byte in the referenced data. 59 | // REQUIRES: n < size() 60 | char operator[](size_t n) const { 61 | assert(n < size()); 62 | return data_[n]; 63 | } 64 | 65 | // Change this slice to refer to an empty array 66 | void clear() { data_ = ""; size_ = 0; } 67 | 68 | // Drop the first "n" bytes from this slice. 69 | void remove_prefix(size_t n) { 70 | assert(n <= size()); 71 | data_ += n; 72 | size_ -= n; 73 | } 74 | 75 | // Return a string that contains the copy of the referenced data. 76 | std::string ToString() const { return std::string(data_, size_); } 77 | 78 | // Three-way comparison. Returns value: 79 | // < 0 iff "*this" < "b", 80 | // == 0 iff "*this" == "b", 81 | // > 0 iff "*this" > "b" 82 | int compare(const Slice& b) const; 83 | 84 | // Return true iff "x" is a prefix of "*this" 85 | bool starts_with(const Slice& x) const { 86 | return ((size_ >= x.size_) && 87 | (memcmp(data_, x.data_, x.size_) == 0)); 88 | } 89 | 90 | private: 91 | const char* data_; 92 | size_t size_; 93 | 94 | // Intentionally copyable 95 | }; 96 | 97 | inline bool operator==(const Slice& x, const Slice& y) { 98 | return ((x.size() == y.size()) && 99 | (memcmp(x.data(), y.data(), x.size()) == 0)); 100 | } 101 | 102 | inline bool operator!=(const Slice& x, const Slice& y) { 103 | return !(x == y); 104 | } 105 | 106 | inline int Slice::compare(const Slice& b) const { 107 | const size_t min_len = (size_ < b.size_) ? size_ : b.size_; 108 | int r = memcmp(data_, b.data_, min_len); 109 | if (r == 0) { 110 | if (size_ < b.size_) r = -1; 111 | else if (size_ > b.size_) r = +1; 112 | } 113 | return r; 114 | } 115 | 116 | } // namespace common 117 | } //namespace baidu 118 | 119 | #endif // BAIDU_COMMON_SLICE_H_ 120 | -------------------------------------------------------------------------------- /include/sliding_window.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #ifndef BAIDU_COMMON_SLIDING_WINDOW_H_ 8 | #define BAIDU_COMMON_SLIDING_WINDOW_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "mutex.h" 16 | 17 | namespace baidu { 18 | namespace common { 19 | 20 | template 21 | class SlidingWindow { 22 | public: 23 | typedef boost::function SlidingCallback; 24 | SlidingWindow(int32_t size, SlidingCallback callback) 25 | : bitmap_(NULL), items_(NULL), item_count_(0), 26 | callback_(callback), size_(size), 27 | base_offset_(0), max_offset_(-1), ready_(0), notifying_(false) { 28 | bitmap_ = new char[size]; 29 | memset(bitmap_, 0, size); 30 | items_ = new Item[size]; 31 | size_ = size; 32 | } 33 | ~SlidingWindow() { 34 | delete[] bitmap_; 35 | delete[] items_; 36 | } 37 | int32_t Size() const { 38 | return item_count_; 39 | } 40 | int32_t GetBaseOffset() const { 41 | return base_offset_; 42 | } 43 | void GetFragments(std::vector >* fragments) { 44 | MutexLock lock(&mu_); 45 | for (int i = 0; i < size_; i++) { 46 | if (bitmap_[(ready_ + i) % size_]) { 47 | fragments->push_back(std::make_pair(base_offset_+i, items_[(ready_ + i) % size_])); 48 | } 49 | } 50 | } 51 | void Notify() { 52 | mu_.AssertHeld(); 53 | notifying_ = true; 54 | while (bitmap_[ready_] == 1) { 55 | mu_.Unlock(); 56 | callback_(base_offset_, items_[ready_]); 57 | mu_.Lock("SlidingWindow::Notify relock"); 58 | bitmap_[ready_] = 0; 59 | ++ready_; 60 | ++base_offset_; 61 | --item_count_; 62 | if (ready_ >= size_) { 63 | ready_ = 0; 64 | } 65 | } 66 | notifying_ = false; 67 | } 68 | int32_t UpBound() const { 69 | MutexLock lock(&mu_); 70 | return base_offset_ + size_ - 1; 71 | } 72 | /// Add a new item to slinding window. 73 | // Returns: 74 | /// 0, Add to receiving buf; 75 | /// 1, Already received 76 | /// -1, Not in receiving window 77 | /// Notes: 78 | /// There is no thread pool, so SlidingCallback would be called by Add. 79 | /// Pay attention to a deadlock. 80 | int Add(int32_t offset, Item item) { 81 | MutexLock lock(&mu_, "Slinding Add", 50000); 82 | int32_t pos = offset - base_offset_; 83 | if (pos >= size_) { 84 | return -1; 85 | } else if (pos < 0) { 86 | return 1; 87 | } 88 | pos = (pos + ready_) % size_; 89 | if (bitmap_[pos]) { 90 | return 1; 91 | } 92 | bitmap_[pos] = 1; 93 | items_[pos] = item; 94 | ++item_count_; 95 | if (offset > max_offset_) { 96 | max_offset_ = offset; 97 | } 98 | if (!notifying_) Notify(); 99 | return 0; 100 | } 101 | int32_t GetMaxOffset() const { 102 | MutexLock lock(&mu_); 103 | return max_offset_; 104 | } 105 | private: 106 | char* bitmap_; 107 | Item* items_; 108 | int32_t item_count_; 109 | SlidingCallback callback_; 110 | int32_t size_; 111 | int32_t base_offset_; 112 | int32_t max_offset_; 113 | int32_t ready_; 114 | bool notifying_; 115 | mutable Mutex mu_; 116 | }; 117 | 118 | } // namespace common 119 | } // namespace baidu 120 | 121 | #endif // BAIDU_COMMON_SLIDING_WINDOW_H_ 122 | 123 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 124 | -------------------------------------------------------------------------------- /include/spin_lock.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: zhangkai17@baidu.com 6 | 7 | #ifndef BAIDU_COMMON_SPIN_LOCK_H_ 8 | #define BAIDU_COMMON_SPIN_LOCK_H_ 9 | 10 | #include 11 | 12 | namespace baidu { 13 | namespace common { 14 | 15 | class SpinLock { 16 | public: 17 | inline SpinLock() { 18 | pthread_spin_init(&spinlock_, PTHREAD_PROCESS_PRIVATE); 19 | } 20 | virtual inline ~SpinLock() { 21 | pthread_spin_destroy(&spinlock_); 22 | } 23 | inline int Lock() { 24 | return pthread_spin_lock(&spinlock_); 25 | } 26 | inline int Unlock() { 27 | return pthread_spin_unlock(&spinlock_); 28 | } 29 | inline int TryLock() { 30 | return pthread_spin_trylock(&spinlock_); 31 | } 32 | 33 | private: 34 | pthread_spinlock_t spinlock_; 35 | }; 36 | 37 | } // namespace common 38 | } // namespace baidu 39 | 40 | #endif // COMMON_SPIN_LOCK_H_ 41 | -------------------------------------------------------------------------------- /include/string_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #ifndef COMMON_STRING_UTIL_H_ 8 | #define COMMON_STRING_UTIL_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace baidu { 16 | namespace common { 17 | 18 | static inline bool IsVisible(char c) { 19 | return (c >= 0x20 && c <= 0x7E); 20 | } 21 | 22 | static inline char ToHex(uint8_t i) { 23 | char j = 0; 24 | if (i < 10) { 25 | j = i + '0'; 26 | } else { 27 | j = i - 10 + 'a'; 28 | } 29 | return j; 30 | } 31 | 32 | static inline std::string DebugString(const std::string& src) { 33 | size_t src_len = src.size(); 34 | std::string dst; 35 | dst.resize(src_len << 2); 36 | 37 | size_t j = 0; 38 | for (size_t i = 0; i < src_len; i++) { 39 | uint8_t c = src[i]; 40 | if (IsVisible(c)) { 41 | dst[j++] = c; 42 | } else { 43 | dst[j++] = '\\'; 44 | dst[j++] = 'x'; 45 | dst[j++] = ToHex(c >> 4); 46 | dst[j++] = ToHex(c & 0xF); 47 | } 48 | } 49 | return dst.substr(0, j); 50 | } 51 | 52 | static inline void SplitString(const std::string& full, 53 | const std::string& delim, 54 | std::vector* result) { 55 | result->clear(); 56 | if (full.empty()) { 57 | return; 58 | } 59 | 60 | std::string tmp; 61 | std::string::size_type pos_begin = full.find_first_not_of(delim); 62 | std::string::size_type comma_pos = 0; 63 | 64 | while (pos_begin != std::string::npos) { 65 | comma_pos = full.find(delim, pos_begin); 66 | if (comma_pos != std::string::npos) { 67 | tmp = full.substr(pos_begin, comma_pos - pos_begin); 68 | pos_begin = comma_pos + delim.length(); 69 | } else { 70 | tmp = full.substr(pos_begin); 71 | pos_begin = comma_pos; 72 | } 73 | 74 | if (!tmp.empty()) { 75 | result->push_back(tmp); 76 | tmp.clear(); 77 | } 78 | } 79 | } 80 | 81 | static inline std::string TrimString(const std::string& str, const std::string& trim) { 82 | std::string::size_type pos = str.find_first_not_of(trim); 83 | if (pos == std::string::npos) { 84 | return ""; 85 | } 86 | std::string::size_type pos2 = str.find_last_not_of(trim); 87 | if (pos2 != std::string::npos) { 88 | return str.substr(pos, pos2 - pos + 1); 89 | } 90 | return str.substr(pos); 91 | } 92 | 93 | 94 | static inline std::string NumToString(int64_t num) { 95 | char buf[32]; 96 | snprintf(buf, sizeof(buf), "%ld", num); 97 | return std::string(buf); 98 | } 99 | 100 | static inline std::string NumToString(int num) { 101 | return NumToString(static_cast(num)); 102 | } 103 | 104 | static inline std::string NumToString(uint32_t num) { 105 | return NumToString(static_cast(num)); 106 | } 107 | 108 | static inline std::string NumToString(double num) { 109 | char buf[32]; 110 | snprintf(buf, sizeof(buf), "%.3f", num); 111 | return std::string(buf); 112 | } 113 | 114 | static inline std::string HumanReadableString(int64_t num) { 115 | static const int max_shift = 6; 116 | static const char* const prefix[max_shift + 1] = {"", " K", " M", " G", " T", " P", " E"}; 117 | int shift = 0; 118 | double v = num; 119 | while ((num>>=10) > 0 && shift < max_shift) { 120 | v /= 1024; 121 | shift++; 122 | } 123 | return NumToString(v) + prefix[shift]; 124 | } 125 | 126 | } 127 | } 128 | 129 | #endif // COMMON_STRING_UTIL_H_ 130 | 131 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 132 | -------------------------------------------------------------------------------- /include/thread.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #ifndef COMMON_THREAD_H_ 8 | #define COMMON_THREAD_H_ 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace baidu { 16 | namespace common { 17 | 18 | class Thread { 19 | public: 20 | Thread() { 21 | memset(&tid_, 0, sizeof(tid_)); 22 | } 23 | bool Start(boost::function thread_proc) { 24 | user_proc_ = thread_proc; 25 | int ret = pthread_create(&tid_, NULL, ProcWrapper, this); 26 | return (ret == 0); 27 | } 28 | typedef void* (Proc)(void*); 29 | bool Start(Proc proc, void* arg) { 30 | int ret = pthread_create(&tid_, NULL, proc, arg); 31 | return (ret == 0); 32 | } 33 | bool Join() { 34 | int ret = pthread_join(tid_, NULL); 35 | return (ret == 0); 36 | } 37 | private: 38 | Thread(const Thread&); 39 | void operator=(const Thread&); 40 | static void* ProcWrapper(void* arg) { 41 | reinterpret_cast(arg)->user_proc_(); 42 | return NULL; 43 | } 44 | private: 45 | boost::function user_proc_; 46 | pthread_t tid_; 47 | }; 48 | 49 | } 50 | } 51 | 52 | #endif // COMMON_THREAD_H_ 53 | 54 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 55 | -------------------------------------------------------------------------------- /include/thread_pool.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #ifndef COMMON_THREAD_POOL_H_ 8 | #define COMMON_THREAD_POOL_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "mutex.h" 17 | #include "timer.h" 18 | 19 | namespace baidu { 20 | namespace common { 21 | 22 | static const int kDebugCheckTime = 5000; 23 | 24 | // An unscalable thread pool implimention. 25 | class ThreadPool { 26 | public: 27 | ThreadPool(int thread_num = 10) 28 | : threads_num_(thread_num), 29 | pending_num_(0), 30 | work_cv_(&mutex_), 31 | stop_(false), 32 | last_task_id_(0), 33 | running_task_id_(0), 34 | schedule_cost_sum_(0), 35 | schedule_count_(0), 36 | task_cost_sum_(0), 37 | task_count_(0) { 38 | Start(); 39 | } 40 | ~ThreadPool() { 41 | Stop(false); 42 | } 43 | // Start a thread_num threads pool. 44 | bool Start() { 45 | MutexLock lock(&mutex_); 46 | if (tids_.size()) { 47 | return false; 48 | } 49 | stop_ = false; 50 | for (int i = 0; i < threads_num_; i++) { 51 | pthread_t tid; 52 | int ret = pthread_create(&tid, NULL, ThreadWrapper, this); 53 | if (ret) { 54 | abort(); 55 | } 56 | tids_.push_back(tid); 57 | } 58 | return true; 59 | } 60 | 61 | // Stop the thread pool. 62 | // Wait for all pending task to complete if wait is true. 63 | bool Stop(bool wait) { 64 | if (wait) { 65 | while (pending_num_ > 0) { 66 | usleep(10000); 67 | } 68 | } 69 | 70 | { 71 | MutexLock lock(&mutex_); 72 | stop_ = true; 73 | work_cv_.Broadcast(); 74 | } 75 | for (uint32_t i = 0; i < tids_.size(); i++) { 76 | pthread_join(tids_[i], NULL); 77 | } 78 | tids_.clear(); 79 | return true; 80 | } 81 | 82 | // Task definition. 83 | typedef boost::function Task; 84 | 85 | // Add a task to the thread pool. 86 | void AddTask(const Task& task) { 87 | MutexLock lock(&mutex_, "AddTask"); 88 | if (stop_) return; 89 | queue_.push_back(BGItem(0, timer::get_micros(), task)); 90 | ++pending_num_; 91 | work_cv_.Signal(); 92 | } 93 | void AddPriorityTask(const Task& task) { 94 | MutexLock lock(&mutex_); 95 | if (stop_) return; 96 | queue_.push_front(BGItem(0, timer::get_micros(), task)); 97 | ++pending_num_; 98 | work_cv_.Signal(); 99 | } 100 | int64_t DelayTask(int64_t delay, const Task& task) { 101 | MutexLock lock(&mutex_); 102 | if (stop_) return 0; 103 | int64_t now_time = timer::get_micros(); 104 | int64_t exe_time = now_time + delay * 1000; 105 | BGItem bg_item(++last_task_id_, exe_time, task); 106 | time_queue_.push(bg_item); 107 | latest_[bg_item.id] = bg_item; 108 | work_cv_.Signal(); 109 | return bg_item.id; 110 | } 111 | /// Cancel a delayed task 112 | /// if running, wait if non_block==false; return immediately if non_block==true 113 | bool CancelTask(int64_t task_id, bool non_block = false, bool* is_running = NULL) { 114 | if (task_id == 0) { 115 | if (is_running != NULL) { 116 | *is_running = false; 117 | } 118 | return false; 119 | } 120 | while (1) { 121 | { 122 | MutexLock lock(&mutex_); 123 | if (running_task_id_ != task_id) { 124 | BGMap::iterator it = latest_.find(task_id); 125 | if (it == latest_.end()) { 126 | if (is_running != NULL) { 127 | *is_running = false; 128 | } 129 | return false; 130 | } 131 | latest_.erase(it); 132 | return true; 133 | } else if (non_block) { 134 | if (is_running != NULL) { 135 | *is_running = true; 136 | } 137 | return false; 138 | } 139 | } 140 | timespec ts = {0, 100000}; 141 | nanosleep(&ts, &ts); 142 | } 143 | } 144 | int64_t PendingNum() const { 145 | return pending_num_; 146 | } 147 | 148 | // log format: 3 numbers seperated by " ", e.g. "15 24 32" 149 | // 1st: thread pool schedule average cost (ms) 150 | // 2nd: user task average cost (ms) 151 | // 3rd: total task count since last ProfilingLog called 152 | std::string ProfilingLog() { 153 | int64_t schedule_cost_sum; 154 | int64_t schedule_count; 155 | int64_t task_cost_sum; 156 | int64_t task_count; 157 | { 158 | MutexLock lock(&mutex_); 159 | schedule_cost_sum = schedule_cost_sum_; 160 | schedule_cost_sum_ = 0; 161 | schedule_count = schedule_count_; 162 | schedule_count_ = 0; 163 | task_cost_sum = task_cost_sum_; 164 | task_cost_sum_ = 0; 165 | task_count = task_count_; 166 | task_count_ = 0; 167 | } 168 | std::stringstream ss; 169 | ss << (schedule_count == 0 ? 0 : schedule_cost_sum / schedule_count / 1000) 170 | << " " << (task_count == 0 ? 0 : task_cost_sum / task_count / 1000) 171 | << " " << task_count; 172 | return ss.str(); 173 | } 174 | 175 | private: 176 | ThreadPool(const ThreadPool&); 177 | void operator=(const ThreadPool&); 178 | 179 | static void* ThreadWrapper(void* arg) { 180 | reinterpret_cast(arg)->ThreadProc(); 181 | return NULL; 182 | } 183 | void ThreadProc() { 184 | while (true) { 185 | Task task; 186 | MutexLock lock(&mutex_, "ThreadProc"); 187 | while (time_queue_.empty() && queue_.empty() && !stop_) { 188 | work_cv_.Wait("ThreadProcWait"); 189 | } 190 | if (stop_) { 191 | break; 192 | } 193 | // Timer task 194 | if (!time_queue_.empty()) { 195 | int64_t now_time = timer::get_micros(); 196 | BGItem bg_item = time_queue_.top(); 197 | int64_t wait_time = (bg_item.exe_time - now_time) / 1000; // in ms 198 | if (wait_time <= 0) { 199 | time_queue_.pop(); 200 | BGMap::iterator it = latest_.find(bg_item.id); 201 | if (it != latest_.end() && it->second.exe_time == bg_item.exe_time) { 202 | schedule_cost_sum_ += now_time - bg_item.exe_time; 203 | schedule_count_++; 204 | task = bg_item.task; 205 | latest_.erase(it); 206 | running_task_id_ = bg_item.id; 207 | mutex_.Unlock(); 208 | task(); 209 | task_cost_sum_ += timer::get_micros() - now_time; 210 | task_count_++; 211 | mutex_.Lock("ThreadProcRelock"); 212 | running_task_id_ = 0; 213 | } 214 | continue; 215 | } else if (queue_.empty() && !stop_) { 216 | work_cv_.TimeWait(wait_time, "ThreadProcTimeWait"); 217 | continue; 218 | } 219 | } 220 | // Normal task; 221 | if (!queue_.empty()) { 222 | task = queue_.front().task; 223 | int64_t exe_time = queue_.front().exe_time; 224 | queue_.pop_front(); 225 | --pending_num_; 226 | int64_t start_time = timer::get_micros(); 227 | schedule_cost_sum_ += start_time - exe_time; 228 | schedule_count_++; 229 | mutex_.Unlock(); 230 | task(); 231 | int64_t finish_time = timer::get_micros(); 232 | task_cost_sum_ += finish_time - start_time; 233 | task_count_++; 234 | mutex_.Lock("ThreadProcRelock2"); 235 | } 236 | } 237 | } 238 | 239 | private: 240 | struct BGItem { 241 | int64_t id; 242 | int64_t exe_time; 243 | Task task; 244 | bool operator<(const BGItem& item) const { 245 | if (exe_time != item.exe_time) { 246 | return exe_time > item.exe_time; 247 | } else { 248 | return id > item.id; 249 | } 250 | } 251 | 252 | BGItem() {} 253 | BGItem(int64_t id_t, int64_t exe_time_t, const Task& task_t) 254 | : id(id_t), exe_time(exe_time_t), task(task_t) {} 255 | }; 256 | typedef std::priority_queue BGQueue; 257 | typedef std::map BGMap; 258 | 259 | int32_t threads_num_; 260 | std::deque queue_; 261 | volatile int pending_num_; 262 | Mutex mutex_; 263 | CondVar work_cv_; 264 | bool stop_; 265 | std::vector tids_; 266 | 267 | BGQueue time_queue_; 268 | BGMap latest_; 269 | int64_t last_task_id_; 270 | int64_t running_task_id_; 271 | 272 | // for profiling 273 | int64_t schedule_cost_sum_; 274 | int64_t schedule_count_; 275 | int64_t task_cost_sum_; 276 | int64_t task_count_; 277 | }; 278 | 279 | } // namespace common 280 | 281 | using common::ThreadPool; 282 | 283 | } // namespace baidu 284 | 285 | #endif //COMMON_THREAD_POOL_H_ 286 | 287 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 288 | -------------------------------------------------------------------------------- /include/timer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #ifndef COMMON_TIMER_H_ 8 | #define COMMON_TIMER_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace baidu { 17 | namespace common { 18 | 19 | namespace timer { 20 | 21 | enum Precision { 22 | kDay, 23 | kMin, 24 | kUsec, 25 | }; 26 | 27 | static inline long get_micros() { 28 | struct timeval tv; 29 | gettimeofday(&tv, NULL); 30 | return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; 31 | } 32 | 33 | static inline int32_t now_time() { 34 | return static_cast(get_micros() / 1000000); 35 | } 36 | 37 | static inline int32_t now_time_str(char* buf, int32_t len, Precision p = kUsec) { 38 | struct timeval tv; 39 | gettimeofday(&tv, NULL); 40 | const time_t seconds = tv.tv_sec; 41 | struct tm t; 42 | localtime_r(&seconds, &t); 43 | int32_t ret = 0; 44 | if (p == kDay) { 45 | ret = snprintf(buf, len, "%02d/%02d", 46 | t.tm_mon + 1, 47 | t.tm_mday); 48 | } else if (p == kMin) { 49 | ret = snprintf(buf, len, "%02d/%02d %02d:%02d", 50 | t.tm_mon + 1, 51 | t.tm_mday, 52 | t.tm_hour, 53 | t.tm_min); 54 | } else { 55 | ret = snprintf(buf, len, "%02d/%02d %02d:%02d:%02d.%06d", 56 | t.tm_mon + 1, 57 | t.tm_mday, 58 | t.tm_hour, 59 | t.tm_min, 60 | t.tm_sec, 61 | static_cast(tv.tv_usec)); 62 | } 63 | return ret; 64 | } 65 | 66 | class AutoTimer { 67 | public: 68 | AutoTimer(double timeout_ms = -1, const char* msg1 = NULL, const char* msg2 = NULL) 69 | : timeout_(timeout_ms), 70 | msg1_(msg1), 71 | msg2_(msg2) { 72 | start_ = get_micros(); 73 | } 74 | int64_t TimeUsed() const { 75 | return get_micros() - start_; 76 | } 77 | ~AutoTimer() { 78 | if (timeout_ == -1) return; 79 | long end = get_micros(); 80 | if (end - start_ > timeout_ * 1000) { 81 | double t = (end - start_) / 1000.0; 82 | if (!msg2_) { 83 | fprintf(stderr, "[AutoTimer] %s use %.3f ms\n", 84 | msg1_, t); 85 | } else { 86 | fprintf(stderr, "[AutoTimer] %s %s use %.3f ms\n", 87 | msg1_, msg2_, t); 88 | } 89 | } 90 | } 91 | private: 92 | long start_; 93 | double timeout_; 94 | const char* msg1_; 95 | const char* msg2_; 96 | }; 97 | 98 | class TimeChecker { 99 | public: 100 | TimeChecker() { 101 | start_ = get_micros(); 102 | } 103 | void Check(int64_t timeout, const std::string& msg) { 104 | int64_t now = get_micros(); 105 | int64_t interval = now - start_; 106 | if (timeout == -1 || interval > timeout) { 107 | char buf[30]; 108 | now_time_str(buf, 30); 109 | fprintf(stderr, "[TimeChecker] %s %s use %ld us\n", buf, msg.c_str(), interval); 110 | } 111 | start_ = get_micros(); 112 | } 113 | void Reset() { 114 | start_ = get_micros(); 115 | } 116 | private: 117 | int64_t start_; 118 | }; 119 | 120 | } 121 | } 122 | } // namespace baidu 123 | 124 | #endif // COMMON_TIMER_H_ 125 | 126 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 127 | -------------------------------------------------------------------------------- /include/tprinter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: Xu Peilin (xupeilin@baidu.com) 6 | 7 | #ifndef BAIDU_COMMON_TPRINTER_H_ 8 | #define BAIDU_COMMON_TPRINTER_H_ 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | 16 | namespace baidu { 17 | namespace common { 18 | 19 | using std::string; 20 | class TPrinter { 21 | public: 22 | typedef std::vector Line; 23 | typedef std::vector Table; 24 | 25 | TPrinter(); 26 | explicit TPrinter(int cols); 27 | TPrinter(int cols, int width); 28 | ~TPrinter(); 29 | 30 | bool AddRow(const std::vector& cols); 31 | 32 | bool AddRow(int argc, ...); 33 | 34 | bool AddRow(const std::vector& cols); 35 | 36 | void Print(bool has_head = true); 37 | 38 | string ToString(bool has_head = true); 39 | 40 | void Reset(); 41 | 42 | void Reset(int cols); 43 | 44 | static string RemoveSubString(const string& input, const string& substr); 45 | 46 | private: 47 | const uint32_t kMaxColWidth; 48 | size_t _cols; 49 | std::vector _col_width; 50 | Table _table; 51 | }; 52 | 53 | } // namespace common 54 | } // namespace baidu 55 | #endif // BAIDU_COMMON_TPRINTER_H_ 56 | -------------------------------------------------------------------------------- /include/util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #ifndef COMMON_UTIL_H_ 8 | #define COMMON_UTIL_H_ 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | namespace baidu { 17 | namespace common { 18 | namespace util { 19 | 20 | static const uint32_t kMaxHostNameSize = 255; 21 | static inline std::string GetLocalHostName() { 22 | char str[kMaxHostNameSize + 1]; 23 | if (0 != gethostname(str, kMaxHostNameSize + 1)) { 24 | return ""; 25 | } 26 | std::string hostname(str); 27 | return hostname; 28 | } 29 | 30 | static const uint32_t MAX_PATH_LENGHT = 10240; 31 | static inline bool SplitPath(const std::string& path, 32 | std::vector* element, 33 | bool* isdir = NULL) { 34 | if (path.empty() || path[0] != '/' || path.size() > MAX_PATH_LENGHT) { 35 | return false; 36 | } 37 | element->clear(); 38 | size_t last_pos = 0; 39 | for (size_t i = 1; i <= path.size(); i++) { 40 | if (i == path.size() || path[i] == '/') { 41 | if (last_pos + 1 < i) { 42 | element->push_back(path.substr(last_pos + 1, i - last_pos - 1)); 43 | } 44 | last_pos = i; 45 | } 46 | } 47 | if (isdir) { 48 | *isdir = (path[path.size() - 1] == '/'); 49 | } 50 | return true; 51 | } 52 | 53 | static inline void EncodeBigEndian(char* buf, uint64_t value) { 54 | buf[0] = (value >> 56) & 0xff; 55 | buf[1] = (value >> 48) & 0xff; 56 | buf[2] = (value >> 40) & 0xff; 57 | buf[3] = (value >> 32) & 0xff; 58 | buf[4] = (value >> 24) & 0xff; 59 | buf[5] = (value >> 16) & 0xff; 60 | buf[6] = (value >> 8) & 0xff; 61 | buf[7] = value & 0xff; 62 | } 63 | 64 | static inline uint64_t DecodeBigEndian64(const char* buf) { 65 | return ((static_cast(static_cast(buf[0]))) << 56 66 | | (static_cast(static_cast(buf[1])) << 48) 67 | | (static_cast(static_cast(buf[2])) << 40) 68 | | (static_cast(static_cast(buf[3])) << 32) 69 | | (static_cast(static_cast(buf[4])) << 24) 70 | | (static_cast(static_cast(buf[5])) << 16) 71 | | (static_cast(static_cast(buf[6])) << 8) 72 | | (static_cast(static_cast(buf[7])))); 73 | } 74 | 75 | static inline void EncodeBigEndian(char* buf, uint32_t value) { 76 | buf[0] = (value >> 24) & 0xff; 77 | buf[1] = (value >> 16) & 0xff; 78 | buf[2] = (value >> 8) & 0xff; 79 | buf[3] = value & 0xff; 80 | } 81 | 82 | static inline uint32_t DecodeBigEndian32(const char* buf) { 83 | return ((static_cast(static_cast(buf[0])) << 24) 84 | | (static_cast(static_cast(buf[1])) << 16) 85 | | (static_cast(static_cast(buf[2])) << 8) 86 | | (static_cast(static_cast(buf[3])))); 87 | } 88 | 89 | } // namespace util 90 | } // namespace common 91 | } // namespace baidu 92 | 93 | #endif //COMMON_UTIL_H_ 94 | 95 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 96 | -------------------------------------------------------------------------------- /src/cache.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 6 | // Use of this source code is governed by a BSD-style license that can be 7 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 8 | 9 | #include "cache.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "mutex.h" 16 | #include "hash.h" 17 | 18 | namespace baidu { 19 | namespace common { 20 | 21 | Cache::~Cache() { 22 | } 23 | 24 | namespace { 25 | 26 | // LRU cache implementation 27 | 28 | // An entry is a variable length heap-allocated structure. Entries 29 | // are kept in a circular doubly linked list ordered by access time. 30 | struct LRUHandle { 31 | void* value; 32 | void (*deleter)(const Slice&, void* value); 33 | LRUHandle* next_hash; 34 | LRUHandle* next; 35 | LRUHandle* prev; 36 | size_t charge; // TODO(opt): Only allow uint32_t? 37 | size_t key_length; 38 | uint32_t refs; 39 | uint32_t hash; // Hash of key(); used for fast sharding and comparisons 40 | char key_data[1]; // Beginning of key 41 | 42 | Slice key() const { 43 | // For cheaper lookups, we allow a temporary Handle object 44 | // to store a pointer to a key in "value". 45 | if (next == this) { 46 | return *(reinterpret_cast(value)); 47 | } else { 48 | return Slice(key_data, key_length); 49 | } 50 | } 51 | }; 52 | 53 | // We provide our own simple hash table since it removes a whole bunch 54 | // of porting hacks and is also faster than some of the built-in hash 55 | // table implementations in some of the compiler/runtime combinations 56 | // we have tested. E.g., readrandom speeds up by ~5% over the g++ 57 | // 4.4.3's builtin hashtable. 58 | class HandleTable { 59 | public: 60 | HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); } 61 | ~HandleTable() { delete[] list_; } 62 | 63 | LRUHandle* Lookup(const Slice& key, uint32_t hash) { 64 | return *FindPointer(key, hash); 65 | } 66 | 67 | LRUHandle* Insert(LRUHandle* h) { 68 | LRUHandle** ptr = FindPointer(h->key(), h->hash); 69 | LRUHandle* old = *ptr; 70 | h->next_hash = (old == NULL ? NULL : old->next_hash); 71 | *ptr = h; 72 | if (old == NULL) { 73 | ++elems_; 74 | if (elems_ > length_) { 75 | // Since each cache entry is fairly large, we aim for a small 76 | // average linked list length (<= 1). 77 | Resize(); 78 | } 79 | } 80 | return old; 81 | } 82 | 83 | LRUHandle* Remove(const Slice& key, uint32_t hash) { 84 | LRUHandle** ptr = FindPointer(key, hash); 85 | LRUHandle* result = *ptr; 86 | if (result != NULL) { 87 | *ptr = result->next_hash; 88 | --elems_; 89 | } 90 | return result; 91 | } 92 | 93 | private: 94 | // The table consists of an array of buckets where each bucket is 95 | // a linked list of cache entries that hash into the bucket. 96 | uint32_t length_; 97 | uint32_t elems_; 98 | LRUHandle** list_; 99 | 100 | // Return a pointer to slot that points to a cache entry that 101 | // matches key/hash. If there is no such cache entry, return a 102 | // pointer to the trailing slot in the corresponding linked list. 103 | LRUHandle** FindPointer(const Slice& key, uint32_t hash) { 104 | LRUHandle** ptr = &list_[hash & (length_ - 1)]; 105 | while (*ptr != NULL && 106 | ((*ptr)->hash != hash || key != (*ptr)->key())) { 107 | ptr = &(*ptr)->next_hash; 108 | } 109 | return ptr; 110 | } 111 | 112 | void Resize() { 113 | uint32_t new_length = 4; 114 | while (new_length < elems_) { 115 | new_length *= 2; 116 | } 117 | LRUHandle** new_list = new LRUHandle*[new_length]; 118 | memset(new_list, 0, sizeof(new_list[0]) * new_length); 119 | uint32_t count = 0; 120 | for (uint32_t i = 0; i < length_; i++) { 121 | LRUHandle* h = list_[i]; 122 | while (h != NULL) { 123 | LRUHandle* next = h->next_hash; 124 | uint32_t hash = h->hash; 125 | LRUHandle** ptr = &new_list[hash & (new_length - 1)]; 126 | h->next_hash = *ptr; 127 | *ptr = h; 128 | h = next; 129 | count++; 130 | } 131 | } 132 | assert(elems_ == count); 133 | delete[] list_; 134 | list_ = new_list; 135 | length_ = new_length; 136 | } 137 | }; 138 | 139 | // A single shard of sharded cache. 140 | class LRUCache { 141 | public: 142 | LRUCache(); 143 | ~LRUCache(); 144 | 145 | // Separate from constructor so caller can easily make an array of LRUCache 146 | void SetCapacity(size_t capacity) { capacity_ = capacity; } 147 | 148 | // Like Cache methods, but with an extra "hash" parameter. 149 | Cache::Handle* Insert(const Slice& key, uint32_t hash, 150 | void* value, size_t charge, 151 | void (*deleter)(const Slice& key, void* value)); 152 | Cache::Handle* Lookup(const Slice& key, uint32_t hash); 153 | void Release(Cache::Handle* handle); 154 | void Erase(const Slice& key, uint32_t hash); 155 | 156 | private: 157 | void LRU_Remove(LRUHandle* e); 158 | void LRU_Append(LRUHandle* e); 159 | void Unref(LRUHandle* e); 160 | 161 | // Initialized before use. 162 | size_t capacity_; 163 | 164 | // mutex_ protects the following state. 165 | Mutex mutex_; 166 | size_t usage_; 167 | 168 | // Dummy head of LRU list. 169 | // lru.prev is newest entry, lru.next is oldest entry. 170 | LRUHandle lru_; 171 | 172 | HandleTable table_; 173 | }; 174 | 175 | LRUCache::LRUCache() 176 | : usage_(0) { 177 | // Make empty circular linked list 178 | lru_.next = &lru_; 179 | lru_.prev = &lru_; 180 | } 181 | 182 | LRUCache::~LRUCache() { 183 | for (LRUHandle* e = lru_.next; e != &lru_; ) { 184 | LRUHandle* next = e->next; 185 | assert(e->refs == 1); // Error if caller has an unreleased handle 186 | Unref(e); 187 | e = next; 188 | } 189 | } 190 | 191 | void LRUCache::Unref(LRUHandle* e) { 192 | assert(e->refs > 0); 193 | e->refs--; 194 | if (e->refs <= 0) { 195 | usage_ -= e->charge; 196 | (*e->deleter)(e->key(), e->value); 197 | free(e); 198 | } 199 | } 200 | 201 | void LRUCache::LRU_Remove(LRUHandle* e) { 202 | e->next->prev = e->prev; 203 | e->prev->next = e->next; 204 | } 205 | 206 | void LRUCache::LRU_Append(LRUHandle* e) { 207 | // Make "e" newest entry by inserting just before lru_ 208 | e->next = &lru_; 209 | e->prev = lru_.prev; 210 | e->prev->next = e; 211 | e->next->prev = e; 212 | } 213 | 214 | Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) { 215 | MutexLock l(&mutex_); 216 | LRUHandle* e = table_.Lookup(key, hash); 217 | if (e != NULL) { 218 | e->refs++; 219 | LRU_Remove(e); 220 | LRU_Append(e); 221 | } 222 | return reinterpret_cast(e); 223 | } 224 | 225 | void LRUCache::Release(Cache::Handle* handle) { 226 | MutexLock l(&mutex_); 227 | Unref(reinterpret_cast(handle)); 228 | } 229 | 230 | Cache::Handle* LRUCache::Insert( 231 | const Slice& key, uint32_t hash, void* value, size_t charge, 232 | void (*deleter)(const Slice& key, void* value)) { 233 | MutexLock l(&mutex_); 234 | 235 | LRUHandle* e = reinterpret_cast( 236 | malloc(sizeof(LRUHandle)-1 + key.size())); 237 | e->value = value; 238 | e->deleter = deleter; 239 | e->charge = charge; 240 | e->key_length = key.size(); 241 | e->hash = hash; 242 | e->refs = 2; // One from LRUCache, one for the returned handle 243 | memcpy(e->key_data, key.data(), key.size()); 244 | LRU_Append(e); 245 | usage_ += charge; 246 | 247 | LRUHandle* old = table_.Insert(e); 248 | if (old != NULL) { 249 | LRU_Remove(old); 250 | Unref(old); 251 | } 252 | 253 | while (usage_ > capacity_ && lru_.next != &lru_) { 254 | LRUHandle* old = lru_.next; 255 | LRU_Remove(old); 256 | table_.Remove(old->key(), old->hash); 257 | Unref(old); 258 | } 259 | 260 | return reinterpret_cast(e); 261 | } 262 | 263 | void LRUCache::Erase(const Slice& key, uint32_t hash) { 264 | MutexLock l(&mutex_); 265 | LRUHandle* e = table_.Remove(key, hash); 266 | if (e != NULL) { 267 | LRU_Remove(e); 268 | Unref(e); 269 | } 270 | } 271 | 272 | static const int kNumShardBits = 4; 273 | static const int kNumShards = 1 << kNumShardBits; 274 | 275 | class ShardedLRUCache : public Cache { 276 | private: 277 | LRUCache shard_[kNumShards]; 278 | Mutex id_mutex_; 279 | uint64_t last_id_; 280 | 281 | static inline uint32_t HashSlice(const Slice& s) { 282 | return Hash(s.data(), s.size(), 0); 283 | } 284 | 285 | static uint32_t Shard(uint32_t hash) { 286 | return hash >> (32 - kNumShardBits); 287 | } 288 | 289 | public: 290 | explicit ShardedLRUCache(size_t capacity) 291 | : last_id_(0) { 292 | const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards; 293 | for (int s = 0; s < kNumShards; s++) { 294 | shard_[s].SetCapacity(per_shard); 295 | } 296 | } 297 | virtual ~ShardedLRUCache() { } 298 | virtual Handle* Insert(const Slice& key, void* value, size_t charge, 299 | void (*deleter)(const Slice& key, void* value)) { 300 | const uint32_t hash = HashSlice(key); 301 | return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter); 302 | } 303 | virtual Handle* Lookup(const Slice& key) { 304 | const uint32_t hash = HashSlice(key); 305 | return shard_[Shard(hash)].Lookup(key, hash); 306 | } 307 | virtual void Release(Handle* handle) { 308 | LRUHandle* h = reinterpret_cast(handle); 309 | shard_[Shard(h->hash)].Release(handle); 310 | } 311 | virtual void Erase(const Slice& key) { 312 | const uint32_t hash = HashSlice(key); 313 | shard_[Shard(hash)].Erase(key, hash); 314 | } 315 | virtual void* Value(Handle* handle) { 316 | return reinterpret_cast(handle)->value; 317 | } 318 | virtual uint64_t NewId() { 319 | MutexLock l(&id_mutex_); 320 | return ++(last_id_); 321 | } 322 | }; 323 | 324 | } // end anonymous namespace 325 | 326 | Cache* NewLRUCache(size_t capacity) { 327 | return new ShardedLRUCache(capacity); 328 | } 329 | 330 | } // namespace common 331 | } 332 | -------------------------------------------------------------------------------- /src/hash.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 6 | // Use of this source code is governed by a BSD-style license that can be 7 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 8 | 9 | #include "hash.h" 10 | 11 | #include 12 | 13 | // The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through 14 | // between switch labels. The real definition should be provided externally. 15 | // This one is a fallback version for unsupported compilers. 16 | #ifndef FALLTHROUGH_INTENDED 17 | #define FALLTHROUGH_INTENDED do { } while (0) 18 | #endif 19 | 20 | namespace baidu { 21 | namespace common { 22 | 23 | uint32_t Hash(const char* data, size_t n, uint32_t seed) { 24 | // Similar to murmur hash 25 | const uint32_t m = 0xc6a4a793; 26 | const uint32_t r = 24; 27 | const char* limit = data + n; 28 | uint32_t h = seed ^ (n * m); 29 | 30 | // Pick up four bytes at a time 31 | while (data + 4 <= limit) { 32 | uint32_t w = *reinterpret_cast(data); 33 | data += 4; 34 | h += w; 35 | h *= m; 36 | h ^= (h >> 16); 37 | } 38 | 39 | // Pick up remaining bytes 40 | switch (limit - data) { 41 | case 3: 42 | h += static_cast(data[2]) << 16; 43 | FALLTHROUGH_INTENDED; 44 | case 2: 45 | h += static_cast(data[1]) << 8; 46 | FALLTHROUGH_INTENDED; 47 | case 1: 48 | h += static_cast(data[0]); 49 | h *= m; 50 | h ^= (h >> r); 51 | break; 52 | } 53 | return h; 54 | } 55 | 56 | } // namespace common 57 | } // namespace baidu 58 | -------------------------------------------------------------------------------- /src/logging.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include "logging.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "mutex.h" 26 | #include "thread.h" 27 | #include "timer.h" 28 | 29 | namespace baidu { 30 | namespace common { 31 | 32 | int g_log_level = INFO; 33 | int64_t g_log_size = 0; 34 | int32_t g_log_count = 0; 35 | FILE* g_log_file = stdout; 36 | std::string g_log_file_name; 37 | FILE* g_warning_file = NULL; 38 | int64_t g_total_size_limit = 0; 39 | 40 | std::set g_log_set; 41 | int64_t current_total_size = 0; 42 | 43 | bool GetNewLog(bool append) { 44 | char buf[30]; 45 | struct timeval tv; 46 | gettimeofday(&tv, NULL); 47 | const time_t seconds = tv.tv_sec; 48 | struct tm t; 49 | localtime_r(&seconds, &t); 50 | snprintf(buf, 30, 51 | "%02d-%02d.%02d:%02d:%02d.%06d", 52 | t.tm_mon + 1, 53 | t.tm_mday, 54 | t.tm_hour, 55 | t.tm_min, 56 | t.tm_sec, 57 | static_cast(tv.tv_usec)); 58 | std::string full_path(g_log_file_name + "."); 59 | full_path.append(buf); 60 | size_t idx = full_path.rfind('/'); 61 | if (idx == std::string::npos) { 62 | idx = 0; 63 | } else { 64 | idx += 1; 65 | } 66 | const char* mode = append ? "ab" : "wb"; 67 | FILE* fp = fopen(full_path.c_str(), mode); 68 | if (fp == NULL) { 69 | return false; 70 | } 71 | if (g_log_file != stdout) { 72 | fclose(g_log_file); 73 | } 74 | g_log_file = fp; 75 | remove(g_log_file_name.c_str()); 76 | symlink(full_path.substr(idx).c_str(), g_log_file_name.c_str()); 77 | g_log_set.insert(full_path); 78 | while ((g_log_count && static_cast(g_log_set.size()) > g_log_count) 79 | || (g_total_size_limit && current_total_size > g_total_size_limit)) { 80 | std::set::iterator it = g_log_set.begin(); 81 | if (it != g_log_set.end()) { 82 | struct stat sta; 83 | if (-1 == lstat(it->c_str(), &sta)) { 84 | return false; 85 | } 86 | remove(it->c_str()); 87 | current_total_size -= sta.st_size; 88 | g_log_set.erase(it++); 89 | } else { 90 | break; 91 | } 92 | } 93 | return true; 94 | } 95 | 96 | void SetLogLevel(int level) { 97 | g_log_level = level; 98 | } 99 | 100 | class AsyncLogger { 101 | public: 102 | AsyncLogger() 103 | : jobs_(&mu_), done_(&mu_), stopped_(false), size_(0) { 104 | buffer_queue_ = new std::queue >; 105 | bg_queue_ = new std::queue >; 106 | thread_.Start(boost::bind(&AsyncLogger::AsyncWriter, this)); 107 | } 108 | ~AsyncLogger() { 109 | stopped_ = true; 110 | { 111 | MutexLock lock(&mu_); 112 | jobs_.Signal(); 113 | } 114 | thread_.Join(); 115 | delete buffer_queue_; 116 | delete bg_queue_; 117 | // close fd 118 | } 119 | void WriteLog(int log_level, const char* buffer, int32_t len) { 120 | std::string* log_str = new std::string(buffer, len); 121 | MutexLock lock(&mu_); 122 | buffer_queue_->push(make_pair(log_level, log_str)); 123 | jobs_.Signal(); 124 | } 125 | void AsyncWriter() { 126 | int64_t loglen = 0; 127 | int64_t wflen = 0; 128 | while (1) { 129 | while (!bg_queue_->empty()) { 130 | int log_level = bg_queue_->front().first; 131 | std::string* str = bg_queue_->front().second; 132 | bg_queue_->pop(); 133 | if (g_log_file != stdout && g_log_size && str && 134 | static_cast(size_ + str->length()) > g_log_size) { 135 | current_total_size += static_cast(size_ + str->length()); 136 | GetNewLog(false); 137 | size_ = 0; 138 | } 139 | if (str && !str->empty()) { 140 | size_t lret = fwrite(str->data(), 1, str->size(), g_log_file); 141 | loglen += lret; 142 | if (g_warning_file && log_level >= 8) { 143 | size_t wret = fwrite(str->data(), 1, str->size(), g_warning_file); 144 | wflen += wret; 145 | } 146 | if (g_log_size) size_ += lret; 147 | } 148 | delete str; 149 | } 150 | MutexLock lock(&mu_); 151 | if (!buffer_queue_->empty()) { 152 | std::swap(buffer_queue_, bg_queue_); 153 | continue; 154 | } 155 | if (loglen) fflush(g_log_file); 156 | if (wflen) fflush(g_warning_file); 157 | done_.Broadcast(); 158 | if (stopped_) { 159 | break; 160 | } 161 | jobs_.Wait(); 162 | loglen = 0; 163 | wflen = 0; 164 | } 165 | } 166 | void Flush() { 167 | MutexLock lock(&mu_); 168 | buffer_queue_->push(std::make_pair(0, reinterpret_cast(NULL))); 169 | jobs_.Signal(); 170 | done_.Wait(); 171 | } 172 | private: 173 | Mutex mu_; 174 | CondVar jobs_; 175 | CondVar done_; 176 | bool stopped_; 177 | int64_t size_; 178 | Thread thread_; 179 | std::queue > * buffer_queue_; 180 | std::queue > * bg_queue_; 181 | }; 182 | 183 | AsyncLogger g_logger; 184 | 185 | bool SetWarningFile(const char* path, bool append) { 186 | const char* mode = append ? "ab" : "wb"; 187 | FILE* fp = fopen(path, mode); 188 | if (fp == NULL) { 189 | return false; 190 | } 191 | if (g_warning_file) { 192 | fclose(g_warning_file); 193 | } 194 | g_warning_file = fp; 195 | return true; 196 | } 197 | 198 | bool RecoverHistory(const char* path) { 199 | std::string log_path(path); 200 | size_t idx = log_path.rfind('/'); 201 | std::string dir = "./"; 202 | std::string log(path); 203 | if (idx != std::string::npos) { 204 | dir = log_path.substr(0, idx + 1); 205 | log = log_path.substr(idx + 1); 206 | } 207 | struct dirent *entry = NULL; 208 | DIR *dir_ptr = opendir(dir.c_str()); 209 | if (dir_ptr == NULL) { 210 | return false; 211 | } 212 | std::vector loglist; 213 | while ((entry = readdir(dir_ptr)) != NULL) { 214 | if (std::string(entry->d_name).find(log) != std::string::npos) { 215 | std::string file_name = dir + std::string(entry->d_name); 216 | struct stat sta; 217 | if (-1 == lstat(file_name.c_str(), &sta)) { 218 | return false; 219 | } 220 | if (S_ISREG(sta.st_mode)) { 221 | loglist.push_back(dir + std::string(entry->d_name)); 222 | current_total_size += sta.st_size; 223 | } 224 | } 225 | } 226 | closedir(dir_ptr); 227 | std::sort(loglist.begin(), loglist.end()); 228 | for (std::vector::iterator it = loglist.begin(); it != loglist.end(); 229 | ++it) { 230 | g_log_set.insert(*it); 231 | } 232 | while ( (g_log_count && static_cast(g_log_set.size()) > g_log_count) 233 | || (g_total_size_limit && current_total_size > g_total_size_limit)) { 234 | std::set::iterator it = g_log_set.begin(); 235 | if (it != g_log_set.end()) { 236 | struct stat sta; 237 | if (-1 == lstat(it->c_str(), &sta)) { 238 | return false; 239 | } 240 | remove(it->c_str()); 241 | current_total_size -= sta.st_size; 242 | g_log_set.erase(it++); 243 | } 244 | } 245 | return true; 246 | } 247 | 248 | bool SetLogFile(const char* path, bool append) { 249 | g_log_file_name.assign(path); 250 | return GetNewLog(append); 251 | } 252 | 253 | bool SetLogSize(int size) { 254 | if (size < 0) { 255 | return false; 256 | } 257 | g_log_size = static_cast(size) << 20; 258 | return true; 259 | } 260 | 261 | bool SetLogCount(int count) { 262 | if (count < 0 || g_total_size_limit != 0) { 263 | return false; 264 | } 265 | g_log_count = count; 266 | if (!RecoverHistory(g_log_file_name.c_str())) { 267 | return false; 268 | } 269 | return true; 270 | } 271 | 272 | bool SetLogSizeLimit(int size) { 273 | if (size < 0 || g_log_count != 0) { 274 | return false; 275 | } 276 | g_total_size_limit = static_cast(size) << 20; 277 | if (!RecoverHistory(g_log_file_name.c_str())) { 278 | return false; 279 | } 280 | return true; 281 | } 282 | 283 | void Logv(int log_level, const char* format, va_list ap) { 284 | static __thread uint64_t thread_id = 0; 285 | static __thread char tid_str[32]; 286 | static __thread int tid_str_len = 0; 287 | if (thread_id == 0) { 288 | thread_id = syscall(__NR_gettid); 289 | tid_str_len = snprintf(tid_str, sizeof(tid_str), " %5d ", static_cast(thread_id)); 290 | } 291 | 292 | static const char level_char[] = { 293 | 'V', 'D', 'I', 'W', 'E', 'F' 294 | }; 295 | char cur_level = level_char[0]; 296 | if (log_level < DEBUG) { 297 | cur_level = level_char[0]; 298 | } else if (log_level < INFO) { 299 | cur_level = level_char[1]; 300 | } else if (log_level < WARNING) { 301 | cur_level = level_char[2]; 302 | } else if (log_level < ERROR) { 303 | cur_level = level_char[3]; 304 | } else if (log_level < FATAL) { 305 | cur_level = level_char[4]; 306 | } else { 307 | cur_level = level_char[5]; 308 | } 309 | 310 | // We try twice: the first time with a fixed-size stack allocated buffer, 311 | // and the second time with a much larger dynamically allocated buffer. 312 | char buffer[500]; 313 | for (int iter = 0; iter < 2; iter++) { 314 | char* base; 315 | int bufsize; 316 | if (iter == 0) { 317 | bufsize = sizeof(buffer); 318 | base = buffer; 319 | } else { 320 | bufsize = 30000; 321 | base = new char[bufsize]; 322 | } 323 | char* p = base; 324 | char* limit = base + bufsize; 325 | 326 | *p++ = cur_level; 327 | *p++ = ' '; 328 | int32_t rlen = timer::now_time_str(p, limit - p); 329 | p += rlen; 330 | memcpy(p, tid_str, tid_str_len); 331 | p += tid_str_len; 332 | 333 | // Print the message 334 | if (p < limit) { 335 | va_list backup_ap; 336 | va_copy(backup_ap, ap); 337 | p += vsnprintf(p, limit - p, format, backup_ap); 338 | va_end(backup_ap); 339 | } 340 | 341 | // Truncate to available space if necessary 342 | if (p >= limit) { 343 | if (iter == 0) { 344 | continue; // Try again with larger buffer 345 | } else { 346 | p = limit - 1; 347 | } 348 | } 349 | 350 | // Add newline if necessary 351 | if (p == base || p[-1] != '\n') { 352 | *p++ = '\n'; 353 | } 354 | 355 | assert(p <= limit); 356 | //fwrite(base, 1, p - base, g_log_file); 357 | //fflush(g_log_file); 358 | //if (g_warning_file && log_level >= 8) { 359 | // fwrite(base, 1, p - base, g_warning_file); 360 | // fflush(g_warning_file); 361 | //} 362 | g_logger.WriteLog(log_level, base, p - base); 363 | if (log_level >= ERROR) { 364 | g_logger.Flush(); 365 | } 366 | if (base != buffer) { 367 | delete[] base; 368 | } 369 | break; 370 | } 371 | } 372 | 373 | void Log(int level, const char* fmt, ...) { 374 | va_list ap; 375 | va_start(ap, fmt); 376 | 377 | if (level >= g_log_level) { 378 | Logv(level, fmt, ap); 379 | } 380 | va_end(ap); 381 | if (level == FATAL) { 382 | abort(); 383 | } 384 | } 385 | 386 | LogStream::LogStream(int level) : level_(level) {} 387 | 388 | LogStream::~LogStream() { 389 | Log(level_, "%s", oss_.str().c_str()); 390 | } 391 | 392 | } // namespace common 393 | } // namespace baidu 394 | 395 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 396 | -------------------------------------------------------------------------------- /src/tprinter.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: Xu Peilin (xupeilin@baidu.com) 6 | 7 | #include "tprinter.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "string_util.h" 15 | #include 16 | 17 | namespace baidu { 18 | namespace common { 19 | 20 | TPrinter::TPrinter() : 21 | kMaxColWidth(50), 22 | _cols(0) { 23 | } 24 | 25 | TPrinter::TPrinter(int cols) : 26 | kMaxColWidth(50), 27 | _cols(cols) { 28 | if (cols > 0) { 29 | _col_width.resize(cols, 0); 30 | } 31 | } 32 | 33 | 34 | TPrinter::TPrinter(int cols, int width) : 35 | kMaxColWidth(width), 36 | _cols(cols) { 37 | assert(width >= 0); 38 | if (cols > 0) { 39 | _col_width.resize(cols, 0); 40 | } 41 | } 42 | 43 | TPrinter::~TPrinter() {} 44 | 45 | bool TPrinter::AddRow(const std::vector& cols) { 46 | if (cols.size() != _cols) { 47 | std::cerr << "arg num error: " << cols.size() << " vs " << _cols << std::endl; 48 | return false; 49 | } 50 | Line line; 51 | for (size_t i = 0; i < cols.size(); ++i) { 52 | string item = cols[i]; 53 | if (item.size() > kMaxColWidth) { 54 | item = item.substr(0, kMaxColWidth); 55 | } 56 | if (item.size() > static_cast(_col_width[i])) { 57 | _col_width[i] = item.size(); 58 | } 59 | if (item.size() == 0) { 60 | item = "-"; 61 | } 62 | line.push_back(item); 63 | } 64 | _table.push_back(line); 65 | return true; 66 | } 67 | 68 | bool TPrinter::AddRow(int argc, ...) { 69 | if (static_cast(argc) != _cols) { 70 | std::cerr << "arg num error: " << argc << " vs " << _cols << std::endl; 71 | return false; 72 | } 73 | std::vector v; 74 | va_list args; 75 | va_start(args, argc); 76 | for (int i = 0; i < argc; ++i) { 77 | string item = va_arg(args, char*); 78 | v.push_back(item); 79 | } 80 | va_end(args); 81 | return AddRow(v); 82 | } 83 | 84 | bool TPrinter::AddRow(const std::vector& cols) { 85 | if (cols.size() != _cols) { 86 | std::cerr << "arg num error: " << cols.size() << " vs " << _cols << std::endl; 87 | return false; 88 | } 89 | std::vector v; 90 | for (size_t i = 0; i < cols.size(); ++i) { 91 | v.push_back(common::NumToString(cols[i])); 92 | } 93 | return AddRow(v); 94 | } 95 | 96 | void TPrinter::Print(bool has_head) { 97 | if (_table.size() < 1) { 98 | return; 99 | } 100 | int line_len = 0; 101 | for (size_t i = 0; i < _cols; ++i) { 102 | line_len += 2 + _col_width[i]; 103 | std::cout << " " << std::setfill(' ') 104 | << std::setw(_col_width[i]) 105 | << std::setiosflags(std::ios::left) 106 | << _table[0][i]; 107 | } 108 | std::cout << std::endl; 109 | if (has_head) { 110 | for (int i = 0; i < line_len + 2; ++i) { 111 | std::cout << "-"; 112 | } 113 | std::cout << std::endl; 114 | } 115 | 116 | for (size_t i = 1; i < _table.size(); ++i) { 117 | for (size_t j = 0; j < _cols; ++j) { 118 | std::cout << " " << std::setfill(' ') 119 | << std::setw(_col_width[j]) 120 | << std::setiosflags(std::ios::left) 121 | << _table[i][j]; 122 | } 123 | std::cout << std::endl; 124 | } 125 | } 126 | 127 | string TPrinter::ToString(bool has_head) { 128 | std::ostringstream ostr; 129 | if (_table.size() < 1) { 130 | return ""; 131 | } 132 | int line_len = 0; 133 | for (size_t i = 0; i < _cols; ++i) { 134 | line_len += 2 + _col_width[i]; 135 | ostr << " " << std::setfill(' ') 136 | << std::setw(_col_width[i]) 137 | << std::setiosflags(std::ios::left) 138 | << _table[0][i]; 139 | } 140 | ostr << std::endl; 141 | if (has_head) { 142 | for (int i = 0; i < line_len + 2; ++i) { 143 | ostr << "-"; 144 | } 145 | ostr << std::endl; 146 | } 147 | 148 | for (size_t i = 1; i < _table.size(); ++i) { 149 | for (size_t j = 0; j < _cols; ++j) { 150 | ostr << " " << std::setfill(' ') 151 | << std::setw(_col_width[j]) 152 | << std::setiosflags(std::ios::left) 153 | << _table[i][j]; 154 | } 155 | ostr << std::endl; 156 | } 157 | return ostr.str(); 158 | } 159 | 160 | void TPrinter::Reset() { 161 | std::vector tmp(_cols, 0); 162 | _col_width.swap(tmp); 163 | _table.clear(); 164 | } 165 | 166 | void TPrinter::Reset(int cols) { 167 | _cols = cols; 168 | Reset(); 169 | } 170 | 171 | string TPrinter::RemoveSubString(const string& input, const string& substr) { 172 | string ret; 173 | string::size_type p = 0; 174 | string tmp = input; 175 | while (1) { 176 | tmp = tmp.substr(p); 177 | p = tmp.find(substr); 178 | ret.append(tmp.substr(0, p)); 179 | if (p == string::npos) { 180 | break; 181 | } 182 | p += substr.size(); 183 | } 184 | 185 | return ret; 186 | } 187 | 188 | } // namespace common 189 | } 190 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | INCLUDEPATH=-I../include/ -Igtest/include -I../../../thirdparty/boost_1_57_0/ 6 | CXXFLAGS=-g -O2 7 | #TCMALLOC=-L../../tera/thirdparty/lib -ltcmalloc_minimal 8 | LDFLAGS=$(TCMALLOC) -L../ -Lgtest/lib -lgtest -lcommon -lpthread -lrt 9 | 10 | TARGETS= test_logging test_thread_pool perf_test_thread_pool perf_test_counter test_mutex \ 11 | test_util perf_test_mutex perf_test_condvar perf_test_framework 12 | all: $(TARGETS) 13 | 14 | test_logging: test_logging.cc ../include/logging.h ../src/logging.cc 15 | $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $< -o $@ $(LDFLAGS) 16 | test_thread_pool: test_thread_pool.cc 17 | $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $< -o $@ $(LDFLAGS) 18 | 19 | test_thread: test_thread.cc 20 | $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $< -o $@ $(LDFLAGS) 21 | 22 | perf_test_counter: perf_test_counter.cc 23 | $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $< -o $@ $(LDFLAGS) 24 | 25 | test_mutex: test_mutex.cc 26 | $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $< -o $@ $(LDFLAGS) 27 | 28 | test_util: test_util.cc ../include/util.h 29 | $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $< -o $@ $(LDFLAGS) 30 | 31 | perf_test_thread_pool: perf_test_thread_pool.cc ../include/thread_pool.h 32 | $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $< -o $@ $(LDFLAGS) 33 | 34 | perf_test_mutex: perf_test_mutex.cc 35 | $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $< -o $@ $(LDFLAGS) 36 | 37 | perf_test_condvar: perf_test_condvar.cc 38 | $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $< -o $@ $(LDFLAGS) 39 | 40 | perf_test_framework: perf_test_framework.cc ../include/mutex.h 41 | $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $< -o $@ $(LDFLAGS) 42 | 43 | 44 | clean: 45 | rm -rf $(TARGETS) 46 | -------------------------------------------------------------------------------- /test/build_test.sh: -------------------------------------------------------------------------------- 1 | 2 | # gtest 3 | # wget --no-check-certificate https://googletest.googlecode.com/files/gtest-1.7.0.zip 4 | git clone --depth=1 https://github.com/xupeilin/gtest_archive 5 | mv gtest_archive/gtest-1.7.0.zip . 6 | unzip gtest-1.7.0.zip 7 | 8 | work_dir=`pwd` 9 | mkdir -p gtest/lib 10 | mkdir -p gtest/include 11 | 12 | cd gtest-1.7.0 13 | ./configure --prefix=$work_dir/gtest --disable-shared --with-pic 14 | make 15 | cp -a lib/.libs/* $work_dir/gtest/lib 16 | cp -a include/gtest $work_dir/gtest/include 17 | cd - 18 | 19 | rm -rf gtest_archive gtest-1.7.0.zip gtest-1.7.0 20 | 21 | make 22 | -------------------------------------------------------------------------------- /test/perf_test_condvar.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | int* ring __attribute__((aligned(64))); 16 | int r_len __attribute__((aligned(64))) = 102400; 17 | volatile long r_s __attribute__((aligned(64))) = 0; 18 | volatile long r_e __attribute__((aligned(64))) = 0; 19 | 20 | 21 | namespace baidu { 22 | namespace common { 23 | 24 | Mutex mu __attribute__((aligned(64))); 25 | int g_xx1 __attribute__((aligned(64))); 26 | CondVar p(&mu); 27 | int g_xx2 __attribute__((aligned(64))); 28 | CondVar c(&mu); 29 | Counter items __attribute__((aligned(64))); 30 | 31 | std::queue queue __attribute__((aligned(64))); 32 | 33 | //boost::lockfree::queue > queue(1024000); 34 | Counter counter __attribute__((aligned(64))); 35 | 36 | void Consumer() { 37 | //sleep(5); 38 | while(1) { 39 | /*while(items.Get() == 0) { 40 | MutexLock lock(&mu); 41 | p.Wait(); 42 | }*/ 43 | //items.Dec(); 44 | //int v; 45 | //queue.pop(); 46 | //ring[atomic_add64(&r_s, 1)%r_len] = -1; 47 | counter.Inc(); 48 | c.Signal(); 49 | } 50 | } 51 | 52 | void Producer() { 53 | while (1) { 54 | while (items.Get() > 100000) { 55 | MutexLock lock(&mu); 56 | c.Wait(); 57 | } 58 | items.Inc(); 59 | //queue.push(1); 60 | //ring[atomic_add64(&r_e, 1)%r_len] = 1; 61 | //p.Signal(); 62 | } 63 | } 64 | 65 | int Run(int tnum) { 66 | Thread* t1 = new Thread[tnum]; 67 | Thread* t2 = new Thread[tnum]; 68 | printf("Thread num: %d\n", tnum); 69 | for (int i = 0; i < tnum; i++) t1[i].Start(Consumer); 70 | for (int i = 0; i < tnum; i++) t2[i].Start(Producer); 71 | while (1) { 72 | sleep(1); 73 | fprintf(stderr, "%ld %ld\n", counter.Clear(), items.Get()); 74 | } 75 | return 0; 76 | } 77 | } 78 | } 79 | 80 | int main(int argc, char* argv[]) { 81 | int tnum = 1; 82 | if (argc > 1) { 83 | tnum = atoi(argv[1]); 84 | } 85 | ring = new int[r_len]; 86 | return baidu::common::Run(tnum); 87 | } 88 | 89 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 90 | -------------------------------------------------------------------------------- /test/perf_test_counter.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include 8 | #include 9 | 10 | 11 | const int kThreadNum = 8; 12 | 13 | namespace baidu { 14 | namespace common { 15 | 16 | 17 | 18 | void* AddProc(void* arg) { 19 | Counter* counter = reinterpret_cast(arg); 20 | while (1) { 21 | counter->Inc(); 22 | } 23 | } 24 | 25 | void RunPerfTest() { 26 | Counter counter; 27 | Thread* threads = new Thread[kThreadNum]; 28 | for (int i = 0; i < kThreadNum; i++) { 29 | threads[i].Start(AddProc, &counter); 30 | } 31 | 32 | long s_time = timer::get_micros(); 33 | while (1) { 34 | usleep(1000000); 35 | long n_time = timer::get_micros(); 36 | long count = counter.Clear(); 37 | printf("%ld\n", count * 1000000 / (n_time - s_time)); 38 | s_time = n_time; 39 | } 40 | } 41 | } 42 | } 43 | 44 | int main(int argc, char* argv[]) { 45 | baidu::common::RunPerfTest(); 46 | return 0; 47 | } 48 | 49 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 50 | -------------------------------------------------------------------------------- /test/perf_test_framework.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace baidu { 17 | namespace common { 18 | 19 | Counter counter; 20 | 21 | void Atomic() { 22 | while (1) { 23 | counter.Inc(); 24 | } 25 | } 26 | 27 | void TestGettime() { 28 | while (1) { 29 | struct timeval tv; 30 | gettimeofday(&tv, NULL); 31 | counter.Inc(); 32 | } 33 | } 34 | 35 | void TestMutex() { 36 | Mutex mu; 37 | while (1) { 38 | { 39 | mu.Lock(); 40 | mu.Unlock(); 41 | } 42 | counter.Inc(); 43 | } 44 | } 45 | 46 | void TestMutexLock() { 47 | Mutex mu; 48 | while (1) { 49 | { 50 | MutexLock lock(&mu); 51 | } 52 | counter.Inc(); 53 | } 54 | } 55 | 56 | void TestRawMutex() { 57 | pthread_mutex_t m; 58 | pthread_mutex_init(&m, NULL); 59 | while (1) { 60 | { 61 | pthread_mutex_lock(&m); 62 | pthread_mutex_unlock(&m); 63 | } 64 | counter.Inc(); 65 | } 66 | pthread_mutex_destroy(&m); 67 | } 68 | 69 | int Run(int argc, char* argv[]) { 70 | if (argc < 3) { 71 | printf("%s function thread_num\n", argv[0]); 72 | return 0; 73 | } 74 | void (*task)() = Atomic; 75 | if (strcmp(argv[1], "mutex") == 0) { 76 | printf("Perf test mutex\n"); 77 | task = TestMutex; 78 | } else if (strcmp(argv[1], "rawmutex") == 0) { 79 | printf("Perf test raw mutex\n"); 80 | task = TestRawMutex; 81 | } else if (strcmp(argv[1], "mutexlock") == 0) { 82 | printf("Perf test MutexLock\n"); 83 | task = TestMutexLock; 84 | } else if (strcmp(argv[1], "gettime") == 0) { 85 | printf("Perf test gettimeofday\n"); 86 | task = TestGettime; 87 | } 88 | 89 | int thread_num = 10; 90 | thread_num = atoi(argv[2]); 91 | 92 | printf("Thread num %d\n", thread_num); 93 | 94 | Thread* t = new Thread[thread_num]; 95 | for (int i = 0; i < thread_num; i++) t[i].Start(task); 96 | while (1) { 97 | sleep(1); 98 | fprintf(stderr, "%ld\n", counter.Clear()); 99 | } 100 | return 0; 101 | } 102 | } 103 | } 104 | 105 | int main(int argc, char* argv[]) { 106 | return baidu::common::Run(argc, argv); 107 | } 108 | 109 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 110 | -------------------------------------------------------------------------------- /test/perf_test_mutex.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | namespace baidu { 14 | namespace common { 15 | 16 | Mutex mu; 17 | CondVar p(&mu); 18 | CondVar c(&mu); 19 | Counter items; 20 | Counter counter; 21 | 22 | void Consumer() { 23 | MutexLock lock(&mu); 24 | while(1) { 25 | while(items.Get() == 0) { 26 | p.Wait(); 27 | } 28 | //printf("Consume\n"); 29 | items.Clear(); 30 | counter.Inc(); 31 | c.Signal(); 32 | } 33 | } 34 | 35 | void Producer() { 36 | MutexLock lock(&mu); 37 | while (1) { 38 | while (items.Get() > 0) { 39 | c.Wait(); 40 | } 41 | //printf("Produce\n"); 42 | items.Inc(); 43 | p.Signal(); 44 | } 45 | } 46 | 47 | int Run() { 48 | baidu::common::Thread t1,t2; 49 | t1.Start(Consumer); 50 | t2.Start(Producer); 51 | while (1) { 52 | sleep(1); 53 | fprintf(stderr, "%ld\n", counter.Clear()); 54 | } 55 | return 0; 56 | } 57 | } 58 | } 59 | 60 | int main() { 61 | return baidu::common::Run(); 62 | } 63 | 64 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 65 | -------------------------------------------------------------------------------- /test/perf_test_thread_pool.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include 8 | #include 9 | 10 | 11 | const int kThreadNum = 8; 12 | const int kMaxPending = 10000; 13 | 14 | namespace baidu { 15 | namespace common { 16 | 17 | void Task() { 18 | } 19 | 20 | void* AddTask(void* arg) { 21 | ThreadPool* tp = reinterpret_cast(arg); 22 | while (1) { 23 | while (tp->PendingNum() > kMaxPending) { 24 | } 25 | tp->AddTask(Task); 26 | } 27 | return NULL; 28 | } 29 | 30 | void RunPerfTest() { 31 | ThreadPool tp; 32 | Thread* threads = new Thread[kThreadNum]; 33 | for (int i = 0; i < kThreadNum; i++) { 34 | threads[i].Start(AddTask, &tp); 35 | } 36 | 37 | long s_time = timer::get_micros(); 38 | while (1) { 39 | usleep(1000000); 40 | long n_time = timer::get_micros(); 41 | std::string plog = tp.ProfilingLog(); 42 | long pending = tp.PendingNum(); 43 | printf("%ld %s\n", pending, plog.c_str()); 44 | } 45 | } 46 | } 47 | } 48 | 49 | int main(int argc, char* argv[]) { 50 | baidu::common::RunPerfTest(); 51 | return 0; 52 | } 53 | 54 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 55 | -------------------------------------------------------------------------------- /test/test_logging.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include 8 | 9 | #include 10 | 11 | int main(int argc, char* argv[]) { 12 | int test_time = 88; 13 | const char* char_pointer = "char*"; 14 | std::string string = "std;"; 15 | LOGS(baidu::INFO) << 88 << " " << char_pointer << " " << string; 16 | LOGS(baidu::INFO) << 88 << " " << char_pointer << " " << string; 17 | 18 | return 0; 19 | } 20 | 21 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 22 | -------------------------------------------------------------------------------- /test/test_mutex.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | baidu::common::Mutex x; 13 | void LockFunc() { 14 | x.Lock(); 15 | } 16 | 17 | void UnlockFunc() { 18 | x.Unlock(); 19 | } 20 | 21 | int main() { 22 | baidu::common::Thread t1,t2; 23 | t1.Start(LockFunc); 24 | t1.Join(); 25 | t2.Start(UnlockFunc); 26 | t2.Join(); 27 | printf("Done\n"); 28 | } 29 | 30 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 31 | -------------------------------------------------------------------------------- /test/test_thread.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace baidu { 14 | namespace common { 15 | 16 | class ThreadTest : public ::testing::Test { 17 | public: 18 | ThreadTest() : task_done_(&task_mu_) {} 19 | void Task() { 20 | MutexLock l(&task_mu_); 21 | task_done_.Signal(); 22 | } 23 | 24 | protected: 25 | CondVar task_done_; 26 | mutable Mutex task_mu_; 27 | }; 28 | 29 | TEST_F(ThreadTest, Start) { 30 | Thread t; 31 | MutexLock l(&task_mu_); 32 | t.Start(boost::bind(&ThreadTest::Task, this)); 33 | bool ret = task_done_.TimeWait(1000); 34 | ASSERT_TRUE(ret); 35 | } 36 | 37 | } 38 | } 39 | 40 | int main(int argc, char* argv[]) { 41 | ::testing::InitGoogleTest(&argc, argv); 42 | return RUN_ALL_TESTS(); 43 | } 44 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 45 | -------------------------------------------------------------------------------- /test/test_thread_pool.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | namespace baidu { 13 | namespace common { 14 | 15 | class ThreadPoolTest : public ::testing::Test { 16 | public: 17 | ThreadPoolTest() : task_done_(&task_mu_) {} 18 | void Task() { 19 | MutexLock l(&task_mu_); 20 | task_done_.Signal(); 21 | } 22 | 23 | protected: 24 | CondVar task_done_; 25 | mutable Mutex task_mu_; 26 | }; 27 | 28 | TEST_F(ThreadPoolTest, AddTask) { 29 | ThreadPool tp; 30 | MutexLock l(&task_mu_); 31 | tp.AddTask(boost::bind(&ThreadPoolTest::Task, this)); 32 | bool ret = task_done_.TimeWait(1000); 33 | ASSERT_TRUE(ret); 34 | } 35 | 36 | } 37 | } 38 | 39 | int main(int argc, char* argv[]) { 40 | ::testing::InitGoogleTest(&argc, argv); 41 | return RUN_ALL_TESTS(); 42 | } 43 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 44 | -------------------------------------------------------------------------------- /test/test_util.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Baidu.com, Inc. All Rights Reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Author: yanshiguang02@baidu.com 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace baidu { 12 | namespace common { 13 | namespace util { 14 | 15 | TEST(UtilTest, TestEncodeDecode) { 16 | char buf1[8]; 17 | char buf2[8]; 18 | uint64_t x = 123456789; 19 | uint64_t y = 200000000; 20 | EncodeBigEndian(buf1, x); 21 | EncodeBigEndian(buf2, y); 22 | printf("%s %s\n", std::string(buf1, 8).c_str(), std::string(buf2, 8).c_str()); 23 | ASSERT_TRUE(std::string(reinterpret_cast(&x), 8) > std::string(reinterpret_cast(&y), 8)); 24 | ASSERT_TRUE(std::string(buf1, 8) < std::string(buf2, 8)); 25 | ASSERT_EQ(DecodeBigEndian64(buf1), x); 26 | ASSERT_EQ(DecodeBigEndian64(buf2), y); 27 | 28 | uint32_t a = 123456789; 29 | uint32_t b = 200000000; 30 | char bufa[4]; 31 | char bufb[4]; 32 | EncodeBigEndian(bufa, a); 33 | EncodeBigEndian(bufb, b); 34 | printf("%s %s\n", std::string(bufa, 4).c_str(), std::string(bufb, 4).c_str()); 35 | ASSERT_TRUE(std::string(reinterpret_cast(&a), 4) > std::string(reinterpret_cast(&b), 4)); 36 | ASSERT_TRUE(std::string(bufa, 4) < std::string(bufb, 4)); 37 | ASSERT_EQ(DecodeBigEndian32(bufa), a); 38 | ASSERT_EQ(DecodeBigEndian32(bufb), b); 39 | } 40 | 41 | } 42 | } 43 | } 44 | 45 | int main(int argc, char* argv[]) { 46 | ::testing::InitGoogleTest(&argc, argv); 47 | return RUN_ALL_TESTS(); 48 | } 49 | /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */ 50 | 51 | --------------------------------------------------------------------------------