首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Timer和syslog组合将导致我的代码挂起。

Timer和syslog组合将导致我的代码挂起。
EN

Stack Overflow用户
提问于 2015-01-21 05:44:49
回答 1查看 530关注 0票数 2

我试图调试问题,这是由于在我的程序中使用计时器和syslog函数。在这里,我附加了示例程序代码和终端和syslog的日志,以适当地调试它。

我不明白为什么这个程序会在一段时间后挂起。所以这里我有两个问题,1.当计时器到期时,睡眠会中断,因为它会产生SIGPROF 2。syslog在第一次试运行一段时间后就会被挂起。

代码:

代码语言:javascript
复制
#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;
}

模板日志:

代码语言:javascript
复制
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#

系统日志消息:

代码语言:javascript
复制
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机器上运行此测试应用程序:

代码语言:javascript
复制
# gcc -o hang_issue timer_hang.c -Wall -lrt
# ./hang_issue
... <You will get logs> ...

任何帮助都将不胜感激。

你好,拉维

EN

回答 1

Stack Overflow用户

发布于 2015-01-22 18:52:00

正如注释中提到的,您不能在信号处理程序中使用syslog()。它不是异步信号安全,而且作为一个复杂的函数(根据需要打开和关闭到syslog的连接),难怪使用它会导致程序以奇怪和不同的方式失败。

相反,使用write()和标准输出和标准错误流。例如,

代码语言:javascript
复制
#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())。(显然,读取和syslog数据的线程或子进程不需要使用异步信号安全函数--毕竟,它不是信号处理程序。)

让我们讨论一下超时的一般主题。

我建议不要在超时时使用正常信号。如果您需要信号,例如用于中断特定线程中的阻塞syscall,则使用实时信号(SIGRTMIN+0SIGRTMAX-0)。

实际上,使用单独的线程来处理一组超时效果要好得多。下面是一个示例,它使用少于400行代码,允许您使用任意数量的并发超时,并为每个超时提供一个易失性标志和一个信号量,以便于使用。它使用CLOCK_MONOTONIC时钟,它不会受到跳转(UTC秒、夏时制等)的影响,但它会尝试保持实时时钟(挂钟)的滴答率:

代码语言:javascript
复制
#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)行为,如果您有数百或数千个并发超时,则这是非常不理想的。将列表处理替换为二进制最小堆,以便使用大量并发超时以获得更好的性能。

此外,不需要总是向超时工作人员发出添加了新超时的信号。由于加法器持有互斥锁,因此仅当新的超时被添加为即将过去的下一个超时时,就足够了。

代码会编译,但我还没有彻底检查逻辑,因此可能会有一些bug潜伏在其中。如果你有发现,让我知道,我会设法修复它们。(不过,我很确定算法和方法本身是可行的。)

我不认为上述代码具有版权,因为它是如此直截了当,但如果有人这样做,我认为它是在公共领域,在那些没有法律概念的法域,在知识共享零许可许可。简而言之:做你想做的事,但没有保证,你不能因为任何破坏而责怪我。

有问题吗?评论?修复窃听器?

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28060399

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档