Are there any well-behaved POSIX interval timers?

前端 未结 4 731
别跟我提以往
别跟我提以往 2020-12-14 08:58

Inspired by the last leap second, I\'ve been exploring timing (specifically, interval timers) using POSIX calls.

POSIX provides several ways to set up timers, but th

4条回答
  •  有刺的猬
    2020-12-14 09:39

    kqueue and kevent can be utilized for this purpose. OSX 10.6 and FreeBSD 8.1 add support for EVFILT_USER, which we can use to wake up the event loop from another thread.

    Note that if you use this to implement your own condition and timedwait, you do not need locks in order to avoid race conditions, contrary to this excellent answer, because you cannot "miss" an event on the queue.

    Sources:

    • FreeBSD man page
    • OS X man page
    • kqueue tutorial
    • libevent source code

    Example Code

    Compile with clang -o test -std=c99 test.c

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    // arbitrary number used for the identifier property
    const int NOTIFY_IDENT = 1337;
    
    static int kq;
    
    static void diep(const char *s) {
       perror(s);
       exit(EXIT_FAILURE);
    }
    
    static void *run_thread(void *arg) {
        struct kevent kev;
        struct kevent out_kev;
        memset(&kev, 0, sizeof(kev));
        kev.ident = NOTIFY_IDENT;
        kev.filter = EVFILT_USER;
        kev.flags = EV_ADD | EV_CLEAR;
    
        struct timespec timeout;
        timeout.tv_sec = 3;
        timeout.tv_nsec = 0;
    
        fprintf(stderr, "thread sleep\n");
    
        if (kevent(kq, &kev, 1, &out_kev, 1, &timeout) == -1)
            diep("kevent: waiting");
    
        fprintf(stderr, "thread wakeup\n");
    
        return NULL;
    }
    
    int main(int argc, char **argv) {
        // create a new kernel event queue
        kq = kqueue();
        if (kq == -1)
            diep("kqueue()");
    
    
        fprintf(stderr, "spawn thread\n");
        pthread_t thread;
        if (pthread_create(&thread, NULL, run_thread, NULL))
            diep("pthread_create");
    
        if (argc > 1) {
            fprintf(stderr, "sleep for 1 second\n");
            sleep(1);
            fprintf(stderr, "wake up thread\n");
    
            struct kevent kev;
            struct timespec timeout = { 0, 0 };
    
            memset(&kev, 0, sizeof(kev));
            kev.ident = NOTIFY_IDENT;
            kev.filter = EVFILT_USER;
            kev.fflags = NOTE_TRIGGER;
    
            if (kevent(kq, &kev, 1, NULL, 0, &timeout) == -1)
                diep("kevent: triggering");
        } else {
            fprintf(stderr, "not waking up thread, pass --wakeup to wake up thread\n");
        }
    
        pthread_join(thread, NULL);
        close(kq);
        return EXIT_SUCCESS;
    }
    

    Output

    $ time ./test
    spawn thread
    not waking up thread, pass --wakeup to wake up thread
    thread sleep
    thread wakeup
    
    real    0m3.010s
    user    0m0.001s
    sys 0m0.002s
    
    $ time ./test --wakeup
    spawn thread
    sleep for 1 second
    thread sleep
    wake up thread
    thread wakeup
    
    real    0m1.010s
    user    0m0.002s
    sys 0m0.002s
    

提交回复
热议问题