描述
主要是对程序的一些简单行为进行更为简易方便的管理。
包括:线程的信息、线程执行权的放弃、程序的退出的额外管理(main 函数执行后继续执行逻辑、异常退出后执行额外的逻辑)。
代码来源:我的个人项目库:tinyBackend/Base/Detail/CurrentThread 部分。
环境要求:Linux 系统,gcc / clang
代码
CurrentThread.hpp:
//
// Created by taganyer on 24-2-20.
//
#ifndef BASE_CURRENTTHREAD_HPP
#define BASE_CURRENTTHREAD_HPP
#ifdef BASE_CURRENTTHREAD_HPP
#include <string>
#include <exception>
#include <pthread.h>
#include <functional>
#include "NoCopy.hpp"
namespace Base {
using std::string;
class CurrentThread : private NoCopy {
public:
/// 判断当前线程是否为主线程。
static bool is_main_thread() { return main_thread_id == thread_tid; };
/// 进程id
static pid_t pid() { return thread_pid; };
/// 线程id
static pthread_t tid() { return thread_tid; };
/// 线程名
static string& thread_name() { return this_thread_name; };
/// 放弃当前线程运行权。
static void yield_this_thread();
/// 获得函数调用栈。
static string stackTrace(bool demangle = true);
using ExitFun = void(*)();
/// 当程序正常退出时,调用注册的函数。可注册多个函数,线程安全,调用顺序为后进先出。
static bool set_exit_function(ExitFun fun);
using ExceptionPtr = std::exception_ptr;
using TerminalFun = std::function<void(ExceptionPtr)>;
/// 当程序异常退出时,调用注册的函数。线程不安全,调用晚于 loacl_terminal_function。
static void set_global_terminal_function(TerminalFun fun);
/// 当程序异常退出时,调用注册的函数。每个线程可设置不同的调用函数,线程安全。
static void set_loacl_terminal_function(TerminalFun fun);
/// 设置错误信息输出文件并把该文件设置为无缓冲模式,默认为 stderr,为 nullptr 不输出错误信息。
static void set_error_message_file(FILE *file);
private:
static TerminalFun global_terminal_function;
static thread_local TerminalFun local_terminal_function;
static FILE *error_message_file;
static const pthread_t main_thread_id;
static const thread_local pid_t thread_pid;
static const thread_local pthread_t thread_tid;
static thread_local string this_thread_name;
friend class Thread;
static void terminal();
};
}
#endif
#endif // BASE_CURRENTTHREAD_HPP
CurrentThread.cpp:
//
// Created by taganyer on 24-2-20.
//
#include <unistd.h>
#include <cxxabi.h>
#include <execinfo.h>
#include <sys/syscall.h>
#include "CurrentThread.hpp"
using namespace Base;
CurrentThread::TerminalFun CurrentThread::global_terminal_function;
thread_local CurrentThread::TerminalFun CurrentThread::local_terminal_function;
FILE* CurrentThread::error_message_file = stderr;
const pthread_t CurrentThread::main_thread_id = [] {
std::set_terminate(terminal);
return syscall(SYS_gettid);
}();
const thread_local pid_t CurrentThread::thread_pid = getpid();
const thread_local pthread_t CurrentThread::thread_tid = syscall(SYS_gettid);
thread_local string CurrentThread::this_thread_name = [] {
if (!is_main_thread()) {
string name("T_");
name += std::to_string(tid());
return name;
} else {
return string { "Main_thread" };
}
}();
void CurrentThread::yield_this_thread() {
sched_yield();
}
string CurrentThread::stackTrace(bool demangle) {
string stack;
constexpr int max_frames = 200;
void* frame[max_frames];
int nptrs = ::backtrace(frame, max_frames);
if (char** strings = ::backtrace_symbols(frame, nptrs)) {
for (int i = 1; i < nptrs; ++i) {
if (demangle) {
char *left_par = nullptr, *plus = nullptr;
for (char* p = strings[i]; *p; ++p) {
if (*p == '(') left_par = p;
else if (*p == '+') plus = p;
}
if (left_par && plus && left_par < plus) {
*plus = '\0';
int status = 0;
char* ret = abi::__cxa_demangle(left_par + 1, nullptr, nullptr, &status);
*plus = '+';
if (status == 0) {
stack.append(strings[i], left_par + 1);
stack.append(ret);
stack.append(plus);
stack.push_back('\n');
std::free(ret);
continue;
}
}
}
stack.append(strings[i]);
stack.push_back('\n');
}
std::free(strings);
} else {
stack = "backtrace_symbols analyze failed.";
}
return stack;
}
bool CurrentThread::set_exit_function(ExitFun fun) {
return std::atexit(fun) == 0;
}
void CurrentThread::set_global_terminal_function(TerminalFun fun) {
global_terminal_function = std::move(fun);
}
void CurrentThread::set_loacl_terminal_function(TerminalFun fun) {
local_terminal_function = std::move(fun);
}
void CurrentThread::set_error_message_file(FILE* file) {
error_message_file = file;
setvbuf(file, nullptr, _IONBF, 0);
}
void CurrentThread::terminal() {
auto eptr = std::current_exception();
if (eptr && error_message_file) {
try {
std::rethrow_exception(eptr);
} catch (const std::exception &ex) {
fprintf(error_message_file, "std::exception in Thread %s:\n", thread_name().c_str());
fprintf(error_message_file, "Reason: %s:\n", ex.what());
fprintf(error_message_file, "StackTrace: %s:\n", stackTrace().data());
} catch (...) {
fprintf(error_message_file, "Unknown exception in Thread %s:\n", thread_name().c_str());
fprintf(error_message_file, "StackTrace: %s:\n", stackTrace().data());
}
}
try {
if (local_terminal_function)
local_terminal_function(eptr);
if (global_terminal_function)
global_terminal_function(eptr);
} catch (...) {
abort();
}
}