Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

timers in linux in c [duplicate]

Tags:

c

linux

timer

Possible Duplicate:
Loops/timers in C

I've been reading about timers for the last 3 days and I'm unable to find anything useful, I'm trying to understand it in real example, can somebody help me figure out how to setup an alarm for the below program.

How can I set a a timer so that it will send 2 args, one is the array name, and the second one is the number to be deleted, I know the below is not safe in anyway, I'm just trying to understand how use alarms with args to call a function.

please note that the environment is Linux, and also I appreciate any link with a working C example.

#include<stdio.h>
int delete_from_array(int arg) ;


    int main()
    {

    int a[10000], i, y ;
    //how to set timer here for to delete any number in array after half a second
    for (y=0; y < 100; y++) {


        for (i=0; i<sizeof(a) / sizeof(int); i++)
            a[i] = i;
    sleep(1);
    printf("wake\n");
    }

    }

    int delete_from_array(int arg) 
    {
    int i, a[1000], number_to_delete=0;

    //number_to_delete = arg->number;

    for (i=0; i<sizeof(a); i++)
        if (a[i] == number_to_delete)
            a[i] = 0;
    printf("deleted\n");

    }

What I'm trying to do is that I have a hash which has has values to be expired after 1 seconds, so after I insert the value into the hash, I need to create a timer so that it will delete that value after let's say 1 second, and IF I got a response from the server before the that interval (1 second) then I delete the value from the hash and delete the timer, almost like retransmission in tcp

like image 589
Bag Dev Avatar asked Oct 06 '12 23:10

Bag Dev


2 Answers

Do you want to use signals or threads?

First, set up the signal handler or prepare a suitable thread function; see man 7 sigevent for details.

Next, create a suitable timer, using timer_create(). See man 2 timer_create for details.

Depending on what you do when the timer fires, you may wish to set the timer to either one-shot, or to repeat at a short interval afterwards. You use timer_settime() to both arm, and to disarm, the timer; see man 2 timer_settime for details.

In practical applications you usually need to multiplex the timer. Even though a process can create multiple timers, they are a limited resource. Especially timeout timers -- which are trivial, either setting a flag and/or sending a signal to a specific thread -- should use a single timer, which fires at the next timeout, sets the related timeout flag, and optionally send a signal (with an empty-body handler) to the desired thread to make sure it is interrupted. (For a single-thread process, the original signal delivery will interrupt blocking I/O calls.) Consider a server, responding to some request: the request itself might have a timeout on the order of a minute or so, while processing the request might need connection timeouts, I/O timeouts, and so on.

Now, the original question is interesting, because timers are powerful when used effectively. However, the example program is basically nonsense. Why don't you create say a program that sets one or more timers, each for example outputting something to standard output? Remember to use write() et al from unistd.h as they are async-signal safe, whereas printf() et cetera from stdio.h are not. (If your signal handlers use non-async-signal safe functions, the results are undefined. It usually works, but it's not guaranteed at all; it may just as well crash as work. Testing will not tell, as it is undefined.)


Edited to add: Here is a bare-bones example of multiplexed timeouts.

