我尝试调试问题,这是由于在程序中使用了计时器和syslog函数所致。在这里,我附上了示例程序代码以及终端和系统日志的日志,以对其进行适当的调试。
我不明白为什么该程序会在一段时间后挂起。
所以这里有2个问题,
1.当计时器到期时,睡眠会中断,因为它会生成SIGPROF
2.第一次尝试一段时间或一段时间后,系统日志将被挂起
代码:
#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#define get_curr_date_time(date_time) \
{ \
time_t t; \
time(&t); \
char *strtime = ctime(&t); \
strncpy(date_time, strtime, strlen(strtime) - 1); \
}
#define DEBUG_INFO(p,x,arg...) \
{\
printf("%s:%d,1\n", __func__, __LINE__);\
char current_time[32] = {0}; \
printf("%s:%d,2\n", __func__, __LINE__);\
get_curr_date_time(current_time); \
printf("%s:%d,3\n", __func__, __LINE__);\
syslog(LOG_INFO,"[%s] : " p " : "#x"\n", current_time, ##arg);\
printf("%s:%d,4\n", __func__, __LINE__);\
}
char exit_flag = 0;
typedef struct _test_ctx_
{
char timer_init;
timer_t timerid;
}test_ctx;
void Timer_Handler(int sig, siginfo_t *si, void *uc)
{
printf("Timer handler is start\n");
DEBUG_INFO("timer_hang", "Timer handler is running\n");
printf("Timer handler is stop\n");
}
int InitTimer(test_ctx *tst_ctx)
{
int status = 0;
struct sigaction sa;
struct sigevent sig;
memset(&sig, 0x00, sizeof(struct sigevent));
memset(&sa, 0x00, sizeof(struct sigaction));
do
{
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = Timer_Handler;
sigemptyset(&sa.sa_mask);
if (0 != (status = sigaction(SIGPROF, &sa, NULL)))
{
printf("Fail to register SIGPROF signal for timer, ret: %d\n", status);
break;
}
sig.sigev_notify = SIGEV_SIGNAL;
sig.sigev_signo = SIGPROF;
sig.sigev_value.sival_ptr = &tst_ctx->timerid;
if (0 != (status = timer_create(CLOCK_REALTIME, &sig, &(tst_ctx->timerid))))
{
printf("Failed to create timer, ret: %d\n", status);
break;
}
//Timer inited sucessfully
tst_ctx->timer_init = 1;
}while(0);
return status;
}
void DeInitTimer(test_ctx *tst_ctx)
{
int status = 0;
if (0 != tst_ctx->timer_init)
{
//Delete timer
if (0 != (status = timer_delete(tst_ctx->timerid)))
{
printf("Fail to delete timer, ret: %d\n", status);
}
tst_ctx->timer_init = 0;
}
}
int SetTimer(test_ctx *tst_ctx)
{
int status = 0;
struct itimerspec in;
memset(&in, 0x00, sizeof(struct itimerspec));
do
{
in.it_value.tv_sec = 0;
in.it_value.tv_nsec = 1;
in.it_interval.tv_sec = 0;
in.it_interval.tv_nsec = 0;
if (0 != (status = timer_settime(tst_ctx->timerid, 0, &in, NULL)))
{
printf("Fail to set timer, ret: %d\n", status);
break;
}
}while(0);
return status;
}
void terminate_app(int sig)
{
exit_flag = 1;
printf("signal %d received exiting application\n", sig);
DEBUG_INFO("timer_hang", "signal %d received exiting application\n", sig);
}
int main(int argc, char *argv[])
{
int status = 0;
test_ctx tst_ctx;
memset(&tst_ctx, 0x00, sizeof(tst_ctx));
do
{
//Register signal handler
signal(SIGTERM, terminate_app);
signal(SIGINT, terminate_app);
//Init timer
status = InitTimer(&tst_ctx);
if (0 != status)
{
break;
}
while(0 == exit_flag)
{
printf("Setting timer\n");
DEBUG_INFO("timer_hang", "Setting timer");
//Set Timer
SetTimer(&tst_ctx);
printf("Hello!!!\n");
DEBUG_INFO("timer_hang", "Hello!!!");
printf("Say!!!\n");
sleep(5);
}
}while(0);
//De init timer
DeInitTimer(&tst_ctx);
return status;
}
原始日志:
root@AHMCPU0085:/home/ravi/work/test_app/timer_hang# ./hang_issue
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,3
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
main:154,4
Say!!!
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
main:154,4
Say!!!
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
Timer_Handler:39,4
Timer handler is stop
Setting timer
main:148,1
main:148,2
main:148,3
main:148,4
Hello!!!
main:154,1
main:154,2
main:154,3
Timer handler is start
Timer_Handler:39,1
Timer_Handler:39,2
Timer_Handler:39,3
^Csignal 2 received exiting application
terminate_app:123,1
terminate_app:123,2
terminate_app:123,3
^Z
[1]+ Stopped ./hang_issue
root@AHMCPU0085:/home/ravi/work/test_app/timer_hang#
root@AHMCPU0085:/home/ravi/work/test_app/timer_hang# killall -s 9 hang_issue
[1]+ Killed ./hang_issue
root@AHMCPU0085:/home/ravi/work/test_app/timer_hang#
root@AHMCPU0085:/home/ravi/work/test_app/timer_hang#
系统日志消息:
root@AHMCPU0085:/home/ravi/work/test_app/timer_hang# tail -f /var/log/messages
...
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Setting timer"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Setting timer"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Setting timer"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Setting timer"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Setting timer"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Setting timer"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Setting timer"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Setting timer"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Setting timer"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Setting timer"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:15 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:15 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:20 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:20 2015] : timer_hang : "Setting timer"
Jan 21 10:52:20 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:20 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:20 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:20 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:20 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:20 2015] : timer_hang : "Setting timer"
Jan 21 10:52:20 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:20 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:20 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:20 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:20 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:20 2015] : timer_hang : "Setting timer"
Jan 21 10:52:20 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:20 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:20 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:20 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Setting timer"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Setting timer"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Setting timer"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Setting timer"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Setting timer"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Hello!!!"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Timer handler is running\n"
Jan 21 10:52:25 AHMCPU0085 hang_issue: [Wed Jan 21 10:52:25 2015] : timer_hang : "Setting timer"
^C
root@AHMCPU0085:/home/ravi/work/test_app/timer_hang#
您将通过以下步骤在Linux计算机上运行此测试应用程序:
# gcc -o hang_issue timer_hang.c -Wall -lrt
# ./hang_issue
... <You will get logs> ...
任何帮助将不胜感激。
问候,
拉维
最佳答案
如评论中所述,您不能在信号处理程序中使用syslog()
。它不是async-signal safe,并且是一个复杂的函数(根据需要打开和关闭与syslog的连接),因此,使用它是否会导致程序以奇怪的和多种方式的失败也就不足为奇了。
而是使用 write()
以及标准输出和标准错误流。例如,
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
static void wr(int fd, const void *const ptr, const size_t len)
{
const char *p = (const char *)ptr;
const char *const q = (const char *)ptr + len;
ssize_t n;
while (p < q) {
n = write(fd, p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1 || errno != EINTR)
return;
}
}
static void wrerr(const char *const string)
{
if (string != NULL)
wr(STDERR_FILENO, string, strlen(string));
}
static void wrout(const char *const string)
{
if (string != NULL)
wr(STDOUT_FILENO, string, strlen(string));
}
上面的
wrout()
和wrerr()
仅采用单个字符串(类似于puts()
,只是它们不会自动附加换行符),并且不如printf()
有用,但它们是异步信号安全的,并且可以从信号处理程序中安全使用。请记住,使用Bash时,可以使用
>out
将标准输出重定向到文件out
,并使用2>err
将标准错误重定向到文件err
。输出到终端的速度出奇地慢,因此在任何时间,都必须重定向到文件(或者更好的是,根本不输出额外的调试信息)。如果确实需要将信号处理程序的某些输出发送到syslog,则需要使用管道或套接字对,并从另一端读取线程或子进程,然后将数据发送到syslog。然后,您可以仅使用异步信号安全功能从信号处理程序写入管道或套接字对(使用
write()
)。 (显然,读取和系统记录数据的线程或子进程不需要使用异步信号安全功能-毕竟,它不是信号处理程序。)让我们通常讨论超时主题。
我建议不要将正常信号用于超时。如果您需要信号,例如要中断特定线程中的阻塞系统调用,请使用实时信号(
SIGRTMIN+0
至SIGRTMAX-0
)。实际上,使用单独的线程来处理一组超时效果更好。这是一个使用少于400行代码的示例,它使您可以使用任意数量的并发超时,并且每个超时都提供volatile标志和信号灯,以便于使用。它使用
CLOCK_MONOTONIC
时钟,该时钟不易发生跳跃(UTC秒,夏时制等),但是尝试保持实时时钟(壁钟)的滴答率:#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>
/* Number of timeouts to test. */
#ifndef TIMEOUT_TESTS
#define TIMEOUT_TESTS 100000
#endif
/* Seconds per timeout. */
#ifndef TIMEOUT_SECONDS
#define TIMEOUT_SECONDS 0.000000001
#endif
/* Timeout thread worker stack size; uses very few local variables. */
#define TIMEOUT_STACK_SIZE 65536
typedef struct timeout timeout;
struct timeout {
struct timeout *next;
struct timespec abstime; /* Using CLOCK_MONOTONIC clock */
sem_t elapsed; /* sem_post()ed when elapsed */
volatile int pending; /* Cleared to zero when expires */
};
static volatile int timeouts_error = -1;
static pthread_t timeouts_thread;
static pthread_mutex_t timeout_lock;
static pthread_cond_t timeout_cond; /* Uses CLOCK_MONOTONIC clock */
static timeout *volatile timeout_pending = NULL;
static void *timeouts_worker(void *unused __attribute__((unused)))
{
struct timespec now;
timeout *curr;
int err;
err = pthread_mutex_lock(&timeout_lock);
if (err) {
timeouts_error = err;
pthread_cond_signal(&timeout_cond);
return (void *)(long)err;
}
timeouts_error = 0;
pthread_cond_signal(&timeout_cond);
while (!timeouts_error) {
/* If there are no pending timeouts,
* all we need to do is wait for a condition. */
if (timeout_pending == NULL) {
pthread_cond_wait(&timeout_cond, &timeout_lock);
continue;
}
/* CLOCK_MONOTONIC is used for the timeouts. */
if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
timeouts_error = errno;
pthread_mutex_unlock(&timeout_lock);
return (void *)(long)timeouts_error;
}
/* Trigger and remove all timeouts that have elapsed thus far. */
curr = timeout_pending;
while (curr != NULL &&
(curr->abstime.tv_sec < now.tv_sec ||
(curr->abstime.tv_sec == now.tv_sec &&
curr->abstime.tv_nsec <= now.tv_nsec))) {
timeout *const prev = curr;
curr = prev->next;
prev->next = NULL;
/* Mark 'prev' timeout elapsed. */
prev->pending = 0;
sem_post(&(prev->elapsed));
}
/* No more timeouts? */
if (timeout_pending == NULL)
continue;
/* Wait for the next one to elapse.
* TODO: Adjust 'now' according to previous over/undershoots,
* Say, by one tenth of previous wakeup error
* (i.e. dynamically estimating the _timedwait latency).
* This would result in much more accurate timeouts.
*/
now = timeout_pending->abstime;
pthread_cond_timedwait(&timeout_cond, &timeout_lock, &now);
}
return (void *)0L;
}
static int timeout_free(timeout *const old_timeout)
{
int err;
if (old_timeout == NULL)
return 0;
err = pthread_mutex_lock(&timeout_lock);
if (err)
return errno = err; /* We also leak memory here. */
/* Remove from timeout_pending chain. */
if (timeout_pending == old_timeout)
timeout_pending = old_timeout->next;
else {
timeout *temp = timeout_pending;
if (temp != NULL)
while (temp->next != NULL)
if (temp->next == old_timeout) {
temp->next = old_timeout->next;
break;
} else
temp = temp->next;
}
/* Unlock mutex; we no longer need to access the chain. */
pthread_mutex_unlock(&timeout_lock);
/* Poison and free the timeout structure. */
sem_destroy(&(old_timeout->elapsed));
old_timeout->next = NULL;
old_timeout->abstime.tv_sec = 0;
old_timeout->abstime.tv_nsec = 0;
old_timeout->pending = 0;
free(old_timeout);
return 0;
}
static timeout *timeout_after(const double seconds)
{
const long sec = (long)seconds;
const long nsec = (long)(1000000000.0 * (seconds - (double)sec));
struct timespec now;
timeout *new_timeout;
int err;
/* Negative time is not allowed. */
if (seconds < 0.0) {
errno = EINVAL;
return NULL;
}
/* Get current monotonic time. */
if (clock_gettime(CLOCK_MONOTONIC, &now))
return NULL;
new_timeout = malloc(sizeof *new_timeout);
if (new_timeout == NULL) {
errno = ENOMEM;
return NULL;
}
if (sem_init(&(new_timeout->elapsed), 0, 0) == -1) {
err = errno;
free(new_timeout);
errno = err;
return NULL;
}
new_timeout->next = NULL;
new_timeout->abstime.tv_sec = now.tv_sec + sec + (now.tv_nsec + nsec) / 1000000000L;
new_timeout->abstime.tv_nsec = (now.tv_nsec + nsec) % 1000000000L;
new_timeout->pending = 1;
/* Already elapsed? */
if (new_timeout->abstime.tv_sec < now.tv_sec ||
(new_timeout->abstime.tv_sec == now.tv_sec &&
new_timeout->abstime.tv_nsec <= now.tv_nsec)) {
new_timeout->next = NULL;
new_timeout->pending = 0;
sem_post(&(new_timeout->elapsed));
return new_timeout;
}
/* Get timeout lock, to add to chain. */
err = pthread_mutex_lock(&timeout_lock);
if (err) {
sem_destroy(&(new_timeout->elapsed));
free(new_timeout);
errno = err;
return NULL;
}
if (timeout_pending == NULL)
timeout_pending = new_timeout;
else
if (timeout_pending->abstime.tv_sec > new_timeout->abstime.tv_sec ||
(timeout_pending->abstime.tv_sec == new_timeout->abstime.tv_sec &&
timeout_pending->abstime.tv_nsec >= new_timeout->abstime.tv_nsec)) {
new_timeout->next = timeout_pending;
timeout_pending = new_timeout;
} else {
timeout *temp = timeout_pending;
while (temp->next != NULL &&
(temp->next->abstime.tv_sec > new_timeout->abstime.tv_sec ||
(temp->next->abstime.tv_sec == new_timeout->abstime.tv_sec &&
temp->next->abstime.tv_nsec >= new_timeout->abstime.tv_nsec)))
temp = temp->next;
new_timeout->next = temp->next;
temp->next = new_timeout;
}
/* Timeout chain manipulated; notify and unlock. */
pthread_cond_signal(&timeout_cond);
pthread_mutex_unlock(&timeout_lock);
errno = 0;
return new_timeout;
}
static int timeouts_end(void)
{
if (timeouts_error == 0) {
int err;
void *errptr;
pthread_mutex_lock(&timeout_lock);
timeouts_error = -1;
pthread_cond_signal(&timeout_cond);
pthread_mutex_unlock(&timeout_lock);
err = pthread_join(timeouts_thread, &errptr);
if (err == 0)
err = (long)errptr;
return errno = err;
} else
if (timeouts_error != -1) {
int err;
void *errptr;
err = pthread_join(timeouts_thread, &errptr);
if (err == 0)
err = (long)errptr;
else
err = timeouts_error;
return errno = err;
} else
return errno = ENOENT;
}
static int timeouts_init(void)
{
pthread_mutexattr_t lock_attrs;
pthread_condattr_t cond_attrs;
pthread_attr_t attrs;
void *errptr;
int err;
if (timeouts_error != -1)
return errno = EEXIST;
/* Initialize timeout_lock as an adaptive mutex. */
err = pthread_mutexattr_init(&lock_attrs);
if (err)
return errno = err;
err = pthread_mutexattr_settype(&lock_attrs, PTHREAD_MUTEX_ADAPTIVE_NP);
if (err)
return errno = err;
err = pthread_mutex_init(&timeout_lock, &lock_attrs);
if (err)
return errno = err;
err = pthread_mutexattr_destroy(&lock_attrs);
if (err)
return errno = err;
/* Initialize timeout_cond as a process-private monotonic clock condition variable. */
err = pthread_condattr_init(&cond_attrs);
if (err)
return errno = err;
err = pthread_condattr_setpshared(&cond_attrs, PTHREAD_PROCESS_PRIVATE);
if (err)
return errno = err;
err = pthread_condattr_setclock(&cond_attrs, CLOCK_MONOTONIC);
if (err)
return errno = err;
err = pthread_cond_init(&timeout_cond, &cond_attrs);
if (err)
return errno = err;
err = pthread_condattr_destroy(&cond_attrs);
if (err)
return errno = err;
/* Initialize the thread attributes to a 64k stack. */
err = pthread_attr_init(&attrs);
if (err)
return errno = err;
err = pthread_attr_setstacksize(&attrs, TIMEOUT_STACK_SIZE);
if (err)
return errno = err;
/* Grab the timeout lock; we'll wait on the cond later. */
err = pthread_mutex_lock(&timeout_lock);
if (err)
return errno = err;
/* Start the timeout worker thread. */
err = pthread_create(&timeouts_thread, &attrs, timeouts_worker, NULL);
if (err)
return errno = err;
pthread_attr_destroy(&attrs);
/* Wait for the worker to be ready. */
pthread_cond_wait(&timeout_cond, &timeout_lock);
/* Failed? */
if (timeouts_error != 0) {
timeouts_error = -1;
pthread_mutex_unlock(&timeout_lock);
err = pthread_join(timeouts_thread, &errptr);
if (err == 0)
err = (long)errptr;
return errno = err;
}
/* Unlock. */
pthread_mutex_unlock(&timeout_lock);
/* Success. */
return 0;
}
int main(void)
{
long i;
timeout *t;
if (timeouts_init()) {
fprintf(stderr, "Cannot initialize timeouts: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
fprintf(stderr, "Testing:\n");
fflush(stderr);
for (i = 1L; i <= TIMEOUT_TESTS; i++) {
t = timeout_after(0.000000001);
if (t == NULL) {
fprintf(stderr, "Test %ld of %ld failed: Cannot obtain a timeout: %s.\n", i, (long)TIMEOUT_TESTS, strerror(errno));
timeouts_end();
return EXIT_FAILURE;
}
printf("Timeout %ld of %ld: ", i, (long)TIMEOUT_TESTS);
fflush(stdout);
/* Wait for timeout to elapse. */
sem_wait(&(t->elapsed));
printf("Elapsed.\n");
fflush(stdout);
timeout_free(t);
}
if (timeouts_end()) {
fprintf(stderr, "Error in disarming timeouts: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
fprintf(stderr, "No errors.\n");
return EXIT_SUCCESS;
}
由于我们有一个专用的超时工作线程,因此上面的代码使用
pthread_cond_timedwait()
等待下一次超时(条件变量设置为使用CLOCK_MONOTONIC
时钟源)或一个信号(另一个线程插入了新的超时)。要中断阻塞的系统调用,请在
pthread_t thread
结构中添加timeout
,然后将timeout_after()
设置为pthread_self()
。安装一个实时信号(例如SIGRTMIN+0
)处理程序(主体为空-计数的是传递,而不是处理程序执行的操作)。最后,在pthread_kill(curr->thread, SIGRTMIN+0)
中添加timeouts_worker()
以在目标线程中发出信号。上面的实现使用了一个简单的排序链表
timeout_pending
,以保留当前未决的超时。这会在添加,删除和触发超时时产生O(N)行为,如果您有成百上千的并发超时,则这不是最佳选择。用二进制min-heap替换列表处理,以提高并发超时的性能。同样,也不必始终向超时工作人员发出信号,通知其已添加新超时。由于加法器拥有互斥量,仅在新超时被添加时发出信号就足够了。
代码可以编译,但是我没有彻底检查逻辑,因此可能潜伏着一些错误。如果您发现任何问题,请告诉我,我将尝试修复它们。 (不过,我可以肯定算法和方法本身是可行的。)
我不认为上述代码具有版权,因为它太简单了,但是如果有人这样做,我认为它属于公共(public) Realm ,并且在那些没有法律概念的辖区中,均受知识共享零许可证授权。简而言之:随心所欲地做,但不能保证,任何破损都不能怪我。
有什么问题吗评论? Bug修复?
关于c - 计时器和系统日志组合将导致我的代码挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28060399/