io-watch 0.6.1 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
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