File Coverage

ulib/clock.c
Criterion Covered Total %
statement 88 106 83.0
branch 47 70 67.1
condition n/a
subroutine n/a
pod n/a
total 135 176 76.7


line stmt bran cond sub pod time code
1             #ifdef __cplusplus
2             extern "C" {
3             #endif
4              
5             #include "ulib/clock.h"
6             #include "ulib/chacha.h"
7             #include "ulib/gettime.h"
8              
9             #ifdef __cplusplus
10             }
11             #endif
12              
13             #ifdef USE_WIN32_NATIVE
14             #define ftruncate(a,b) _chsize(a,b)
15             #endif
16             #undef open
17              
18             #define state_fd UCXT.clock_state_fd
19             #define state_f UCXT.clock_state_f
20             #define thread_persist UCXT.clock_persist
21             #define adjustment SMEM->clock_adj
22             #define last SMEM->clock_last
23             #define clock_seq SMEM->clock_seq
24             #define prev_reg SMEM->clock_prev_reg
25             #define defer_100ns SMEM->clock_defer_100ns
26             #define global_persist SMEM->clock_persist
27              
28             /* state_fd:
29             * -4 cannot create
30             * -3 untried
31             * -2 symlink
32             * -1 can create
33             * >=0 open fd
34             */
35             #define STATEFD_NOCREATE -4
36             #define STATEFD_UNTRIED -3
37             #define STATEFD_SYMLINK -2
38             #define STATEFD_CREATEOK -1
39              
40              
41             /* compare paths, return 0 if same or longer length if not */
42 42471           static IV persistNE(persist_t *a, persist_t *b) {
43 42471           STRLEN longer = a->len >= b->len ? a->len : b->len;
44             IV i;
45 42471 50         if (a->len != b->len)
46 0           return longer;
47 42837 100         for (i=0 ; i
48 366 50         if(a->path[i] != b->path[i])
49 0           return longer;
50 42471           return 0;
51             }
52              
53             /* called at boot */
54 215           void uu_clock_init(pUCXT) {
55             UV ptod[2];
56              
57             /* gettimeofday(&tv, 0); */
58 215           (*uu_gettime_U2time)(aTHX_ ptod);
59 215           last.tv_sec = (long)ptod[0];
60 215           last.tv_usec = (long)ptod[1];
61              
62 215           uu_chacha_rand16(aUCXT, &clock_seq);
63 215           clock_seq &= 0x3fff;
64              
65 215           state_fd = STATEFD_UNTRIED;
66 215           state_f = NULL;
67 215           adjustment = 0;
68 215           prev_reg = 0;
69 215           defer_100ns = 0;
70 215           Zero(&global_persist, 1, persist_t);
71 215           Zero(&thread_persist, 1, persist_t);
72 215           }
73              
74 10           void uu_clock_getpath(pUCXT, persist_t *persist) {
75 10           Copy(&global_persist, &thread_persist, 1, persist_t);
76 10           Copy(&thread_persist, persist, 1, persist_t);
77 10           }
78              
79 30           void uu_clock_setpath(pUCXT, persist_t *persist) {
80 30           Copy(persist, &thread_persist, 1, persist_t);
81 30           Copy(persist, &global_persist, 1, persist_t);
82 30 100         if (state_fd >= 0)
83 7           fclose(state_f);
84 30           state_fd = STATEFD_UNTRIED;
85             /* doing this would break inter-process deferrals */
86             /* prev_reg = 0; */
87 30           }
88              
89             /* returns 100ns intervals since unix epoch.
90             * since gettimeofday() is in 1usec intervals,
91             * last digit is simulated via adjustment.
92             */
93 42471           IV uu_clock(pUCXT, U64 *ret_clock_reg, U16 *ret_clock_seq) {
94             struct timeval tv;
95             mode_t save_umask;
96             U64 clock_reg;
97             STRLEN longer;
98             UV ptod[2];
99             #ifdef HAVE_LSTAT
100             struct stat statbuf;
101             #endif
102              
103 42471 50         if ((longer = persistNE(&global_persist, &thread_persist))) {
104 0           Copy(&global_persist, &thread_persist, longer, UCHAR);
105 0 0         if (state_fd >= 0)
106 0           fclose(state_f);
107 0           state_fd = STATEFD_UNTRIED;
108             }
109              
110 42471 100         if (state_fd == STATEFD_UNTRIED) {
111             #ifdef HAVE_LSTAT
112 106 100         if (lstat((char*)thread_persist.path, &statbuf) < 0) { /* this covers EINTR too.. ugh */
113 103 50         if (errno == ENOENT)
114 103           state_fd = STATEFD_CREATEOK;
115             else
116 0           state_fd = STATEFD_NOCREATE;
117             }
118 3 100         else if ((statbuf.st_mode & S_IFMT) == S_IFLNK)
119 1           state_fd = STATEFD_SYMLINK;
120             else {
121             #endif
122 2           state_fd = open((char*)thread_persist.path, O_RDWR);
123 2 50         if (state_fd < 0 && errno == ENOENT)
    0          
124 0           state_fd = STATEFD_CREATEOK;
125 2 50         else if (state_fd >= 0) {
126             #ifdef HAVE_LSTAT
127 2           state_f = NULL;
128 2 50         if ((lstat((char*)thread_persist.path, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) != S_IFLNK))
    50          
129             #endif
130 2           state_f = fdopen(state_fd, "r+");
131 2 50         if (!state_f) {
132 0           close(state_fd);
133 0           state_fd = STATEFD_SYMLINK;
134             }
135             }
136             #ifdef HAVE_LSTAT
137             }
138             #endif
139             }
140              
141 42471 100         if (state_fd >= 0) {
142             unsigned int cl;
143             unsigned long tv1, tv2;
144             int a;
145              
146 2           rewind(state_f);
147              
148 2 100         if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n", &cl, &tv1, &tv2, &a) == 4) {
149 1           clock_seq = cl & 0x3fff;
150 1           last.tv_sec = tv1;
151 1           last.tv_usec = tv2;
152 1           adjustment = a;
153             }
154             }
155              
156             /* gettimeofday(&tv, 0); */
157 42471           (*uu_gettime_U2time)(aTHX_ ptod);
158 42471           tv.tv_sec = (long)ptod[0];
159 42471           tv.tv_usec = (long)ptod[1];
160              
161 42471 100         if ((tv.tv_sec == last.tv_sec) && (tv.tv_usec == last.tv_usec)) {
    100          
162             /* same time as last */
163 2470 50         if (++adjustment >= MAX_ADJUSTMENT) {
164 0           clock_seq = (clock_seq+1) & 0x3fff;
165 0           adjustment = 0;
166             }
167             }
168             else {
169             /* time changed */
170 40001 100         if (((tv.tv_sec == last.tv_sec) && (tv.tv_usec < last.tv_usec)) || (tv.tv_sec < last.tv_sec))
    50          
    50          
171 0           clock_seq = (clock_seq+1) & 0x3fff; /* time moved backward */
172 40001           adjustment = 0;
173 40001           last = tv;
174             }
175              
176 42471           clock_reg = tv.tv_usec*10 + adjustment;
177 42471           clock_reg += ((U64)tv.tv_sec)*10000000;
178             /* *clock_reg += (((U64)0x01b21dd2) << 32) + 0x13814000; */
179              
180 42471 100         if ((clock_reg - prev_reg) >= defer_100ns) {
181 42469 100         if (state_fd == STATEFD_CREATEOK) {
182             #ifdef HAVE_LSTAT
183 101 50         if ((lstat((char*)thread_persist.path, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) == S_IFLNK))
    0          
184 0           state_fd = STATEFD_SYMLINK;
185             else {
186             #endif
187 101           save_umask = umask(0);
188 101           state_fd = open((char*)thread_persist.path, O_RDWR|O_CREAT, 0660);
189 101 100         if (state_fd < 0)
190 95           state_fd = STATEFD_NOCREATE;
191 101           (void) umask(save_umask);
192 101 100         if (state_fd >= 0) {
193             #ifdef HAVE_LSTAT
194 6           state_f = NULL;
195 6 50         if ((lstat((char*)thread_persist.path, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) != S_IFLNK))
    50          
196             #endif
197 6           state_f = fdopen(state_fd, "r+");
198 6 50         if (!state_f) {
199 0           close(state_fd);
200 0           state_fd = STATEFD_SYMLINK;
201             }
202             }
203             #ifdef HAVE_LSTAT
204             }
205             #endif
206             }
207              
208 42469 100         if (state_fd > 0) {
209 8           rewind(state_f);
210 16           STRLEN len = fprintf(state_f,
211             "clock: %04x tv: %016lu %08lu adj: %08d\n",
212 8           clock_seq, (unsigned long)last.tv_sec,
213 8           (unsigned long)last.tv_usec, adjustment);
214 8           fflush(state_f);
215 8 50         if (ftruncate(state_fd, len) < 0) {
216 0           fprintf(state_f, " \n");
217 0           fflush(state_f);
218             }
219 8           rewind(state_f);
220             }
221             }
222              
223 42471           prev_reg = clock_reg;
224              
225             /* *clock_high = clock_reg >> 32; */
226             /* *clock_low = (U32)clock_reg; */
227 42471           *ret_clock_reg = clock_reg;
228 42471           *ret_clock_seq = clock_seq;
229 42471           return 0;
230             }
231              
232             /* ex:set ts=2 sw=2 itab=spaces: */