(To the extent possible under law, I dedicate all copyright and related and neighboring rights to the code snippets shown below to the public domain worldwide; see CC0 Public Domain Dedication. In other words, feel free to use the code below in any way you wish, just don't blame me for any problems with it.)

I used old-style GCC atomic built-ins, so it should be thread-safe. With a few additions, it should work for multithreaded code too. (You cannot use for example mutexes, because pthread_mutex_lock() is not async-signal safe. Atomically manipulating the timeout states should work, although there might be some races left if you disable a timeout just when it fires.)

#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <errno.h>

#define   TIMEOUTS       16
#define   TIMEOUT_SIGNAL (SIGRTMIN+0)

#define   TIMEOUT_USED   1
#define   TIMEOUT_ARMED  2
#define   TIMEOUT_PASSED 4

static timer_t               timeout_timer;
static volatile sig_atomic_t timeout_state[TIMEOUTS] = { 0 };
static struct timespec       timeout_time[TIMEOUTS];


/* Return the number of seconds between before and after, (after - before).
 * This must be async-signal safe, so it cannot use difftime().
*/
static inline double timespec_diff(const struct timespec after, const struct timespec before)
{
    return (double)(after.tv_sec - before.tv_sec)
         + (double)(after.tv_nsec - before.tv_nsec) / 1000000000.0;
}

/* Add positive seconds to a timespec, nothing if seconds is negative.
 * This must be async-signal safe.
*/
static inline void timespec_add(struct timespec *const to, const double seconds)
{
    if (to && seconds > 0.0) {
        long  s = (long)seconds;
        long  ns = (long)(0.5 + 1000000000.0 * (seconds - (double)s));

        /* Adjust for rounding errors. */
        if (ns < 0L)
            ns = 0L;
        else
        if (ns > 999999999L)
            ns = 999999999L;

        to->tv_sec += (time_t)s;
        to->tv_nsec += ns;

        if (to->tv_nsec >= 1000000000L) {
            to->tv_nsec -= 1000000000L;
            to->tv_sec++;
        }
    }
}

/* Set the timespec to the specified number of seconds, or zero if negative seconds.
*/
static inline void timespec_set(struct timespec *const to, const double seconds)
{
    if (to) {
        if (seconds > 0.0) {
            const long  s = (long)seconds;
            long       ns = (long)(0.5 + 1000000000.0 * (seconds - (double)s));

            if (ns < 0L)
                ns = 0L;
            else
            if (ns > 999999999L)
                ns = 999999999L;

            to->tv_sec = (time_t)s;
            to->tv_nsec = ns;

        } else {
            to->tv_sec = (time_t)0;
            to->tv_nsec = 0L;
        }
    }
}


/* Return nonzero if the timeout has occurred.
*/
static inline int timeout_passed(const int timeout)
{
    if (timeout >= 0 && timeout < TIMEOUTS) {
        const int  state = __sync_or_and_fetch(&timeout_state[timeout], 0);

        /* Refers to an unused timeout? */
        if (!(state & TIMEOUT_USED))
            return -1;

        /* Not armed? */
        if (!(state & TIMEOUT_ARMED))
            return -1;

        /* Return 1 if timeout passed, 0 otherwise. */
        return (state & TIMEOUT_PASSED) ? 1 : 0;

    } else {
        /* Invalid timeout number. */
        return -1;
    }
}

/* Release the timeout.
 * Returns 0 if the timeout had not fired yet, 1 if it had.
*/
static inline int timeout_unset(const int timeout)
{
    if (timeout >= 0 && timeout < TIMEOUTS) {
        /* Obtain the current timeout state to 'state',
         * then clear all but the TIMEOUT_PASSED flag
         * for the specified timeout.
         * Thanks to Bylos for catching this bug. */
        const int  state = __sync_fetch_and_and(&timeout_state[timeout], TIMEOUT_PASSED);

        /* Invalid timeout? */
        if (!(state & TIMEOUT_USED))
            return -1;

        /* Not armed? */
        if (!(state & TIMEOUT_ARMED))
            return -1;

        /* Return 1 if passed, 0 otherwise. */
        return (state & TIMEOUT_PASSED) ? 1 : 0;

    } else {
        /* Invalid timeout number. */
        return -1;
    }
}


int timeout_set(const double seconds)
{
    struct timespec   now, then;
    struct itimerspec when;
    double            next;
    int               timeout, i;

    /* Timeout must be in the future. */
    if (seconds <= 0.0)
        return -1;

    /* Get current time, */
    if (clock_gettime(CLOCK_REALTIME, &now))
        return -1;

    /* and calculate when the timeout should fire. */
    then = now;
    timespec_add(&then, seconds);

    /* Find an unused timeout. */
    for (timeout = 0; timeout < TIMEOUTS; timeout++)
        if (!(__sync_fetch_and_or(&timeout_state[timeout], TIMEOUT_USED) & TIMEOUT_USED))
            break;

    /* No unused timeouts? */
    if (timeout >= TIMEOUTS)
        return -1;

    /* Clear all but TIMEOUT_USED from the state, */
    __sync_and_and_fetch(&timeout_state[timeout], TIMEOUT_USED);

    /* update the timeout details, */
    timeout_time[timeout] = then;

    /* and mark the timeout armable. */
    __sync_or_and_fetch(&timeout_state[timeout], TIMEOUT_ARMED);

    /* How long till the next timeout? */
    next = seconds;
    for (i = 0; i < TIMEOUTS; i++)
        if ((__sync_fetch_and_or(&timeout_state[i], 0) & (TIMEOUT_USED | TIMEOUT_ARMED | TIMEOUT_PASSED)) == (TIMEOUT_USED | TIMEOUT_ARMED)) {
            const double secs = timespec_diff(timeout_time[i], now);
            if (secs >= 0.0 && secs < next)
                next = secs;
        }

    /* Calculate duration when to fire the timeout next, */
    timespec_set(&when.it_value, next);
    when.it_interval.tv_sec = 0;
    when.it_interval.tv_nsec = 0L;

    /* and arm the timer. */
    if (timer_settime(timeout_timer, 0, &when, NULL)) {
        /* Failed. */
        __sync_and_and_fetch(&timeout_state[timeout], 0);
        return -1;
    }

    /* Return the timeout number. */
    return timeout;
}


static void timeout_signal_handler(int signum __attribute__((unused)), siginfo_t *info, void *context __attribute__((unused)))
{
    struct timespec   now;
    struct itimerspec when;
    int               saved_errno, i;
    double            next;

    /* Not a timer signal? */
    if (!info || info->si_code != SI_TIMER)
        return;

    /* Save errno; some of the functions used may modify errno. */
    saved_errno = errno;

    if (clock_gettime(CLOCK_REALTIME, &now)) {
        errno = saved_errno;
        return;
    }

    /* Assume no next timeout. */
    next = -1.0;

    /* Check all timeouts that are used and armed, but not passed yet. */
    for (i = 0; i < TIMEOUTS; i++)
        if ((__sync_or_and_fetch(&timeout_state[i], 0) & (TIMEOUT_USED | TIMEOUT_ARMED | TIMEOUT_PASSED)) == (TIMEOUT_USED | TIMEOUT_ARMED)) {
            const double  seconds = timespec_diff(timeout_time[i], now);
            if (seconds <= 0.0) {
                /* timeout [i] fires! */
                __sync_or_and_fetch(&timeout_state[i], TIMEOUT_PASSED);

            } else
            if (next <= 0.0 || seconds < next) {
                /* This is the soonest timeout in the future. */
                next = seconds;
            }
        }

    /* Note: timespec_set() will set the time to zero if next <= 0.0,
     *       which in turn will disarm the timer.
     * The timer is one-shot; it_interval == 0.
    */
    timespec_set(&when.it_value, next);
    when.it_interval.tv_sec = 0;
    when.it_interval.tv_nsec = 0L;
    timer_settime(timeout_timer, 0, &when, NULL);

    /* Restore errno. */
    errno = saved_errno;
}


int timeout_init(void)
{
    struct sigaction  act;
    struct sigevent   evt;
    struct itimerspec arm;

    /* Install timeout_signal_handler. */
    sigemptyset(&act.sa_mask);
    act.sa_sigaction = timeout_signal_handler;
    act.sa_flags = SA_SIGINFO;
    if (sigaction(TIMEOUT_SIGNAL, &act, NULL))
        return errno;

    /* Create a timer that will signal to timeout_signal_handler. */
    evt.sigev_notify = SIGEV_SIGNAL;
    evt.sigev_signo = TIMEOUT_SIGNAL;
    evt.sigev_value.sival_ptr = NULL;
    if (timer_create(CLOCK_REALTIME, &evt, &timeout_timer))
        return errno;

    /* Disarm the timeout timer (for now). */
    arm.it_value.tv_sec = 0;
    arm.it_value.tv_nsec = 0L;
    arm.it_interval.tv_sec = 0;
    arm.it_interval.tv_nsec = 0L;
    if (timer_settime(timeout_timer, 0, &arm, NULL))
        return errno;

    return 0;
}

int timeout_done(void)
{
    struct sigaction  act;
    struct itimerspec arm;
    int               errors = 0;

    /* Ignore the timeout signals. */
    sigemptyset(&act.sa_mask);
    act.sa_handler = SIG_IGN;
    if (sigaction(TIMEOUT_SIGNAL, &act, NULL))
        if (!errors) errors = errno;

    /* Disarm any current timeouts. */
    arm.it_value.tv_sec = 0;
    arm.it_value.tv_nsec = 0L;
    arm.it_interval.tv_sec = 0;
    arm.it_interval.tv_nsec = 0;
    if (timer_settime(timeout_timer, 0, &arm, NULL))
        if (!errors) errors = errno;

    /* Destroy the timer itself. */
    if (timer_delete(timeout_timer))
        if (!errors) errors = errno;

    /* If any errors occurred, set errno. */
    if (errors)
        errno = errors;

    /* Return 0 if success, errno otherwise. */
    return errors;
}

Remember to include the rt library when compiling, i.e. use gcc -W -Wall *source*.c -lrt -o *binary* to compile.

The idea is that the main program first calls timeout_init() to install all the necessary handlers et cetera, and may call timeout_done() to deistall it before exiting (or in a child process after fork()ing).

To set a timeout, you call timeout_set(seconds). The return value is a timeout descriptor. Currently there is just a flag you can check using timeout_passed(), but the delivery of the timeout signal also interrupts any blocking I/O calls. Thus, you can expect the timeout to interrupt any blocking I/O call.

If you want to do anything more than set a flag at timeout, you cannot do it in the signal handler; remember, in a signal handler, you're limited to async-signal safe functions. The easiest way around that is to use a separate thread with an endless loop over sigwaitinfo(), with the TIMEOUT_SIGNAL signal blocked in all other threads. That way the dedicated thread is guaranteed to catch the signal, but at the same time, is not limited to async-signal safe functions. It can, for example, do much more work, or even send a signal to a specific thread using pthread_kill(). (As long as that signal has a handler, even one with an empty body, its delivery will interrupt any blocking I/O call in that thread.)

Here is a simple example main() for using the timeouts. It is silly, and relies on fgets() not retrying (when interrupted by a signal), but it seems to work.

#include <string.h>
#include <stdio.h>

int main(void)
{
    char    buffer[1024], *line;
    int t1, t2, warned1;

    if (timeout_init()) {
        fprintf(stderr, "timeout_init(): %s.\n", strerror(errno));
        return 1;
    }

    printf("You have five seconds to type something.\n");
    t1 = timeout_set(2.5); warned1 = 0;
    t2 = timeout_set(5.0);
    line = NULL;

    while (1) {

        if (timeout_passed(t1)) {
            /* Print only the first time we notice. */
            if (!warned1++)
                printf("\nTwo and a half seconds left, buddy.\n");
        }

        if (timeout_passed(t2)) {
            printf("\nAw, just forget it, then.\n");
            break;
        }

        line = fgets(buffer, sizeof buffer, stdin);
        if (line) {
            printf("\nOk, you typed: %s\n", line);
            break;
        }
    }

    /* The two timeouts are no longer needed. */
    timeout_unset(t1);
    timeout_unset(t2);

    /* Note: 'line' is non-NULL if the user did type a line. */

    if (timeout_done()) {
        fprintf(stderr, "timeout_done(): %s.\n", strerror(errno));
        return 1;
    }

    return 0;
}
like image 131
Nominal Animal Avatar answered Nov 09 '22 22:11

Nominal Animal


A useful read is the time(7) man page. Notice that Linux also provides the timerfd_create(2) Linux specific syscall, often used with a multiplexing syscall like poll(2) (or ppoll(2) or the older select(2) syscall).

If you want to use signals don't forget to read carefully signal(7) man page (there are restrictions about coding signal handlers; you might want to set a volatile sigatomic_t variable in your signal handlers; you should not do any new or delete -or malloc & free- memory menagenment operations inside a signal handler, where only async-safe function calls are permitted.).

Notice also that event-oriented programming, such as GUI applications, often provide ways (in Gtk, in Qt, with libevent, ....) to manage timers in their event loop.

like image 29
Basile Starynkevitch Avatar answered Nov 10 '22 00:11

Basile Starynkevitch