等待所有线程计时器回调完成的安全方法

时间:2014-11-11 10:55:05

标签: c linux multithreading timer pthreads

在单次定时器的情况下,我可以使用信号量来等待定时器回调完成。 但是如果计时器被多次触发它并没有帮助。请考虑以下代码:

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>

#define N 10

void timer_threaded_function(sigval_t si)
{
    uint8_t *shared_resource = si.sival_ptr;

    sleep(rand() % 7);

    /* ... manipulate with shared_resource */

    return;
}

int main()
{
    struct sigevent sig_ev = {0};
    uint8_t *shared_resource = malloc(123);
    timer_t timer_id;
    int i;

    sig_ev.sigev_notify = SIGEV_THREAD;
    sig_ev.sigev_value.sival_ptr = shared_resource;
    sig_ev.sigev_notify_function = timer_threaded_function;
    sig_ev.sigev_notify_attributes = NULL;

    timer_create(CLOCK_REALTIME, &sig_ev, &timer_id);

    for (i = 0; i < N; i++) {
        /* arm timer for 1 nanosecond */
        timer_settime(timer_id, 0,
                      &(struct itimerspec){{0,0},{0,1}}, NULL);

        /* sleep a little bit, so timer will be fired */
        usleep(1);
    }

    /* only disarms timer, but timer callbacks still can be running */
    timer_delete(timer_id);

    /* 
     * TODO: safe wait for all callbacks to end, so shared resource
     * can be freed without races.
     */
    ...

    free(shared_resource);

    return 0;
}

timer_delete()仅解除计时器(如果已设防)并释放与计时器资源相关联的计时器。但是计时器回调仍然可以运行。所以我们不能释放shared_resource,否则可能会出现竞争条件。有没有办法来应对这种情况?

我对引用计数感兴趣,但它没有帮助,因为我们不知道实际上有多少线程会尝试访问共享资源(导致计时器溢出)。

1 个答案:

答案 0 :(得分:1)

完全不能令人满意:-(。我看了,似乎没有任何方法可以发现sigevent(a)是否未被解雇,或者(b)是否正在等待,或者(c)是否正在运行,或(d)已完成。

我能建议的最好是间接的额外级别,以及共享资源的静态指向。所以:

  static foo_t* p_shared ;
   ....
  p_shared = shared_resourse ;
   .....
  sig_ev.sigev_value.sival_ptr = &p_shared ;

其中foo_t是共享资源的类型。

现在我们可以在timer_threaded_function()中使用一些原子......

  foo_t** pp_shared ;
  foo_t*  p_locked ;
  foo_t*  p_shared ;

  pp_shared = so.sival_ptr ;
  p_locked  = (void*)UINPTR_MAX ;
  p_shared  = atomic_swap(pp_shared, p_locked) ;

  if (p_shared == p_locked)
    return ;                    // locked already.

  if (p_shared == NULL)
    return ;                    // destroyed already.

  .... proceed to do the usual work ...

  if (atomic_cmp_swap(pp_shared, &p_locked, p_shared))
    return ;                    // was locked and is now restored

  assert(p_locked == NULL) ;

  ... the shared resource needs to be freed ...

在控制线程中:

  timer_delete(timer_id) ;              // no more events, thank you

  p_s = atomic_swap(&p_shared, NULL) ;  // stop processing events

  if (p_s == (void*)UINTPTR_MAX)
    // an event is being processed.

  if (p_s != NULL)
    ... the shared resource needs to be freed ...

当事件线程发现需要释放共享资源时,它可以自己执行,或者向控制线程发信号通知事件已被处理,以便控制线程可以继续执行并自由执行。这主要是品味问题。

基本上,这是使用atomics来提供一种锁,其值为三态:NULL&lt; =&gt;被毁UINTPTR_MAX&lt; =&gt;锁定其他任何&lt; =&gt;解锁。

向下是static p_shared,它必须保持存在直到timer_threaded_function()完成并且永远不会被再次调用......并且因为那些正是不可知的事物, static p_shared实际上是一个固定装置: - (。