io-watch 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/bin/io-watch +1 -0
- data/ext/configure +2 -2
- data/ext/io/watch/fsevent.c +2 -1
- data/ext/io/watch/inotify.c +99 -6
- data/lib/io/watch/monitor.rb +1 -0
- data/lib/io/watch/version.rb +1 -1
- data/lib/io/watch.rb +3 -0
- data/license.md +1 -0
- data/readme.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +3 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14f3ccb0d7a3066e70e3ab6e25221c585f1a17034e0e9731fe85637e6797b4d8
|
4
|
+
data.tar.gz: 35d7f43fd20d2ef7cf68558c787344cc49c614498e98e8ccfc25bfca08d5b5d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 045d52cf0bc46fcc1410af1eb75362b21584561350996baa58c0b87a8b0216d24013d18ed97542d9c987f4b457b828b115f10f0ad5848734842194b45f2b09a0
|
7
|
+
data.tar.gz: 1d1a110f3d8f85e1d4f74fd2b55d927d4c91a3b02bbf506ce4b1f20272b67ba6c8830d3e265fa4948feac4de1ee5d2531ca76c8f2211255b21cc9d5cab33644f
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/bin/io-watch
CHANGED
data/ext/configure
CHANGED
@@ -43,7 +43,7 @@ esac
|
|
43
43
|
cat > Makefile <<EOF
|
44
44
|
TARGET = io-watch
|
45
45
|
CC = $CC
|
46
|
-
CFLAGS = -Wall -Wextra
|
46
|
+
CFLAGS = -Wall -Wextra -Werror
|
47
47
|
LDFLAGS = $LDFLAGS
|
48
48
|
SOURCES = $SOURCES
|
49
49
|
OBJECTS = \$(SOURCES:.c=.o)
|
@@ -74,4 +74,4 @@ uninstall:
|
|
74
74
|
.PHONY: all clean install uninstall
|
75
75
|
EOF
|
76
76
|
|
77
|
-
echo "Configuration complete. Run 'make' to build the project."
|
77
|
+
echo "Configuration complete. Run 'make' to build the project."
|
data/ext/io/watch/fsevent.c
CHANGED
@@ -44,6 +44,7 @@ void IO_Watch_FSEvent_callback(
|
|
44
44
|
const FSEventStreamEventFlags eventFlags[],
|
45
45
|
const FSEventStreamEventId eventIds[]) {
|
46
46
|
|
47
|
+
(void)streamRef; // unused
|
47
48
|
const char **eventPaths = (const char**)eventData;
|
48
49
|
struct IO_Watch *watch = context;
|
49
50
|
|
@@ -104,4 +105,4 @@ void IO_Watch_run(struct IO_Watch *watch) {
|
|
104
105
|
}
|
105
106
|
free(pathsToWatch);
|
106
107
|
CFRelease(pathsToWatchArray);
|
107
|
-
}
|
108
|
+
}
|
data/ext/io/watch/inotify.c
CHANGED
@@ -9,6 +9,8 @@
|
|
9
9
|
#include <dirent.h>
|
10
10
|
#include <string.h>
|
11
11
|
#include <sys/stat.h>
|
12
|
+
#include <time.h>
|
13
|
+
#include <poll.h>
|
12
14
|
|
13
15
|
enum {
|
14
16
|
DEBUG = 0,
|
@@ -18,11 +20,16 @@ struct IO_Watch_Watch {
|
|
18
20
|
int watch_descriptor;
|
19
21
|
char *path;
|
20
22
|
int index;
|
23
|
+
|
24
|
+
int modified;
|
21
25
|
};
|
22
26
|
|
23
27
|
struct IO_Watch_Watch_Array {
|
24
28
|
size_t size;
|
25
29
|
size_t capacity;
|
30
|
+
|
31
|
+
size_t pending;
|
32
|
+
|
26
33
|
struct IO_Watch_Watch *watches;
|
27
34
|
};
|
28
35
|
|
@@ -31,6 +38,7 @@ struct IO_Watch_Watch_Array {
|
|
31
38
|
void IO_Watch_Watch_Array_initialize(struct IO_Watch_Watch_Array *array) {
|
32
39
|
array->size = 0;
|
33
40
|
array->capacity = 16;
|
41
|
+
array->pending = 0;
|
34
42
|
array->watches = malloc(array->capacity * sizeof(struct IO_Watch_Watch));
|
35
43
|
if (!array->watches) {
|
36
44
|
perror("io-watch:IO_Watch_Watch_Array_initialize:malloc");
|
@@ -176,6 +184,46 @@ void IO_Watch_INotify_print_event(struct inotify_event *event) {
|
|
176
184
|
fprintf(stderr, "\n");
|
177
185
|
}
|
178
186
|
|
187
|
+
static size_t IO_Watch_INotify_flush_events(struct IO_Watch_Watch_Array watch_array) {
|
188
|
+
size_t count = 0;
|
189
|
+
|
190
|
+
if (watch_array.pending == 0) {
|
191
|
+
return 0;
|
192
|
+
}
|
193
|
+
|
194
|
+
for (size_t i = 0; i < watch_array.size; i++) {
|
195
|
+
int modified = watch_array.watches[i].modified;
|
196
|
+
if (modified) {
|
197
|
+
count++;
|
198
|
+
|
199
|
+
printf("{\"index\":%d,\"mask\":%u}\n", watch_array.watches[i].index, modified);
|
200
|
+
watch_array.watches[i].modified = 0;
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
watch_array.pending = 0;
|
205
|
+
|
206
|
+
fflush(stdout);
|
207
|
+
|
208
|
+
if (DEBUG) fprintf(stderr, "io-watch:IO_Watch_INotify_flush_events: Flushed %zu events\n", count);
|
209
|
+
|
210
|
+
return count;
|
211
|
+
}
|
212
|
+
|
213
|
+
static float IO_Watch_handle_timeout(float latency, struct timespec *start_time) {
|
214
|
+
if (start_time) {
|
215
|
+
struct timespec current_time;
|
216
|
+
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
|
217
|
+
|
218
|
+
float delta = current_time.tv_sec - start_time->tv_sec;
|
219
|
+
delta += (current_time.tv_nsec - start_time->tv_nsec) / 1e9;
|
220
|
+
|
221
|
+
return latency - delta;
|
222
|
+
} else {
|
223
|
+
return 0.0;
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
179
227
|
void IO_Watch_run(struct IO_Watch *watch) {
|
180
228
|
int fd = inotify_init1(IN_NONBLOCK);
|
181
229
|
if (fd == -1) {
|
@@ -193,16 +241,60 @@ void IO_Watch_run(struct IO_Watch *watch) {
|
|
193
241
|
IO_Watch_Watch_Array_scan(fd, &watch_array, path, i);
|
194
242
|
}
|
195
243
|
|
244
|
+
float latency = watch->latency;
|
245
|
+
|
246
|
+
// If start_time is non-null, it means we are now coallescing events, up to the specified latency:
|
247
|
+
struct timespec start_time_buffer, *start_time = NULL;
|
248
|
+
|
196
249
|
printf("{\"status\":\"started\"}\n");
|
197
250
|
fflush(stdout);
|
198
251
|
|
199
252
|
char buffer[BUFFER_SIZE] __attribute__ ((aligned(8)));
|
200
253
|
|
201
254
|
while (1) {
|
255
|
+
// Check for any events:
|
202
256
|
ssize_t result = read(fd, buffer, BUFFER_SIZE);
|
203
|
-
|
204
|
-
|
205
|
-
|
257
|
+
|
258
|
+
// Calculate the timeout, if any:
|
259
|
+
float timeout = IO_Watch_handle_timeout(latency, start_time);
|
260
|
+
|
261
|
+
// We need to check if the timeout has expired:
|
262
|
+
if (timeout < 0.0) {
|
263
|
+
if (DEBUG) fprintf(stderr, "io-watch:IO_Watch_run: Timeout expired\n");
|
264
|
+
|
265
|
+
start_time = NULL;
|
266
|
+
// Flush any pending events:
|
267
|
+
IO_Watch_INotify_flush_events(watch_array);
|
268
|
+
}
|
269
|
+
|
270
|
+
// If no events are available, we have to wait for the timeout:
|
271
|
+
if (result == -1) {
|
272
|
+
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
273
|
+
perror("(io-watch:IO_Watch_run:read)");
|
274
|
+
exit(EXIT_FAILURE);
|
275
|
+
}
|
276
|
+
|
277
|
+
if (DEBUG) fprintf(stderr, "io-watch:IO_Watch_run: No events available, waiting for %0.4f seconds\n", timeout);
|
278
|
+
|
279
|
+
// Wait until the file descriptor becomes readable:
|
280
|
+
struct pollfd poll_fd = {fd, POLLIN, 0};
|
281
|
+
int poll_timeout = start_time ? (int) (timeout * 1e3) : -1;
|
282
|
+
|
283
|
+
// If we have a timeout of 0.0, we have to poll for at least 1 ms:
|
284
|
+
if (poll_timeout == 0 && timeout > 0.0) {
|
285
|
+
poll_timeout = 1;
|
286
|
+
}
|
287
|
+
|
288
|
+
if (DEBUG) fprintf(stderr, "io-watch:IO_Watch_run:poll: Polling for %d ms\n", poll_timeout);
|
289
|
+
poll(&poll_fd, 1, poll_timeout);
|
290
|
+
continue;
|
291
|
+
}
|
292
|
+
|
293
|
+
// If we have received events, we have to set the start_time if it is not set:
|
294
|
+
if (!start_time) {
|
295
|
+
if (DEBUG) fprintf(stderr, "io-watch:IO_Watch_run: Setting start_time\n");
|
296
|
+
start_time = &start_time_buffer;
|
297
|
+
clock_gettime(CLOCK_MONOTONIC, start_time);
|
206
298
|
}
|
207
299
|
|
208
300
|
for (ssize_t offset = 0; offset < result;) {
|
@@ -225,7 +317,9 @@ void IO_Watch_run(struct IO_Watch *watch) {
|
|
225
317
|
ssize_t index = IO_Watch_Watch_Array_find(&watch_array, event->wd);
|
226
318
|
|
227
319
|
if (index != -1) {
|
228
|
-
|
320
|
+
if (DEBUG) fprintf(stderr, "io-watch:IO_Watch_run: Modified path %s mask=%d\n", watch_array.watches[index].path, event->mask);
|
321
|
+
watch_array.watches[index].modified |= event->mask;
|
322
|
+
watch_array.pending += 1;
|
229
323
|
|
230
324
|
// If a new directory is created, add a watch for it
|
231
325
|
if (event->mask & IN_CREATE && event->mask & IN_ISDIR) {
|
@@ -239,9 +333,8 @@ void IO_Watch_run(struct IO_Watch *watch) {
|
|
239
333
|
|
240
334
|
offset += sizeof(struct inotify_event) + event->len;
|
241
335
|
}
|
242
|
-
fflush(stdout);
|
243
336
|
}
|
244
|
-
|
337
|
+
|
245
338
|
for (size_t i = 0; i < watch_array.size; i++) {
|
246
339
|
inotify_rm_watch(fd, watch_array.watches[i].watch_descriptor);
|
247
340
|
free(watch_array.watches[i].path);
|
data/lib/io/watch/monitor.rb
CHANGED
@@ -9,6 +9,7 @@ class IO
|
|
9
9
|
module Watch
|
10
10
|
# Represents a list of roots to watch for changes.
|
11
11
|
class Monitor
|
12
|
+
# The path to the compiled `io-watch` command.
|
12
13
|
def self.command_path
|
13
14
|
if extension_path = Gem.loaded_specs['io-watch']&.extension_dir
|
14
15
|
if File.exist?(extension_path)
|
data/lib/io/watch/version.rb
CHANGED
data/lib/io/watch.rb
CHANGED
data/license.md
CHANGED
data/readme.md
CHANGED
@@ -4,6 +4,10 @@ Monitor the file-system for changes.
|
|
4
4
|
|
5
5
|
[](https://github.com/socketry/io-watch/actions?workflow=Test)
|
6
6
|
|
7
|
+
## Motivation
|
8
|
+
|
9
|
+
Previously, I was using the `listen` gem in combination with `rb-inotify` or `rb-fsevent` to watch for file-system changes. However, those libraries have been around for an extremely long time and have accumulated a lot of cruft. In addition, I don't like having to multiplex in application code depending on the underlying platform. I created this library to provide a simple, unified interface for watching directories for changes. This is the most consistently supported behaviour across all platforms, and fits the needs of most applications without a huge amount of complexity.
|
10
|
+
|
7
11
|
## Usage
|
8
12
|
|
9
13
|
Please see the [project documentation](https://socketry.github.io/io-watch/) for more details.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: io-watch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
|
+
- Étienne Barrié
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain:
|
@@ -37,7 +38,7 @@ cert_chain:
|
|
37
38
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
38
39
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
39
40
|
-----END CERTIFICATE-----
|
40
|
-
date: 2024-08-
|
41
|
+
date: 2024-08-12 00:00:00.000000000 Z
|
41
42
|
dependencies: []
|
42
43
|
description:
|
43
44
|
email:
|
metadata.gz.sig
CHANGED
Binary file
|