diff --git a/.clang-format b/.clang-format index 90542e2..83c2ce5 100644 --- a/.clang-format +++ b/.clang-format @@ -36,7 +36,7 @@ BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeColon ConstructorInitializerAllOnOneLineOrOnePerLine: true BreakInheritanceList: BeforeColon -ColumnLimit: 120 +ColumnLimit: 80 CompactNamespaces: false ContinuationIndentWidth: 4 EmptyLineBeforeAccessModifier: LogicalBlock @@ -69,4 +69,5 @@ SpacesInContainerLiterals: false SpacesInParentheses: false SpacesInSquareBrackets: false TabWidth: 4 -UseTab: Never \ No newline at end of file +UseTab: Never + diff --git a/CMakeLists.txt b/CMakeLists.txt index da612f5..1526c6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,8 @@ else() add_library(${PROJECT_NAME} STATIC "") endif() target_sources(${PROJECT_NAME} PRIVATE + src/ulib/base/location.h + src/ulib/base/location.cpp src/ulib/concorrency/barrier.cpp src/ulib/concorrency/barrier.h src/ulib/concorrency/mutex.cpp @@ -36,7 +38,10 @@ target_sources(${PROJECT_NAME} PRIVATE src/ulib/concorrency/internal/condition_variable_impl.cpp src/ulib/concorrency/internal/condition_variable_impl.h src/ulib/concorrency/event.cpp - src/ulib/concorrency/event.h) + src/ulib/concorrency/event.h + src/ulib/system/thread.h + src/ulib/system/thread.cpp +) find_package(Threads REQUIRED) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") diff --git a/src/ulib/base/location.cpp b/src/ulib/base/location.cpp new file mode 100644 index 0000000..31013e6 --- /dev/null +++ b/src/ulib/base/location.cpp @@ -0,0 +1,60 @@ +#include "location.h" +#include + +namespace ulib { +#if defined(__clang__) && defined(_MSC_VER) +const char stripped[] = "ulib\\base\\location.cpp"; +#else +const char stripped[] = "ulib/base/location.cpp"; +#endif + +#if !defined(ULIB_LOCATION_STRIPPED_PREFIX_LENGTH) \ + && !defined(ULIB_PROJECT_ROOT_PATH) +inline size_t +StrippedFilePathPrefixLength() +{ + const char path[] = __FILE__; + static size_t stripped_file_path_prefix_length = + sizeof(path) - sizeof(stripped); + return stripped_file_path_prefix_length; +} +#elif defined(ULIB_LOCATION_STRIPPED_PREFIX_LENGTH) +inline size_t +StrippedFilePathPrefixLength() +{ + return ULIB_LOCATION_STRIPPED_PREFIX_LENGTH; +} +#else +inline size_t +StrippedFilePathPrefixLength() +{ + return sizeof(ULIB_PROJECT_ROOT_PATH); +} +#endif + +Location::Location() = default; + +Location::Location(const Location &other) + : function_name_(other.function_name_), + file_name_(other.file_name_), + line_number_(other.line_number_) +{} + +Location & +Location::operator=(const Location &other) +{ + function_name_ = other.function_name_; + file_name_ = other.file_name_; + line_number_ = other.line_number_; + return *this; +} + +std::string +Location::ToString() const +{ + std::stringstream ss; + ss << function_name_ << "@" << file_name_ << ":" << line_number_; + return ss.str(); +} + +}// namespace ulib diff --git a/src/ulib/base/location.h b/src/ulib/base/location.h new file mode 100644 index 0000000..aafdc81 --- /dev/null +++ b/src/ulib/base/location.h @@ -0,0 +1,37 @@ +#ifndef ULIB_SRC_ULIB_BASE_LOCATION_H_ +#define ULIB_SRC_ULIB_BASE_LOCATION_H_ + +#include "types.h" +#include "stringize_macros.h" +#include + +namespace ulib { +class Location { +public: + Location(); + Location(const Location &other); + Location &operator=(const Location &other); + + std::string ToString() const; + + const char *function_name() const { return function_name_; } + + const char *file_name() const { return file_name_; } + + int line_number() const { return line_number_; } + + static Location Current(const char *function_name = __builtin_FUNCTION(), + const char *file_name = __builtin_FILE(), + int line_number = __builtin_LINE()); + +private: + Location(const char *function_name, const char *file_name, int line_number); + const char *function_name_ = nullptr; + const char *file_name_ = nullptr; + int line_number_ = -1; +}; + +#define ULIB_FROM_HERE ::ulib::Location::Current() + +}// namespace ulib +#endif// ULIB_SRC_ULIB_BASE_LOCATION_H_ diff --git a/src/ulib/base/stringize_macros.h b/src/ulib/base/stringize_macros.h new file mode 100644 index 0000000..00f95c2 --- /dev/null +++ b/src/ulib/base/stringize_macros.h @@ -0,0 +1,8 @@ +#ifndef ULIB_SRC_ULIB_BASE_STRINGIZE_MACROS_H_ +#define ULIB_SRC_ULIB_BASE_STRINGIZE_MACROS_H_ + +#define STRINGIZE_NO_EXPANSION(x) #x + +#define STRINGIZE STRINGIZE_NO_EXPANSION(x) + +#endif// ULIB_SRC_ULIB_BASE_STRINGIZE_MACROS_H_ diff --git a/src/ulib/log/log.h b/src/ulib/log/log.h index 856c259..0ee6ba4 100644 --- a/src/ulib/log/log.h +++ b/src/ulib/log/log.h @@ -5,17 +5,20 @@ #ifndef ULIB_SRC_LOG_LOG_H_ #define ULIB_SRC_LOG_LOG_H_ +#include "ulib/base/location.h" #include "logger.h" #include "level.h" #include namespace tqcq { -#define _ULOG(level, tag, fmt_str, ...) \ - tqcq::Logger::GetInstance().Log(level, __FILE__, __FUNCTION__, __LINE__, tag, \ - fmt::format(fmt_str, ##__VA_ARGS__).c_str()) +#define _ULOG(level, tag, fmt_str, ...) \ + tqcq::Logger::GetInstance().Log( \ + level, __FILE__, __FUNCTION__, __LINE__, tag, \ + fmt::format(fmt_str, ##__VA_ARGS__).c_str()) -#define ULOG_SET_STRIPPED_PREFIX_LEN(len) ::tqcq::Logger::GetInstance().SetStrippedPrefixLen(len) +#define ULOG_SET_STRIPPED_PREFIX_LEN(len) \ + ::tqcq::Logger::GetInstance().SetStrippedPrefixLen(len) #if ULOG_LEVEL <= ULOG_LEVEL_TRACE #define ULOG_TRACE(tag, ...) _ULOG(ULOG_LEVEL_TRACE, tag, __VA_ARGS__) @@ -53,6 +56,17 @@ namespace tqcq { #define ULOG_FATAL(...) ((void) 0) #endif +#define ULOG_ASSERT_WITH_TAG(expr, tag, ...) \ + do { \ + if (!(expr)) { \ + _ULOG(ULOG_LEVEL_FATAL, tag, ##__VA_ARGS__); \ + abort(); \ + } \ + } while (0) + +#define ULOG_ASSERT(expr, ...) \ + ULOG_ASSERT_WITH_TAG(expr, "ulib_assert", ##__VA_ARGS__) + }// namespace tqcq #endif//ULIB_SRC_LOG_LOG_H_ diff --git a/src/ulib/system/thread.cpp b/src/ulib/system/thread.cpp new file mode 100644 index 0000000..871c3db --- /dev/null +++ b/src/ulib/system/thread.cpp @@ -0,0 +1,125 @@ +#include "thread.h" +#include +#include +#include "ulib/log/log.h" +#include "ulib/concorrency/countdown_latch.h" +#include +#include +#include + +namespace ulib { + +pid_t +GetTid() +{ + // TODO cache tid +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 + uint64_t tid64; + pthread_threadid_np(NULL, &tid64); + return static_cast(tid64); +#else + return static_cast(::syscall(SYS_gettid)); +#endif +} + +class Thread::Impl { +public: + Impl(const ThreadFunc &func, const std::string &thread_name) + : thread_(0), + started_(false), + joined_(false), + thread_name_(thread_name), + func_(func), + tid_(new pid_t(0)), + latch_(1) + {} + + ~Impl() + { + if (started_ && !joined_) { pthread_detach(thread_); } + } + + static void *ThreadRun(void *obj) + { + Impl *impl = static_cast(obj); + *impl->tid_ = GetTid(); + impl->latch_.CountDown(); + try { + impl->func_(); + } catch (std::exception &ex) { + // do nothing + ULOG_ERROR("system.thread", + "exception caught in Thread %s, reson: %s", + impl->thread_name_.c_str(), ex.what()); + } catch (...) { + // do nothing + } + return NULL; + } + + void Start() + { + ULOG_ASSERT(!started_, "thread already started"); + started_ = true; + if (pthread_create(&thread_, NULL, Impl::ThreadRun, this)) { + started_ = false; + } else { + latch_.Await(); + ULOG_ASSERT(tid_.get(), "tid_ is null"); + } + } + + int Join() + { + ULOG_ASSERT(started_, "thread not started"); + ULOG_ASSERT(!joined_, "thread already joined"); + joined_ = true; + return pthread_join(thread_, NULL); + } + + bool Started() const { return started_; } + + bool Tid() const { return *tid_; } + +private: + pthread_t thread_; + bool started_; + bool joined_; + + std::string thread_name_; + ThreadFunc func_; + std::shared_ptr tid_; + CountDownLatch latch_; +}; + +Thread::Thread(const ThreadFunc &func, const std::string &thread_name) + : impl_(new Impl(func, thread_name)) +{} + +// Thread(ThreadFunc &&func, const std::string &thread_name = std::string()); +Thread::~Thread() { delete impl_; } + +void +Thread::Start() +{ + impl_->Start(); +} + +int +Thread::Join() +{ + return impl_->Join(); +} + +bool +Thread::Started() const +{ + return impl_->Started(); +} + +pid_t +Thread::Tid() const +{ + return impl_->Tid(); +} +}// namespace ulib diff --git a/src/ulib/system/thread.h b/src/ulib/system/thread.h new file mode 100644 index 0000000..a4de54c --- /dev/null +++ b/src/ulib/system/thread.h @@ -0,0 +1,29 @@ +#ifndef ULIB_SRC_ULIB_SYSTEM_THREAD_H_ +#define ULIB_SRC_ULIB_SYSTEM_THREAD_H_ +#include "ulib/base/types.h" +#include +#include + +namespace ulib { +class Thread { +public: + using ThreadFunc = std::function; + Thread(const ThreadFunc &func, + const std::string &thread_name = std::string()); + // Thread(ThreadFunc &&func, const std::string &thread_name = std::string()); + ~Thread(); + + void Start(); + int Join(); + + bool Started() const; + pid_t Tid() const; + const std::string &name() const; + +private: + class Impl; + Impl *impl_; +}; + +}// namespace ulib +#endif// ULIB_SRC_ULIB_SYSTEM_THREAD_H_ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8e3dc07..63baa0b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(ulib_test ulib/concorrency/mutex_unittest.cpp ulib/concorrency/event_unittest.cpp ulib/concorrency/countdown_latch_unittest.cpp + ulib/system/thread_unittest.cpp ) target_link_libraries(ulib_test PRIVATE ulib diff --git a/tests/ulib/system/thread_unittest.cpp b/tests/ulib/system/thread_unittest.cpp new file mode 100644 index 0000000..190a905 --- /dev/null +++ b/tests/ulib/system/thread_unittest.cpp @@ -0,0 +1,17 @@ +#include +#include +#include + +TEST(Thread, Start) +{ + volatile int i = 0; + ulib::Thread thread([&]() { + EXPECT_EQ(i, 0); + i = 1; + EXPECT_EQ(i, 1); + }); + EXPECT_EQ(i, 0); + thread.Start(); + thread.Join(); + EXPECT_EQ(i, 1); +}