gettimeofday异步信号安全吗?如果在信号处理程序中使用它会导致死锁吗?

Dur*_*esh 5 c c++ linux multithreading signals

以前我问了一个关于如何终止阻塞I/O的线程的问题.考虑到一些优点,我已经使用了pthread_kill()代替pthread_cancel()或写入管道.在使用代码发送信号(SIGUSR2)到目标线程之后pthread_kill(),最初它工作正常并且没有遇到任何问题.

static void signalHandler(int signum) {
    LogInfo("SIGNAL HANDLER : %d",signum);  //print info message using logger utility
}

void setSignalHandler() {
    struct sigaction actions;

    memset(&actions, 0, sizeof(actions));
    sigemptyset(&actions.sa_mask);
    actions.sa_flags = 0;
    actions.sa_handler = signalHandler;

    int rc = sigaction(SIGUSR2,&actions,NULL);
    if(rc < 0) {
        LogError("Error in sigaction");  //print error using logger utility
    }
}

void stopThread() {
    fStop = 1;  //set the flag to stop the thread
    if( ftid ) { //ftid is pthread_t variable of thread that needs to be terminated.
        LogInfo("Before pthread_kill()");
        int kill_result = pthread_kill(ftid,SIGUSR2);  // send signal to interrupt the thread if blocked for poll() I/O
        LogInfo("pthread_kill() returned : %d ( %s )",kill_result,strerror(kill_result));
        int result = pthread_join( ftid, NULL );
        LogInfo("Event loop stopped for %s", fStreamName);
        LogInfo( "pthread_join() -> %d ( %s )\n", result, strerror(result) );
        if( result == 0 ) ftid = 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

最近我注意到一个死锁问题,其中几个线程(试图记录语句)被下面的stacktrace阻塞

#0  0x00007fc1b2dc565c in __lll_lock_wait_private () from /lib64/libc.so.6
#1  0x00007fc1b2d70f0c in _L_lock_2462 () from /lib64/libc.so.6
#2  0x00007fc1b2d70d47 in __tz_convert () from /lib64/libc.so.6
#3  0x00000000004708f7 in Logger::getCurrentTimestamp(char*) ()
Run Code Online (Sandbox Code Playgroud)

getCurrentTimestamp(char*)当调用函数LogInfo()LogError()函数时,从我们的记录器模块调用函数.此函数在内部调用gettimeofday()以在日志中打印当前时间.所以我怀疑在signalHandler()内部LogInfo()调用的函数(哪些调用gettimeofday())可能导致死锁问题.但我对此并不自信,因为这个问题没有经常复制,我没有得到任何信息表明gettimeofday()不是异步信号安全.

gettimeofday()是线程安全的,我认为它不是异步信号安全,因为它没有在异步信号安全功能下列出.

1)我可以考虑gettimeofday()不是异步信号安全,它是死锁的根本原因吗?

2)是否存在pthread_kill()可能导致死锁的已知问题?

编辑:

以下是getCurrentTimeStamp()功能的定义

char* Logger::getCurrentTimestamp ( char *tm_buff ) {
    char curr_time[ 32 ] = { 0 };
    time_t current = time( NULL );
    struct timeval detail_time;

    if ( tm_buff ) {
        strftime( curr_time, sizeof(curr_time), "%Y-%m-%d-%H:%M:%S", localtime( &current ) );
        gettimeofday( &detail_time, NULL );
        sprintf( tm_buff, "%s:%03d", curr_time, (int) (detail_time.tv_usec / 1000) );
        return (tm_buff);
    }
    return (NULL);
}
Run Code Online (Sandbox Code Playgroud)

堆栈被信号中断的线程的跟踪.

#0  0x00007fc1b2dc565c in __lll_lock_wait_private () from /lib64/libc.so.6
#1  0x00007fc1b2d70f0c in _L_lock_2462 () from /lib64/libc.so.6
#2  0x00007fc1b2d70d47 in __tz_convert () from /lib64/libc.so.6
#3  0x00000000004708f7 in Logger::getCurrentTimestamp(char*) ()
#4  0x000000000046e80f in Log::write(Logger*, LogType, std::string const&, char const*) ()
#5  0x000000000046df74 in Log::i(Logger*, char const*, std::string const&, std::string const&, char const*, ...) ()
#6  0x0000000000456b67 in ?? ()
#7  <signal handler called>
#8  0x00007fc1b2da8dc5 in _xstat () from /lib64/libc.so.6
#9  0x00007fc1b2d71056 in __tzfile_read () from /lib64/libc.so.6
#10 0x00007fc1b2d703b4 in tzset_internal () from /lib64/libc.so.6
#11 0x00007fc1b2d70d63 in __tz_convert () from /lib64/libc.so.6
#12 0x00000000004708f7 in Logger::getCurrentTimestamp(char*) ()
#13 0x000000000047030e in Logger::writeLog(int, std::string&, std::string&) ()
#14 0x0000000000470633 in Logger::Write(Logger*, int, std::string, std::string const&) ()
#15 0x000000000046eb02 in Log::write(Logger*, LogType, std::string const&, char const*) ()
#16 0x000000000046df74 in Log::i(Logger*, char const*, std::string const&, std::string const&, char const*, ...) ()
Run Code Online (Sandbox Code Playgroud)

hel*_*low 5

gettimeofday 你已经发现,它本身并不是信号安全的.

但是,"POSIX.1-2008将gettimeofday()标记为过时,建议使用clock_gettime(2)." (来源)并且clock_gettime信号安全的(也是线程安全的),因此可能是您的替代方案.


zwo*_*wol 3

gettimeofday未定义为异步信号安全,但如果您为第二个(时区)参数传递 0,则它不必执行任何操作timeclock_gettime两者都是正式的异步信号安全)也是如此,因此从 QoI 的角度来看,在这种情况下它应该是异步信号安全的。

\n\n

您的死锁位于内部 libc 函数内部__tz_convert

\n\n
#2  0x00007fc1b2d70d47 in __tz_convert () from /lib64/libc.so.6\n#3  0x00000000004708f7 in Logger::getCurrentTimestamp(char*) ()\n
Run Code Online (Sandbox Code Playgroud)\n\n

它似乎是直接从 调用的Logger::getCurrentTimestamp,但那是因为它是从记录的 API 函数“尾部调用”的。GNU libc 中只有四个函数可以调用__tz_convert(我 grep 了源代码):localtimelocaltime_rgmtimegmtime_r。因此,您的问题不是您正在调用gettimeofday,而是您正在调用这些函数之一。

\n\n

localtime并且gmtime显然不是异步信号安全的,因为它们写入全局变量。 localtime_r并且gmtime_r也不是异步信号安全的,因为它们必须查看时区信息的全局数据库(是的,即使gmtime_r是 \xe2\x80\x94 ,也可能将其更改为不需要这样做,但它仍然不是你可以依赖的跨平台的东西)。

\n\n

我认为没有好的解决方法。异步信号处理程序的格式化输出会遇到各种其他问题;我的建议是重构您的代码,以便您永远不需要从异步信号处理程序调用日志记录函数。

\n