1 : /*
2 : * clock.c - Fast-lookup 1Hz clock
3 : * $Id: clock.c 1853 2006-12-15 17:49:53Z remi $
4 : */
5 :
6 : /***********************************************************************
7 : * Copyright © 2006 Rémi Denis-Courmont. *
8 : * This program is free software; you can redistribute and/or modify *
9 : * it under the terms of the GNU General Public License as published *
10 : * by the Free Software Foundation; version 2 of the license. *
11 : * *
12 : * This program is distributed in the hope that it will be useful, *
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
15 : * See the GNU General Public License for more details. *
16 : * *
17 : * You should have received a copy of the GNU General Public License *
18 : * along with this program; if not, you can get it from: *
19 : * http://www.gnu.org/copyleft/gpl.html *
20 : ***********************************************************************/
21 :
22 : #ifdef HAVE_CONFIG_H
23 : # include <config.h>
24 : #endif
25 :
26 : #include <time.h>
27 : #include <assert.h>
28 : #include <limits.h>
29 :
30 : #include <sys/types.h>
31 : #include <sys/time.h>
32 : #include <unistd.h> // _POSIX_*
33 : #include <pthread.h>
34 :
35 : #include "clock.h"
36 : #include "debug.h"
37 :
38 : typedef struct clock_data_t
39 : {
40 : unsigned long value;
41 : clockid_t id;
42 : pthread_rwlock_t lock;
43 : pthread_t thread;
44 : } clock_data_t;
45 :
46 :
47 : /**
48 : * Userland low-precision (1 Hz) clock
49 : *
50 : * This is way faster than calling time() for every packet transmitted or
51 : * received. The first implementation was using POSIX timers, but it might
52 : * be a bit overkill to spawn a thread every second to simply increment an
53 : * integer. Also, POSIX timers with thread event delivery has a terrible
54 : * portability at the time of writing (June 2006). Basically, recent
55 : * GNU/Linux have it, and that's about it... no uClibc support, only in
56 : * -current for FreeBSD...
57 : */
58 : static LIBTEREDO_NORETURN void *clock_thread (void *o)
59 4 : {
60 4 : clock_data_t *context = (clock_data_t *)o;
61 4 : clockid_t id = context->id;
62 :
63 : for (;;)
64 : {
65 : struct timespec ts;
66 4 : clock_gettime (id, &ts);
67 :
68 4 : pthread_rwlock_wrlock (&context->lock);
69 4 : context->value = ts.tv_sec;
70 4 : pthread_rwlock_unlock (&context->lock);
71 :
72 4 : ts.tv_sec++;
73 4 : ts.tv_nsec = 0;
74 :
75 4 : clock_nanosleep (id, TIMER_ABSTIME, &ts, NULL);
76 0 : }
77 : }
78 :
79 :
80 : static clock_data_t data;
81 :
82 :
83 : unsigned long teredo_clock (void)
84 0 : {
85 0 : clock_data_t *context = (clock_data_t *)&data;
86 : unsigned long value;
87 :
88 0 : pthread_rwlock_rdlock (&context->lock);
89 0 : value = context->value;
90 0 : pthread_rwlock_unlock (&context->lock);
91 0 : return value;
92 : }
93 :
94 :
95 : static unsigned users = 0;
96 : static pthread_mutex_t user_mutex = PTHREAD_MUTEX_INITIALIZER;
97 :
98 : /**
99 : * Starts the clock. Thread-safe.
100 : *
101 : * @return 0 in case of success, an errno in case of error.
102 : */
103 : int teredo_clock_create (void)
104 4 : {
105 4 : int val = -1;
106 :
107 4 : pthread_mutex_lock (&user_mutex);
108 :
109 4 : if (users == 0)
110 : {
111 4 : clock_data_t *ctx = (clock_data_t *)&data;
112 : struct timespec ts;
113 :
114 : #if (_POSIX_CLOCK_SELECTION - 0 >= 0) && (_POSIX_MONOTONIC_CLOCK - 0 >= 0)
115 : /* Run-time POSIX monotonic clock detection */
116 4 : ctx->id = CLOCK_MONOTONIC;
117 4 : if (clock_gettime (CLOCK_MONOTONIC, &ts))
118 : #endif
119 : {
120 0 : ctx->id = CLOCK_REALTIME;
121 0 : clock_gettime (CLOCK_REALTIME, &ts);
122 : }
123 :
124 4 : ctx->value = ts.tv_sec;
125 :
126 4 : val = pthread_rwlock_init (&ctx->lock, NULL);
127 4 : if (val == 0)
128 : {
129 4 : val = pthread_create (&ctx->thread, NULL, clock_thread, ctx);
130 4 : if (val == 0)
131 4 : users = 1;
132 : else
133 0 : pthread_rwlock_destroy (&ctx->lock);
134 : }
135 : }
136 : else
137 0 : if (users < UINT_MAX)
138 0 : users++;
139 :
140 4 : pthread_mutex_unlock (&user_mutex);
141 4 : return val;
142 : }
143 :
144 :
145 : /**
146 : * Stops the clock. Thread-safe.
147 : *
148 : * @return nothing (always succeeds when defined).
149 : */
150 : void teredo_clock_destroy (void)
151 4 : {
152 4 : pthread_mutex_lock (&user_mutex);
153 4 : assert (users > 0);
154 :
155 4 : if (--users == 0)
156 : {
157 4 : clock_data_t *ctx = (clock_data_t *)&data;
158 :
159 4 : pthread_cancel (ctx->thread);
160 4 : pthread_join (ctx->thread, NULL);
161 4 : pthread_rwlock_destroy (&ctx->lock);
162 : }
163 4 : pthread_mutex_unlock (&user_mutex);
164 4 : }
165 :
166 :
|