以前,我曾问过question关于如何终止为I / O阻塞的线程。考虑到很少的优点,我使用pthread_kill()
代替了pthread_cancel()
或写了管道。在使用pthread_kill()
实现了将信号(SIGUSR2)发送到目标线程的代码后,最初它运行良好,并且没有遇到任何问题。
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;
}
}
最近,我注意到一个死锁问题,其中很少有线程(试图记录语句)被低于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*) ()
当调用getCurrentTimestamp(char*)
或LogInfo()
函数时,从记录器模块中调用 LogError()
函数。此函数在内部调用gettimeofday()以在日志中打印当前时间。因此,我怀疑在signalHandler()内部调用的LogInfo()
函数(调用gettimeofday()
)可能导致死锁问题。但是我对此不太确定,因为这个问题不会经常被重现,并且我也没有得到任何信息表明gettimeofday()
不是异步信号安全的。
gettimeofday()为thread safe,并且我认为它不是异步信号安全的,因为它未在async signal safe functions下列出。
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( ¤t ) );
gettimeofday( &detail_time, NULL );
sprintf( tm_buff, "%s:%03d", curr_time, (int) (detail_time.tv_usec / 1000) );
return (tm_buff);
}
return (NULL);
}
被信号中断的线程的堆栈跟踪。
#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*, ...) ()
答案 0 :(得分:5)
gettimeofday
本身并不安全。
但是,“ POSIX.1-2008将gettimeofday()标记为过时,建议改用clock_gettime(2)。” (source)和clock_gettime
是signal-safe(也是线程安全的),因此这可能是您的替代选择。
答案 1 :(得分:2)
gettimeofday
未被定义为异步信号安全的,但是如果您为第二个(时区)参数传递0,则它不必执行time
和{{1} }(这两个都是正式是异步信号安全的)也没有这样做,因此从QoI的角度来看,在这种情况下,它应该是异步信号安全的。
您的死锁位于内部libc函数clock_gettime
中。
__tz_convert
它似乎是直接从#2 0x00007fc1b2d70d47 in __tz_convert () from /lib64/libc.so.6
#3 0x00000000004708f7 in Logger::getCurrentTimestamp(char*) ()
调用的,但这是因为它是从已记录的API函数“尾部调用”的。 GNU libc中只有四个函数调用Logger::getCurrentTimestamp
(我略过源代码):__tz_convert
,localtime
,localtime_r
和gmtime
。因此,您的问题不是您正在调用gmtime_r
,而是您正在调用这些函数之一。
gettimeofday
和localtime
显然不是异步信号安全的,因为它们写入全局变量。 gmtime
和localtime_r
也不都是异步信号安全的,因为它们必须查看时区信息的全局数据库(是的,即使gmtime_r
也会这样做-可能有可能进行更改不需要这样做,但是您仍然可以依靠跨平台来解决问题。
我认为没有好的解决方法。异步信号处理程序的格式化输出将解决所有其他问题。我的建议是重新组织代码,以使您永远不需要从异步信号处理程序调用日志记录功能。