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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b3d57b500111de6639098f96580da6ddfc5029f77fc0daf97159e54415ea7c5
4
- data.tar.gz: 0ee6cfbdb782214fc141b713f7396448e92ae438244939ed48b9681a6e620f7f
3
+ metadata.gz: 14f3ccb0d7a3066e70e3ab6e25221c585f1a17034e0e9731fe85637e6797b4d8
4
+ data.tar.gz: 35d7f43fd20d2ef7cf68558c787344cc49c614498e98e8ccfc25bfca08d5b5d6
5
5
  SHA512:
6
- metadata.gz: 3e4873bbd69789206c2704fe3e20914c7955c79d2310513adcf8dd9371c6773bf9d0a729b19b1de4a18fe852631435184559d98600d7de5b2ecd81a5a633075a
7
- data.tar.gz: cbc9fdaedb8f1383e36e845cd07341e2427e706c205a94da35b355eb87ae049f9d2a256d30beb3a4616f35fbcf10f66e5022e0653849a3762a01376a7937f003
6
+ metadata.gz: 045d52cf0bc46fcc1410af1eb75362b21584561350996baa58c0b87a8b0216d24013d18ed97542d9c987f4b457b828b115f10f0ad5848734842194b45f2b09a0
7
+ data.tar.gz: 1d1a110f3d8f85e1d4f74fd2b55d927d4c91a3b02bbf506ce4b1f20272b67ba6c8830d3e265fa4948feac4de1ee5d2531ca76c8f2211255b21cc9d5cab33644f
checksums.yaml.gz.sig CHANGED
Binary file
data/bin/io-watch CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'console'
4
5
  require 'io/watch'
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."
@@ -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
+ }
@@ -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, &current_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
- if (result == -1 && errno != EAGAIN) {
204
- perror("(io-watch:IO_Watch_run:read)");
205
- exit(EXIT_FAILURE);
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
- printf("{\"index\":%d,\"mask\":%u}\n", watch_array.watches[index].index, event->mask);
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);
@@ -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)
@@ -5,6 +5,6 @@
5
5
 
6
6
  class IO
7
7
  module Watch
8
- VERSION = '0.6.1'
8
+ VERSION = '0.6.3'
9
9
  end
10
10
  end
data/lib/io/watch.rb CHANGED
@@ -6,8 +6,11 @@
6
6
  require_relative 'watch/version'
7
7
  require_relative 'watch/monitor'
8
8
 
9
+ # @namespace
9
10
  class IO
11
+ # @namespace
10
12
  module Watch
13
+ # Create a new monitor.
11
14
  def self.new(...)
12
15
  Monitor.new(...)
13
16
  end
data/license.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # MIT License
2
2
 
3
3
  Copyright, 2024, by Samuel Williams.
4
+ Copyright, 2024, by Étienne Barrié.
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
data/readme.md CHANGED
@@ -4,6 +4,10 @@ Monitor the file-system for changes.
4
4
 
5
5
  [![Development Status](https://github.com/socketry/io-watch/workflows/Test/badge.svg)](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.1
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-11 00:00:00.000000000 Z
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