c - 计时器和系统日志组合将导致我的代码挂起

标签 c linux timer signals syslog

我尝试调试问题,这是由于在程序中使用了计时器和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+0SIGRTMAX-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/

相关文章:

c - 瓦尔格林德 C : How to input string from stdio

c - 如何从 C 程序为接口(interface)设置 ipv6 地址

c - 编写打印人类可读时间的内核系统调用

java - 在 RecyclerView Adapter 中实现计时器

javascript - 为什么 setTimeout 调用会产生不同的结果

java - 一段时间后重复调用webservice

c - 如何用特定值填充非紧凑结构的所有填充字节?

c - 如何在C中将字符串添加到二维数组

linux - 如何监听鼠标或键盘事件

linux - 无法在 Linux 上构建受限的 Zephyr 端口