forked from ish-app/ish
-
Notifications
You must be signed in to change notification settings - Fork 11
/
timer.c
85 lines (80 loc) · 2.45 KB
/
timer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include "util/timer.h"
#include "misc.h"
struct timer *timer_new(clockid_t clockid, timer_callback_t callback, void *data) {
struct timer *timer = malloc(sizeof(struct timer));
timer->clockid = clockid;
timer->callback = callback;
timer->data = data;
timer->running = false;
timer->dead = false;
lock_init(&timer->lock);
return timer;
}
void timer_free(struct timer *timer) {
lock(&timer->lock);
if (timer->running) {
timer->running = false;
timer->dead = true;
pthread_kill(timer->thread, SIGUSR1);
unlock(&timer->lock);
} else {
unlock(&timer->lock);
if (!timer->dead)
free(timer);
}
}
static void *timer_thread(void *param) {
struct timer *timer = param;
lock(&timer->lock);
while (true) {
struct timespec remaining = timespec_subtract(timer->end, timespec_now(timer->clockid));
while (timer->running && timespec_positive(remaining)) {
unlock(&timer->lock);
nanosleep(&remaining, NULL);
lock(&timer->lock);
remaining = timespec_subtract(timer->end, timespec_now(timer->clockid));
}
if (timer->running)
timer->callback(timer->data);
if (timer->running && timespec_positive(timer->interval)) {
timer->start = timer->end;
timer->end = timespec_add(timer->start, timer->interval);
} else {
break;
}
}
timer->running = false;
if (timer->dead)
free(timer);
else
unlock(&timer->lock);
return NULL;
}
int timer_set(struct timer *timer, struct timer_spec spec, struct timer_spec *oldspec) {
lock(&timer->lock);
struct timespec now = timespec_now(timer->clockid);
if (oldspec != NULL) {
oldspec->value = timespec_subtract(timer->end, now);
oldspec->interval = timer->interval;
}
timer->start = now;
timer->end = timespec_add(timer->start, spec.value);
timer->interval = spec.interval;
if (!timespec_is_zero(spec.value)) {
if (!timer->running) {
timer->running = true;
pthread_create(&timer->thread, NULL, timer_thread, timer);
pthread_detach(timer->thread);
}
} else {
if (timer->running) {
timer->running = false;
pthread_kill(timer->thread, SIGUSR1);
}
}
unlock(&timer->lock);
return 0;
}