Linux 环境下C++进程及线程终止行为额外管理

描述

主要是对程序的一些简单行为进行更为简易方便的管理。

包括:线程的信息、线程执行权的放弃、程序的退出的额外管理(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();
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值