├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── common ├── Atomic.h ├── BlockingQueue.h ├── BoundedBlockingQueue.h ├── Condition.cpp ├── Condition.h ├── CountDownLatch.cpp ├── CountDownLatch.h ├── CurrentThread.h ├── Exception.cpp ├── Exception.h ├── FileUtil.cpp ├── FileUtil.h ├── LogFile.cpp ├── LogFile.h ├── LogStream.cpp ├── LogStream.h ├── Logging.cpp ├── Logging.h ├── Makefile ├── Mutex.h ├── ProcessInfo.cpp ├── ProcessInfo.h ├── Singleton.h ├── StringPiece.h ├── Thread.cpp ├── Thread.h ├── ThreadLocal.h ├── ThreadLocalSingleton.h ├── ThreadPool.cpp ├── ThreadPool.h ├── Timestamp.cpp ├── Timestamp.h └── tests │ ├── Atomic_unittest.cpp │ ├── BlockingQueue_bench.cpp │ ├── BlockingQueue_test.cpp │ ├── BoundedBlockingQueue_test.cpp │ ├── CountDownLatch_test1.cpp │ ├── CountDownLatch_test2.cpp │ ├── Exception_test.cpp │ ├── LogFile_test.cpp │ ├── LogStream_bench.cpp │ ├── LogStream_test.cpp │ ├── Log_test1.cpp │ ├── Log_test2.cpp │ ├── Logging_test.cpp │ ├── Makefile │ ├── Mutex_test.cpp │ ├── SingletonThreadLocal_test.cpp │ ├── Singleton_test.cpp │ ├── ThreadLocalSingleton_test.cpp │ ├── ThreadLocal_test.cpp │ ├── ThreadPool_test.cpp │ ├── Thread_test.cpp │ ├── Thread_unittest.cpp │ ├── Timestamp_unittest.cpp │ └── boost_test.cpp ├── net ├── Channel.cpp ├── Channel.h ├── DefaultPoller.cpp ├── EPollPoller.cpp ├── EPollPoller.h ├── EventLoop.cpp ├── EventLoop.h ├── Makefile ├── PollPoller.cpp ├── PollPoller.h ├── Poller.cpp ├── Poller.h └── test │ ├── Makefile │ ├── Reactor_test01 │ ├── Reactor_test01.cpp │ ├── Reactor_test02 │ └── Reactor_test02.cpp └── src ├── socketutil.c └── socketutil.h /.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) 2016, Bin Hou 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 cpp 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | make -C ./common/ 3 | make -C ./common/tests/ 4 | make -C ./net/ 5 | 6 | 7 | clean: 8 | make -C ./common/ clean 9 | make -C ./common/tests clean 10 | make -C ./net/ clean 11 | 12 | .PHONY: all clean 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | this is just for learning 2 | -------------------------------------------------------------------------------- /common/Atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_ATOMIC_H_ 2 | #define _XNET_COMMON_ATOMIC_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace xnet 8 | { 9 | 10 | namespace detail 11 | { 12 | template 13 | class AtomicIntegerT: boost::noncopyable 14 | { 15 | public: 16 | AtomicIntegerT() : 17 | value_(0) 18 | { 19 | } 20 | 21 | T get() 22 | { 23 | return __sync_val_compare_and_swap(&value_, 0, 0); 24 | } 25 | 26 | T getAndAdd(T x) 27 | { 28 | return __sync_fetch_and_add(&value_, x); 29 | } 30 | 31 | T addAndGet(T x) 32 | { 33 | return getAndAdd(x) + x; 34 | } 35 | 36 | T incrementAndGet() 37 | { 38 | return addAndGet(1); 39 | } 40 | 41 | T decrementAndGet() 42 | { 43 | return addAndGet(-1); 44 | } 45 | 46 | void add(T x) 47 | { 48 | getAndAdd(x); 49 | } 50 | 51 | void increment() 52 | { 53 | incrementAndGet(); 54 | } 55 | 56 | void decrement() 57 | { 58 | decrementAndGet(); 59 | } 60 | 61 | T getAndSet(T newValue) 62 | { 63 | return __sync_lock_test_and_set(&value_, newValue); 64 | } 65 | 66 | private: 67 | volatile T value_; 68 | }; 69 | } 70 | 71 | typedef detail::AtomicIntegerT AtomicInt32; 72 | typedef detail::AtomicIntegerT AtomicInt64; 73 | } 74 | 75 | #endif // _XNET_COMMON_ATOMIC_H_ 76 | -------------------------------------------------------------------------------- /common/BlockingQueue.h: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style license 2 | // that can be found in the License file. 3 | 4 | #ifndef _XNET_COMMON_BLOCKINGQUEUE_H_ 5 | #define _XNET_COMMON_BLOCKINGQUEUE_H_ 6 | 7 | #include "Condition.h" 8 | #include "Mutex.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace xnet 15 | { 16 | 17 | template 18 | class BlockingQueue: boost::noncopyable 19 | { 20 | public: 21 | BlockingQueue() : 22 | mutex_(), notEmpty_(mutex_), queue_() 23 | { 24 | } 25 | 26 | void put(const T& x) 27 | { 28 | MutexLockGuard lock(mutex_); 29 | queue_.push_back(x); 30 | notEmpty_.notify(); 31 | } 32 | 33 | T take() 34 | { 35 | MutexLockGuard lock(mutex_); 36 | while (queue_.empty()) 37 | { 38 | notEmpty_.wait(); 39 | } 40 | assert(!queue_.empty()); 41 | T front(queue_.front()); 42 | queue_.pop_front(); 43 | return front; 44 | } 45 | 46 | size_t size() const 47 | { 48 | MutexLockGuard lock(mutex_); 49 | return queue_.size(); 50 | } 51 | 52 | private: 53 | mutable MutexLock mutex_; 54 | Condition notEmpty_; 55 | std::deque queue_; 56 | }; 57 | 58 | } 59 | 60 | #endif // _XNET_COMMON_BLOCKINGQUEUE_H_ 61 | -------------------------------------------------------------------------------- /common/BoundedBlockingQueue.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_BOUNDEDBLOCKINGQUEUE_H_ 2 | #define _XNET_COMMON_BOUNDEDBLOCKINGQUEUE_H_ 3 | 4 | #include "Condition.h" 5 | #include "Mutex.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace xnet 12 | { 13 | 14 | template 15 | class BoundedBlockingQueue: boost::noncopyable 16 | { 17 | public: 18 | explicit BoundedBlockingQueue(int maxSize) : 19 | mutex_(), notEmpty_(mutex_), notFull_(mutex_), queue_(maxSize) 20 | { 21 | } 22 | 23 | void put(const T& x) 24 | { 25 | MutexLockGuard lock(mutex_); 26 | while (queue_.full()) 27 | { 28 | notFull_.wait(); 29 | } 30 | assert(!queue_.full()); 31 | queue_.push_back(x); 32 | notEmpty_.notify(); // TODO: move outside of lock 33 | } 34 | 35 | T take() 36 | { 37 | MutexLockGuard lock(mutex_); 38 | while (queue_.empty()) 39 | { 40 | notEmpty_.wait(); 41 | } 42 | assert(!queue_.empty()); 43 | T front(queue_.front()); 44 | queue_.pop_front(); 45 | notFull_.notify(); 46 | return front; 47 | } 48 | 49 | bool empty() const 50 | { 51 | MutexLockGuard lock(mutex_); 52 | return queue_.empty(); 53 | } 54 | 55 | bool full() const 56 | { 57 | MutexLockGuard lock(mutex_); 58 | return queue_.full(); 59 | } 60 | 61 | size_t size() const 62 | { 63 | MutexLockGuard lock(mutex_); 64 | return queue_.size(); 65 | } 66 | 67 | size_t capacity() const 68 | { 69 | MutexLockGuard lock(mutex_); 70 | return queue_.capacity(); 71 | } 72 | 73 | private: 74 | mutable MutexLock mutex_; 75 | Condition notEmpty_; 76 | Condition notFull_; 77 | boost::circular_buffer queue_; 78 | }; 79 | 80 | } 81 | 82 | #endif // _XNET_COMMON_BOUNDEDBLOCKINGQUEUE_H_ 83 | -------------------------------------------------------------------------------- /common/Condition.cpp: -------------------------------------------------------------------------------- 1 | #include "Condition.h" 2 | 3 | #include 4 | 5 | 6 | bool xnet::Condition::waitForSeconds(int seconds) 7 | { 8 | struct timespec abstime; 9 | clock_gettime(CLOCK_REALTIME, &abstime); 10 | abstime.tv_sec += seconds; 11 | return ETIMEDOUT == pthread_cond_timedwait(&pcond_, mutex_.getPthreadMutex(), &abstime); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /common/Condition.h: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style license 2 | // that can be found in the License file. 3 | 4 | #ifndef _XNET_COMMON_CONDITION_H_ 5 | #define _XNET_COMMON_CONDITION_H_ 6 | 7 | #include "Mutex.h" 8 | 9 | #include 10 | #include 11 | 12 | namespace xnet 13 | { 14 | 15 | class Condition: boost::noncopyable 16 | { 17 | public: 18 | explicit Condition(MutexLock& mutex) : 19 | mutex_(mutex) 20 | { 21 | pthread_cond_init(&pcond_, NULL); 22 | } 23 | 24 | ~Condition() 25 | { 26 | pthread_cond_destroy(&pcond_); 27 | } 28 | 29 | void wait() 30 | { 31 | pthread_cond_wait(&pcond_, mutex_.getPthreadMutex()); 32 | } 33 | 34 | bool waitForSeconds(int seconds); 35 | 36 | void notify() 37 | { 38 | pthread_cond_signal(&pcond_); 39 | } 40 | 41 | void notifyAll() 42 | { 43 | pthread_cond_broadcast(&pcond_); 44 | } 45 | 46 | private: 47 | MutexLock& mutex_; 48 | pthread_cond_t pcond_; 49 | }; 50 | 51 | } 52 | #endif // _XNET_COMMON_CONDITION_H_ 53 | -------------------------------------------------------------------------------- /common/CountDownLatch.cpp: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style license 2 | // that can be found in the License file. 3 | // 4 | 5 | #include "CountDownLatch.h" 6 | 7 | using namespace xnet; 8 | 9 | CountDownLatch::CountDownLatch(int count) : 10 | mutex_(), condition_(mutex_), count_(count) 11 | { 12 | } 13 | 14 | void CountDownLatch::wait() 15 | { 16 | MutexLockGuard lock(mutex_); 17 | while (count_ > 0) 18 | { 19 | condition_.wait(); 20 | } 21 | } 22 | 23 | void CountDownLatch::countDown() 24 | { 25 | MutexLockGuard lock(mutex_); 26 | --count_; 27 | if (count_ == 0) 28 | { 29 | condition_.notifyAll(); 30 | } 31 | } 32 | 33 | int CountDownLatch::getCount() const 34 | { 35 | MutexLockGuard lock(mutex_); 36 | return count_; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /common/CountDownLatch.h: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style license 2 | // that can be found in the License file. 3 | 4 | #ifndef _XNET_COMMON_COUNTDOWNLATCH_H_ 5 | #define _XNET_COMMON_COUNTDOWNLATCH_H_ 6 | 7 | #include "Condition.h" 8 | #include "Mutex.h" 9 | 10 | #include 11 | 12 | namespace xnet 13 | { 14 | 15 | class CountDownLatch: boost::noncopyable 16 | { 17 | public: 18 | 19 | explicit CountDownLatch(int count); 20 | void wait(); 21 | void countDown(); 22 | int getCount() const; 23 | private: 24 | mutable MutexLock mutex_; 25 | Condition condition_; 26 | int count_; 27 | }; 28 | 29 | } 30 | #endif // _XNET_COMMON_COUNTDOWNLATCH_H_ 31 | -------------------------------------------------------------------------------- /common/CurrentThread.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_CURRENTTHREAD_H_ 2 | #define _XNET_COMMON_CURRENTTHREAD_H_ 3 | 4 | namespace xnet 5 | { 6 | namespace CurrentThread 7 | { 8 | // internal 9 | extern __thread int t_cachedTid; 10 | extern __thread char t_tidString[32]; 11 | extern __thread const char* t_threadName; 12 | void cacheTid(); 13 | 14 | inline int tid() 15 | { 16 | if (t_cachedTid == 0) 17 | { 18 | cacheTid(); 19 | } 20 | return t_cachedTid; 21 | } 22 | 23 | inline const char* tidString() // for logging 24 | { 25 | return t_tidString; 26 | } 27 | 28 | inline const char* name() 29 | { 30 | return t_threadName; 31 | } 32 | 33 | bool isMainThread(); 34 | } 35 | } 36 | 37 | #endif //_XNET_COMMON_CURRENTTHREAD_H_ 38 | -------------------------------------------------------------------------------- /common/Exception.cpp: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style license 2 | 3 | #include "Exception.h" 4 | 5 | //#include 6 | #include 7 | #include 8 | 9 | using namespace xnet; 10 | 11 | Exception::Exception(const char* msg) 12 | : message_(msg) 13 | { 14 | fillStackTrace(); 15 | } 16 | 17 | Exception::Exception(const std::string& msg) 18 | : message_(msg) 19 | { 20 | fillStackTrace(); 21 | } 22 | 23 | Exception::~Exception() throw () 24 | { 25 | } 26 | 27 | const char* Exception::what() const throw() 28 | { 29 | return message_.c_str(); 30 | } 31 | 32 | const char* Exception::stackTrace() const throw() 33 | { 34 | return stack_.c_str(); 35 | } 36 | 37 | void Exception::fillStackTrace() 38 | { 39 | const int len = 200; 40 | void* buffer[len]; 41 | int nptrs = ::backtrace(buffer, len); 42 | char** strings = ::backtrace_symbols(buffer, nptrs); 43 | if (strings) 44 | { 45 | for (int i = 0; i < nptrs; ++i) 46 | { 47 | // TODO demangle funcion name with abi::__cxa_demangle 48 | stack_.append(strings[i]); 49 | stack_.push_back('\n'); 50 | } 51 | free(strings); 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /common/Exception.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_EXCEPTION_H_ 2 | #define _XNET_COMMON_EXCEPTION_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace xnet 8 | { 9 | 10 | class Exception: public std::exception 11 | { 12 | public: 13 | explicit Exception(const char* what); 14 | explicit Exception(const std::string& what); 15 | virtual ~Exception() throw (); 16 | virtual const char* what() const throw (); 17 | const char* stackTrace() const throw (); 18 | 19 | private: 20 | void fillStackTrace(); 21 | 22 | std::string message_; 23 | std::string stack_; 24 | }; 25 | 26 | } 27 | 28 | #endif // _XNET_COMMON_EXCEPTION_H_ 29 | -------------------------------------------------------------------------------- /common/FileUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "FileUtil.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace xnet; 12 | 13 | FileUtil::SmallFile::SmallFile(StringPiece filename) : 14 | fd_(::open(filename.data(), O_RDONLY | O_CLOEXEC)), err_(0) 15 | { 16 | buf_[0] = '\0'; 17 | if (fd_ < 0) 18 | { 19 | err_ = errno; 20 | } 21 | } 22 | 23 | FileUtil::SmallFile::~SmallFile() 24 | { 25 | if (fd_ >= 0) 26 | { 27 | ::close(fd_); // FIXME: check EINTR 28 | } 29 | } 30 | 31 | // return errno 32 | template 33 | int FileUtil::SmallFile::readToString(int maxSize, String* content, int64_t* fileSize, int64_t* modifyTime, int64_t* createTime) 34 | { 35 | BOOST_STATIC_ASSERT(sizeof(off_t) == 8); 36 | assert(content != NULL); 37 | int err = err_; 38 | if (fd_ >= 0) 39 | { 40 | content->clear(); 41 | 42 | if (fileSize) 43 | { 44 | struct stat statbuf; 45 | if (::fstat(fd_, &statbuf) == 0) 46 | { 47 | if (S_ISREG(statbuf.st_mode)) 48 | { 49 | *fileSize = statbuf.st_size; 50 | content->reserve(static_cast(std::min(boost::implicit_cast(maxSize), *fileSize))); 51 | } 52 | else if (S_ISDIR(statbuf.st_mode)) 53 | { 54 | err = EISDIR; 55 | } 56 | if (modifyTime) 57 | { 58 | *modifyTime = statbuf.st_mtime; 59 | } 60 | if (createTime) 61 | { 62 | *createTime = statbuf.st_ctime; 63 | } 64 | } 65 | else 66 | { 67 | err = errno; 68 | } 69 | } 70 | 71 | while (content->size() < boost::implicit_cast(maxSize)) 72 | { 73 | size_t toRead = std::min(boost::implicit_cast(maxSize) - content->size(), sizeof(buf_)); 74 | ssize_t n = ::read(fd_, buf_, toRead); 75 | if (n > 0) 76 | { 77 | content->append(buf_, n); 78 | } 79 | else 80 | { 81 | if (n < 0) 82 | { 83 | err = errno; 84 | } 85 | break; 86 | } 87 | } 88 | } 89 | return err; 90 | } 91 | 92 | int FileUtil::SmallFile::readToBuffer(int* size) 93 | { 94 | int err = err_; 95 | if (fd_ >= 0) 96 | { 97 | ssize_t n = ::pread(fd_, buf_, sizeof(buf_) - 1, 0); 98 | if (n >= 0) 99 | { 100 | if (size) 101 | { 102 | *size = static_cast(n); 103 | } 104 | buf_[n] = '\0'; 105 | } 106 | else 107 | { 108 | err = errno; 109 | } 110 | } 111 | return err; 112 | } 113 | 114 | template int FileUtil::readFile(StringPiece filename, int maxSize, std::string* content, int64_t*, int64_t*, int64_t*); 115 | 116 | template int FileUtil::SmallFile::readToString(int maxSize, std::string* content, int64_t*, int64_t*, int64_t*); 117 | 118 | -------------------------------------------------------------------------------- /common/FileUtil.h: -------------------------------------------------------------------------------- 1 | // This is a public header file, it must only include public header files. 2 | 3 | #ifndef _XNET_COMMON_FILEUTIL_H_ 4 | #define _XNET_COMMON_FILEUTIL_H_ 5 | 6 | #include "StringPiece.h" 7 | #include 8 | 9 | namespace xnet 10 | { 11 | 12 | namespace FileUtil 13 | { 14 | 15 | class SmallFile: boost::noncopyable 16 | { 17 | public: 18 | SmallFile(StringPiece filename); 19 | ~SmallFile(); 20 | 21 | // return errno 22 | template 23 | int readToString(int maxSize, String* content, int64_t* fileSize, int64_t* modifyTime, int64_t* createTime); 24 | 25 | // return errno 26 | int readToBuffer(int* size); 27 | 28 | const char* buffer() const 29 | { 30 | return buf_; 31 | } 32 | 33 | static const int kBufferSize = 65536; 34 | 35 | private: 36 | int fd_; 37 | int err_; 38 | char buf_[kBufferSize]; 39 | }; 40 | 41 | // read the file content, returns errno if error happens. 42 | template 43 | int readFile(StringPiece filename, int maxSize, String* content, int64_t* fileSize = NULL, int64_t* modifyTime = NULL, int64_t* createTime = NULL) 44 | { 45 | SmallFile file(filename); 46 | return file.readToString(maxSize, content, fileSize, modifyTime, createTime); 47 | } 48 | 49 | } 50 | 51 | } 52 | 53 | #endif // _XNET_COMMON_FILEUTIL_H_ 54 | 55 | -------------------------------------------------------------------------------- /common/LogFile.cpp: -------------------------------------------------------------------------------- 1 | #include "LogFile.h" 2 | #include "Logging.h" 3 | #include "ProcessInfo.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace xnet; 10 | 11 | // not thread safe 12 | class LogFile::File : boost::noncopyable 13 | { 14 | public: 15 | explicit File(const std::string& filename) 16 | : fp_(::fopen(filename.data(), "ae")), 17 | writtenBytes_(0) 18 | { 19 | assert(fp_); 20 | ::setbuffer(fp_, buffer_, sizeof buffer_); 21 | // posix_fadvise POSIX_FADV_DONTNEED ? 22 | } 23 | 24 | ~File() 25 | { 26 | ::fclose(fp_); 27 | } 28 | 29 | void append(const char* logline, const size_t len) 30 | { 31 | size_t n = write(logline, len); 32 | size_t remain = len - n; 33 | // remain>0表示没写完,需要继续写直到写完 34 | while (remain > 0) 35 | { 36 | size_t x = write(logline + n, remain); 37 | if (x == 0) 38 | { 39 | int err = ferror(fp_); 40 | if (err) 41 | { 42 | fprintf(stderr, "LogFile::File::append() failed %s\n", strerror_tl(err)); 43 | } 44 | break; 45 | } 46 | n += x; 47 | remain = len - n; // remain -= x 48 | } 49 | 50 | writtenBytes_ += len; 51 | } 52 | 53 | void flush() 54 | { 55 | ::fflush(fp_); 56 | } 57 | 58 | size_t writtenBytes() const { return writtenBytes_; } 59 | 60 | private: 61 | 62 | size_t write(const char* logline, size_t len) 63 | { 64 | #undef fwrite_unlocked 65 | return ::fwrite_unlocked(logline, 1, len, fp_); 66 | } 67 | 68 | FILE* fp_; 69 | char buffer_[64*1024]; 70 | size_t writtenBytes_; 71 | }; 72 | 73 | LogFile::LogFile(const std::string& basename, 74 | size_t rollSize, 75 | bool threadSafe, 76 | int flushInterval) 77 | : basename_(basename), 78 | rollSize_(rollSize), 79 | flushInterval_(flushInterval), 80 | count_(0), 81 | mutex_(threadSafe ? new MutexLock : NULL), 82 | startOfPeriod_(0), 83 | lastRoll_(0), 84 | lastFlush_(0) 85 | { 86 | assert(basename.find('/') == std::string::npos); 87 | rollFile(); 88 | } 89 | 90 | LogFile::~LogFile() 91 | { 92 | } 93 | 94 | void LogFile::append(const char* logline, int len) 95 | { 96 | if (mutex_) 97 | { 98 | MutexLockGuard lock(*mutex_); 99 | append_unlocked(logline, len); 100 | } 101 | else 102 | { 103 | append_unlocked(logline, len); 104 | } 105 | } 106 | 107 | void LogFile::flush() 108 | { 109 | if (mutex_) 110 | { 111 | MutexLockGuard lock(*mutex_); 112 | file_->flush(); 113 | } 114 | else 115 | { 116 | file_->flush(); 117 | } 118 | } 119 | 120 | void LogFile::append_unlocked(const char* logline, int len) 121 | { 122 | file_->append(logline, len); 123 | 124 | if (file_->writtenBytes() > rollSize_) 125 | { 126 | rollFile(); 127 | } 128 | else 129 | { 130 | if (count_ > kCheckTimeRoll_) 131 | { 132 | count_ = 0; 133 | time_t now = ::time(NULL); 134 | time_t thisPeriod_ = now / kRollPerSeconds_ * kRollPerSeconds_; 135 | if (thisPeriod_ != startOfPeriod_) 136 | { 137 | rollFile(); 138 | } 139 | else if (now - lastFlush_ > flushInterval_) 140 | { 141 | lastFlush_ = now; 142 | file_->flush(); 143 | } 144 | } 145 | else 146 | { 147 | ++count_; 148 | } 149 | } 150 | } 151 | 152 | void LogFile::rollFile() 153 | { 154 | time_t now = 0; 155 | std::string filename = getLogFileName(basename_, &now); 156 | // 注意,这里先除kRollPerSeconds_ 后乘kRollPerSeconds_表示 157 | // 对齐至kRollPerSeconds_整数倍,也就是时间调整到当天零点。 158 | time_t start = now / kRollPerSeconds_ * kRollPerSeconds_; 159 | 160 | if (now > lastRoll_) 161 | { 162 | lastRoll_ = now; 163 | lastFlush_ = now; 164 | startOfPeriod_ = start; 165 | file_.reset(new File(filename)); 166 | } 167 | } 168 | 169 | std::string LogFile::getLogFileName(const std::string& basename, time_t* now) 170 | { 171 | std::string filename; 172 | filename.reserve(basename.size() + 64); 173 | filename = basename; 174 | 175 | char timebuf[32]; 176 | char pidbuf[32]; 177 | struct tm tm; 178 | *now = time(NULL); 179 | gmtime_r(now, &tm); // FIXME: localtime_r ? 180 | strftime(timebuf, sizeof timebuf, ".%Y%m%d-%H%M%S.", &tm); 181 | filename += timebuf; 182 | filename += ProcessInfo::hostname(); 183 | snprintf(pidbuf, sizeof pidbuf, ".%d", ProcessInfo::pid()); 184 | filename += pidbuf; 185 | filename += ".log"; 186 | 187 | return filename; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /common/LogFile.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_LOGFILE_H_ 2 | #define _XNET_COMMON_LOGFILE_H_ 3 | 4 | #include "Mutex.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace xnet 11 | { 12 | 13 | class LogFile: boost::noncopyable 14 | { 15 | public: 16 | LogFile(const std::string& basename, size_t rollSize, bool threadSafe = true, int flushInterval = 3); 17 | ~LogFile(); 18 | 19 | void append(const char* logline, int len); 20 | void flush(); 21 | 22 | private: 23 | void append_unlocked(const char* logline, int len); 24 | 25 | static std::string getLogFileName(const std::string& basename, time_t* now); 26 | void rollFile(); 27 | 28 | const std::string basename_; // 日志文件basename 29 | const size_t rollSize_; // 日志文件达到rolSize_换一个新文件 30 | const int flushInterval_; // 日志写入间隔时间 31 | 32 | int count_; 33 | 34 | boost::scoped_ptr mutex_; 35 | time_t startOfPeriod_; // 开始记录日志时间(调整至零点的时间) 36 | time_t lastRoll_; // 上一次滚动日志文件时间 37 | time_t lastFlush_; // 上一次日志写入文件时间 38 | class File; 39 | boost::scoped_ptr file_; 40 | 41 | const static int kCheckTimeRoll_ = 1024; 42 | const static int kRollPerSeconds_ = 60 * 60 * 24; 43 | }; 44 | 45 | } 46 | #endif // _XNET_COMMON_LOGFILE_H_ 47 | -------------------------------------------------------------------------------- /common/LogStream.cpp: -------------------------------------------------------------------------------- 1 | #include "LogStream.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace xnet; 13 | using namespace xnet::detail; 14 | 15 | #pragma GCC diagnostic ignored "-Wtype-limits" 16 | namespace xnet 17 | { 18 | namespace detail 19 | { 20 | 21 | const char digits[] = "9876543210123456789"; 22 | const char* zero = digits + 9; 23 | BOOST_STATIC_ASSERT(sizeof(digits) == 20); 24 | 25 | const char digitsHex[] = "0123456789ABCDEF"; 26 | BOOST_STATIC_ASSERT(sizeof digitsHex == 17); 27 | 28 | // Efficient Integer to String Conversions, by Matthew Wilson. 29 | template 30 | size_t convert(char buf[], T value) 31 | { 32 | T i = value; 33 | char* p = buf; 34 | 35 | do 36 | { 37 | int lsd = static_cast(i % 10); 38 | i /= 10; 39 | *p++ = zero[lsd]; 40 | } while (i != 0); 41 | 42 | if (value < 0) 43 | { 44 | *p++ = '-'; 45 | } 46 | *p = '\0'; 47 | std::reverse(buf, p); 48 | 49 | return p - buf; 50 | } 51 | 52 | size_t convertHex(char buf[], uintptr_t value) 53 | { 54 | uintptr_t i = value; 55 | char* p = buf; 56 | 57 | do 58 | { 59 | int lsd = i % 16; 60 | i /= 16; 61 | *p++ = digitsHex[lsd]; 62 | } while (i != 0); 63 | 64 | *p = '\0'; 65 | std::reverse(buf, p); 66 | 67 | return p - buf; 68 | } 69 | 70 | } 71 | } 72 | 73 | template 74 | const char* FixedBuffer::debugString() 75 | { 76 | *cur_ = '\0'; 77 | return data_; 78 | } 79 | 80 | template 81 | void FixedBuffer::cookieStart() 82 | { 83 | } 84 | 85 | template 86 | void FixedBuffer::cookieEnd() 87 | { 88 | } 89 | 90 | template class FixedBuffer ; 91 | template class FixedBuffer ; 92 | 93 | void LogStream::staticCheck() 94 | { 95 | BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits::digits10); 96 | BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits::digits10); 97 | BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits::digits10); 98 | BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits::digits10); 99 | } 100 | 101 | template 102 | void LogStream::formatInteger(T v) 103 | { 104 | if (buffer_.avail() >= kMaxNumericSize) 105 | { 106 | size_t len = convert(buffer_.current(), v); 107 | buffer_.add(len); 108 | } 109 | } 110 | 111 | LogStream& LogStream::operator<<(short v) 112 | { 113 | *this << static_cast(v); 114 | return *this; 115 | } 116 | 117 | LogStream& LogStream::operator<<(unsigned short v) 118 | { 119 | *this << static_cast(v); 120 | return *this; 121 | } 122 | 123 | LogStream& LogStream::operator<<(int v) 124 | { 125 | formatInteger(v); 126 | return *this; 127 | } 128 | 129 | LogStream& LogStream::operator<<(unsigned int v) 130 | { 131 | formatInteger(v); 132 | return *this; 133 | } 134 | 135 | LogStream& LogStream::operator<<(long v) 136 | { 137 | formatInteger(v); 138 | return *this; 139 | } 140 | 141 | LogStream& LogStream::operator<<(unsigned long v) 142 | { 143 | formatInteger(v); 144 | return *this; 145 | } 146 | 147 | LogStream& LogStream::operator<<(long long v) 148 | { 149 | formatInteger(v); 150 | return *this; 151 | } 152 | 153 | LogStream& LogStream::operator<<(unsigned long long v) 154 | { 155 | formatInteger(v); 156 | return *this; 157 | } 158 | 159 | LogStream& LogStream::operator<<(const void* p) 160 | { 161 | uintptr_t v = reinterpret_cast(p); 162 | if (buffer_.avail() >= kMaxNumericSize) 163 | { 164 | char* buf = buffer_.current(); 165 | buf[0] = '0'; 166 | buf[1] = 'x'; 167 | size_t len = convertHex(buf + 2, v); 168 | buffer_.add(len + 2); 169 | } 170 | return *this; 171 | } 172 | 173 | // FIXME: replace this with Grisu3 by Florian Loitsch. 174 | LogStream& LogStream::operator<<(double v) 175 | { 176 | if (buffer_.avail() >= kMaxNumericSize) 177 | { 178 | int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12g", v); 179 | buffer_.add(len); 180 | } 181 | return *this; 182 | } 183 | 184 | template 185 | Fmt::Fmt(const char* fmt, T val) 186 | { 187 | BOOST_STATIC_ASSERT(boost::is_arithmetic::value == true); 188 | 189 | length_ = snprintf(buf_, sizeof buf_, fmt, val); 190 | assert(static_cast(length_) < sizeof buf_); 191 | } 192 | 193 | // Explicit instantiations 194 | 195 | template Fmt::Fmt(const char* fmt, char); 196 | 197 | template Fmt::Fmt(const char* fmt, short); 198 | template Fmt::Fmt(const char* fmt, unsigned short); 199 | template Fmt::Fmt(const char* fmt, int); 200 | template Fmt::Fmt(const char* fmt, unsigned int); 201 | template Fmt::Fmt(const char* fmt, long); 202 | template Fmt::Fmt(const char* fmt, unsigned long); 203 | template Fmt::Fmt(const char* fmt, long long); 204 | template Fmt::Fmt(const char* fmt, unsigned long long); 205 | 206 | template Fmt::Fmt(const char* fmt, float); 207 | template Fmt::Fmt(const char* fmt, double); 208 | -------------------------------------------------------------------------------- /common/LogStream.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_LOGSTREAM_H_ 2 | #define _XNET_COMMON_LOGSTREAM_H_ 3 | 4 | #include "StringPiece.h" 5 | 6 | #include 7 | #include // memcpy 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace xnet 14 | { 15 | 16 | namespace detail 17 | { 18 | 19 | const int kSmallBuffer = 4000; 20 | const int kLargeBuffer = 4000 * 1000; 21 | 22 | template 23 | class FixedBuffer: boost::noncopyable 24 | { 25 | public: 26 | FixedBuffer() : 27 | cur_(data_) 28 | { 29 | setCookie(cookieStart); 30 | } 31 | 32 | ~FixedBuffer() 33 | { 34 | setCookie(cookieEnd); 35 | } 36 | 37 | void append(const char* /*restrict*/buf, size_t len) 38 | { 39 | if (boost::implicit_cast(avail()) > len) 40 | { 41 | memcpy(cur_, buf, len); 42 | cur_ += len; 43 | } 44 | } 45 | 46 | const char* data() const 47 | { 48 | return data_; 49 | } 50 | int length() const 51 | { 52 | return static_cast(cur_ - data_); 53 | } 54 | 55 | // write to data_ directly 56 | char* current() 57 | { 58 | return cur_; 59 | } 60 | int avail() const 61 | { 62 | return static_cast(end() - cur_); 63 | } 64 | void add(size_t len) 65 | { 66 | cur_ += len; 67 | } 68 | 69 | void reset() 70 | { 71 | cur_ = data_; 72 | } 73 | void bzero() 74 | { 75 | ::bzero(data_, sizeof data_); 76 | } 77 | 78 | // for used by GDB 79 | const char* debugString(); 80 | void setCookie(void (*cookie)()) 81 | { 82 | cookie_ = cookie; 83 | } 84 | // for used by unit test 85 | std::string asString() const 86 | { 87 | return std::string(data_, length()); 88 | } 89 | 90 | private: 91 | const char* end() const 92 | { 93 | return data_ + sizeof data_; 94 | } 95 | // Must be outline function for cookies. 96 | static void cookieStart(); 97 | static void cookieEnd(); 98 | 99 | void (*cookie_)(); 100 | char data_[SIZE]; 101 | char* cur_; 102 | }; 103 | 104 | } 105 | 106 | class LogStream: boost::noncopyable 107 | { 108 | public: 109 | typedef detail::FixedBuffer Buffer; 110 | 111 | LogStream& operator<<(bool v) 112 | { 113 | buffer_.append(v ? "1" : "0", 1); 114 | return *this; 115 | } 116 | 117 | LogStream& operator<<(short); 118 | LogStream& operator<<(unsigned short); 119 | LogStream& operator<<(int); 120 | LogStream& operator<<(unsigned int); 121 | LogStream& operator<<(long); 122 | LogStream& operator<<(unsigned long); 123 | LogStream& operator<<(long long); 124 | LogStream& operator<<(unsigned long long); 125 | 126 | LogStream& operator<<(const void*); 127 | 128 | LogStream& operator<<(float v) 129 | { 130 | *this << static_cast(v); 131 | return *this; 132 | } 133 | LogStream& operator<<(double); 134 | 135 | LogStream& operator<<(char v) 136 | { 137 | buffer_.append(&v, 1); 138 | return *this; 139 | } 140 | 141 | // LogStream& operator<<(signed char); 142 | // LogStream& operator<<(unsigned char); 143 | 144 | LogStream& operator<<(const char* v) 145 | { 146 | buffer_.append(v, strlen(v)); 147 | return *this; 148 | } 149 | 150 | LogStream& operator<<(const std::string& v) 151 | { 152 | buffer_.append(v.c_str(), v.size()); 153 | return *this; 154 | } 155 | 156 | LogStream& operator<<(const StringPiece& v) 157 | { 158 | buffer_.append(v.data(), v.size()); 159 | return *this; 160 | } 161 | 162 | void append(const char* data, int len) 163 | { 164 | buffer_.append(data, len); 165 | } 166 | const Buffer& buffer() const 167 | { 168 | return buffer_; 169 | } 170 | void resetBuffer() 171 | { 172 | buffer_.reset(); 173 | } 174 | 175 | private: 176 | void staticCheck(); 177 | 178 | template 179 | void formatInteger(T); 180 | 181 | Buffer buffer_; 182 | 183 | static const int kMaxNumericSize = 32; 184 | }; 185 | 186 | class Fmt // : boost::noncopyable 187 | { 188 | public: 189 | template 190 | Fmt(const char* fmt, T val); 191 | 192 | const char* data() const 193 | { 194 | return buf_; 195 | } 196 | int length() const 197 | { 198 | return length_; 199 | } 200 | 201 | private: 202 | char buf_[32]; 203 | int length_; 204 | }; 205 | 206 | inline LogStream& operator<<(LogStream& s, const Fmt& fmt) 207 | { 208 | s.append(fmt.data(), fmt.length()); 209 | return s; 210 | } 211 | 212 | } 213 | #endif // _XNET_COMMON_LOGSTREAM_H_ 214 | 215 | -------------------------------------------------------------------------------- /common/Logging.cpp: -------------------------------------------------------------------------------- 1 | #include "Logging.h" 2 | 3 | #include "CurrentThread.h" 4 | #include "StringPiece.h" 5 | #include "Timestamp.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | namespace xnet 14 | { 15 | 16 | /* 17 | class LoggerImpl 18 | { 19 | public: 20 | typedef Logger::LogLevel LogLevel; 21 | LoggerImpl(LogLevel level, int old_errno, const char* file, int line); 22 | void finish(); 23 | 24 | Timestamp time_; 25 | LogStream stream_; 26 | LogLevel level_; 27 | int line_; 28 | const char* fullname_; 29 | const char* basename_; 30 | }; 31 | */ 32 | 33 | __thread char t_errnobuf[512]; 34 | __thread char t_time[32]; 35 | __thread time_t t_lastSecond; 36 | 37 | const char* strerror_tl(int savedErrno) 38 | { 39 | return strerror_r(savedErrno, t_errnobuf, sizeof t_errnobuf); 40 | } 41 | 42 | Logger::LogLevel initLogLevel() 43 | { 44 | return Logger::TRACE; 45 | /* 46 | if (::getenv("XNET_LOG_TRACE")) 47 | return Logger::TRACE; 48 | else if (::getenv("XNET_LOG_DEBUG")) 49 | return Logger::DEBUG; 50 | else 51 | return Logger::INFO; 52 | */ 53 | } 54 | 55 | Logger::LogLevel g_logLevel = initLogLevel(); 56 | 57 | const char* LogLevelName[Logger::NUM_LOG_LEVELS] = { "TRACE ", "DEBUG ", "INFO ", "WARN ", "ERROR ", "FATAL ", }; 58 | 59 | // helper class for known string length at compile time 60 | class T 61 | { 62 | public: 63 | T(const char* str, unsigned len) : 64 | str_(str), len_(len) 65 | { 66 | assert(strlen(str) == len_); 67 | } 68 | 69 | const char* str_; 70 | const unsigned len_; 71 | }; 72 | 73 | inline LogStream& operator<<(LogStream& s, T v) 74 | { 75 | s.append(v.str_, v.len_); 76 | return s; 77 | } 78 | 79 | inline LogStream& operator<<(LogStream& s, const Logger::SourceFile& v) 80 | { 81 | s.append(v.data_, v.size_); 82 | return s; 83 | } 84 | 85 | void defaultOutput(const char* msg, int len) 86 | { 87 | size_t n = fwrite(msg, 1, len, stdout); 88 | //FIXME check n 89 | (void) n; 90 | } 91 | 92 | void defaultFlush() 93 | { 94 | fflush(stdout); 95 | } 96 | 97 | Logger::OutputFunc g_output = defaultOutput; 98 | Logger::FlushFunc g_flush = defaultFlush; 99 | 100 | } 101 | 102 | using namespace xnet; 103 | 104 | Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line) : 105 | time_(Timestamp::now()), stream_(), level_(level), line_(line), basename_(file) 106 | { 107 | formatTime(); 108 | CurrentThread::tid(); 109 | stream_ << T(CurrentThread::tidString(), 6); 110 | stream_ << T(LogLevelName[level], 6); 111 | if (savedErrno != 0) 112 | { 113 | stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") "; 114 | } 115 | } 116 | 117 | void Logger::Impl::formatTime() 118 | { 119 | int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch(); 120 | time_t seconds = static_cast(microSecondsSinceEpoch / 1000000); 121 | int microseconds = static_cast(microSecondsSinceEpoch % 1000000); 122 | if (seconds != t_lastSecond) 123 | { 124 | t_lastSecond = seconds; 125 | struct tm tm_time; 126 | ::gmtime_r(&seconds, &tm_time); // FIXME TimeZone::fromUtcTime 127 | 128 | int len = snprintf(t_time, sizeof(t_time), "%4d%02d%02d %02d:%02d:%02d", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, 129 | tm_time.tm_sec); 130 | assert(len == 17); 131 | (void) len; 132 | } 133 | Fmt us(".%06dZ ", microseconds); 134 | assert(us.length() == 9); 135 | stream_ << T(t_time, 17) << T(us.data(), 9); 136 | } 137 | 138 | void Logger::Impl::finish() 139 | { 140 | stream_ << " - " << basename_ << ':' << line_ << '\n'; 141 | } 142 | 143 | Logger::Logger(SourceFile file, int line) : 144 | impl_(INFO, 0, file, line) 145 | { 146 | } 147 | 148 | Logger::Logger(SourceFile file, int line, LogLevel level, const char* func) : 149 | impl_(level, 0, file, line) 150 | { 151 | impl_.stream_ << func << ' '; 152 | } 153 | 154 | Logger::Logger(SourceFile file, int line, LogLevel level) : 155 | impl_(level, 0, file, line) 156 | { 157 | } 158 | 159 | Logger::Logger(SourceFile file, int line, bool toAbort) : 160 | impl_(toAbort ? FATAL : ERROR, errno, file, line) 161 | { 162 | } 163 | 164 | Logger::~Logger() 165 | { 166 | impl_.finish(); 167 | const LogStream::Buffer& buf(stream().buffer()); 168 | g_output(buf.data(), buf.length()); 169 | if (impl_.level_ == FATAL) 170 | { 171 | g_flush(); 172 | abort(); 173 | } 174 | } 175 | 176 | void Logger::setLogLevel(Logger::LogLevel level) 177 | { 178 | g_logLevel = level; 179 | } 180 | 181 | void Logger::setOutput(OutputFunc out) 182 | { 183 | g_output = out; 184 | } 185 | 186 | void Logger::setFlush(FlushFunc flush) 187 | { 188 | g_flush = flush; 189 | } 190 | -------------------------------------------------------------------------------- /common/Logging.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_LOGGING_H_ 2 | #define _XNET_COMMON_LOGGING_H_ 3 | 4 | #include "LogStream.h" 5 | #include "Timestamp.h" 6 | 7 | namespace xnet 8 | { 9 | class Logger 10 | { 11 | public: 12 | enum LogLevel 13 | { 14 | TRACE, DEBUG, INFO, WARN, ERROR, FATAL, NUM_LOG_LEVELS, 15 | }; 16 | 17 | 18 | class SourceFile 19 | { 20 | public: 21 | template 22 | inline SourceFile(const char (&arr)[N]) : 23 | data_(arr), size_(N - 1) 24 | { 25 | const char* slash = strrchr(data_, '/'); // builtin function 26 | if (slash) 27 | { 28 | data_ = slash + 1; 29 | size_ -= static_cast(data_ - arr); 30 | } 31 | } 32 | 33 | explicit SourceFile(const char* filename) : 34 | data_(filename) 35 | { 36 | const char* slash = strrchr(filename, '/'); 37 | if (slash) 38 | { 39 | data_ = slash + 1; 40 | } 41 | size_ = static_cast(strlen(data_)); 42 | } 43 | 44 | const char* data_; 45 | int size_; 46 | }; 47 | 48 | Logger(SourceFile file, int line); 49 | Logger(SourceFile file, int line, LogLevel level); 50 | Logger(SourceFile file, int line, LogLevel level, const char* func); 51 | Logger(SourceFile file, int line, bool toAbort); 52 | ~Logger(); 53 | 54 | LogStream& stream() 55 | { 56 | return impl_.stream_; 57 | } 58 | 59 | static LogLevel logLevel(); 60 | static void setLogLevel(LogLevel level); 61 | 62 | typedef void (*OutputFunc)(const char* msg, int len); 63 | typedef void (*FlushFunc)(); 64 | static void setOutput(OutputFunc); 65 | static void setFlush(FlushFunc); 66 | 67 | private: 68 | 69 | class Impl 70 | { 71 | public: 72 | typedef Logger::LogLevel LogLevel; 73 | Impl(LogLevel level, int old_errno, const SourceFile& file, int line); 74 | void formatTime(); 75 | void finish(); 76 | 77 | Timestamp time_; 78 | LogStream stream_; 79 | LogLevel level_; 80 | int line_; 81 | SourceFile basename_; 82 | }; 83 | 84 | Impl impl_; 85 | 86 | }; 87 | 88 | extern Logger::LogLevel g_logLevel; 89 | 90 | inline Logger::LogLevel Logger::logLevel() 91 | { 92 | return g_logLevel; 93 | } 94 | 95 | #define LOG_TRACE if (xnet::Logger::logLevel() <= xnet::Logger::TRACE) \ 96 | xnet::Logger(__FILE__, __LINE__, xnet::Logger::TRACE, __func__).stream() 97 | #define LOG_DEBUG if (xnet::Logger::logLevel() <= xnet::Logger::DEBUG) \ 98 | xnet::Logger(__FILE__, __LINE__, xnet::Logger::DEBUG, __func__).stream() 99 | #define LOG_INFO if (xnet::Logger::logLevel() <= xnet::Logger::INFO) \ 100 | xnet::Logger(__FILE__, __LINE__).stream() 101 | #define LOG_WARN xnet::Logger(__FILE__, __LINE__, xnet::Logger::WARN).stream() 102 | #define LOG_ERROR xnet::Logger(__FILE__, __LINE__, xnet::Logger::ERROR).stream() 103 | #define LOG_FATAL xnet::Logger(__FILE__, __LINE__, xnet::Logger::FATAL).stream() 104 | #define LOG_SYSERR xnet::Logger(__FILE__, __LINE__, false).stream() 105 | #define LOG_SYSFATAL xnet::Logger(__FILE__, __LINE__, true).stream() 106 | 107 | const char* strerror_tl(int savedErrno); 108 | 109 | // Taken from glog/logging.h 110 | // 111 | // Check that the input is non NULL. This very useful in constructor 112 | // initializer lists. 113 | 114 | #define CHECK_NOTNULL(val) \ 115 | ::xnet::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val)) 116 | 117 | // A small helper for CHECK_NOTNULL(). 118 | template 119 | T* CheckNotNull(Logger::SourceFile file, int line, const char *names, T* ptr) 120 | { 121 | if (ptr == NULL) 122 | { 123 | Logger(file, line, Logger::FATAL).stream() << names; 124 | } 125 | return ptr; 126 | } 127 | 128 | } 129 | 130 | #endif // _XNET_COMMON_LOGGING_H_ 131 | -------------------------------------------------------------------------------- /common/Makefile: -------------------------------------------------------------------------------- 1 | # cross compile... 2 | CROSS_COMPILE = 3 | 4 | CC = $(CROSS_COMPILE)gcc 5 | CXX = $(CROSS_COMPILE)g++ 6 | AR = $(CROSS_COMPILE)ar 7 | 8 | ARFLAGS = cr 9 | RM = -rm -rf 10 | MAKE = make 11 | 12 | debug = y 13 | 14 | 15 | CFLAGS = -D_FILE_OFFSET_BITS=64\ 16 | -Wall\ 17 | -Wextra\ 18 | -Werror\ 19 | -Wconversion\ 20 | -Wno-unused-parameter\ 21 | -Wpointer-arith\ 22 | -Wshadow\ 23 | -Wwrite-strings\ 24 | -march=native 25 | 26 | CPPFLAGS = -Wold-style-cast\ 27 | -Woverloaded-virtual 28 | 29 | ifeq ($(debug), y) 30 | CFLAGS += -g 31 | else 32 | CFLAGS += -O2 -s 33 | endif 34 | 35 | DLDFLAGS += -rdynamic 36 | 37 | # source file(s), including c file(s) cpp file(s) 38 | # you can also use $(wildcard *.c), etc. 39 | SRC_C := $(wildcard *.c) 40 | SRC_CPP := $(wildcard *.cpp) 41 | 42 | # object file(s) 43 | OBJ_C := $(patsubst %.c,%.o,$(SRC_C)) 44 | OBJ_CPP := $(patsubst %.cpp,%.o,$(SRC_CPP)) 45 | 46 | 47 | LIB = libcommon.a 48 | 49 | 50 | $(LIB): $(OBJ_C) $(OBJ_CPP) 51 | @echo "Generating static library: " $(notdir $@) 52 | @$(AR) $(ARFLAGS) $@ $^ 53 | make --no-print-directory post-build 54 | # make clean 55 | 56 | 57 | 58 | # make all .c or .cpp 59 | %.o: %.c 60 | @echo "Compling: " $(addsuffix .c, $(basename $(notdir $@))) 61 | @$(CC) $(CFLAGS) -c $< -o $@ 62 | 63 | %.o: %.cpp 64 | @echo "Compling: " $(addsuffix .cpp, $(basename $(notdir $@))) 65 | @$(CXX) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ 66 | 67 | clean: 68 | @echo "cleaning..." 69 | @$(RM) $(LIB) 70 | @$(RM) *.o *.back *~ 71 | 72 | post-build: 73 | cp $(LIB) ../ 74 | 75 | 76 | .PHONY: all clean 77 | .SECONDARY:post-build 78 | 79 | -------------------------------------------------------------------------------- /common/Mutex.h: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style license 2 | // that can be found in the License file. 3 | // 4 | 5 | #ifndef _XNET_COMMON_MUTEX_H_ 6 | #define _XNET_COMMON_MUTEX_H_ 7 | 8 | #include "CurrentThread.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace xnet 15 | { 16 | 17 | class MutexLock: boost::noncopyable 18 | { 19 | public: 20 | MutexLock() : 21 | holder_(0) 22 | { 23 | int ret = pthread_mutex_init(&mutex_, NULL); 24 | assert(ret == 0); 25 | (void) ret; 26 | } 27 | 28 | ~MutexLock() 29 | { 30 | assert(holder_ == 0); 31 | int ret = pthread_mutex_destroy(&mutex_); 32 | assert(ret == 0); 33 | (void) ret; 34 | } 35 | 36 | bool isLockedByThisThread() 37 | { 38 | return holder_ == CurrentThread::tid(); 39 | } 40 | 41 | void assertLocked() 42 | { 43 | assert(isLockedByThisThread()); 44 | } 45 | 46 | // internal usage 47 | 48 | void lock() 49 | { 50 | pthread_mutex_lock(&mutex_); 51 | holder_ = CurrentThread::tid(); 52 | } 53 | 54 | void unlock() 55 | { 56 | holder_ = 0; 57 | pthread_mutex_unlock(&mutex_); 58 | } 59 | 60 | pthread_mutex_t* getPthreadMutex() /* non-const */ 61 | { 62 | return &mutex_; 63 | } 64 | 65 | private: 66 | 67 | pthread_mutex_t mutex_; 68 | pid_t holder_; 69 | }; 70 | 71 | class MutexLockGuard: boost::noncopyable 72 | { 73 | public: 74 | explicit MutexLockGuard(MutexLock& mutex) : 75 | mutex_(mutex) 76 | { 77 | mutex_.lock(); 78 | } 79 | 80 | ~MutexLockGuard() 81 | { 82 | mutex_.unlock(); 83 | } 84 | 85 | private: 86 | 87 | MutexLock& mutex_; 88 | }; 89 | 90 | } 91 | 92 | // Prevent misuse like: 93 | // MutexLockGuard(mutex_); 94 | // A tempory object doesn't hold the lock for long! 95 | #define MutexLockGuard(x) error "Missing guard object name" 96 | 97 | #endif // _XNET_COMMON_MUTEX_H_ 98 | -------------------------------------------------------------------------------- /common/ProcessInfo.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "ProcessInfo.h" 4 | #include "FileUtil.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include // snprintf 12 | #include 13 | #include 14 | #include 15 | 16 | namespace xnet 17 | { 18 | namespace detail 19 | { 20 | __thread int t_numOpenedFiles = 0; 21 | int fdDirFilter(const struct dirent* d) 22 | { 23 | if (::isdigit(d->d_name[0])) 24 | { 25 | ++t_numOpenedFiles; 26 | } 27 | return 0; 28 | } 29 | 30 | __thread std::vector* t_pids = NULL; 31 | int taskDirFilter(const struct dirent* d) 32 | { 33 | if (::isdigit(d->d_name[0])) 34 | { 35 | t_pids->push_back(atoi(d->d_name)); 36 | } 37 | return 0; 38 | } 39 | 40 | int scanDir(const char *dirpath, int (*filter)(const struct dirent *)) 41 | { 42 | struct dirent** namelist = NULL; 43 | int result = ::scandir(dirpath, &namelist, filter, alphasort); 44 | assert(namelist == NULL); 45 | return result; 46 | } 47 | 48 | Timestamp g_startTime = Timestamp::now(); 49 | } 50 | } 51 | 52 | using namespace xnet; 53 | using namespace xnet::detail; 54 | 55 | pid_t ProcessInfo::pid() 56 | { 57 | return ::getpid(); 58 | } 59 | 60 | std::string ProcessInfo::pidString() 61 | { 62 | char buf[32]; 63 | snprintf(buf, sizeof buf, "%d", pid()); 64 | return buf; 65 | } 66 | 67 | uid_t ProcessInfo::uid() 68 | { 69 | return ::getuid(); 70 | } 71 | 72 | std::string ProcessInfo::username() 73 | { 74 | struct passwd pwd; 75 | struct passwd* result = NULL; 76 | char buf[8192]; 77 | const char* name = "unknownuser"; 78 | 79 | getpwuid_r(uid(), &pwd, buf, sizeof buf, &result); 80 | if (result) 81 | { 82 | name = pwd.pw_name; 83 | } 84 | return name; 85 | } 86 | 87 | uid_t ProcessInfo::euid() 88 | { 89 | return ::geteuid(); 90 | } 91 | 92 | Timestamp ProcessInfo::startTime() 93 | { 94 | return g_startTime; 95 | } 96 | 97 | std::string ProcessInfo::hostname() 98 | { 99 | char buf[64] = "unknownhost"; 100 | buf[sizeof(buf)-1] = '\0'; 101 | ::gethostname(buf, sizeof buf); 102 | return buf; 103 | } 104 | 105 | std::string ProcessInfo::procStatus() 106 | { 107 | std::string result; 108 | FileUtil::readFile("/proc/self/status", 65536, &result); 109 | 110 | return result; 111 | } 112 | 113 | int ProcessInfo::openedFiles() 114 | { 115 | t_numOpenedFiles = 0; 116 | scanDir("/proc/self/fd", fdDirFilter); 117 | return t_numOpenedFiles; 118 | } 119 | 120 | int ProcessInfo::maxOpenFiles() 121 | { 122 | struct rlimit rl; 123 | if (::getrlimit(RLIMIT_NOFILE, &rl)) 124 | { 125 | return openedFiles(); 126 | } 127 | else 128 | { 129 | return static_cast(rl.rlim_cur); 130 | } 131 | } 132 | 133 | int ProcessInfo::numThreads() 134 | { 135 | int result = 0; 136 | std::string status = procStatus(); 137 | size_t pos = status.find("Threads:"); 138 | if (pos != std::string::npos) 139 | { 140 | result = ::atoi(status.c_str() + pos + 8); 141 | } 142 | return result; 143 | } 144 | 145 | std::vector ProcessInfo::threads() 146 | { 147 | std::vector result; 148 | t_pids = &result; 149 | scanDir("/proc/self/task", taskDirFilter); 150 | t_pids = NULL; 151 | std::sort(result.begin(), result.end()); 152 | return result; 153 | } 154 | 155 | -------------------------------------------------------------------------------- /common/ProcessInfo.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _XNET_COMMON_PROCESSINFO_H_ 3 | #define _XNET_COMMON_PROCESSINFO_H_ 4 | 5 | 6 | #include "Timestamp.h" 7 | #include 8 | 9 | namespace xnet 10 | { 11 | 12 | namespace ProcessInfo 13 | { 14 | pid_t pid(); 15 | std::string pidString(); 16 | uid_t uid(); 17 | std::string username(); 18 | uid_t euid(); 19 | Timestamp startTime(); 20 | 21 | std::string hostname(); 22 | 23 | /// read /proc/self/status 24 | std::string procStatus(); 25 | 26 | int openedFiles(); 27 | int maxOpenFiles(); 28 | 29 | int numThreads(); 30 | std::vector threads(); 31 | } 32 | 33 | } 34 | 35 | #endif // _XNET_COMMON_PROCESSINFO_H_ 36 | -------------------------------------------------------------------------------- /common/Singleton.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_SINGLETON_H_ 2 | #define _XNET_COMMON_SINGLETON_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace xnet 9 | { 10 | 11 | template 12 | class Singleton: boost::noncopyable 13 | { 14 | public: 15 | static T& instance() 16 | { 17 | pthread_once(&ponce_, &Singleton::init); 18 | return *value_; 19 | } 20 | 21 | private: 22 | Singleton(); 23 | ~Singleton(); 24 | 25 | static void init() 26 | { 27 | value_ = new T(); 28 | ::atexit(destroy); 29 | } 30 | 31 | static void destroy() 32 | { 33 | typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1]; 34 | delete value_; 35 | } 36 | 37 | private: 38 | static pthread_once_t ponce_; 39 | static T* value_; 40 | }; 41 | 42 | template 43 | pthread_once_t Singleton::ponce_ = PTHREAD_ONCE_INIT; 44 | 45 | template 46 | T* Singleton::value_ = NULL; 47 | 48 | } 49 | #endif //_XNET_COMMON_SINGLETON_H_ 50 | 51 | -------------------------------------------------------------------------------- /common/StringPiece.h: -------------------------------------------------------------------------------- 1 | // Taken from PCRE pcre_stringpiece.h 2 | // 3 | // Copyright (c) 2005, Google Inc. 4 | // All rights reserved. 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are 8 | // met: 9 | // 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above 13 | // copyright notice, this list of conditions and the following disclaimer 14 | // in the documentation and/or other materials provided with the 15 | // distribution. 16 | // * Neither the name of Google Inc. nor the names of its 17 | // contributors may be used to endorse or promote products derived from 18 | // this software without specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | // 32 | // Author: Sanjay Ghemawat 33 | // 34 | // A string like object that points into another piece of memory. 35 | // Useful for providing an interface that allows clients to easily 36 | // pass in either a "const char*" or a "string". 37 | // 38 | // Arghh! I wish C++ literals were automatically of type "string". 39 | 40 | #ifndef _XNET_COMMON_STRINGPIECE_H_ 41 | #define _XNET_COMMON_STRINGPIECE_H_ 42 | 43 | #include 44 | #include // for ostream forward-declaration 45 | 46 | #include 47 | 48 | namespace xnet 49 | { 50 | 51 | class StringPiece 52 | { 53 | private: 54 | const char* ptr_; 55 | int length_; 56 | 57 | public: 58 | // We provide non-explicit singleton constructors so users can pass 59 | // in a "const char*" or a "string" wherever a "StringPiece" is 60 | // expected. 61 | StringPiece() : 62 | ptr_(NULL), length_(0) 63 | { 64 | } 65 | StringPiece(const char* str) : 66 | ptr_(str), length_(static_cast(strlen(ptr_))) 67 | { 68 | } 69 | StringPiece(const unsigned char* str) : 70 | ptr_(reinterpret_cast(str)), length_(static_cast(strlen(ptr_))) 71 | { 72 | } 73 | StringPiece(const std::string& str) : 74 | ptr_(str.data()), length_(static_cast(str.size())) 75 | { 76 | } 77 | 78 | StringPiece(const char* offset, int len) : 79 | ptr_(offset), length_(len) 80 | { 81 | } 82 | 83 | // data() may return a pointer to a buffer with embedded NULs, and the 84 | // returned buffer may or may not be null terminated. Therefore it is 85 | // typically a mistake to pass data() to a routine that expects a NUL 86 | // terminated string. Use "as_string().c_str()" if you really need to do 87 | // this. Or better yet, change your routine so it does not rely on NUL 88 | // termination. 89 | const char* data() const 90 | { 91 | return ptr_; 92 | } 93 | int size() const 94 | { 95 | return length_; 96 | } 97 | bool empty() const 98 | { 99 | return length_ == 0; 100 | } 101 | 102 | void clear() 103 | { 104 | ptr_ = NULL; 105 | length_ = 0; 106 | } 107 | void set(const char* buffer, int len) 108 | { 109 | ptr_ = buffer; 110 | length_ = len; 111 | } 112 | void set(const char* str) 113 | { 114 | ptr_ = str; 115 | length_ = static_cast(strlen(str)); 116 | } 117 | void set(const void* buffer, int len) 118 | { 119 | ptr_ = reinterpret_cast(buffer); 120 | length_ = len; 121 | } 122 | 123 | char operator[](int i) const 124 | { 125 | return ptr_[i]; 126 | } 127 | 128 | void remove_prefix(int n) 129 | { 130 | ptr_ += n; 131 | length_ -= n; 132 | } 133 | 134 | void remove_suffix(int n) 135 | { 136 | length_ -= n; 137 | } 138 | 139 | bool operator==(const StringPiece& x) const 140 | { 141 | return ((length_ == x.length_) && (memcmp(ptr_, x.ptr_, length_) == 0)); 142 | } 143 | bool operator!=(const StringPiece& x) const 144 | { 145 | return !(*this == x); 146 | } 147 | 148 | #define STRINGPIECE_BINARY_PREDICATE(cmp,auxcmp) \ 149 | bool operator cmp (const StringPiece& x) const { \ 150 | int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); \ 151 | return ((r auxcmp 0) || ((r == 0) && (length_ cmp x.length_))); \ 152 | } 153 | STRINGPIECE_BINARY_PREDICATE(<, <); 154 | STRINGPIECE_BINARY_PREDICATE(<=, <); 155 | STRINGPIECE_BINARY_PREDICATE(>=, >); 156 | STRINGPIECE_BINARY_PREDICATE(>, >); 157 | 158 | 159 | #undef STRINGPIECE_BINARY_PREDICATE 160 | 161 | int compare(const StringPiece& x) const 162 | { 163 | int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); 164 | if (r == 0) 165 | { 166 | if (length_ < x.length_) 167 | r = -1; 168 | else if (length_ > x.length_) 169 | r = +1; 170 | } 171 | return r; 172 | } 173 | 174 | std::string as_string() const 175 | { 176 | return std::string(data(), size()); 177 | } 178 | 179 | void CopyToString(std::string* target) const 180 | { 181 | target->assign(ptr_, length_); 182 | } 183 | 184 | void CopyToStdString(std::string* target) const 185 | { 186 | target->assign(ptr_, length_); 187 | } 188 | 189 | // Does "this" start with "x" 190 | bool starts_with(const StringPiece& x) const 191 | { 192 | return ((length_ >= x.length_) && (memcmp(ptr_, x.ptr_, x.length_) == 0)); 193 | } 194 | }; 195 | 196 | } // namespace xnet 197 | 198 | // ------------------------------------------------------------------ 199 | // Functions used to create STL containers that use StringPiece 200 | // Remember that a StringPiece's lifetime had better be less than 201 | // that of the underlying string or char*. If it is not, then you 202 | // cannot safely store a StringPiece into an STL container 203 | // ------------------------------------------------------------------ 204 | 205 | #ifdef HAVE_TYPE_TRAITS 206 | // This makes vector really fast for some STL implementations 207 | template<> struct __type_traits 208 | { 209 | typedef __true_type has_trivial_default_constructor; 210 | typedef __true_type has_trivial_copy_constructor; 211 | typedef __true_type has_trivial_assignment_operator; 212 | typedef __true_type has_trivial_destructor; 213 | typedef __true_type is_POD_type; 214 | }; 215 | #endif 216 | 217 | // allow StringPiece to be logged 218 | std::ostream& operator<<(std::ostream& o, const xnet::StringPiece& piece); 219 | 220 | #endif // _XNET_COMMON_STRINGPIECE_H_ 221 | -------------------------------------------------------------------------------- /common/Thread.cpp: -------------------------------------------------------------------------------- 1 | #include "Thread.h" 2 | #include "CurrentThread.h" 3 | #include "Exception.h" 4 | //#include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace xnet 17 | { 18 | namespace CurrentThread 19 | { 20 | __thread int t_cachedTid = 0; 21 | __thread char t_tidString[32]; 22 | __thread const char* t_threadName = "unknown"; 23 | const bool sameType = boost::is_same::value; 24 | BOOST_STATIC_ASSERT (sameType); 25 | } 26 | 27 | namespace detail 28 | { 29 | 30 | pid_t gettid() 31 | { 32 | return static_cast(::syscall(SYS_gettid)); 33 | } 34 | 35 | void afterFork() 36 | { 37 | xnet::CurrentThread::t_cachedTid = 0; 38 | xnet::CurrentThread::t_threadName = "main"; 39 | CurrentThread::tid(); 40 | // no need to call pthread_atfork(NULL, NULL, &afterFork); 41 | } 42 | 43 | class ThreadNameInitializer 44 | { 45 | public: 46 | ThreadNameInitializer() 47 | { 48 | xnet::CurrentThread::t_threadName = "main"; 49 | CurrentThread::tid(); 50 | pthread_atfork(NULL, NULL, &afterFork); 51 | } 52 | }; 53 | 54 | ThreadNameInitializer init; 55 | } 56 | } 57 | 58 | 59 | 60 | 61 | 62 | 63 | using namespace xnet; 64 | 65 | void CurrentThread::cacheTid() 66 | { 67 | if (t_cachedTid == 0) 68 | { 69 | t_cachedTid = detail::gettid(); 70 | int n = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid); 71 | assert(n == 6); 72 | (void) n; 73 | } 74 | } 75 | 76 | bool CurrentThread::isMainThread() 77 | { 78 | return tid() == ::getpid(); 79 | } 80 | 81 | AtomicInt32 Thread::numCreated_; 82 | 83 | Thread::Thread(const ThreadFunc& func, const std::string& n) : 84 | started_(false), pthreadId_(0), tid_(0), func_(func), name_(n) 85 | { 86 | numCreated_.increment(); 87 | } 88 | 89 | Thread::~Thread() 90 | { 91 | // no join 92 | } 93 | 94 | void Thread::start() 95 | { 96 | assert(!started_); 97 | started_ = true; 98 | errno = pthread_create(&pthreadId_, NULL, &startThread, this); 99 | if (errno != 0) 100 | { 101 | //LOG_SYSFATAL << "Failed in pthread_create"; 102 | } 103 | } 104 | 105 | int Thread::join() 106 | { 107 | assert(started_); 108 | return pthread_join(pthreadId_, NULL); 109 | } 110 | 111 | void* Thread::startThread(void* obj) 112 | { 113 | Thread* thread = static_cast(obj); 114 | thread->runInThread(); 115 | return NULL; 116 | } 117 | 118 | void Thread::runInThread() 119 | { 120 | tid_ = CurrentThread::tid(); 121 | xnet::CurrentThread::t_threadName = name_.c_str(); 122 | try 123 | { 124 | func_(); 125 | xnet::CurrentThread::t_threadName = "finished"; 126 | } 127 | catch (const Exception& ex) 128 | { 129 | xnet::CurrentThread::t_threadName = "crashed"; 130 | fprintf(stderr, "exception caught in Thread %s\n", name_.c_str()); 131 | fprintf(stderr, "reason: %s\n", ex.what()); 132 | fprintf(stderr, "stack trace: %s\n", ex.stackTrace()); 133 | abort(); 134 | } 135 | catch (const std::exception& ex) 136 | { 137 | xnet::CurrentThread::t_threadName = "crashed"; 138 | fprintf(stderr, "exception caught in Thread %s\n", name_.c_str()); 139 | fprintf(stderr, "reason: %s\n", ex.what()); 140 | abort(); 141 | } 142 | catch (...) 143 | { 144 | xnet::CurrentThread::t_threadName = "crashed"; 145 | fprintf(stderr, "unknown exception caught in Thread %s\n", name_.c_str()); 146 | throw; // rethrow 147 | } 148 | } 149 | 150 | -------------------------------------------------------------------------------- /common/Thread.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_THREAD_H_ 2 | #define _XNET_COMMON_THREAD_H_ 3 | 4 | #include "Atomic.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace xnet 12 | { 13 | 14 | class Thread: boost::noncopyable 15 | { 16 | public: 17 | typedef boost::function ThreadFunc; 18 | 19 | explicit Thread(const ThreadFunc&, const std::string& name = std::string()); 20 | ~Thread(); 21 | 22 | void start(); 23 | int join(); // return pthread_join() 24 | 25 | bool isStarted() const 26 | { 27 | return started_; 28 | } 29 | 30 | pid_t tid() const 31 | { 32 | return tid_; 33 | } 34 | const std::string& name() const 35 | { 36 | return name_; 37 | } 38 | 39 | static int numCreated() 40 | { 41 | return numCreated_.get(); 42 | } 43 | 44 | private: 45 | static void* startThread(void* thread); 46 | void runInThread(); 47 | 48 | bool started_; 49 | pthread_t pthreadId_; 50 | pid_t tid_; 51 | ThreadFunc func_; 52 | std::string name_; 53 | 54 | static AtomicInt32 numCreated_; 55 | }; 56 | 57 | } 58 | #endif 59 | -------------------------------------------------------------------------------- /common/ThreadLocal.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_THREADLOCAL_H_ 2 | #define _XNET_COMMON_THREADLOCAL_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace xnet 8 | { 9 | 10 | template 11 | class ThreadLocal: boost::noncopyable 12 | { 13 | public: 14 | ThreadLocal() 15 | { 16 | pthread_key_create(&pkey_, &ThreadLocal::destructor); 17 | } 18 | 19 | ~ThreadLocal() 20 | { 21 | pthread_key_delete(pkey_); 22 | } 23 | 24 | T& value() 25 | { 26 | T* perThreadValue = static_cast(pthread_getspecific(pkey_)); 27 | if (!perThreadValue) 28 | { 29 | T* newObj = new T(); 30 | pthread_setspecific(pkey_, newObj); 31 | perThreadValue = newObj; 32 | } 33 | return *perThreadValue; 34 | } 35 | 36 | private: 37 | 38 | static void destructor(void *x) 39 | { 40 | T* obj = static_cast(x); 41 | typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1]; 42 | delete obj; 43 | } 44 | 45 | private: 46 | pthread_key_t pkey_; 47 | }; 48 | 49 | } 50 | #endif //_XNET_COMMON_THREADLOCAL_H_ 51 | -------------------------------------------------------------------------------- /common/ThreadLocalSingleton.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef _XNET_COMMON_THREADLOCALSINGLETON_H_ 4 | #define _XNET_COMMON_THREADLOCALSINGLETON_H_ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace xnet 11 | { 12 | 13 | template 14 | class ThreadLocalSingleton: boost::noncopyable 15 | { 16 | public: 17 | 18 | static T& instance() 19 | { 20 | if (!t_value_) 21 | { 22 | t_value_ = new T(); 23 | deleter_.set(t_value_); 24 | } 25 | return *t_value_; 26 | } 27 | 28 | static T* pointer() 29 | { 30 | return t_value_; 31 | } 32 | 33 | private: 34 | 35 | static void destructor(void* obj) 36 | { 37 | assert(obj == t_value_); 38 | typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1]; 39 | delete t_value_; 40 | t_value_ = 0; 41 | } 42 | 43 | class Deleter 44 | { 45 | public: 46 | Deleter() 47 | { 48 | pthread_key_create(&pkey_, &ThreadLocalSingleton::destructor); 49 | } 50 | 51 | ~Deleter() 52 | { 53 | pthread_key_delete(pkey_); 54 | } 55 | 56 | void set(T* newObj) 57 | { 58 | assert(pthread_getspecific(pkey_) == NULL); 59 | pthread_setspecific(pkey_, newObj); 60 | } 61 | 62 | pthread_key_t pkey_; 63 | }; 64 | 65 | static __thread T* t_value_; 66 | static Deleter deleter_; 67 | }; 68 | 69 | template 70 | __thread T* ThreadLocalSingleton::t_value_ = 0; 71 | 72 | template 73 | typename ThreadLocalSingleton::Deleter ThreadLocalSingleton::deleter_; 74 | 75 | } 76 | #endif //_XNET_COMMON_THREADLOCALSINGLETON_H_ 77 | -------------------------------------------------------------------------------- /common/ThreadPool.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadPool.h" 2 | #include "Exception.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace xnet; 9 | 10 | ThreadPool::ThreadPool(const std::string& name) : 11 | mutex_(), cond_(mutex_), name_(name), running_(false) 12 | { 13 | } 14 | 15 | ThreadPool::~ThreadPool() 16 | { 17 | if (running_) 18 | { 19 | stop(); 20 | } 21 | } 22 | 23 | void ThreadPool::start(int numThreads) 24 | { 25 | assert(threads_.empty()); 26 | running_ = true; 27 | threads_.reserve(numThreads); 28 | for (int i = 0; i < numThreads; ++i) 29 | { 30 | char id[32]; 31 | snprintf(id, sizeof id, "%d", i); 32 | threads_.push_back(new xnet::Thread(boost::bind(&ThreadPool::runInThread, this), name_ + id)); 33 | threads_[i].start(); 34 | } 35 | } 36 | 37 | void ThreadPool::stop() 38 | { 39 | { 40 | MutexLockGuard lock(mutex_); 41 | running_ = false; 42 | cond_.notifyAll(); 43 | } 44 | for_each(threads_.begin(), threads_.end(), boost::bind(&xnet::Thread::join, _1)); 45 | } 46 | 47 | void ThreadPool::run(const Task& task) 48 | { 49 | if (threads_.empty()) 50 | { 51 | task(); 52 | } 53 | else 54 | { 55 | MutexLockGuard lock(mutex_); 56 | queue_.push_back(task); 57 | cond_.notify(); 58 | } 59 | } 60 | 61 | ThreadPool::Task ThreadPool::take() 62 | { 63 | MutexLockGuard lock(mutex_); 64 | // always use a while-loop, due to spurious wakeup 65 | while (queue_.empty() && running_) 66 | { 67 | cond_.wait(); 68 | } 69 | Task task; 70 | if (!queue_.empty()) 71 | { 72 | task = queue_.front(); 73 | queue_.pop_front(); 74 | } 75 | return task; 76 | } 77 | 78 | void ThreadPool::runInThread() 79 | { 80 | try 81 | { 82 | while (running_) 83 | { 84 | Task task(take()); 85 | if (task) 86 | { 87 | task(); 88 | } 89 | } 90 | } 91 | catch (const Exception& ex) 92 | { 93 | fprintf(stderr, "exception caught in ThreadPool %s\n", name_.c_str()); 94 | fprintf(stderr, "reason: %s\n", ex.what()); 95 | fprintf(stderr, "stack trace: %s\n", ex.stackTrace()); 96 | abort(); 97 | } 98 | catch (const std::exception& ex) 99 | { 100 | fprintf(stderr, "exception caught in ThreadPool %s\n", name_.c_str()); 101 | fprintf(stderr, "reason: %s\n", ex.what()); 102 | abort(); 103 | } 104 | catch (...) 105 | { 106 | fprintf(stderr, "unknown exception caught in ThreadPool %s\n", name_.c_str()); 107 | throw; // rethrow 108 | } 109 | } 110 | 111 | -------------------------------------------------------------------------------- /common/ThreadPool.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _XNET_COMMON_THREADPOOL_H_ 3 | #define _XNET_COMMON_THREADPOOL_H_ 4 | 5 | #include "Condition.h" 6 | #include "Mutex.h" 7 | #include "Thread.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | namespace xnet 17 | { 18 | 19 | class ThreadPool: boost::noncopyable 20 | { 21 | public: 22 | typedef boost::function Task; 23 | 24 | explicit ThreadPool(const std::string& name = std::string()); 25 | ~ThreadPool(); 26 | 27 | void start(int numThreads); 28 | void stop(); 29 | 30 | void run(const Task& f); 31 | 32 | private: 33 | void runInThread(); 34 | Task take(); 35 | 36 | MutexLock mutex_; 37 | Condition cond_; 38 | std::string name_; 39 | boost::ptr_vector threads_; 40 | std::deque queue_; 41 | bool running_; 42 | }; 43 | 44 | } 45 | 46 | #endif //_XNET_COMMON_THREADPOOL_H_ 47 | -------------------------------------------------------------------------------- /common/Timestamp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include "Timestamp.h" 6 | 7 | using namespace xnet; 8 | 9 | //static assert(assert at compile) 10 | BOOST_STATIC_ASSERT(sizeof(Timestamp) == sizeof(int64_t)); 11 | 12 | Timestamp::Timestamp(int64_t microseconds) : 13 | microSecondsSinceEpoch_(microseconds) 14 | { 15 | } 16 | 17 | std::string Timestamp::toString() const 18 | { 19 | char buf[32] = { 0 }; 20 | int64_t seconds = microSecondsSinceEpoch_ / kmicroSeconds_PerSecond; 21 | int64_t microseconds = microSecondsSinceEpoch_ % kmicroSeconds_PerSecond; 22 | snprintf(buf, sizeof(buf)-1, "%" PRId64 ".%06" PRId64 "", seconds, microseconds); 23 | return buf; //pass by value 24 | } 25 | 26 | std::string Timestamp::toFormattedString() const 27 | { 28 | char buf[32] = { 0 }; 29 | time_t seconds = static_cast(microSecondsSinceEpoch_ / kmicroSeconds_PerSecond); 30 | int microseconds = static_cast(microSecondsSinceEpoch_ % kmicroSeconds_PerSecond); 31 | struct tm tm_time; 32 | gmtime_r(&seconds, &tm_time); 33 | 34 | snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d.%06d", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, microseconds); 35 | return buf; 36 | } 37 | 38 | Timestamp Timestamp::now() 39 | { 40 | struct timeval tv; 41 | gettimeofday(&tv, NULL); 42 | int64_t seconds = tv.tv_sec; 43 | return Timestamp(seconds * kmicroSeconds_PerSecond + tv.tv_usec); 44 | } 45 | 46 | Timestamp Timestamp::invalid() 47 | { 48 | return Timestamp(); 49 | } 50 | 51 | -------------------------------------------------------------------------------- /common/Timestamp.h: -------------------------------------------------------------------------------- 1 | #ifndef _XNET_COMMON_TIMESTAMP_H_ 2 | #define _XNET_COMMON_TIMESTAMP_H_ 3 | 4 | #include 5 | #include 6 | 7 | #define __STDC_FORMAT_MACROS 8 | #include 9 | #undef __STDC_FORMAT_MACROS 10 | 11 | namespace xnet 12 | { 13 | 14 | class Timestamp 15 | { 16 | public: 17 | 18 | Timestamp() : 19 | microSecondsSinceEpoch_(0) 20 | { 21 | } 22 | 23 | 24 | explicit Timestamp(int64_t microSeconds); 25 | 26 | 27 | void swap(Timestamp& that) 28 | { 29 | std::swap(microSecondsSinceEpoch_, that.microSecondsSinceEpoch_); 30 | } 31 | 32 | std::string toString() const; 33 | std::string toFormattedString() const; 34 | 35 | bool valid() const 36 | { 37 | return microSecondsSinceEpoch_ > 0; 38 | } 39 | 40 | int64_t microSeconds() const 41 | { 42 | return microSecondsSinceEpoch_; 43 | } 44 | int64_t microSecondsSinceEpoch() const { return microSecondsSinceEpoch_; } 45 | time_t secondsSinceEpoch() const //microSecondsSinceEpoch 46 | { 47 | return static_cast(microSecondsSinceEpoch_ / kmicroSeconds_PerSecond); 48 | } 49 | 50 | 51 | static Timestamp now(); 52 | static Timestamp invalid(); 53 | static const int kmicroSeconds_PerSecond = 1000 * 1000; 54 | 55 | private: 56 | int64_t microSecondsSinceEpoch_; 57 | }; 58 | 59 | inline bool operator<(Timestamp left, Timestamp right) 60 | { 61 | return left.microSeconds() < right.microSeconds(); 62 | } 63 | 64 | inline bool operator==(Timestamp left, Timestamp right) 65 | { 66 | return left.microSeconds() == right.microSeconds(); 67 | } 68 | 69 | inline double timeDifference(Timestamp high, Timestamp low) 70 | { 71 | int64_t diff = high.microSeconds() - low.microSeconds(); 72 | return static_cast(diff) / Timestamp::kmicroSeconds_PerSecond; 73 | } 74 | 75 | inline Timestamp addTime(Timestamp timestamp, double seconds) 76 | { 77 | int64_t delta = static_cast(seconds * Timestamp::kmicroSeconds_PerSecond); 78 | return Timestamp(timestamp.microSeconds() + delta); 79 | } 80 | 81 | } 82 | #endif // _XNET_COMMON_TIMESTAMP_H_ 83 | -------------------------------------------------------------------------------- /common/tests/Atomic_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include "Atomic.h" 2 | #include 3 | 4 | int main() 5 | { 6 | { 7 | xnet::AtomicInt64 a0; 8 | assert(a0.get() == 0); 9 | assert(a0.getAndAdd(1) == 0); 10 | assert(a0.get() == 1); 11 | assert(a0.addAndGet(2) == 3); 12 | assert(a0.get() == 3); 13 | assert(a0.incrementAndGet() == 4); 14 | assert(a0.get() == 4); 15 | a0.increment(); 16 | assert(a0.get() == 5); 17 | assert(a0.addAndGet(-3) == 2); 18 | assert(a0.getAndSet(100) == 2); 19 | assert(a0.get() == 100); 20 | } 21 | 22 | { 23 | xnet::AtomicInt32 a1; 24 | assert(a1.get() == 0); 25 | assert(a1.getAndAdd(1) == 0); 26 | assert(a1.get() == 1); 27 | assert(a1.addAndGet(2) == 3); 28 | assert(a1.get() == 3); 29 | assert(a1.incrementAndGet() == 4); 30 | assert(a1.get() == 4); 31 | a1.increment(); 32 | assert(a1.get() == 5); 33 | assert(a1.addAndGet(-3) == 2); 34 | assert(a1.getAndSet(100) == 2); 35 | assert(a1.get() == 100); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /common/tests/BlockingQueue_bench.cpp: -------------------------------------------------------------------------------- 1 | #include "BlockingQueue.h" 2 | #include "CountDownLatch.h" 3 | #include "Thread.h" 4 | #include "Timestamp.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class Bench 13 | { 14 | public: 15 | Bench(int numThreads) 16 | : latch_(numThreads), 17 | threads_(numThreads) 18 | { 19 | for (int i = 0; i < numThreads; ++i) 20 | { 21 | char name[32]; 22 | snprintf(name, sizeof name, "work thread %d", i); 23 | threads_.push_back(new xnet::Thread( 24 | boost::bind(&Bench::threadFunc, this), std::string(name))); 25 | } 26 | for_each(threads_.begin(), threads_.end(), boost::bind(&xnet::Thread::start, _1)); 27 | } 28 | 29 | void run(int times) 30 | { 31 | printf("waiting for count down latch\n"); 32 | latch_.wait(); 33 | printf("all threads started\n"); 34 | for (int i = 0; i < times; ++i) 35 | { 36 | xnet::Timestamp now(xnet::Timestamp::now()); 37 | queue_.put(now); 38 | usleep(1000); 39 | } 40 | } 41 | 42 | void joinAll() 43 | { 44 | for (size_t i = 0; i < threads_.size(); ++i) 45 | { 46 | queue_.put(xnet::Timestamp::invalid()); 47 | } 48 | 49 | for_each(threads_.begin(), threads_.end(), boost::bind(&xnet::Thread::join, _1)); 50 | } 51 | 52 | private: 53 | 54 | void threadFunc() 55 | { 56 | printf("tid=%d, %s started\n", 57 | xnet::CurrentThread::tid(), 58 | xnet::CurrentThread::name()); 59 | 60 | std::map delays; 61 | latch_.countDown(); 62 | bool running = true; 63 | while (running) 64 | { 65 | xnet::Timestamp t(queue_.take()); 66 | xnet::Timestamp now(xnet::Timestamp::now()); 67 | if (t.valid()) 68 | { 69 | int delay = static_cast(timeDifference(now, t) * 1000000); 70 | // printf("tid=%d, latency = %d us\n", 71 | // xnet::CurrentThread::tid(), delay); 72 | ++delays[delay]; 73 | } 74 | running = t.valid(); 75 | } 76 | 77 | printf("tid=%d, %s stopped\n", 78 | xnet::CurrentThread::tid(), 79 | xnet::CurrentThread::name()); 80 | for (std::map::iterator it = delays.begin(); 81 | it != delays.end(); ++it) 82 | { 83 | printf("tid = %d, delay = %d, count = %d\n", 84 | xnet::CurrentThread::tid(), 85 | it->first, it->second); 86 | } 87 | } 88 | 89 | xnet::BlockingQueue queue_; 90 | xnet::CountDownLatch latch_; 91 | boost::ptr_vector threads_; 92 | }; 93 | 94 | int main(int argc, char* argv[]) 95 | { 96 | int threads = argc > 1 ? atoi(argv[1]) : 1; 97 | 98 | Bench t(threads); 99 | t.run(10000); 100 | t.joinAll(); 101 | } 102 | -------------------------------------------------------------------------------- /common/tests/BlockingQueue_test.cpp: -------------------------------------------------------------------------------- 1 | #include "BlockingQueue.h" 2 | #include "CountDownLatch.h" 3 | #include "Thread.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Test 11 | { 12 | public: 13 | Test(int numThreads) 14 | : latch_(numThreads), 15 | threads_(numThreads) 16 | { 17 | for (int i = 0; i < numThreads; ++i) 18 | { 19 | char name[32]; 20 | snprintf(name, sizeof name, "work thread %d", i); 21 | threads_.push_back(new xnet::Thread( 22 | boost::bind(&Test::threadFunc, this), std::string(name))); 23 | } 24 | for_each(threads_.begin(), threads_.end(), boost::bind(&xnet::Thread::start, _1)); 25 | } 26 | 27 | void run(int times) 28 | { 29 | printf("waiting for count down latch\n"); 30 | latch_.wait(); 31 | printf("all threads started\n"); 32 | for (int i = 0; i < times; ++i) 33 | { 34 | char buf[32]; 35 | snprintf(buf, sizeof buf, "hello %d", i); 36 | queue_.put(buf); 37 | printf("tid=%d, put data = %s, size = %zd\n", xnet::CurrentThread::tid(), buf, queue_.size()); 38 | } 39 | } 40 | 41 | void joinAll() 42 | { 43 | for (size_t i = 0; i < threads_.size(); ++i) 44 | { 45 | queue_.put("stop"); 46 | } 47 | 48 | for_each(threads_.begin(), threads_.end(), boost::bind(&xnet::Thread::join, _1)); 49 | } 50 | 51 | private: 52 | 53 | void threadFunc() 54 | { 55 | printf("tid=%d, %s started\n", 56 | xnet::CurrentThread::tid(), 57 | xnet::CurrentThread::name()); 58 | 59 | latch_.countDown(); 60 | bool running = true; 61 | while (running) 62 | { 63 | std::string d(queue_.take()); 64 | printf("tid=%d, get data = %s, size = %zd\n", xnet::CurrentThread::tid(), d.c_str(), queue_.size()); 65 | running = (d != "stop"); 66 | } 67 | 68 | printf("tid=%d, %s stopped\n", 69 | xnet::CurrentThread::tid(), 70 | xnet::CurrentThread::name()); 71 | } 72 | 73 | xnet::BlockingQueue queue_; 74 | xnet::CountDownLatch latch_; 75 | boost::ptr_vector threads_; 76 | }; 77 | 78 | int main() 79 | { 80 | printf("pid=%d, tid=%d\n", ::getpid(), xnet::CurrentThread::tid()); 81 | Test t(5); 82 | t.run(100); 83 | t.joinAll(); 84 | 85 | printf("number of created threads %d\n", xnet::Thread::numCreated()); 86 | } 87 | -------------------------------------------------------------------------------- /common/tests/BoundedBlockingQueue_test.cpp: -------------------------------------------------------------------------------- 1 | #include "BoundedBlockingQueue.h" 2 | #include "CountDownLatch.h" 3 | #include "Thread.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Test 11 | { 12 | public: 13 | Test(int numThreads) 14 | : queue_(20), 15 | latch_(numThreads), 16 | threads_(numThreads) 17 | { 18 | for (int i = 0; i < numThreads; ++i) 19 | { 20 | char name[32]; 21 | snprintf(name, sizeof name, "work thread %d", i); 22 | threads_.push_back(new xnet::Thread( 23 | boost::bind(&Test::threadFunc, this), std::string(name))); 24 | } 25 | for_each(threads_.begin(), threads_.end(), boost::bind(&xnet::Thread::start, _1)); 26 | } 27 | 28 | void run(int times) 29 | { 30 | printf("waiting for count down latch\n"); 31 | latch_.wait(); 32 | printf("all threads started\n"); 33 | for (int i = 0; i < times; ++i) 34 | { 35 | char buf[32]; 36 | snprintf(buf, sizeof buf, "hello %d", i); 37 | queue_.put(buf); 38 | printf("tid=%d, put data = %s, size = %zd\n", xnet::CurrentThread::tid(), buf, queue_.size()); 39 | } 40 | } 41 | 42 | void joinAll() 43 | { 44 | for (size_t i = 0; i < threads_.size(); ++i) 45 | { 46 | queue_.put("stop"); 47 | } 48 | 49 | for_each(threads_.begin(), threads_.end(), boost::bind(&xnet::Thread::join, _1)); 50 | } 51 | 52 | private: 53 | 54 | void threadFunc() 55 | { 56 | printf("tid=%d, %s started\n", 57 | xnet::CurrentThread::tid(), 58 | xnet::CurrentThread::name()); 59 | 60 | latch_.countDown(); 61 | bool running = true; 62 | while (running) 63 | { 64 | std::string d(queue_.take()); 65 | printf("tid=%d, get data = %s, size = %zd\n", xnet::CurrentThread::tid(), d.c_str(), queue_.size()); 66 | running = (d != "stop"); 67 | } 68 | 69 | printf("tid=%d, %s stopped\n", 70 | xnet::CurrentThread::tid(), 71 | xnet::CurrentThread::name()); 72 | } 73 | 74 | xnet::BoundedBlockingQueue queue_; 75 | xnet::CountDownLatch latch_; 76 | boost::ptr_vector threads_; 77 | }; 78 | 79 | int main() 80 | { 81 | printf("pid=%d, tid=%d\n", ::getpid(), xnet::CurrentThread::tid()); 82 | Test t(5); 83 | t.run(100); 84 | t.joinAll(); 85 | 86 | printf("number of created threads %d\n", xnet::Thread::numCreated()); 87 | } 88 | -------------------------------------------------------------------------------- /common/tests/CountDownLatch_test1.cpp: -------------------------------------------------------------------------------- 1 | #include "CountDownLatch.h" 2 | #include "Thread.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace xnet; 10 | 11 | class Test 12 | { 13 | public: 14 | Test(int numThreads) 15 | : latch_(1), 16 | threads_(numThreads) 17 | { 18 | for (int i = 0; i < numThreads; ++i) 19 | { 20 | char name[32]; 21 | snprintf(name, sizeof name, "work thread %d", i); 22 | threads_.push_back(new xnet::Thread( 23 | boost::bind(&Test::threadFunc, this), std::string(name))); 24 | } 25 | for_each(threads_.begin(), threads_.end(), boost::bind(&Thread::start, _1)); 26 | } 27 | 28 | void run() 29 | { 30 | latch_.countDown(); 31 | } 32 | 33 | void joinAll() 34 | { 35 | for_each(threads_.begin(), threads_.end(), boost::bind(&Thread::join, _1)); 36 | } 37 | 38 | private: 39 | 40 | void threadFunc() 41 | { 42 | latch_.wait(); 43 | printf("tid=%d, %s started\n", 44 | CurrentThread::tid(), 45 | CurrentThread::name()); 46 | 47 | 48 | 49 | printf("tid=%d, %s stopped\n", 50 | CurrentThread::tid(), 51 | CurrentThread::name()); 52 | } 53 | 54 | CountDownLatch latch_; 55 | boost::ptr_vector threads_; 56 | }; 57 | 58 | int main() 59 | { 60 | printf("pid=%d, tid=%d\n", ::getpid(), CurrentThread::tid()); 61 | Test t(3); 62 | sleep(3); 63 | printf("pid=%d, tid=%d %s running ...\n", ::getpid(), CurrentThread::tid(), CurrentThread::name()); 64 | t.run(); 65 | t.joinAll(); 66 | 67 | printf("number of created threads %d\n", Thread::numCreated()); 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /common/tests/CountDownLatch_test2.cpp: -------------------------------------------------------------------------------- 1 | #include "CountDownLatch.h" 2 | #include "Thread.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace xnet; 10 | 11 | class Test 12 | { 13 | public: 14 | Test(int numThreads) 15 | : latch_(numThreads), 16 | threads_(numThreads) 17 | { 18 | for (int i = 0; i < numThreads; ++i) 19 | { 20 | char name[32]; 21 | snprintf(name, sizeof name, "work thread %d", i); 22 | threads_.push_back(new xnet::Thread( 23 | boost::bind(&Test::threadFunc, this), std::string(name))); 24 | } 25 | for_each(threads_.begin(), threads_.end(), boost::bind(&xnet::Thread::start, _1)); 26 | } 27 | 28 | void wait() 29 | { 30 | latch_.wait(); 31 | } 32 | 33 | void joinAll() 34 | { 35 | for_each(threads_.begin(), threads_.end(), boost::bind(&Thread::join, _1)); 36 | } 37 | 38 | private: 39 | 40 | void threadFunc() 41 | { 42 | sleep(3); 43 | latch_.countDown(); 44 | printf("tid=%d, %s started\n", 45 | CurrentThread::tid(), 46 | CurrentThread::name()); 47 | 48 | 49 | 50 | printf("tid=%d, %s stopped\n", 51 | CurrentThread::tid(), 52 | CurrentThread::name()); 53 | } 54 | 55 | CountDownLatch latch_; 56 | boost::ptr_vector threads_; 57 | }; 58 | 59 | int main() 60 | { 61 | printf("pid=%d, tid=%d\n", ::getpid(), CurrentThread::tid()); 62 | Test t(3); 63 | t.wait(); 64 | printf("pid=%d, tid=%d %s running ...\n", ::getpid(), CurrentThread::tid(), CurrentThread::name()); 65 | t.joinAll(); 66 | 67 | printf("number of created threads %d\n", Thread::numCreated()); 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /common/tests/Exception_test.cpp: -------------------------------------------------------------------------------- 1 | #include "Exception.h" 2 | #include 3 | 4 | class Trace 5 | { 6 | public: 7 | void test() 8 | { 9 | throw xnet::Exception("oops"); 10 | } 11 | }; 12 | 13 | void A() 14 | { 15 | Trace t; 16 | t.test(); 17 | } 18 | 19 | 20 | void B() 21 | { 22 | A(); 23 | } 24 | 25 | void C() 26 | { 27 | B(); 28 | } 29 | 30 | int main() 31 | { 32 | try 33 | { 34 | C(); 35 | } 36 | catch (const xnet::Exception& ex) 37 | { 38 | printf("reason: %s\n", ex.what()); 39 | printf("stack trace: %s\n", ex.stackTrace()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /common/tests/LogFile_test.cpp: -------------------------------------------------------------------------------- 1 | #include "LogFile.h" 2 | #include "Logging.h" 3 | 4 | boost::scoped_ptr g_logFile; 5 | 6 | void outputFunc(const char* msg, int len) 7 | { 8 | g_logFile->append(msg, len); 9 | } 10 | 11 | void flushFunc() 12 | { 13 | g_logFile->flush(); 14 | } 15 | 16 | int main(int argc, char* argv[]) 17 | { 18 | char name[256]; 19 | strncpy(name, argv[0], 256); 20 | g_logFile.reset(new xnet::LogFile(::basename(name), 200 * 1000)); 21 | xnet::Logger::setOutput(outputFunc); 22 | xnet::Logger::setFlush(flushFunc); 23 | 24 | std::string line = "1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ "; 25 | 26 | for (int i = 0; i < 10000; ++i) 27 | { 28 | LOG_INFO << line << i; 29 | 30 | usleep(1000); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /common/tests/LogStream_bench.cpp: -------------------------------------------------------------------------------- 1 | #include "LogStream.h" 2 | #include "Timestamp.h" 3 | 4 | #include 5 | #include 6 | #define __STDC_FORMAT_MACROS 7 | #include 8 | 9 | using namespace xnet; 10 | 11 | const size_t N = 1000000; 12 | 13 | #pragma GCC diagnostic ignored "-Wold-style-cast" 14 | 15 | template 16 | void benchPrintf(const char* fmt) 17 | { 18 | char buf[32]; 19 | Timestamp start(Timestamp::now()); 20 | for (size_t i = 0; i < N; ++i) 21 | snprintf(buf, sizeof buf, fmt, (T) (i)); 22 | Timestamp end(Timestamp::now()); 23 | 24 | printf("benchPrintf %f\n", timeDifference(end, start)); 25 | } 26 | 27 | template 28 | void benchStringStream() 29 | { 30 | Timestamp start(Timestamp::now()); 31 | std::ostringstream os; 32 | 33 | for (size_t i = 0; i < N; ++i) 34 | { 35 | os << (T) (i); 36 | os.seekp(0, std::ios_base::beg); 37 | } 38 | Timestamp end(Timestamp::now()); 39 | 40 | printf("benchStringStream %f\n", timeDifference(end, start)); 41 | } 42 | 43 | template 44 | void benchLogStream() 45 | { 46 | Timestamp start(Timestamp::now()); 47 | LogStream os; 48 | for (size_t i = 0; i < N; ++i) 49 | { 50 | os << (T) (i); 51 | os.resetBuffer(); 52 | } 53 | Timestamp end(Timestamp::now()); 54 | 55 | printf("benchLogStream %f\n", timeDifference(end, start)); 56 | } 57 | 58 | int main() 59 | { 60 | benchPrintf("%d"); 61 | 62 | puts("int"); 63 | benchPrintf("%d"); 64 | benchStringStream(); 65 | benchLogStream(); 66 | 67 | puts("double"); 68 | benchPrintf("%.12g"); 69 | benchStringStream(); 70 | benchLogStream(); 71 | 72 | puts("int64_t"); 73 | benchPrintf("%" PRId64); 74 | benchStringStream(); 75 | benchLogStream(); 76 | 77 | puts("void*"); 78 | benchPrintf("%p"); 79 | benchStringStream(); 80 | benchLogStream(); 81 | 82 | } 83 | -------------------------------------------------------------------------------- /common/tests/LogStream_test.cpp: -------------------------------------------------------------------------------- 1 | #include "LogStream.h" 2 | 3 | #include 4 | #include 5 | 6 | #define BOOST_TEST_MODULE LogStreamTest 7 | #define BOOST_TEST_MAIN 8 | #define BOOST_TEST_DYN_LINK 9 | 10 | #include 11 | 12 | using std::string; 13 | 14 | BOOST_AUTO_TEST_CASE(testLogStreamBooleans) 15 | { 16 | xnet::LogStream os; 17 | const xnet::LogStream::Buffer& buf = os.buffer(); 18 | BOOST_CHECK_EQUAL(buf.asString(), string("")); 19 | os << true; 20 | BOOST_CHECK_EQUAL(buf.asString(), string("1")); 21 | os << '\n'; 22 | BOOST_CHECK_EQUAL(buf.asString(), string("1\n")); 23 | os << false; 24 | BOOST_CHECK_EQUAL(buf.asString(), string("1\n0")); 25 | } 26 | 27 | BOOST_AUTO_TEST_CASE(testLogStreamIntegers) 28 | { 29 | xnet::LogStream os; 30 | const xnet::LogStream::Buffer& buf = os.buffer(); 31 | BOOST_CHECK_EQUAL(buf.asString(), string("")); 32 | os << 1; 33 | BOOST_CHECK_EQUAL(buf.asString(), string("1")); 34 | os << 0; 35 | BOOST_CHECK_EQUAL(buf.asString(), string("10")); 36 | os << -1; 37 | BOOST_CHECK_EQUAL(buf.asString(), string("10-1")); 38 | os.resetBuffer(); 39 | 40 | os << 0 << " " << 123 << 'x' << 0x64; 41 | BOOST_CHECK_EQUAL(buf.asString(), string("0 123x100")); 42 | } 43 | 44 | BOOST_AUTO_TEST_CASE(testLogStreamIntegerLimits) 45 | { 46 | xnet::LogStream os; 47 | const xnet::LogStream::Buffer& buf = os.buffer(); 48 | os << -2147483647; 49 | BOOST_CHECK_EQUAL(buf.asString(), string("-2147483647")); 50 | os << static_cast(-2147483647 - 1); 51 | BOOST_CHECK_EQUAL(buf.asString(), string("-2147483647-2147483648")); 52 | os << ' '; 53 | os << 2147483647; 54 | BOOST_CHECK_EQUAL(buf.asString(), string("-2147483647-2147483648 2147483647")); 55 | os.resetBuffer(); 56 | 57 | os << std::numeric_limits::min(); 58 | BOOST_CHECK_EQUAL(buf.asString(), string("-32768")); 59 | os.resetBuffer(); 60 | 61 | os << std::numeric_limits::max(); 62 | BOOST_CHECK_EQUAL(buf.asString(), string("32767")); 63 | os.resetBuffer(); 64 | 65 | os << std::numeric_limits::min(); 66 | BOOST_CHECK_EQUAL(buf.asString(), string("0")); 67 | os.resetBuffer(); 68 | 69 | os << std::numeric_limits::max(); 70 | BOOST_CHECK_EQUAL(buf.asString(), string("65535")); 71 | os.resetBuffer(); 72 | 73 | os << std::numeric_limits::min(); 74 | BOOST_CHECK_EQUAL(buf.asString(), string("-2147483648")); 75 | os.resetBuffer(); 76 | 77 | os << std::numeric_limits::max(); 78 | BOOST_CHECK_EQUAL(buf.asString(), string("2147483647")); 79 | os.resetBuffer(); 80 | 81 | os << std::numeric_limits::min(); 82 | BOOST_CHECK_EQUAL(buf.asString(), string("0")); 83 | os.resetBuffer(); 84 | 85 | os << std::numeric_limits::max(); 86 | BOOST_CHECK_EQUAL(buf.asString(), string("4294967295")); 87 | os.resetBuffer(); 88 | 89 | os << std::numeric_limits::min(); 90 | BOOST_CHECK_EQUAL(buf.asString(), string("-9223372036854775808")); 91 | os.resetBuffer(); 92 | 93 | os << std::numeric_limits::max(); 94 | BOOST_CHECK_EQUAL(buf.asString(), string("9223372036854775807")); 95 | os.resetBuffer(); 96 | 97 | os << std::numeric_limits::min(); 98 | BOOST_CHECK_EQUAL(buf.asString(), string("0")); 99 | os.resetBuffer(); 100 | 101 | os << std::numeric_limits::max(); 102 | BOOST_CHECK_EQUAL(buf.asString(), string("18446744073709551615")); 103 | os.resetBuffer(); 104 | 105 | int16_t a = 0; 106 | int32_t b = 0; 107 | int64_t c = 0; 108 | os << a; 109 | os << b; 110 | os << c; 111 | BOOST_CHECK_EQUAL(buf.asString(), string("000")); 112 | } 113 | 114 | BOOST_AUTO_TEST_CASE(testLogStreamFloats) 115 | { 116 | xnet::LogStream os; 117 | const xnet::LogStream::Buffer& buf = os.buffer(); 118 | 119 | os << 0.0; 120 | BOOST_CHECK_EQUAL(buf.asString(), string("0")); 121 | os.resetBuffer(); 122 | 123 | os << 1.0; 124 | BOOST_CHECK_EQUAL(buf.asString(), string("1")); 125 | os.resetBuffer(); 126 | 127 | os << 0.1; 128 | BOOST_CHECK_EQUAL(buf.asString(), string("0.1")); 129 | os.resetBuffer(); 130 | 131 | os << 0.05; 132 | BOOST_CHECK_EQUAL(buf.asString(), string("0.05")); 133 | os.resetBuffer(); 134 | 135 | os << 0.15; 136 | BOOST_CHECK_EQUAL(buf.asString(), string("0.15")); 137 | os.resetBuffer(); 138 | 139 | double a = 0.1; 140 | os << a; 141 | BOOST_CHECK_EQUAL(buf.asString(), string("0.1")); 142 | os.resetBuffer(); 143 | 144 | double b = 0.05; 145 | os << b; 146 | BOOST_CHECK_EQUAL(buf.asString(), string("0.05")); 147 | os.resetBuffer(); 148 | 149 | double c = 0.15; 150 | os << c; 151 | BOOST_CHECK_EQUAL(buf.asString(), string("0.15")); 152 | os.resetBuffer(); 153 | 154 | os << a + b; 155 | BOOST_CHECK_EQUAL(buf.asString(), string("0.15")); 156 | os.resetBuffer(); 157 | 158 | BOOST_CHECK(a + b != c); 159 | 160 | os << 1.23456789; 161 | BOOST_CHECK_EQUAL(buf.asString(), string("1.23456789")); 162 | os.resetBuffer(); 163 | 164 | os << 1.234567; 165 | BOOST_CHECK_EQUAL(buf.asString(), string("1.234567")); 166 | os.resetBuffer(); 167 | 168 | os << -123.456; 169 | BOOST_CHECK_EQUAL(buf.asString(), string("-123.456")); 170 | os.resetBuffer(); 171 | } 172 | 173 | BOOST_AUTO_TEST_CASE(testLogStreamVoid) 174 | { 175 | xnet::LogStream os; 176 | const xnet::LogStream::Buffer& buf = os.buffer(); 177 | 178 | os << static_cast(0); 179 | BOOST_CHECK_EQUAL(buf.asString(), string("0x0")); 180 | os.resetBuffer(); 181 | 182 | os << reinterpret_cast(8888); 183 | BOOST_CHECK_EQUAL(buf.asString(), string("0x22B8")); 184 | os.resetBuffer(); 185 | } 186 | 187 | BOOST_AUTO_TEST_CASE(testLogStreamStrings) 188 | { 189 | xnet::LogStream os; 190 | const xnet::LogStream::Buffer& buf = os.buffer(); 191 | 192 | os << "Hello "; 193 | BOOST_CHECK_EQUAL(buf.asString(), string("Hello ")); 194 | 195 | string tests = "Test..."; 196 | os << tests; 197 | BOOST_CHECK_EQUAL(buf.asString(), string("Hello World")); 198 | } 199 | 200 | BOOST_AUTO_TEST_CASE(testLogStreamFmts) 201 | { 202 | xnet::LogStream os; 203 | const xnet::LogStream::Buffer& buf = os.buffer(); 204 | 205 | os << xnet::Fmt("%4d", 1); 206 | BOOST_CHECK_EQUAL(buf.asString(), string(" 1")); 207 | os.resetBuffer(); 208 | 209 | os << xnet::Fmt("%4.2f", 1.2); 210 | BOOST_CHECK_EQUAL(buf.asString(), string("1.20")); 211 | os.resetBuffer(); 212 | 213 | os << xnet::Fmt("%4.2f", 1.2) << xnet::Fmt("%4d", 43); 214 | BOOST_CHECK_EQUAL(buf.asString(), string("1.20 43")); 215 | os.resetBuffer(); 216 | } 217 | 218 | BOOST_AUTO_TEST_CASE(testLogStreamLong) 219 | { 220 | xnet::LogStream os; 221 | const xnet::LogStream::Buffer& buf = os.buffer(); 222 | for (int i = 0; i < 399; ++i) 223 | { 224 | os << "123456789 "; 225 | BOOST_CHECK_EQUAL(buf.length(), 10 * (i + 1)); 226 | BOOST_CHECK_EQUAL(buf.avail(), 4000 - 10 * (i + 1)); 227 | } 228 | 229 | os << "abcdefghi "; 230 | BOOST_CHECK_EQUAL(buf.length(), 3990); 231 | BOOST_CHECK_EQUAL(buf.avail(), 10); 232 | 233 | os << "abcdefghi"; 234 | BOOST_CHECK_EQUAL(buf.length(), 3999); 235 | BOOST_CHECK_EQUAL(buf.avail(), 1); 236 | } 237 | 238 | 239 | //http://lists.boost.org/boost-users/2012/09/75886.php 240 | 241 | //This use case seems really common and I was surprised it gave me problems. 242 | //I assume that nullptr_t in C++11 fixes this but then I was wondering 243 | //if the cases were basic and common enough it would make sense to have 244 | //a BOOST_CHECK_NULL / BOOST_CHECK_NOT_NULL and the assorted variants. 245 | //james 246 | //--- 247 | //#define BOOST_TEST_DYN_LINK 248 | //#define BOOST_TEST_MODULE Hello 249 | //#include 250 | //// main() etc... is defined by the headers 251 | //BOOST_AUTO_TEST_CASE( null_compare ) 252 | //{ 253 | // const char *ptr = NULL; 254 | // // 255 | // // Equality comparisons with NULL Fail to compile using boost 1.46 256 | // // and g++-4.6 257 | // // 258 | // // /usr/include/boost/test/test_tools.hpp:536:20: 259 | // // error: ISO C++ forbids comparison between pointer and 260 | // // integer [-fpermissive] 261 | // // 262 | // // Which seems to be flagging this 263 | // // 264 | // // template 265 | // // predicate_result equal_impl( Left const& left, Right const& right ) 266 | // // { 267 | // // return left == right; 268 | // // } 269 | // // 270 | // // Where Left is of type "const char *" and Right is of type "long int". 271 | // // 272 | // BOOST_CHECK_EQUAL(ptr, NULL); 273 | // // 274 | // // these tests compile and pass 275 | // // 276 | // BOOST_CHECK(ptr == NULL); 277 | // BOOST_CHECK_EQUAL(ptr, ptr); 278 | // BOOST_CHECK_EQUAL(ptr, (void *)ptr); 279 | // BOOST_CHECK_EQUAL(ptr, (void *)NULL); 280 | //} 281 | -------------------------------------------------------------------------------- /common/tests/Log_test1.cpp: -------------------------------------------------------------------------------- 1 | #include "Logging.h" 2 | #include 3 | 4 | using namespace xnet; 5 | 6 | int main() 7 | { 8 | LOG_TRACE<<"trace ..."; 9 | LOG_DEBUG<<"debug ..."; 10 | LOG_INFO<<"info ..."; 11 | LOG_WARN<<"warn ..."; 12 | LOG_ERROR<<"error ..."; 13 | //LOG_FATAL<<"fatal ..."; 14 | errno = 13; 15 | LOG_SYSERR<<"syserr ..."; 16 | LOG_SYSFATAL<<"sysfatal ..."; 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /common/tests/Log_test2.cpp: -------------------------------------------------------------------------------- 1 | #include "Logging.h" 2 | #include 3 | #include 4 | 5 | using namespace xnet; 6 | 7 | FILE* g_file; 8 | 9 | void dummyOutput(const char* msg, int len) 10 | { 11 | if (g_file) 12 | { 13 | fwrite(msg, 1, len, g_file); 14 | } 15 | } 16 | 17 | void dummyFlush() 18 | { 19 | fflush(g_file); 20 | } 21 | 22 | int main() 23 | { 24 | g_file = ::fopen("/tmp/xnet_log", "ae"); 25 | Logger::setOutput(dummyOutput); 26 | Logger::setFlush(dummyFlush); 27 | 28 | LOG_TRACE<<"trace ..."; 29 | LOG_DEBUG<<"debug ..."; 30 | LOG_INFO<<"info ..."; 31 | LOG_WARN<<"warn ..."; 32 | LOG_ERROR<<"error ..."; 33 | //LOG_FATAL<<"fatal ..."; 34 | errno = 13; 35 | LOG_SYSERR<<"syserr ..."; 36 | //LOG_SYSFATAL<<"sysfatal ..."; 37 | 38 | ::fclose(g_file); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /common/tests/Logging_test.cpp: -------------------------------------------------------------------------------- 1 | #include "Logging.h" 2 | #include "LogFile.h" 3 | #include "ThreadPool.h" 4 | 5 | #include 6 | 7 | int g_total; 8 | FILE* g_file; 9 | boost::scoped_ptr g_logFile; 10 | 11 | void dummyOutput(const char* msg, int len) 12 | { 13 | g_total += len; 14 | if (g_file) 15 | { 16 | fwrite(msg, 1, len, g_file); 17 | } 18 | else if (g_logFile) 19 | { 20 | g_logFile->append(msg, len); 21 | } 22 | } 23 | 24 | void bench(const char* type) 25 | { 26 | xnet::Logger::setOutput(dummyOutput); 27 | xnet::Timestamp start(xnet::Timestamp::now()); 28 | g_total = 0; 29 | 30 | int n = 1000*1000; 31 | const bool kLongLog = false; 32 | std::string empty = " "; 33 | std::string longStr(3000, 'X'); 34 | longStr += " "; 35 | for (int i = 0; i < n; ++i) 36 | { 37 | LOG_INFO << "Hello 0123456789" << " abcdefghijklmnopqrstuvwxyz" 38 | << (kLongLog ? longStr : empty) 39 | << i; 40 | } 41 | xnet::Timestamp end(xnet::Timestamp::now()); 42 | double seconds = timeDifference(end, start); 43 | printf("%12s: %f seconds, %d bytes, %10.2f msg/s, %.2f MiB/s\n", 44 | type, seconds, g_total, n / seconds, g_total / seconds / (1024 * 1024)); 45 | } 46 | 47 | void logInThread() 48 | { 49 | LOG_INFO << "logInThread"; 50 | usleep(1000); 51 | } 52 | 53 | int main() 54 | { 55 | getppid(); // for ltrace and strace 56 | 57 | xnet::ThreadPool pool("pool"); 58 | pool.start(5); 59 | pool.run(logInThread); 60 | pool.run(logInThread); 61 | pool.run(logInThread); 62 | pool.run(logInThread); 63 | pool.run(logInThread); 64 | 65 | LOG_TRACE << "trace"; 66 | LOG_DEBUG << "debug"; 67 | LOG_INFO << "Hello"; 68 | LOG_WARN << "World"; 69 | LOG_ERROR << "Error"; 70 | LOG_INFO << sizeof(xnet::Logger); 71 | LOG_INFO << sizeof(xnet::LogStream); 72 | LOG_INFO << sizeof(xnet::Fmt); 73 | LOG_INFO << sizeof(xnet::LogStream::Buffer); 74 | 75 | sleep(1); 76 | bench("nop"); 77 | 78 | char buffer[64*1024]; 79 | 80 | g_file = fopen("/dev/null", "w"); 81 | setbuffer(g_file, buffer, sizeof buffer); 82 | bench("/dev/null"); 83 | fclose(g_file); 84 | 85 | g_file = fopen("/tmp/log", "w"); 86 | setbuffer(g_file, buffer, sizeof buffer); 87 | bench("/tmp/log"); 88 | fclose(g_file); 89 | 90 | g_file = NULL; 91 | g_logFile.reset(new xnet::LogFile("test_log_st", 500*1000*1000, false)); 92 | bench("test_log_st"); 93 | 94 | g_logFile.reset(new xnet::LogFile("test_log_mt", 500*1000*1000, true)); 95 | bench("test_log_mt"); 96 | g_logFile.reset(); 97 | } 98 | -------------------------------------------------------------------------------- /common/tests/Makefile: -------------------------------------------------------------------------------- 1 | # cross compile... 2 | CROSS_COMPILE = 3 | 4 | CC = $(CROSS_COMPILE)gcc 5 | CXX = $(CROSS_COMPILE)g++ 6 | AR = $(CROSS_COMPILE)ar 7 | 8 | ARFLAGS = cr 9 | RM = -rm -rf 10 | MAKE = make 11 | 12 | CFLAGS += -Wall\ 13 | -Wextra\ 14 | -Werror\ 15 | -Wconversion\ 16 | -Wno-unused-parameter\ 17 | -Wno-unused-local-typedefs\ 18 | -Wold-style-cast\ 19 | -Woverloaded-virtual\ 20 | -Wpointer-arith\ 21 | -Wshadow\ 22 | -Wwrite-strings\ 23 | -march=native 24 | 25 | debug = y 26 | 27 | ifeq ($(debug), y) 28 | CFLAGS += -g 29 | else 30 | CFLAGS += -O2 -s 31 | endif 32 | 33 | 34 | 35 | LDFLAGS = -rdynamic ../../libcommon.a -lpthread 36 | 37 | INCDIRS = ../ 38 | 39 | CFLAGS += -I$(INCDIRS) 40 | 41 | 42 | 43 | DES = Thread_unittest\ 44 | Timestamp_unittest\ 45 | Mutex_test\ 46 | CountDownLatch_test1\ 47 | CountDownLatch_test2\ 48 | BlockingQueue_test\ 49 | BlockingQueue_bench\ 50 | BoundedBlockingQueue_test\ 51 | ThreadPool_test\ 52 | Singleton_test\ 53 | ThreadLocal_test\ 54 | SingletonThreadLocal_test\ 55 | ThreadLocalSingleton_test 56 | 57 | all:$(DES) 58 | 59 | 60 | Atomic_unittest:Atomic_unittest.cpp 61 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 62 | 63 | BlockingQueue_bench:BlockingQueue_bench.cpp 64 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 65 | 66 | BlockingQueue_test:BlockingQueue_test.cpp 67 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 68 | 69 | 70 | BoundedBlockingQueue_test:BoundedBlockingQueue_test.cpp 71 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 72 | 73 | CountDownLatch_test1:CountDownLatch_test1.cpp 74 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 75 | 76 | 77 | CountDownLatch_test2:CountDownLatch_test2.cpp 78 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 79 | 80 | 81 | 82 | Exception_test:Exception_test.cpp 83 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 84 | 85 | Mutex_test:Mutex_test.cpp 86 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 87 | 88 | Singleton_test:Singleton_test.cpp 89 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 90 | 91 | SingletonThreadLocal_test:SingletonThreadLocal_test.cpp 92 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 93 | 94 | ThreadLocalSingleton_test:ThreadLocalSingleton_test.cpp 95 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 96 | 97 | ThreadLocal_test:ThreadLocal_test.cpp 98 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 99 | 100 | ThreadPool_test:ThreadPool_test.cpp 101 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 102 | 103 | Thread_unittest:Thread_unittest.cpp 104 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 105 | 106 | Timestamp_unittest:Timestamp_unittest.cpp 107 | $(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 108 | 109 | 110 | clean: 111 | @echo "cleaning..." 112 | @$(RM) $(DES) 113 | 114 | @$(RM) *.o *.back *~ 115 | 116 | .PHONY: all clean 117 | -------------------------------------------------------------------------------- /common/tests/Mutex_test.cpp: -------------------------------------------------------------------------------- 1 | //#include "CountDownLatch.h" 2 | #include "Mutex.h" 3 | #include "Thread.h" 4 | #include "Timestamp.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace xnet; 12 | using namespace std; 13 | 14 | MutexLock g_mutex; 15 | vector g_vec; 16 | const int kCount = 10*1000*1000; 17 | 18 | void threadFunc() 19 | { 20 | for (int i = 0; i < kCount; ++i) 21 | { 22 | MutexLockGuard lock(g_mutex); 23 | g_vec.push_back(i); 24 | } 25 | } 26 | 27 | int main() 28 | { 29 | const int kMaxThreads = 8; 30 | g_vec.reserve(kMaxThreads * kCount); 31 | 32 | Timestamp start(Timestamp::now()); 33 | for (int i = 0; i < kCount; ++i) 34 | { 35 | g_vec.push_back(i); 36 | } 37 | 38 | printf("single thread without lock %f\n", timeDifference(Timestamp::now(), start)); 39 | 40 | start = Timestamp::now(); 41 | threadFunc(); 42 | printf("single thread with lock %f\n", timeDifference(Timestamp::now(), start)); 43 | 44 | for (int nthreads = 1; nthreads < kMaxThreads; ++nthreads) 45 | { 46 | boost::ptr_vector threads; 47 | g_vec.clear(); 48 | start = Timestamp::now(); 49 | for (int i = 0; i < nthreads; ++i) 50 | { 51 | threads.push_back(new Thread(&threadFunc)); 52 | threads.back().start(); 53 | } 54 | for (int i = 0; i < nthreads; ++i) 55 | { 56 | threads[i].join(); 57 | } 58 | printf("%d thread(s) with lock %f\n", nthreads, timeDifference(Timestamp::now(), start)); 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /common/tests/SingletonThreadLocal_test.cpp: -------------------------------------------------------------------------------- 1 | #include "Singleton.h" 2 | #include "CurrentThread.h" 3 | #include "ThreadLocal.h" 4 | #include "Thread.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class Test: boost::noncopyable 11 | { 12 | public: 13 | Test() 14 | { 15 | printf("tid=%d, constructing %p\n", xnet::CurrentThread::tid(), this); 16 | } 17 | 18 | ~Test() 19 | { 20 | printf("tid=%d, destructing %p %s\n", xnet::CurrentThread::tid(), this, name_.c_str()); 21 | } 22 | 23 | const std::string& name() const 24 | { 25 | return name_; 26 | } 27 | void setName(const std::string& n) 28 | { 29 | name_ = n; 30 | } 31 | 32 | private: 33 | std::string name_; 34 | }; 35 | 36 | #define STL xnet::Singleton >::instance().value() 37 | 38 | void print() 39 | { 40 | printf("tid=%d, %p name=%s\n", xnet::CurrentThread::tid(), &STL, 41 | STL.name().c_str()); 42 | } 43 | 44 | void threadFunc(const char* changeTo) 45 | { 46 | print(); 47 | STL.setName(changeTo); 48 | sleep(1); 49 | print(); 50 | } 51 | 52 | int main() 53 | { 54 | STL.setName("main one"); 55 | xnet::Thread t1(boost::bind(threadFunc, "thread1")); 56 | xnet::Thread t2(boost::bind(threadFunc, "thread2")); 57 | t1.start(); 58 | t2.start(); 59 | t1.join(); 60 | print(); 61 | t2.join(); 62 | pthread_exit(0); 63 | } 64 | -------------------------------------------------------------------------------- /common/tests/Singleton_test.cpp: -------------------------------------------------------------------------------- 1 | #include "Singleton.h" 2 | #include "CurrentThread.h" 3 | #include "Thread.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | class Test: boost::noncopyable 10 | { 11 | public: 12 | Test() 13 | { 14 | printf("tid=%d, constructing %p\n", xnet::CurrentThread::tid(), this); 15 | } 16 | 17 | ~Test() 18 | { 19 | printf("tid=%d, destructing %p %s\n", xnet::CurrentThread::tid(), this, name_.c_str()); 20 | } 21 | 22 | const std::string& name() const 23 | { 24 | return name_; 25 | } 26 | void setName(const std::string& n) 27 | { 28 | name_ = n; 29 | } 30 | 31 | private: 32 | std::string name_; 33 | }; 34 | 35 | void threadFunc() 36 | { 37 | printf("tid=%d, %p name=%s\n", xnet::CurrentThread::tid(), &xnet::Singleton::instance(), xnet::Singleton::instance().name().c_str()); 38 | xnet::Singleton::instance().setName("only one, changed"); 39 | } 40 | 41 | int main() 42 | { 43 | xnet::Singleton::instance().setName("only one"); 44 | xnet::Thread t1(threadFunc); 45 | t1.start(); 46 | t1.join(); 47 | printf("tid=%d, %p name=%s\n", xnet::CurrentThread::tid(), &xnet::Singleton::instance(), xnet::Singleton::instance().name().c_str()); 48 | } 49 | -------------------------------------------------------------------------------- /common/tests/ThreadLocalSingleton_test.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadLocalSingleton.h" 2 | #include "CurrentThread.h" 3 | #include "Thread.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | class Test: boost::noncopyable 10 | { 11 | public: 12 | Test() 13 | { 14 | printf("tid=%d, constructing %p\n", xnet::CurrentThread::tid(), this); 15 | } 16 | 17 | ~Test() 18 | { 19 | printf("tid=%d, destructing %p %s\n", xnet::CurrentThread::tid(), this, name_.c_str()); 20 | } 21 | 22 | const std::string& name() const 23 | { 24 | return name_; 25 | } 26 | void setName(const std::string& n) 27 | { 28 | name_ = n; 29 | } 30 | 31 | private: 32 | std::string name_; 33 | }; 34 | 35 | void threadFunc(const char* changeTo) 36 | { 37 | printf("tid=%d, %p name=%s\n", xnet::CurrentThread::tid(), &xnet::ThreadLocalSingleton::instance(), xnet::ThreadLocalSingleton::instance().name().c_str()); 38 | xnet::ThreadLocalSingleton::instance().setName(changeTo); 39 | printf("tid=%d, %p name=%s\n", xnet::CurrentThread::tid(), &xnet::ThreadLocalSingleton::instance(), xnet::ThreadLocalSingleton::instance().name().c_str()); 40 | 41 | // no need to manually delete it 42 | // xnet::ThreadLocalSingleton::destroy(); 43 | } 44 | 45 | int main() 46 | { 47 | xnet::ThreadLocalSingleton::instance().setName("main one"); 48 | xnet::Thread t1(boost::bind(threadFunc, "thread1")); 49 | xnet::Thread t2(boost::bind(threadFunc, "thread2")); 50 | t1.start(); 51 | t2.start(); 52 | t1.join(); 53 | printf("tid=%d, %p name=%s\n", xnet::CurrentThread::tid(), &xnet::ThreadLocalSingleton::instance(), xnet::ThreadLocalSingleton::instance().name().c_str()); 54 | t2.join(); 55 | 56 | pthread_exit(0); 57 | } 58 | -------------------------------------------------------------------------------- /common/tests/ThreadLocal_test.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadLocal.h" 2 | #include "CurrentThread.h" 3 | #include "Thread.h" 4 | 5 | #include 6 | #include 7 | 8 | class Test: boost::noncopyable 9 | { 10 | public: 11 | Test() 12 | { 13 | printf("tid=%d, constructing %p\n", xnet::CurrentThread::tid(), this); 14 | } 15 | 16 | ~Test() 17 | { 18 | printf("tid=%d, destructing %p %s\n", xnet::CurrentThread::tid(), this, name_.c_str()); 19 | } 20 | 21 | const std::string& name() const 22 | { 23 | return name_; 24 | } 25 | void setName(const std::string& n) 26 | { 27 | name_ = n; 28 | } 29 | 30 | private: 31 | std::string name_; 32 | }; 33 | 34 | xnet::ThreadLocal testObj1; 35 | xnet::ThreadLocal testObj2; 36 | 37 | void print() 38 | { 39 | printf("tid=%d, obj1 %p name=%s\n", xnet::CurrentThread::tid(), &testObj1.value(), testObj1.value().name().c_str()); 40 | printf("tid=%d, obj2 %p name=%s\n", xnet::CurrentThread::tid(), &testObj2.value(), testObj2.value().name().c_str()); 41 | } 42 | 43 | void threadFunc() 44 | { 45 | print(); 46 | testObj1.value().setName("changed 1"); 47 | testObj2.value().setName("changed 42"); 48 | print(); 49 | } 50 | 51 | int main() 52 | { 53 | testObj1.value().setName("main one"); 54 | print(); 55 | xnet::Thread t1(threadFunc); 56 | t1.start(); 57 | t1.join(); 58 | testObj2.value().setName("main two"); 59 | print(); 60 | 61 | pthread_exit(0); 62 | } 63 | -------------------------------------------------------------------------------- /common/tests/ThreadPool_test.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadPool.h" 2 | #include "CountDownLatch.h" 3 | #include "CurrentThread.h" 4 | 5 | #include 6 | #include 7 | 8 | void print() 9 | { 10 | printf("tid=%d\n", xnet::CurrentThread::tid()); 11 | } 12 | 13 | void printString(const std::string& str) 14 | { 15 | printf("tid=%d, str=%s\n", xnet::CurrentThread::tid(), str.c_str()); 16 | } 17 | 18 | int main() 19 | { 20 | xnet::ThreadPool pool("MainThreadPool"); 21 | pool.start(5); 22 | 23 | pool.run(print); 24 | pool.run(print); 25 | for (int i = 0; i < 100; ++i) 26 | { 27 | char buf[32]; 28 | snprintf(buf, sizeof buf, "task %d", i); 29 | pool.run(boost::bind(printString, std::string(buf))); 30 | } 31 | 32 | xnet::CountDownLatch latch(1); 33 | pool.run(boost::bind(&xnet::CountDownLatch::countDown, &latch)); 34 | latch.wait(); 35 | pool.stop(); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /common/tests/Thread_test.cpp: -------------------------------------------------------------------------------- 1 | #include "Thread.h" 2 | #include "CurrentThread.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | void threadFunc() 9 | { 10 | printf("tid=%d\n", xnet::CurrentThread::tid()); 11 | } 12 | 13 | void threadFunc2(int x) 14 | { 15 | printf("tid=%d, x=%d\n", xnet::CurrentThread::tid(), x); 16 | } 17 | 18 | class Foo 19 | { 20 | public: 21 | explicit Foo(double x) 22 | : x_(x) 23 | { 24 | } 25 | 26 | void memberFunc() 27 | { 28 | printf("tid=%d, Foo::x_=%f\n", xnet::CurrentThread::tid(), x_); 29 | } 30 | 31 | void memberFunc2(const std::string& text) 32 | { 33 | printf("tid=%d, Foo::x_=%f, text=%s\n", xnet::CurrentThread::tid(), x_, text.c_str()); 34 | } 35 | 36 | private: 37 | double x_; 38 | }; 39 | 40 | int main() 41 | { 42 | printf("pid=%d, tid=%d\n", ::getpid(), xnet::CurrentThread::tid()); 43 | 44 | xnet::Thread t1(threadFunc); 45 | t1.start(); 46 | t1.join(); 47 | 48 | xnet::Thread t2(boost::bind(threadFunc2, 42), 49 | "thread for free function with argument"); 50 | t2.start(); 51 | t2.join(); 52 | 53 | Foo foo(87.53); 54 | xnet::Thread t3(boost::bind(&Foo::memberFunc, &foo), 55 | "thread for member function without argument"); 56 | t3.start(); 57 | t3.join(); 58 | 59 | xnet::Thread t4(boost::bind(&Foo::memberFunc2, boost::ref(foo), std::string("Thread test"))); 60 | t4.start(); 61 | t4.join(); 62 | 63 | printf("number of created threads %d\n", xnet::Thread::numCreated()); 64 | } 65 | -------------------------------------------------------------------------------- /common/tests/Thread_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include "Thread.h" 2 | #include "CurrentThread.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | void threadFunc() 9 | { 10 | printf("tid=%d\n", xnet::CurrentThread::tid()); 11 | } 12 | 13 | void threadFunc2(int x) 14 | { 15 | printf("tid=%d, x=%d\n", xnet::CurrentThread::tid(), x); 16 | } 17 | 18 | class Foo 19 | { 20 | public: 21 | explicit Foo(double x) 22 | : x_(x) 23 | { 24 | } 25 | 26 | void memberFunc() 27 | { 28 | printf("tid=%d, Foo::x_=%f\n", xnet::CurrentThread::tid(), x_); 29 | } 30 | 31 | void memberFunc2(const std::string& text) 32 | { 33 | printf("tid=%d, Foo::x_=%f, text=%s\n", xnet::CurrentThread::tid(), x_, text.c_str()); 34 | } 35 | 36 | private: 37 | double x_; 38 | }; 39 | 40 | int main() 41 | { 42 | printf("pid=%d, tid=%d\n", ::getpid(), xnet::CurrentThread::tid()); 43 | 44 | xnet::Thread t1(threadFunc); 45 | t1.start(); 46 | t1.join(); 47 | 48 | xnet::Thread t2(boost::bind(threadFunc2, 42), 49 | "thread for free function with argument"); 50 | t2.start(); 51 | t2.join(); 52 | 53 | Foo foo(87.53); 54 | xnet::Thread t3(boost::bind(&Foo::memberFunc, &foo), 55 | "thread for member function without argument"); 56 | t3.start(); 57 | t3.join(); 58 | 59 | xnet::Thread t4(boost::bind(&Foo::memberFunc2, boost::ref(foo), std::string("Testing..."))); 60 | t4.start(); 61 | t4.join(); 62 | 63 | printf("number of created threads %d\n", xnet::Thread::numCreated()); 64 | } 65 | -------------------------------------------------------------------------------- /common/tests/Timestamp_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Timestamp.h" 4 | 5 | using xnet::Timestamp; 6 | 7 | void passByConstReference(const Timestamp& x) 8 | { 9 | printf("%s\n", x.toString().c_str()); 10 | } 11 | 12 | void passByValue(Timestamp x) 13 | { 14 | printf("%s\n", x.toString().c_str()); 15 | } 16 | 17 | void benchmark() 18 | { 19 | const int kNumber = 1000 * 1000; 20 | 21 | std::vector notes; 22 | notes.reserve(kNumber); 23 | for (int i = 0; i < kNumber; ++i) 24 | { 25 | notes.push_back(Timestamp::now()); 26 | } 27 | printf("%s\n", notes.front().toString().c_str()); 28 | printf("%s\n", notes.back().toString().c_str()); 29 | printf("%f\n", timeDifference(notes.back(), notes.front())); 30 | 31 | int increments[100] = 32 | { 0 }; 33 | int64_t start = notes.front().microSeconds(); 34 | for (int i = 1; i < kNumber; ++i) 35 | { 36 | int64_t next = notes[i].microSeconds(); 37 | int64_t inc = next - start; 38 | start = next; 39 | if (inc < 0) 40 | { 41 | printf("reverse!\n"); 42 | } 43 | else if (inc < 100) 44 | { 45 | ++increments[inc]; 46 | } 47 | else 48 | { 49 | printf("big gap %d\n", static_cast(inc)); 50 | } 51 | } 52 | 53 | for (int i = 0; i < 100; ++i) 54 | { 55 | printf("%2d: %d\n", i, increments[i]); 56 | } 57 | } 58 | 59 | int main() 60 | { 61 | Timestamp now(Timestamp::now()); 62 | printf("%s\n", now.toString().c_str()); 63 | passByValue(now); 64 | passByConstReference(now); 65 | 66 | Timestamp invalid(Timestamp::invalid()); 67 | printf("\ninvalid.microSecondsSinceEpoch=%ld\n\n", invalid.microSecondsSinceEpoch()); 68 | benchmark(); 69 | } 70 | 71 | -------------------------------------------------------------------------------- /common/tests/boost_test.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE example 2 | #include 3 | 4 | BOOST_AUTO_TEST_CASE( test ) 5 | { 6 | int i = 2; 7 | int j = 1; 8 | //BOOST_REQUIRE_EQUAL( i, j ); 9 | BOOST_CHECK_EQUAL(i, j); 10 | } 11 | -------------------------------------------------------------------------------- /net/Channel.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "Logging.h" 4 | #include "Channel.h" 5 | #include "EventLoop.h" 6 | 7 | #include 8 | 9 | #include 10 | 11 | using namespace xnet; 12 | using namespace xnet::net; 13 | 14 | const int Channel::kNoneEvent = 0; 15 | const int Channel::kReadEvent = POLLIN | POLLPRI; 16 | const int Channel::kWriteEvent = POLLOUT; 17 | 18 | Channel::Channel(EventLoop* loop, int fd__) 19 | : loop_(loop), 20 | fd_(fd__), 21 | events_(0), 22 | revents_(0), 23 | index_(-1), 24 | logHup_(true), 25 | tied_(false), 26 | eventHandling_(false) 27 | { 28 | } 29 | 30 | Channel::~Channel() 31 | { 32 | assert(!eventHandling_); 33 | } 34 | 35 | void Channel::tie(const boost::shared_ptr& obj) 36 | { 37 | tie_ = obj; 38 | tied_ = true; 39 | } 40 | 41 | void Channel::update() 42 | { 43 | loop_->updateChannel(this); 44 | } 45 | 46 | // 调用这个函数之前确保调用disableAll 47 | void Channel::remove() 48 | { 49 | assert(isNoneEvent()); 50 | loop_->removeChannel(this); 51 | } 52 | 53 | void Channel::handleEvent(Timestamp receiveTime) 54 | { 55 | boost::shared_ptr guard; 56 | if (tied_) 57 | { 58 | guard = tie_.lock(); 59 | if (guard) 60 | { 61 | handleEventWithGuard(receiveTime); 62 | } 63 | } 64 | else 65 | { 66 | handleEventWithGuard(receiveTime); 67 | } 68 | } 69 | 70 | void Channel::handleEventWithGuard(Timestamp receiveTime) 71 | { 72 | eventHandling_ = true; 73 | if ((revents_ & POLLHUP) && !(revents_ & POLLIN)) 74 | { 75 | if (logHup_) 76 | { 77 | LOG_WARN << "Channel::handle_event() POLLHUP"; 78 | } 79 | if (closeCallback_) closeCallback_(); 80 | } 81 | 82 | if (revents_ & POLLNVAL) 83 | { 84 | LOG_WARN << "Channel::handle_event() POLLNVAL"; 85 | } 86 | 87 | if (revents_ & (POLLERR | POLLNVAL)) 88 | { 89 | if (errorCallback_) errorCallback_(); 90 | } 91 | if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) 92 | { 93 | if (readCallback_) readCallback_(receiveTime); 94 | } 95 | if (revents_ & POLLOUT) 96 | { 97 | if (writeCallback_) writeCallback_(); 98 | } 99 | eventHandling_ = false; 100 | } 101 | 102 | std::string Channel::reventsToString() const 103 | { 104 | std::ostringstream oss; 105 | oss << fd_ << ": "; 106 | if (revents_ & POLLIN) 107 | oss << "IN "; 108 | if (revents_ & POLLPRI) 109 | oss << "PRI "; 110 | if (revents_ & POLLOUT) 111 | oss << "OUT "; 112 | if (revents_ & POLLHUP) 113 | oss << "HUP "; 114 | if (revents_ & POLLRDHUP) 115 | oss << "RDHUP "; 116 | if (revents_ & POLLERR) 117 | oss << "ERR "; 118 | if (revents_ & POLLNVAL) 119 | oss << "NVAL "; 120 | 121 | return oss.str().c_str(); 122 | } 123 | -------------------------------------------------------------------------------- /net/Channel.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef XNET_NET_CHANNEL_H 3 | #define XNET_NET_CHANNEL_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Timestamp.h" 11 | 12 | namespace xnet 13 | { 14 | namespace net 15 | { 16 | 17 | class EventLoop; 18 | 19 | /// 20 | /// A selectable I/O channel. 21 | /// 22 | /// This class doesn't own the file descriptor. 23 | /// The file descriptor could be a socket, 24 | /// an eventfd, a timerfd, or a signalfd 25 | class Channel: boost::noncopyable 26 | { 27 | public: 28 | typedef boost::function EventCallback; 29 | typedef boost::function ReadEventCallback; 30 | 31 | Channel(EventLoop* loop, int fd); 32 | ~Channel(); 33 | 34 | void handleEvent(Timestamp receiveTime); 35 | void setReadCallback(const ReadEventCallback& cb) 36 | { 37 | readCallback_ = cb; 38 | } 39 | void setWriteCallback(const EventCallback& cb) 40 | { 41 | writeCallback_ = cb; 42 | } 43 | void setCloseCallback(const EventCallback& cb) 44 | { 45 | closeCallback_ = cb; 46 | } 47 | void setErrorCallback(const EventCallback& cb) 48 | { 49 | errorCallback_ = cb; 50 | } 51 | 52 | /// Tie this channel to the owner object managed by shared_ptr, 53 | /// prevent the owner object being destroyed in handleEvent. 54 | void tie(const boost::shared_ptr&); 55 | 56 | int fd() const 57 | { 58 | return fd_; 59 | } 60 | int events() const 61 | { 62 | return events_; 63 | } 64 | void set_revents(int revt) 65 | { 66 | revents_ = revt; 67 | } // used by pollers 68 | // int revents() const { return revents_; } 69 | bool isNoneEvent() const 70 | { 71 | return events_ == kNoneEvent; 72 | } 73 | 74 | void enableReading() 75 | { 76 | events_ |= kReadEvent; 77 | update(); 78 | } 79 | // void disableReading() { events_ &= ~kReadEvent; update(); } 80 | void enableWriting() 81 | { 82 | events_ |= kWriteEvent; 83 | update(); 84 | } 85 | void disableWriting() 86 | { 87 | events_ &= ~kWriteEvent; 88 | update(); 89 | } 90 | void disableAll() 91 | { 92 | events_ = kNoneEvent; 93 | update(); 94 | } 95 | bool isWriting() const 96 | { 97 | return events_ & kWriteEvent; 98 | } 99 | 100 | // for Poller 101 | int index() 102 | { 103 | return index_; 104 | } 105 | void set_index(int idx) 106 | { 107 | index_ = idx; 108 | } 109 | 110 | // for debug 111 | std::string reventsToString() const; 112 | 113 | void doNotLogHup() 114 | { 115 | logHup_ = false; 116 | } 117 | 118 | EventLoop* ownerLoop() 119 | { 120 | return loop_; 121 | } 122 | void remove(); 123 | 124 | private: 125 | void update(); 126 | void handleEventWithGuard(Timestamp receiveTime); 127 | 128 | static const int kNoneEvent; 129 | static const int kReadEvent; 130 | static const int kWriteEvent; 131 | 132 | EventLoop* loop_; // 所属EventLoop 133 | const int fd_; // 文件描述符,但不负责关闭该文件描述符 134 | int events_; // 关注的事件 135 | int revents_; // poll/epoll返回的事件 136 | int index_; // used by Poller.表示在poll的事件数组中的序号 137 | bool logHup_; // for POLLHUP 138 | 139 | boost::weak_ptr tie_; 140 | bool tied_; 141 | bool eventHandling_; // 是否处于处理事件中 142 | ReadEventCallback readCallback_; 143 | EventCallback writeCallback_; 144 | EventCallback closeCallback_; 145 | EventCallback errorCallback_; 146 | }; 147 | 148 | } 149 | } 150 | #endif // XNET_NET_CHANNEL_H 151 | -------------------------------------------------------------------------------- /net/DefaultPoller.cpp: -------------------------------------------------------------------------------- 1 | #include "Poller.h" 2 | #include "PollPoller.h" 3 | #include "EPollPoller.h" 4 | 5 | #include 6 | 7 | using namespace xnet::net; 8 | 9 | Poller* Poller::newDefaultPoller(EventLoop* loop) 10 | { 11 | if (::getenv("XNET_USE_POLL")) 12 | { 13 | return new PollPoller(loop); 14 | } 15 | else 16 | { 17 | return new EPollPoller(loop); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /net/EPollPoller.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "EPollPoller.h" 3 | 4 | #include "Logging.h" 5 | #include "Channel.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace xnet; 15 | using namespace xnet::net; 16 | 17 | // On Linux, the constants of poll(2) and epoll(4) 18 | // are expected to be the same. 19 | BOOST_STATIC_ASSERT(EPOLLIN == POLLIN); 20 | BOOST_STATIC_ASSERT(EPOLLPRI == POLLPRI); 21 | BOOST_STATIC_ASSERT(EPOLLOUT == POLLOUT); 22 | BOOST_STATIC_ASSERT(EPOLLRDHUP == POLLRDHUP); 23 | BOOST_STATIC_ASSERT(EPOLLERR == POLLERR); 24 | BOOST_STATIC_ASSERT(EPOLLHUP == POLLHUP); 25 | 26 | namespace 27 | { 28 | const int kNew = -1; 29 | const int kAdded = 1; 30 | const int kDeleted = 2; 31 | } 32 | 33 | EPollPoller::EPollPoller(EventLoop* loop) : 34 | Poller(loop), epollfd_(::epoll_create1(EPOLL_CLOEXEC)), events_(kInitEventListSize) 35 | { 36 | if (epollfd_ < 0) 37 | { 38 | LOG_SYSFATAL<< "EPollPoller::EPollPoller"; 39 | } 40 | } 41 | 42 | EPollPoller::~EPollPoller() 43 | { 44 | ::close(epollfd_); 45 | } 46 | 47 | Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels) 48 | { 49 | int numEvents = ::epoll_wait(epollfd_, &*events_.begin(), static_cast(events_.size()), timeoutMs); 50 | Timestamp now(Timestamp::now()); 51 | if (numEvents > 0) 52 | { 53 | LOG_TRACE << numEvents << " events happended"; 54 | fillActiveChannels(numEvents, activeChannels); 55 | if (boost::implicit_cast(numEvents) == events_.size()) 56 | { 57 | events_.resize(events_.size() * 2); 58 | } 59 | } 60 | else if (numEvents == 0) 61 | { 62 | LOG_TRACE << " nothing happended"; 63 | } 64 | else 65 | { 66 | LOG_SYSERR<< "EPollPoller::poll()"; 67 | } 68 | return now; 69 | } 70 | 71 | void EPollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const 72 | { 73 | assert(boost::implicit_cast(numEvents) <= events_.size()); 74 | for (int i = 0; i < numEvents; ++i) 75 | { 76 | Channel* channel = static_cast(events_[i].data.ptr); 77 | #ifndef NDEBUG 78 | int fd = channel->fd(); 79 | ChannelMap::const_iterator it = channels_.find(fd); 80 | assert(it != channels_.end()); 81 | assert(it->second == channel); 82 | #endif 83 | channel->set_revents(events_[i].events); 84 | activeChannels->push_back(channel); 85 | } 86 | } 87 | 88 | void EPollPoller::updateChannel(Channel* channel) 89 | { 90 | Poller::assertInLoopThread(); 91 | LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events(); 92 | const int index = channel->index(); 93 | if (index == kNew || index == kDeleted) 94 | { 95 | // a new one, add with EPOLL_CTL_ADD 96 | int fd = channel->fd(); 97 | if (index == kNew) 98 | { 99 | assert(channels_.find(fd) == channels_.end()); 100 | channels_[fd] = channel; 101 | } 102 | else // index == kDeleted 103 | { 104 | assert(channels_.find(fd) != channels_.end()); 105 | assert(channels_[fd] == channel); 106 | } 107 | channel->set_index(kAdded); 108 | update(EPOLL_CTL_ADD, channel); 109 | } 110 | else 111 | { 112 | // update existing one with EPOLL_CTL_MOD/DEL 113 | int fd = channel->fd(); 114 | (void) fd; 115 | assert(channels_.find(fd) != channels_.end()); 116 | assert(channels_[fd] == channel); 117 | assert(index == kAdded); 118 | if (channel->isNoneEvent()) 119 | { 120 | update(EPOLL_CTL_DEL, channel); 121 | channel->set_index(kDeleted); 122 | } 123 | else 124 | { 125 | update(EPOLL_CTL_MOD, channel); 126 | } 127 | } 128 | } 129 | 130 | void EPollPoller::removeChannel(Channel* channel) 131 | { 132 | Poller::assertInLoopThread(); 133 | int fd = channel->fd(); 134 | LOG_TRACE << "fd = " << fd; 135 | assert(channels_.find(fd) != channels_.end()); 136 | assert(channels_[fd] == channel); 137 | assert(channel->isNoneEvent()); 138 | int index = channel->index(); 139 | assert(index == kAdded || index == kDeleted); 140 | size_t n = channels_.erase(fd); 141 | (void) n; 142 | assert(n == 1); 143 | 144 | if (index == kAdded) 145 | { 146 | update(EPOLL_CTL_DEL, channel); 147 | } 148 | channel->set_index(kNew); 149 | } 150 | 151 | void EPollPoller::update(int operation, Channel* channel) 152 | { 153 | struct epoll_event event; 154 | bzero(&event, sizeof event); 155 | event.events = channel->events(); 156 | event.data.ptr = channel; 157 | int fd = channel->fd(); 158 | if (::epoll_ctl(epollfd_, operation, fd, &event) < 0) 159 | { 160 | if (operation == EPOLL_CTL_DEL) 161 | { 162 | LOG_SYSERR<< "epoll_ctl op=" << operation << " fd=" << fd; 163 | } 164 | else 165 | { 166 | LOG_SYSFATAL << "epoll_ctl op=" << operation << " fd=" << fd; 167 | } 168 | } 169 | } 170 | 171 | -------------------------------------------------------------------------------- /net/EPollPoller.h: -------------------------------------------------------------------------------- 1 | #ifndef XNET_NET_POLLER_EPOLLPOLLER_H 2 | #define XNET_NET_POLLER_EPOLLPOLLER_H 3 | 4 | #include "Poller.h" 5 | 6 | #include 7 | #include 8 | 9 | struct epoll_event; 10 | 11 | namespace xnet 12 | { 13 | namespace net 14 | { 15 | 16 | /// IO Multiplexing with epoll(4). 17 | class EPollPoller: public Poller 18 | { 19 | public: 20 | EPollPoller(EventLoop* loop); 21 | virtual ~EPollPoller(); 22 | 23 | virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels); 24 | virtual void updateChannel(Channel* channel); 25 | virtual void removeChannel(Channel* channel); 26 | 27 | private: 28 | static const int kInitEventListSize = 16; 29 | 30 | void fillActiveChannels(int numEvents, ChannelList* activeChannels) const; 31 | void update(int operation, Channel* channel); 32 | 33 | typedef std::vector EventList; 34 | typedef std::map ChannelMap; 35 | 36 | int epollfd_; 37 | EventList events_; 38 | ChannelMap channels_; 39 | }; 40 | 41 | } 42 | } 43 | #endif // XNET_NET_POLLER_EPOLLPOLLER_H 44 | -------------------------------------------------------------------------------- /net/EventLoop.cpp: -------------------------------------------------------------------------------- 1 | #include "EventLoop.h" 2 | 3 | #include "Logging.h" 4 | #include "Channel.h" 5 | #include "Poller.h" 6 | 7 | //#include 8 | 9 | using namespace xnet; 10 | using namespace xnet::net; 11 | 12 | namespace 13 | { 14 | // 当前线程EventLoop对象指针 15 | // 线程局部存储 16 | __thread EventLoop* t_loopInThisThread = 0; 17 | 18 | const int kPollTimeMs = 10000; 19 | } 20 | 21 | EventLoop* EventLoop::getEventLoopOfCurrentThread() 22 | { 23 | return t_loopInThisThread; 24 | } 25 | 26 | EventLoop::EventLoop() : 27 | looping_(false), quit_(false), eventHandling_(false), threadId_(CurrentThread::tid()), poller_(Poller::newDefaultPoller(this)), currentActiveChannel_(NULL) 28 | { 29 | LOG_TRACE << "EventLoop created " << this << " in thread " << threadId_; 30 | // 如果当前线程已经创建了EventLoop对象,终止(LOG_FATAL) 31 | if (t_loopInThisThread) 32 | { 33 | LOG_FATAL<< "Another EventLoop " << t_loopInThisThread 34 | << " exists in this thread " << threadId_; 35 | } 36 | else 37 | { 38 | t_loopInThisThread = this; 39 | } 40 | } 41 | 42 | EventLoop::~EventLoop() 43 | { 44 | t_loopInThisThread = NULL; 45 | } 46 | 47 | // 事件循环,该函数不能跨线程调用 48 | // 只能在创建该对象的线程中调用 49 | void EventLoop::loop() 50 | { 51 | assert(!looping_); 52 | // 断言当前处于创建该对象的线程中 53 | assertInLoopThread(); 54 | looping_ = true; 55 | LOG_TRACE << "EventLoop " << this << " start looping"; 56 | 57 | //::poll(NULL, 0, 5*1000); 58 | while (!quit_) 59 | { 60 | activeChannels_.clear(); 61 | pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_); 62 | //++iteration_; 63 | if (Logger::logLevel() <= Logger::TRACE) 64 | { 65 | printActiveChannels(); 66 | } 67 | // TODO sort channel by priority 68 | eventHandling_ = true; 69 | for (ChannelList::iterator it = activeChannels_.begin(); it != activeChannels_.end(); ++it) 70 | { 71 | currentActiveChannel_ = *it; 72 | currentActiveChannel_->handleEvent(pollReturnTime_); 73 | } 74 | currentActiveChannel_ = NULL; 75 | eventHandling_ = false; 76 | //doPendingFunctors(); 77 | } 78 | 79 | LOG_TRACE << "EventLoop " << this << " stop looping"; 80 | looping_ = false; 81 | } 82 | 83 | // 该函数可以跨线程调用 84 | void EventLoop::quit() 85 | { 86 | quit_ = true; 87 | if (!isInLoopThread()) 88 | { 89 | //wakeup(); 90 | } 91 | } 92 | 93 | void EventLoop::updateChannel(Channel* channel) 94 | { 95 | assert(channel->ownerLoop() == this); 96 | assertInLoopThread(); 97 | poller_->updateChannel(channel); 98 | } 99 | 100 | void EventLoop::removeChannel(Channel* channel) 101 | { 102 | assert(channel->ownerLoop() == this); 103 | assertInLoopThread(); 104 | if (eventHandling_) 105 | { 106 | assert(currentActiveChannel_ == channel || std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end()); 107 | } 108 | poller_->removeChannel(channel); 109 | } 110 | 111 | void EventLoop::abortNotInLoopThread() 112 | { 113 | LOG_FATAL<< "EventLoop::abortNotInLoopThread - EventLoop " << this 114 | << " was created in threadId_ = " << threadId_ 115 | << ", current thread id = " << CurrentThread::tid(); 116 | } 117 | 118 | void EventLoop::printActiveChannels() const 119 | { 120 | for (ChannelList::const_iterator it = activeChannels_.begin(); it != activeChannels_.end(); ++it) 121 | { 122 | const Channel* ch = *it; 123 | LOG_TRACE << "{" << ch->reventsToString() << "} "; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /net/EventLoop.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef XNET_NET_EVENTLOOP_H 4 | #define XNET_NET_EVENTLOOP_H 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "CurrentThread.h" 12 | #include "Thread.h" 13 | #include "Timestamp.h" 14 | 15 | namespace xnet 16 | { 17 | namespace net 18 | { 19 | 20 | class Channel; 21 | class Poller; 22 | /// 23 | /// Reactor, at most one per thread. 24 | /// 25 | /// This is an interface class, so don't expose too much details. 26 | class EventLoop : boost::noncopyable 27 | { 28 | public: 29 | EventLoop(); 30 | ~EventLoop(); // force out-line dtor, for scoped_ptr members. 31 | 32 | /// 33 | /// Loops forever. 34 | /// 35 | /// Must be called in the same thread as creation of the object. 36 | /// 37 | void loop(); 38 | 39 | void quit(); 40 | 41 | /// 42 | /// Time when poll returns, usually means data arrivial. 43 | /// 44 | Timestamp pollReturnTime() const { return pollReturnTime_; } 45 | 46 | // internal usage 47 | void updateChannel(Channel* channel); // 在Poller中添加或者更新通道 48 | void removeChannel(Channel* channel); // 从Poller中移除通道 49 | 50 | void assertInLoopThread() 51 | { 52 | if (!isInLoopThread()) 53 | { 54 | abortNotInLoopThread(); 55 | } 56 | } 57 | bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); } 58 | 59 | static EventLoop* getEventLoopOfCurrentThread(); 60 | 61 | private: 62 | void abortNotInLoopThread(); 63 | 64 | void printActiveChannels() const; // DEBUG 65 | 66 | typedef std::vector ChannelList; 67 | 68 | bool looping_; /* atomic */ 69 | bool quit_; /* atomic */ 70 | bool eventHandling_; /* atomic */ 71 | const pid_t threadId_; // 当前对象所属线程ID 72 | Timestamp pollReturnTime_; 73 | boost::scoped_ptr poller_; 74 | ChannelList activeChannels_; // Poller返回的活动通道 75 | Channel* currentActiveChannel_; // 当前正在处理的活动通道 76 | }; 77 | 78 | } 79 | } 80 | #endif // XNET_NET_EVENTLOOP_H 81 | -------------------------------------------------------------------------------- /net/Makefile: -------------------------------------------------------------------------------- 1 | # cross compile... 2 | CROSS_COMPILE = 3 | 4 | CC = $(CROSS_COMPILE)gcc 5 | CXX = $(CROSS_COMPILE)g++ 6 | AR = $(CROSS_COMPILE)ar 7 | 8 | ARFLAGS = cr 9 | RM = -rm -rf 10 | MAKE = make 11 | 12 | debug = y 13 | 14 | 15 | CFLAGS = -D_FILE_OFFSET_BITS=64\ 16 | -Wall\ 17 | -Wextra\ 18 | -Werror\ 19 | -Wconversion\ 20 | -Wno-unused-parameter\ 21 | -Wpointer-arith\ 22 | -Wshadow\ 23 | -Wwrite-strings\ 24 | -march=native 25 | 26 | CPPFLAGS = -Wold-style-cast\ 27 | -Woverloaded-virtual 28 | 29 | ifeq ($(debug), y) 30 | CFLAGS += -g 31 | else 32 | CFLAGS += -O2 -s 33 | endif 34 | 35 | DLDFLAGS += -rdynamic 36 | 37 | INCLUDE = ../common/ 38 | 39 | 40 | # source file(s), including c file(s) cpp file(s) 41 | # you can also use $(wildcard *.c), etc. 42 | SRC_C := $(wildcard *.c) 43 | SRC_CPP := $(wildcard *.cpp) 44 | 45 | # object file(s) 46 | OBJ_C := $(patsubst %.c,%.o,$(SRC_C)) 47 | OBJ_CPP := $(patsubst %.cpp,%.o,$(SRC_CPP)) 48 | 49 | 50 | LIB = libnet.a 51 | 52 | 53 | $(LIB): $(OBJ_C) $(OBJ_CPP) 54 | @echo "Generating static library: " $(notdir $@) 55 | @$(AR) $(ARFLAGS) $@ $^ 56 | make --no-print-directory post-build 57 | # make clean 58 | 59 | 60 | 61 | # make all .c or .cpp 62 | %.o: %.c 63 | @echo "Compling: " $(addsuffix .c, $(basename $(notdir $@))) 64 | @$(CC) $(CFLAGS) -I$(INCLUDE) -c $< -o $@ 65 | 66 | %.o: %.cpp 67 | @echo "Compling: " $(addsuffix .cpp, $(basename $(notdir $@))) 68 | @$(CXX) $(CFLAGS) $(CPPFLAGS) -I$(INCLUDE) -c $< -o $@ 69 | 70 | clean: 71 | @echo "cleaning..." 72 | @$(RM) $(LIB) 73 | @$(RM) *.o *.back *~ 74 | 75 | post-build: 76 | cp $(LIB) ../ 77 | 78 | .PHONY: all clean 79 | .SECONDARY:post-build 80 | -------------------------------------------------------------------------------- /net/PollPoller.cpp: -------------------------------------------------------------------------------- 1 | #include "PollPoller.h" 2 | #include "Logging.h" 3 | #include "Channel.h" 4 | 5 | #include 6 | #include 7 | 8 | using namespace xnet; 9 | using namespace xnet::net; 10 | 11 | PollPoller::PollPoller(EventLoop* loop) : 12 | Poller(loop) 13 | { 14 | } 15 | 16 | PollPoller::~PollPoller() 17 | { 18 | } 19 | 20 | Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels) 21 | { 22 | int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs); 23 | Timestamp now(Timestamp::now()); 24 | if (numEvents > 0) 25 | { 26 | LOG_TRACE << numEvents << " events happended"; 27 | fillActiveChannels(numEvents, activeChannels); 28 | } 29 | else if (numEvents == 0) 30 | { 31 | LOG_TRACE << " nothing happended"; 32 | } 33 | else 34 | { 35 | LOG_SYSERR<< "PollPoller::poll()"; 36 | } 37 | return now; 38 | } 39 | 40 | void PollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const 41 | { 42 | for (PollFdList::const_iterator pfd = pollfds_.begin(); pfd != pollfds_.end() && numEvents > 0; ++pfd) 43 | { 44 | if (pfd->revents > 0) 45 | { 46 | --numEvents; 47 | ChannelMap::const_iterator ch = channels_.find(pfd->fd); 48 | assert(ch != channels_.end()); 49 | Channel* channel = ch->second; 50 | assert(channel->fd() == pfd->fd); 51 | channel->set_revents(pfd->revents); 52 | // pfd->revents = 0; 53 | activeChannels->push_back(channel); 54 | } 55 | } 56 | } 57 | 58 | void PollPoller::updateChannel(Channel* channel) 59 | { 60 | Poller::assertInLoopThread(); 61 | LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events(); 62 | if (channel->index() < 0) 63 | { 64 | assert(channels_.find(channel->fd()) == channels_.end()); 65 | struct pollfd pfd; 66 | pfd.fd = channel->fd(); 67 | pfd.events = static_cast(channel->events()); 68 | pfd.revents = 0; 69 | pollfds_.push_back(pfd); 70 | int idx = static_cast(pollfds_.size()) - 1; 71 | channel->set_index(idx); 72 | channels_[pfd.fd] = channel; 73 | } 74 | else 75 | { 76 | // update existing one 77 | assert(channels_.find(channel->fd()) != channels_.end()); 78 | assert(channels_[channel->fd()] == channel); 79 | int idx = channel->index(); 80 | assert(0 <= idx && idx < static_cast(pollfds_.size())); 81 | struct pollfd& pfd = pollfds_[idx]; 82 | assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd() - 1); 83 | pfd.events = static_cast(channel->events()); 84 | pfd.revents = 0; 85 | 86 | if (channel->isNoneEvent()) 87 | { 88 | pfd.fd = -channel->fd() - 1; 89 | } 90 | } 91 | } 92 | 93 | void PollPoller::removeChannel(Channel* channel) 94 | { 95 | Poller::assertInLoopThread(); 96 | LOG_TRACE << "fd = " << channel->fd(); 97 | assert(channels_.find(channel->fd()) != channels_.end()); 98 | assert(channels_[channel->fd()] == channel); 99 | assert(channel->isNoneEvent()); 100 | int idx = channel->index(); 101 | assert(0 <= idx && idx < static_cast(pollfds_.size())); 102 | const struct pollfd& pfd = pollfds_[idx]; 103 | (void) pfd; 104 | assert(pfd.fd == -channel->fd() - 1 && pfd.events == channel->events()); 105 | size_t n = channels_.erase(channel->fd()); 106 | assert(n == 1); 107 | (void) n; 108 | if (boost::implicit_cast < size_t > (idx) == pollfds_.size() - 1) 109 | { 110 | pollfds_.pop_back(); 111 | } 112 | else 113 | { 114 | int channelAtEnd = pollfds_.back().fd; 115 | iter_swap(pollfds_.begin() + idx, pollfds_.end() - 1); 116 | if (channelAtEnd < 0) 117 | { 118 | channelAtEnd = -channelAtEnd - 1; 119 | } 120 | channels_[channelAtEnd]->set_index(idx); 121 | pollfds_.pop_back(); 122 | } 123 | } 124 | 125 | -------------------------------------------------------------------------------- /net/PollPoller.h: -------------------------------------------------------------------------------- 1 | #ifndef XNET_NET_POLLER_POLLPOLLER_H 2 | #define XNET_NET_POLLER_POLLPOLLER_H 3 | 4 | #include "Poller.h" 5 | 6 | #include 7 | #include 8 | 9 | struct pollfd; 10 | 11 | namespace xnet 12 | { 13 | namespace net 14 | { 15 | 16 | /// 17 | /// IO Multiplexing with poll(2). 18 | /// 19 | class PollPoller: public Poller 20 | { 21 | public: 22 | 23 | PollPoller(EventLoop* loop); 24 | virtual ~PollPoller(); 25 | 26 | virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels); 27 | virtual void updateChannel(Channel* channel); 28 | virtual void removeChannel(Channel* channel); 29 | 30 | private: 31 | void fillActiveChannels(int numEvents, ChannelList* activeChannels) const; 32 | 33 | typedef std::vector PollFdList; 34 | typedef std::map ChannelMap; // key是文件描述符,value是Channel* 35 | PollFdList pollfds_; 36 | ChannelMap channels_; 37 | }; 38 | 39 | } 40 | } 41 | #endif // XNET_NET_POLLER_POLLPOLLER_H 42 | -------------------------------------------------------------------------------- /net/Poller.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Poller.h" 3 | 4 | using namespace xnet; 5 | using namespace xnet::net; 6 | 7 | Poller::Poller(EventLoop* loop) 8 | : ownerLoop_(loop) 9 | { 10 | } 11 | 12 | Poller::~Poller() 13 | { 14 | } 15 | 16 | -------------------------------------------------------------------------------- /net/Poller.h: -------------------------------------------------------------------------------- 1 | #ifndef XNET_NET_POLLER_H 2 | #define XNET_NET_POLLER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "Timestamp.h" 8 | #include "EventLoop.h" 9 | 10 | namespace xnet 11 | { 12 | namespace net 13 | { 14 | 15 | class Channel; 16 | 17 | 18 | class Poller: boost::noncopyable 19 | { 20 | public: 21 | typedef std::vector ChannelList; 22 | 23 | Poller(EventLoop* loop); 24 | virtual ~Poller(); 25 | 26 | /// Polls the I/O events. 27 | /// Must be called in the loop thread. 28 | virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0; 29 | 30 | /// Changes the interested I/O events. 31 | /// Must be called in the loop thread. 32 | virtual void updateChannel(Channel* channel) = 0; 33 | 34 | /// Remove the channel, when it destructs. 35 | /// Must be called in the loop thread. 36 | virtual void removeChannel(Channel* channel) = 0; 37 | 38 | static Poller* newDefaultPoller(EventLoop* loop); 39 | 40 | void assertInLoopThread() 41 | { 42 | ownerLoop_->assertInLoopThread(); 43 | } 44 | 45 | private: 46 | EventLoop* ownerLoop_; // Poller所属EventLoop 47 | }; 48 | 49 | } 50 | } 51 | #endif // XNET_NET_POLLER_H 52 | -------------------------------------------------------------------------------- /net/test/Makefile: -------------------------------------------------------------------------------- 1 | # cross compile... 2 | CROSS_COMPILE = 3 | 4 | CC = $(CROSS_COMPILE)gcc 5 | CXX = $(CROSS_COMPILE)g++ 6 | AR = $(CROSS_COMPILE)ar 7 | 8 | ARFLAGS = cr 9 | RM = -rm -rf 10 | MAKE = make 11 | 12 | CFLAGS += -Wall\ 13 | -Wextra\ 14 | -Werror\ 15 | -Wconversion\ 16 | -Wno-unused-parameter\ 17 | -Wno-unused-local-typedefs\ 18 | -Wold-style-cast\ 19 | -Woverloaded-virtual\ 20 | -Wpointer-arith\ 21 | -Wshadow\ 22 | -Wwrite-strings\ 23 | -march=native 24 | 25 | debug = y 26 | 27 | ifeq ($(debug), y) 28 | CFLAGS += -g 29 | else 30 | CFLAGS += -O2 -s 31 | endif 32 | 33 | 34 | 35 | LDFLAGS = -rdynamic -lnet -lcommon -lpthread -L../../ 36 | 37 | INCDIRS = -I../ -I../../common/ 38 | 39 | CFLAGS += $(INCDIRS) 40 | 41 | 42 | 43 | DES = Reactor_test01\ 44 | Reactor_test02 45 | 46 | all:$(DES) 47 | 48 | 49 | Reactor_test01:Reactor_test01.cpp 50 | @echo "Compling: " $(addsuffix .c, $(basename $(notdir $@))) 51 | @$(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 52 | 53 | Reactor_test02:Reactor_test02.cpp 54 | @echo "Compling: " $(addsuffix .c, $(basename $(notdir $@))) 55 | @$(CXX) $(CFLAGS) $^ -o $@ $(LDFLAGS) 56 | 57 | 58 | 59 | 60 | clean: 61 | @echo "cleaning..." 62 | @$(RM) $(DES) 63 | @$(RM) *.o *.back *~ 64 | 65 | .PHONY: all clean 66 | -------------------------------------------------------------------------------- /net/test/Reactor_test01: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blingbin/xnet/023d184940507bcd1a4f66cf486d3368121d4d74/net/test/Reactor_test01 -------------------------------------------------------------------------------- /net/test/Reactor_test01.cpp: -------------------------------------------------------------------------------- 1 | #include "EventLoop.h" 2 | 3 | #include 4 | 5 | using namespace xnet; 6 | using namespace xnet::net; 7 | 8 | void threadFunc() 9 | { 10 | printf("threadFunc(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid()); 11 | 12 | EventLoop loop; 13 | loop.loop(); 14 | } 15 | 16 | int main(void) 17 | { 18 | printf("main(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid()); 19 | 20 | EventLoop loop; 21 | 22 | Thread t(threadFunc); 23 | t.start(); 24 | 25 | loop.loop(); 26 | t.join(); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /net/test/Reactor_test02: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blingbin/xnet/023d184940507bcd1a4f66cf486d3368121d4d74/net/test/Reactor_test02 -------------------------------------------------------------------------------- /net/test/Reactor_test02.cpp: -------------------------------------------------------------------------------- 1 | #include "EventLoop.h" 2 | 3 | #include 4 | 5 | using namespace xnet; 6 | using namespace xnet::net; 7 | 8 | EventLoop* g_loop; 9 | 10 | void threadFunc() 11 | { 12 | g_loop->loop(); 13 | } 14 | 15 | int main(void) 16 | { 17 | EventLoop loop; 18 | g_loop = &loop; 19 | Thread t(threadFunc); 20 | t.start(); 21 | t.join(); 22 | return 0; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/socketutil.c: -------------------------------------------------------------------------------- 1 | #include "socketutil.h" 2 | 3 | int read_timeout(int fd, unsigned int wait_seconds) 4 | { 5 | int ret = 0; 6 | if (wait_seconds > 0) 7 | { 8 | fd_set read_fdset; 9 | struct timeval timeout; 10 | 11 | FD_ZERO(&read_fdset); 12 | FD_SET(fd, &read_fdset); 13 | 14 | timeout.tv_sec = wait_seconds; 15 | timeout.tv_usec = 0; 16 | 17 | do 18 | { 19 | ret = select(fd + 1, &read_fdset, NULL, NULL, &timeout); 20 | } while (ret < 0 && errno == EINTR); 21 | 22 | if (ret == 0) 23 | { 24 | ret = -1; 25 | errno = ETIMEDOUT; 26 | } 27 | else if (ret == 1) 28 | ret = 0; 29 | } 30 | 31 | return ret; 32 | } 33 | 34 | 35 | int write_timeout(int fd, unsigned int wait_seconds) 36 | { 37 | int ret = 0; 38 | if (wait_seconds > 0) 39 | { 40 | fd_set write_fdset; 41 | struct timeval timeout; 42 | 43 | FD_ZERO(&write_fdset); 44 | FD_SET(fd, &write_fdset); 45 | 46 | timeout.tv_sec = wait_seconds; 47 | timeout.tv_usec = 0; 48 | do 49 | { 50 | ret = select(fd + 1, NULL, &write_fdset, NULL, &timeout); 51 | } while (ret < 0 && errno == EINTR); 52 | 53 | if (ret == 0) 54 | { 55 | ret = -1; 56 | errno = ETIMEDOUT; 57 | } 58 | else if (ret == 1) 59 | ret = 0; 60 | } 61 | 62 | return ret; 63 | } 64 | 65 | 66 | int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) 67 | { 68 | int ret; 69 | socklen_t addrlen = sizeof(struct sockaddr_in); 70 | 71 | if (wait_seconds > 0) 72 | { 73 | fd_set accept_fdset; 74 | struct timeval timeout; 75 | FD_ZERO(&accept_fdset); 76 | FD_SET(fd, &accept_fdset); 77 | timeout.tv_sec = wait_seconds; 78 | timeout.tv_usec = 0; 79 | do 80 | { 81 | ret = select(fd + 1, &accept_fdset, NULL, NULL, &timeout); 82 | } while (ret < 0 && errno == EINTR); 83 | if (ret == -1) 84 | return -1; 85 | else if (ret == 0) 86 | { 87 | errno = ETIMEDOUT; 88 | return -1; 89 | } 90 | } 91 | 92 | if (addr != NULL) 93 | ret = accept(fd, (struct sockaddr*) addr, &addrlen); 94 | else 95 | ret = accept(fd, NULL, NULL); 96 | if (ret == -1) 97 | { 98 | ret = errno; 99 | printf("func accept() err:%d \n", ret); 100 | return ret; 101 | } 102 | 103 | return ret; 104 | } 105 | 106 | 107 | int activate_nonblock(int fd) 108 | { 109 | int ret = 0; 110 | int flags = fcntl(fd, F_GETFL); 111 | if (flags == -1) 112 | { 113 | ret = flags; 114 | printf("func activate_nonblock() err:%d", ret); 115 | return ret; 116 | } 117 | flags |= O_NONBLOCK; 118 | ret = fcntl(fd, F_SETFL, flags); 119 | if (ret == -1) 120 | { 121 | printf("func activate_nonblock() err:%d", ret); 122 | return ret; 123 | } 124 | return ret; 125 | } 126 | 127 | int deactivate_nonblock(int fd) 128 | { 129 | int ret = 0; 130 | int flags = fcntl(fd, F_GETFL); 131 | if (flags == -1) 132 | { 133 | ret = flags; 134 | printf("func deactivate_nonblock() err:%d", ret); 135 | return ret; 136 | } 137 | 138 | flags &= ~O_NONBLOCK; 139 | ret = fcntl(fd, F_SETFL, flags); 140 | return ret; 141 | } 142 | 143 | 144 | int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) 145 | { 146 | int ret; 147 | socklen_t addrlen = sizeof(struct sockaddr_in); 148 | if (wait_seconds > 0) 149 | activate_nonblock(fd); 150 | ret = connect(fd, (struct sockaddr*) addr, addrlen); 151 | if (ret < 0 && errno == EINPROGRESS) 152 | { 153 | fd_set connect_fdset; 154 | struct timeval timeout; 155 | FD_ZERO(&connect_fdset); 156 | FD_SET(fd, &connect_fdset); 157 | 158 | timeout.tv_sec = wait_seconds; 159 | timeout.tv_usec = 0; 160 | do 161 | { 162 | ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout); 163 | } while (ret < 0 && errno == EINTR); 164 | if (ret == 0) 165 | { 166 | ret = -1; 167 | errno = ETIMEDOUT; 168 | } 169 | else if (ret < 0) 170 | return -1; 171 | else if (ret == 1) 172 | { 173 | int err; 174 | socklen_t socklen = sizeof(err); 175 | int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen); 176 | if (sockoptret == -1) 177 | { 178 | return -1; 179 | } 180 | if (err == 0) 181 | { 182 | ret = 0; 183 | } 184 | else 185 | { 186 | errno = err; 187 | ret = -1; 188 | } 189 | } 190 | } 191 | if (wait_seconds > 0) 192 | { 193 | deactivate_nonblock(fd); 194 | } 195 | return ret; 196 | } 197 | 198 | 199 | ssize_t readn(int fd, void *buf, size_t count) 200 | { 201 | size_t nleft = count; 202 | ssize_t nread; 203 | char *bufp = (char*) buf; 204 | while (nleft > 0) 205 | { 206 | if ((nread = read(fd, bufp, nleft)) < 0) 207 | { 208 | if (errno == EINTR) 209 | continue; 210 | return -1; 211 | } 212 | else if (nread == 0) 213 | return count - nleft; 214 | bufp += nread; 215 | nleft -= nread; 216 | } 217 | return count; 218 | } 219 | 220 | ssize_t writen(int fd, const void *buf, size_t count) 221 | { 222 | size_t nleft = count; 223 | ssize_t nwritten; 224 | char *bufp = (char*) buf; 225 | while (nleft > 0) 226 | { 227 | if ((nwritten = write(fd, bufp, nleft)) < 0) 228 | { 229 | if (errno == EINTR) 230 | continue; 231 | return -1; 232 | } 233 | else if (nwritten == 0) 234 | continue; 235 | bufp += nwritten; 236 | nleft -= nwritten; 237 | } 238 | return count; 239 | } 240 | 241 | 242 | ssize_t recv_peek(int sockfd, void *buf, size_t len) 243 | { 244 | while (1) 245 | { 246 | int ret = recv(sockfd, buf, len, MSG_PEEK); 247 | if (ret == -1 && errno == EINTR) 248 | continue; 249 | return ret; 250 | } 251 | } 252 | 253 | ssize_t readline(int sockfd, void *buf, size_t maxline) 254 | { 255 | int ret; 256 | int nread; 257 | char *bufp = buf; 258 | int nleft = maxline; 259 | while (1) 260 | { 261 | ret = recv_peek(sockfd, bufp, nleft); 262 | if (ret < 0) 263 | return ret; 264 | else if (ret == 0) 265 | return ret; 266 | 267 | nread = ret; 268 | int i; 269 | for (i = 0; i < nread; i++) 270 | { 271 | if (bufp[i] == '\n') 272 | { 273 | ret = readn(sockfd, bufp, i + 1); 274 | if (ret != i + 1) 275 | exit(EXIT_FAILURE); 276 | 277 | return ret; 278 | } 279 | } 280 | 281 | if (nread > nleft) 282 | exit(EXIT_FAILURE); 283 | 284 | nleft -= nread; 285 | ret = readn(sockfd, bufp, nread); 286 | if (ret != nread) 287 | exit(EXIT_FAILURE); 288 | 289 | bufp += nread; 290 | } 291 | 292 | return -1; 293 | } 294 | -------------------------------------------------------------------------------- /src/socketutil.h: -------------------------------------------------------------------------------- 1 | #ifndef _SOCKETUTIL_H_ 2 | #define _SOCKETUTIL_H_ 3 | 4 | #ifdef __cplusplus 5 | extern 'C' 6 | { 7 | #endif 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | int activate_nonblock(int fd); // 27 | int deactivate_nonblock(int fd); 28 | 29 | int read_timeout(int fd, unsigned int wait_seconds); 30 | int write_timeout(int fd, unsigned int wait_seconds); 31 | int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds); 32 | int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds); 33 | 34 | ssize_t readn(int fd, void *buf, size_t count); 35 | ssize_t writen(int fd, const void *buf, size_t count); 36 | ssize_t recv_peek(int sockfd, void *buf, size_t len); 37 | ssize_t readline(int sockfd, void *buf, size_t maxline); 38 | 39 | #ifdef __cpluspluse 40 | } 41 | #endif 42 | 43 | #endif //_SOCKETUTIL_H_ 44 | --------------------------------------------------------------------------------