forked from facebook/watchman
-
Notifications
You must be signed in to change notification settings - Fork 0
/
clockspec.c
204 lines (174 loc) · 5.81 KB
/
clockspec.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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/* Copyright 2012-present Facebook, Inc.
* Licensed under the Apache License, Version 2.0 */
#include "watchman.h"
static int proc_pid;
static uint64_t proc_start_time;
void w_clockspec_init(void) {
struct timeval tv;
proc_pid = (int)getpid();
if (gettimeofday(&tv, NULL) == -1) {
w_log(W_LOG_FATAL, "gettimeofday failed: %s\n", strerror(errno));
}
proc_start_time = (uint64_t)tv.tv_sec;
}
struct w_clockspec *w_clockspec_new_clock(uint32_t root_number,
uint32_t ticks) {
struct w_clockspec *spec;
spec = calloc(1, sizeof(*spec));
if (!spec) {
return NULL;
}
spec->tag = w_cs_clock;
spec->clock.start_time = proc_start_time;
spec->clock.pid = proc_pid;
spec->clock.root_number = root_number;
spec->clock.ticks = ticks;
return spec;
}
struct w_clockspec *w_clockspec_parse(json_t *value) {
const char *str;
uint64_t start_time;
int pid;
uint32_t root_number;
uint32_t ticks;
struct w_clockspec *spec;
spec = calloc(1, sizeof(*spec));
if (!spec) {
return NULL;
}
if (json_is_integer(value)) {
spec->tag = w_cs_timestamp;
spec->timestamp.tv_usec = 0;
spec->timestamp.tv_sec = (time_t)json_integer_value(value);
return spec;
}
str = json_string_value(value);
if (!str) {
free(spec);
return NULL;
}
if (str[0] == 'n' && str[1] == ':') {
spec->tag = w_cs_named_cursor;
// spec owns the ref to the string
spec->named_cursor.cursor = w_string_new(str);
return spec;
}
if (sscanf(str, "c:%" PRIu64 ":%d:%" PRIu32 ":%" PRIu32,
&start_time, &pid, &root_number, &ticks) == 4) {
spec->tag = w_cs_clock;
spec->clock.start_time = start_time;
spec->clock.pid = pid;
spec->clock.root_number = root_number;
spec->clock.ticks = ticks;
return spec;
}
if (sscanf(str, "c:%d:%" PRIu32, &pid, &ticks) == 2) {
// old-style clock value (<= 2.8.2) -- by setting clock time and root number
// to 0 we guarantee that this is treated as a fresh instance
spec->tag = w_cs_clock;
spec->clock.start_time = 0;
spec->clock.pid = pid;
spec->clock.root_number = root_number;
spec->clock.ticks = ticks;
return spec;
}
free(spec);
return NULL;
}
// must be called with the root locked
// spec can be null, in which case a fresh instance is assumed
void w_clockspec_eval(w_root_t *root, const struct w_clockspec *spec,
struct w_query_since *since) {
if (spec == NULL) {
since->is_timestamp = false;
since->clock.is_fresh_instance = true;
since->clock.ticks = 0;
return;
}
if (spec->tag == w_cs_timestamp) {
// just copy the values over
since->is_timestamp = true;
since->timestamp = spec->timestamp;
return;
}
since->is_timestamp = false;
if (spec->tag == w_cs_named_cursor) {
w_ht_val_t ticks_val;
w_string_t *cursor = spec->named_cursor.cursor;
since->clock.is_fresh_instance = !w_ht_lookup(root->cursors,
w_ht_ptr_val(cursor),
&ticks_val, false);
if (!since->clock.is_fresh_instance) {
since->clock.is_fresh_instance = ticks_val < root->last_age_out_tick;
}
if (since->clock.is_fresh_instance) {
since->clock.ticks = 0;
} else {
since->clock.ticks = (uint32_t)ticks_val;
}
// Bump the tick value and record it against the cursor.
// We need to bump the tick value so that repeated queries
// when nothing has changed in the filesystem won't continue
// to return the same set of files; we only want the first
// of these to return the files and the rest to return nothing
// until something subsequently changes
w_ht_replace(root->cursors, w_ht_ptr_val(cursor), ++root->ticks);
w_log(W_LOG_DBG, "resolved cursor %.*s -> %" PRIu32 "\n",
cursor->len, cursor->buf, since->clock.ticks);
return;
}
// spec->tag == w_cs_clock
if (spec->clock.start_time == proc_start_time &&
spec->clock.pid == proc_pid &&
spec->clock.root_number == root->number) {
since->clock.is_fresh_instance =
spec->clock.ticks < root->last_age_out_tick;
if (since->clock.is_fresh_instance) {
since->clock.ticks = 0;
} else {
since->clock.ticks = spec->clock.ticks;
}
if (spec->clock.ticks == root->ticks) {
/* Force ticks to increment. This avoids returning and querying the
* same tick value over and over when no files have changed in the
* meantime */
root->ticks++;
}
return;
}
// If the pid, start time or root number don't match, they asked a different
// incarnation of the server or a different instance of this root, so we treat
// them as having never spoken to us before
since->clock.is_fresh_instance = true;
since->clock.ticks = 0;
}
void w_clockspec_free(struct w_clockspec *spec) {
if (spec->tag == w_cs_named_cursor) {
w_string_delref(spec->named_cursor.cursor);
}
free(spec);
}
bool clock_id_string(uint32_t root_number, uint32_t ticks, char *buf,
size_t bufsize) {
int res = snprintf(buf, bufsize, "c:%" PRIu64 ":%d:%u:%" PRIu32,
proc_start_time, proc_pid, root_number, ticks);
if (res == -1) {
return false;
}
return (size_t)res < bufsize;
}
// Renders the current clock id string to the supplied buffer.
// Must be called with the root locked.
static bool current_clock_id_string(w_root_t *root, char *buf, size_t bufsize) {
return clock_id_string(root->number, root->ticks, buf, bufsize);
}
/* Add the current clock value to the response.
* must be called with the root locked */
void annotate_with_clock(w_root_t *root, json_t *resp) {
char buf[128];
if (current_clock_id_string(root, buf, sizeof(buf))) {
set_prop(resp, "clock", json_string_nocheck(buf));
}
}
/* vim:ts=2:sw=2:et:
*/