cool.io 1.9.3 → 1.9.5

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: 744af9f4092900f46b6db222fadf805f4fe107fb8f3c5c0a5c9661508d957ac5
4
- data.tar.gz: 594ae4eb024a1c4519ec912859126b5e739f52cc3bdfab1f93ae8fc9cbf7610f
3
+ metadata.gz: 3da0843b8a62fd01aab297ce31f885a5856b4326bef2fb3c90d363f9576fe9a3
4
+ data.tar.gz: d073dd104500ccc2c1b62411c8f02c083df004353f9d2798ccf8ae41166effbd
5
5
  SHA512:
6
- metadata.gz: 5b27d1af86f5647d9409b7b394d5ed9f2c85de9b68ab88d1d6363ca94aa05d84326f4feed754319f8409b371c53c61cb814292f7849618d806235b7e6de65007
7
- data.tar.gz: 58839f3800e0aa902f4c0b60b7b23eff2414cb1c34a14df688b318e4b1d31151ce7dfcc6a8e9d7b1d818d5160cf858b0a8a533ee5d1adf1c195d00baa7f50d43
6
+ metadata.gz: b3f2fab2151abdbd3e0762aa04675cab8b4d196990227bf5d068f12360a62c87e669480f50e70b47b289a9c0f29deeddb28836590422faafe5d0a5ecaf6173fb
7
+ data.tar.gz: 233ea14784f4390b6b1f1b8fb131d5b1d20d14d0b78f9f1fd3b39f89bd25d34be505267d23d1b5647be2febcd5216e33a459c0e135ee49737334a06a0626bb47
@@ -12,6 +12,7 @@ jobs:
12
12
  continue-on-error: ${{matrix.experimental}}
13
13
 
14
14
  strategy:
15
+ fail-fast: false
15
16
  matrix:
16
17
  os:
17
18
  - ubuntu
@@ -0,0 +1,44 @@
1
+ name: Memcheck
2
+
3
+ on: [push, pull_request]
4
+
5
+ permissions:
6
+ contents: read
7
+
8
+ jobs:
9
+ test:
10
+ name: ${{matrix.ruby}} on ${{matrix.os}}
11
+ runs-on: ${{matrix.os}}-latest
12
+ continue-on-error: ${{matrix.experimental}}
13
+
14
+ strategy:
15
+ matrix:
16
+ os:
17
+ - ubuntu
18
+
19
+ ruby:
20
+ - "3.2"
21
+ - "3.3"
22
+ - "3.4"
23
+ - "4.0"
24
+
25
+ experimental: [false]
26
+
27
+ include:
28
+ - os: ubuntu
29
+ ruby: head
30
+ experimental: true
31
+
32
+ steps:
33
+ - uses: actions/checkout@v6
34
+ - uses: ruby/setup-ruby@v1
35
+ with:
36
+ ruby-version: ${{matrix.ruby}}
37
+ bundler-cache: true
38
+ - name: Install Valgrind
39
+ run: |
40
+ sudo apt-get update
41
+ sudo apt-get install -y valgrind
42
+ - name: Run tests
43
+ timeout-minutes: 10
44
+ run: bundle exec rake spec:valgrind
data/Rakefile CHANGED
@@ -22,7 +22,7 @@ end
22
22
 
23
23
  require 'rake/extensiontask'
24
24
 
25
- spec = eval(File.read("cool.io.gemspec"))
25
+ spec = Gem::Specification.load("cool.io.gemspec")
26
26
 
27
27
  def configure_cross_compilation(ext)
28
28
  unless RUBY_PLATFORM =~ /mswin|mingw/
@@ -48,24 +48,11 @@ namespace :build do
48
48
  end
49
49
  end
50
50
 
51
- # adapted from http://flavoriffic.blogspot.com/2009/06/easily-valgrind-gdb-your-ruby-c.html
52
- def specs_command
53
- require "find"
54
- files = []
55
- Find.find("spec") do |f|
56
- files << f if File.basename(f) =~ /.*spec.*\.rb$/
57
- end
58
- cmdline = "#{RUBY} -I.:lib:ext:spec \
59
- -e '%w[#{files.join(' ')}].each { |f| require f }'"
60
- end
61
-
62
- namespace :spec do
63
- desc "run specs with valgrind"
64
- task :valgrind => :compile do
65
- system "valgrind --num-callers=15 \
66
- --partial-loads-ok=yes --undef-value-errors=no \
67
- --tool=memcheck --leak-check=yes --track-fds=yes \
68
- --show-reachable=yes #{specs_command}"
51
+ if RUBY_PLATFORM.include?('linux')
52
+ require 'ruby_memcheck'
53
+ require 'ruby_memcheck/rspec/rake_task'
54
+ namespace :spec do
55
+ RubyMemcheck::RSpec::RakeTask.new(valgrind: :compile)
69
56
  end
70
57
  end
71
58
 
data/cool.io.gemspec CHANGED
@@ -1,6 +1,4 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "cool.io/version"
1
+ require_relative "lib/cool.io/version"
4
2
 
5
3
  Gem::Specification.new do |s|
6
4
  s.name = "cool.io"
data/ext/cool.io/buffer.c CHANGED
@@ -28,17 +28,22 @@
28
28
 
29
29
  /* Default number of bytes in each node's buffer. Should be >= MTU */
30
30
  #define DEFAULT_NODE_SIZE 16384
31
- static unsigned default_node_size = DEFAULT_NODE_SIZE;
31
+ static size_t default_node_size = DEFAULT_NODE_SIZE;
32
32
 
33
+ /*
34
+ * Byte counts are kept in size_t so the accounting cannot wrap while the
35
+ * buffer itself still holds the data. node_size is capped at MAX_BUFFER_SIZE,
36
+ * but size grows with whatever the buffer is asked to hold.
37
+ */
33
38
  struct buffer {
34
- unsigned size, node_size;
39
+ size_t size, node_size;
35
40
  struct buffer_node *head, *tail;
36
41
  struct buffer_node *pool_head, *pool_tail;
37
42
 
38
43
  };
39
44
 
40
45
  struct buffer_node {
41
- unsigned start, end;
46
+ size_t start, end;
42
47
  struct buffer_node *next;
43
48
  unsigned char data[0];
44
49
  };
@@ -68,13 +73,13 @@ static struct buffer *buffer_init(struct buffer *);
68
73
  static void buffer_clear(struct buffer * buf);
69
74
  static void buffer_free(struct buffer * buf);
70
75
  static void buffer_free_pool(struct buffer * buf);
71
- static void buffer_prepend(struct buffer * buf, char *str, unsigned len);
72
- static void buffer_append(struct buffer * buf, char *str, unsigned len);
73
- static void buffer_read(struct buffer * buf, char *str, unsigned len);
76
+ static void buffer_prepend(struct buffer * buf, char *str, size_t len);
77
+ static void buffer_append(struct buffer * buf, char *str, size_t len);
78
+ static void buffer_read(struct buffer * buf, char *str, size_t len);
74
79
  static int buffer_read_frame(struct buffer * buf, VALUE str, char frame_mark);
75
- static void buffer_copy(struct buffer * buf, char *str, unsigned len);
76
- static int buffer_read_from(struct buffer * buf, int fd);
77
- static int buffer_write_to(struct buffer * buf, int fd);
80
+ static void buffer_copy(struct buffer * buf, char *str, size_t len);
81
+ static ssize_t buffer_read_from(struct buffer * buf, int fd);
82
+ static ssize_t buffer_write_to(struct buffer * buf, int fd);
78
83
 
79
84
  /*
80
85
  * High-performance I/O buffer intended for use in non-blocking programs
@@ -145,21 +150,21 @@ Coolio_Buffer_free(void * buf)
145
150
 
146
151
  /**
147
152
  * call-seq:
148
- * Coolio::Buffer.default_node_size -> 4096
153
+ * Coolio::Buffer.default_node_size -> 16384
149
154
  *
150
155
  * Retrieves the current value of the default node size.
151
156
  */
152
157
  static VALUE
153
158
  Coolio_Buffer_default_node_size(VALUE klass)
154
159
  {
155
- return UINT2NUM(default_node_size);
160
+ return SIZET2NUM(default_node_size);
156
161
  }
157
162
 
158
163
  /*
159
164
  * safely converts node sizes from Ruby numerics to C and raising
160
165
  * ArgumentError or RangeError on invalid sizes
161
166
  */
162
- static unsigned
167
+ static size_t
163
168
  convert_node_size(VALUE size)
164
169
  {
165
170
  if (
@@ -168,7 +173,7 @@ convert_node_size(VALUE size)
168
173
  )
169
174
  rb_raise(rb_eArgError, "invalid buffer size");
170
175
 
171
- return (unsigned) NUM2INT(size);
176
+ return NUM2SIZET(size);
172
177
  }
173
178
 
174
179
  /**
@@ -241,7 +246,7 @@ Coolio_Buffer_size(VALUE self)
241
246
  struct buffer *buf;
242
247
  TypedData_Get_Struct(self, struct buffer, &Coolio_Buffer_type, buf);
243
248
 
244
- return INT2NUM(buf->size);
249
+ return SIZET2NUM(buf->size);
245
250
  }
246
251
 
247
252
  /**
@@ -311,15 +316,18 @@ static VALUE
311
316
  Coolio_Buffer_read(int argc, VALUE * argv, VALUE self)
312
317
  {
313
318
  VALUE length_obj, str;
314
- int length;
319
+ size_t length;
315
320
  struct buffer *buf;
316
321
 
317
322
  TypedData_Get_Struct(self, struct buffer, &Coolio_Buffer_type, buf);
318
323
 
319
324
  if (rb_scan_args(argc, argv, "01", &length_obj) == 1) {
320
- length = NUM2INT(length_obj);
321
- if(length < 1)
325
+ /* Read signed so a negative argument still raises ArgumentError here
326
+ * rather than RangeError out of the conversion */
327
+ long requested = NUM2LONG(length_obj);
328
+ if(requested < 1)
322
329
  rb_raise(rb_eArgError, "length must be greater than zero");
330
+ length = (size_t) requested;
323
331
  if(length > buf->size)
324
332
  length = buf->size;
325
333
  } else
@@ -328,7 +336,7 @@ Coolio_Buffer_read(int argc, VALUE * argv, VALUE self)
328
336
  if(buf->size == 0)
329
337
  return rb_str_new2("");
330
338
 
331
- str = rb_str_new(0, length);
339
+ str = rb_str_new(0, (long) length);
332
340
  buffer_read(buf, RSTRING_PTR(str), length);
333
341
 
334
342
  return str;
@@ -352,6 +360,9 @@ Coolio_Buffer_read_frame(VALUE self, VALUE data, VALUE mark)
352
360
 
353
361
  TypedData_Get_Struct(self, struct buffer, &Coolio_Buffer_type, buf);
354
362
 
363
+ StringValue(data);
364
+ rb_str_modify(data);
365
+
355
366
  if (buffer_read_frame(buf, data, mark_c)) {
356
367
  return Qtrue;
357
368
  } else {
@@ -373,7 +384,7 @@ Coolio_Buffer_to_str(VALUE self)
373
384
 
374
385
  TypedData_Get_Struct(self, struct buffer, &Coolio_Buffer_type, buf);
375
386
 
376
- str = rb_str_new(0, buf->size);
387
+ str = rb_str_new(0, (long) buf->size);
377
388
  buffer_copy(buf, RSTRING_PTR(str), buf->size);
378
389
 
379
390
  return str;
@@ -391,7 +402,7 @@ static VALUE
391
402
  Coolio_Buffer_read_from(VALUE self, VALUE io)
392
403
  {
393
404
  struct buffer *buf;
394
- int ret;
405
+ ssize_t ret;
395
406
  #if defined(HAVE_RB_IO_T) || defined(HAVE_RB_IO_DESCRIPTOR)
396
407
  rb_io_t *fptr;
397
408
  #else
@@ -408,7 +419,7 @@ Coolio_Buffer_read_from(VALUE self, VALUE io)
408
419
  #else
409
420
  ret = buffer_read_from(buf, FPTR_TO_FD(fptr));
410
421
  #endif
411
- return ret == -1 ? Qnil : INT2NUM(ret);
422
+ return ret == -1 ? Qnil : SSIZET2NUM(ret);
412
423
  }
413
424
 
414
425
  /**
@@ -435,9 +446,9 @@ Coolio_Buffer_write_to(VALUE self, VALUE io)
435
446
  rb_io_set_nonblock(fptr);
436
447
 
437
448
  #ifdef HAVE_RB_IO_DESCRIPTOR
438
- return INT2NUM(buffer_write_to(buf, rb_io_descriptor(io)));
449
+ return SSIZET2NUM(buffer_write_to(buf, rb_io_descriptor(io)));
439
450
  #else
440
- return INT2NUM(buffer_write_to(buf, FPTR_TO_FD(fptr)));
451
+ return SSIZET2NUM(buffer_write_to(buf, FPTR_TO_FD(fptr)));
441
452
  #endif
442
453
  }
443
454
 
@@ -535,7 +546,7 @@ buffer_node_free(struct buffer * buf, struct buffer_node * node)
535
546
 
536
547
  /* Prepend data to the front of the buffer */
537
548
  static void
538
- buffer_prepend(struct buffer * buf, char *str, unsigned len)
549
+ buffer_prepend(struct buffer * buf, char *str, size_t len)
539
550
  {
540
551
  struct buffer_node *node, *tmp;
541
552
  buf->size += len;
@@ -576,9 +587,9 @@ buffer_prepend(struct buffer * buf, char *str, unsigned len)
576
587
 
577
588
  /* Append data to the front of the buffer */
578
589
  static void
579
- buffer_append(struct buffer * buf, char *str, unsigned len)
590
+ buffer_append(struct buffer * buf, char *str, size_t len)
580
591
  {
581
- unsigned nbytes;
592
+ size_t nbytes;
582
593
  buf->size += len;
583
594
 
584
595
  /* If it fits in the remaining space in the tail */
@@ -613,9 +624,9 @@ buffer_append(struct buffer * buf, char *str, unsigned len)
613
624
 
614
625
  /* Read data from the buffer (and clear what we've read) */
615
626
  static void
616
- buffer_read(struct buffer * buf, char *str, unsigned len)
627
+ buffer_read(struct buffer * buf, char *str, size_t len)
617
628
  {
618
- unsigned nbytes;
629
+ size_t nbytes;
619
630
  struct buffer_node *tmp;
620
631
 
621
632
  while (buf->size > 0 && len > 0) {
@@ -649,7 +660,7 @@ buffer_read(struct buffer * buf, char *str, unsigned len)
649
660
  static int
650
661
  buffer_read_frame(struct buffer * buf, VALUE str, char frame_mark)
651
662
  {
652
- unsigned nbytes = 0;
663
+ size_t nbytes = 0;
653
664
  struct buffer_node *tmp;
654
665
 
655
666
  while (buf->size > 0) {
@@ -664,7 +675,7 @@ buffer_read_frame(struct buffer * buf, VALUE str, char frame_mark)
664
675
  }
665
676
 
666
677
  /* Copy less than everything if we found a frame byte */
667
- rb_str_cat(str, s, nbytes);
678
+ rb_str_cat(str, s, (long) nbytes);
668
679
 
669
680
  /* Fixup the buffer pointers to indicate the bytes were consumed */
670
681
  head->start += nbytes;
@@ -688,9 +699,9 @@ buffer_read_frame(struct buffer * buf, VALUE str, char frame_mark)
688
699
 
689
700
  /* Copy data from the buffer without clearing it */
690
701
  static void
691
- buffer_copy(struct buffer * buf, char *str, unsigned len)
702
+ buffer_copy(struct buffer * buf, char *str, size_t len)
692
703
  {
693
- unsigned nbytes;
704
+ size_t nbytes;
694
705
  struct buffer_node *node;
695
706
 
696
707
  node = buf->head;
@@ -709,10 +720,10 @@ buffer_copy(struct buffer * buf, char *str, unsigned len)
709
720
  }
710
721
 
711
722
  /* Write data from the buffer to a file descriptor */
712
- static int
723
+ static ssize_t
713
724
  buffer_write_to(struct buffer * buf, int fd)
714
725
  {
715
- int bytes_written, total_bytes_written = 0;
726
+ ssize_t bytes_written, total_bytes_written = 0;
716
727
  struct buffer_node *tmp;
717
728
 
718
729
  while (buf->head) {
@@ -730,7 +741,7 @@ buffer_write_to(struct buffer * buf, int fd)
730
741
  buf->size -= bytes_written;
731
742
 
732
743
  /* If the write blocked... */
733
- if (bytes_written < buf->head->end - buf->head->start) {
744
+ if ((size_t) bytes_written < buf->head->end - buf->head->start) {
734
745
  buf->head->start += bytes_written;
735
746
  return total_bytes_written;
736
747
  }
@@ -748,11 +759,11 @@ buffer_write_to(struct buffer * buf, int fd)
748
759
 
749
760
  /* Read data from a file descriptor to a buffer */
750
761
  /* Append data to the front of the buffer */
751
- static int
762
+ static ssize_t
752
763
  buffer_read_from(struct buffer * buf, int fd)
753
764
  {
754
- int bytes_read, total_bytes_read = 0;
755
- unsigned nbytes;
765
+ ssize_t bytes_read, total_bytes_read = 0;
766
+ size_t nbytes;
756
767
 
757
768
  /* Empty list needs initialized */
758
769
  if (!buf->head) {
@@ -782,7 +793,7 @@ buffer_read_from(struct buffer * buf, int fd)
782
793
  buf->tail->next = buffer_node_new(buf);
783
794
  buf->tail = buf->tail->next;
784
795
  }
785
- } while (bytes_read == nbytes);
796
+ } while ((size_t) bytes_read == nbytes);
786
797
 
787
798
  return total_bytes_read;
788
799
  }
@@ -55,6 +55,12 @@ struct Coolio_Watcher
55
55
  int enabled;
56
56
  VALUE loop;
57
57
 
58
+ /* Stable copy of the watched path for ev_stat watchers. libev retains the
59
+ * path pointer passed to ev_stat_init() for the lifetime of the watcher, so
60
+ * it must not point into a Ruby String whose buffer GC.compact may relocate.
61
+ * NULL for watcher types that don't use it. */
62
+ char *stat_path;
63
+
58
64
  void (*dispatch_callback)(VALUE self, int revents);
59
65
  };
60
66
 
@@ -11,7 +11,6 @@ have_func('rb_thread_alone')
11
11
  have_func('rb_str_set_len')
12
12
  have_library('rt', 'clock_gettime')
13
13
 
14
- have_func("rb_io_descriptor")
15
14
  have_library("c", "main")
16
15
  if have_macro("HAVE_RB_IO_T", "ruby/io.h")
17
16
  have_struct_member("rb_io_t", "fd", "ruby/io.h")
data/ext/cool.io/loop.c CHANGED
@@ -110,8 +110,7 @@ void Coolio_Loop_process_event(VALUE watcher, int revents)
110
110
  struct Coolio_Loop *loop_data;
111
111
  struct Coolio_Watcher *watcher_data;
112
112
 
113
- /* The Global VM lock isn't held right now, but hopefully
114
- * we can still do this safely */
113
+ /* The Global VM Lock is held here, see the explanation below */
115
114
  watcher_data = Coolio_Watcher_ptr(watcher);
116
115
 
117
116
  if (watcher_data->enabled == 0) {
@@ -126,13 +125,14 @@ void Coolio_Loop_process_event(VALUE watcher, int revents)
126
125
  *
127
126
  * Our call path up to here looks a little something like:
128
127
  *
129
- * -> release GVL -> event syscall -> libev callback
130
- * (GVL = Global VM Lock) ^^^ You are here
128
+ * -> release GVL -> event syscall -> reacquire GVL -> libev callback
129
+ * (GVL = Global VM Lock) ^^^ You are here
131
130
  *
132
- * We released the GVL in the Coolio_Loop_run_once() function
133
- * so other Ruby threads can run while we make a blocking
134
- * system call (one of epoll, kqueue, port, poll, or select,
135
- * depending on the platform).
131
+ * libev is patched (see ev.c) to release the GVL around the blocking
132
+ * system call (one of epoll, kqueue, port, poll, or select, depending
133
+ * on the platform) so other Ruby threads can run while we wait there.
134
+ * Only that call runs without the GVL: libev invokes the events it
135
+ * collected after the call has returned, so we hold the GVL here.
136
136
  *
137
137
  * More specifically, this is a libev callback abstraction
138
138
  * called from a real libev callback in every watcher,
@@ -150,19 +150,19 @@ void Coolio_Loop_process_event(VALUE watcher, int revents)
150
150
  * event fired, why the hell is it telling the loop? Why
151
151
  * doesn't it just rb_funcall() the appropriate callback?
152
152
  *
153
- * Well, the problem is the Global VM Lock isn't held right
154
- * now, so we can't rb_funcall() anything. In order to get
155
- * it back we have to:
153
+ * Because Ruby code doesn't run from inside libev's own event
154
+ * invocation. Instead:
156
155
  *
157
- * stash event and return -> acquire GVL -> dispatch to Ruby
156
+ * stash event and return -> ev_loop() returns -> dispatch to Ruby
158
157
  *
159
- * Which is kinda ugly and confusing, but still gives us
158
+ * Which is kinda ugly and confusing, but still gives us
160
159
  * an O(1) event loop whose heart is in the kernel itself. w00t!
161
160
  *
162
161
  * So, stash the event in the loop's data struct. When we return
163
162
  * the ev_loop() call being made in the Coolio_Loop_run_once_blocking()
164
- * function below will also return, at which point the GVL is
165
- * reacquired and we can call out to Ruby */
163
+ * function below will also return, and Coolio_Loop_dispatch_events()
164
+ * walks what we stashed and calls out to Ruby. A watcher detached
165
+ * along the way nils its own stashed entries, which that walk skips */
166
166
 
167
167
  /* Grow the event buffer if it's too small */
168
168
  if(loop_data->events_received >= loop_data->eventbuf_size) {
@@ -79,7 +79,9 @@ void Init_coolio_stat_watcher()
79
79
  */
80
80
  static VALUE Coolio_StatWatcher_initialize(int argc, VALUE *argv, VALUE self)
81
81
  {
82
- VALUE path, interval;
82
+ VALUE path, interval;
83
+ const char *path_str;
84
+ long path_len;
83
85
  struct Coolio_Watcher *watcher_data;
84
86
 
85
87
  rb_scan_args(argc, argv, "11", &path, &interval);
@@ -87,15 +89,26 @@ static VALUE Coolio_StatWatcher_initialize(int argc, VALUE *argv, VALUE self)
87
89
  interval = rb_convert_type(interval, T_FLOAT, "Float", "to_f");
88
90
 
89
91
  path = rb_String(path);
92
+ path_str = StringValueCStr(path);
90
93
  rb_iv_set(self, "@path", path);
91
94
 
92
95
  watcher_data = Coolio_Watcher_ptr(self);
93
96
 
97
+ /* libev keeps the path pointer passed to ev_stat_init() for the lifetime of
98
+ * the watcher. RSTRING_PTR(path) points into a Ruby String whose buffer
99
+ * GC.compact may relocate, leaving libev with a dangling pointer. Keep an
100
+ * owned copy instead; it is released in Coolio_Watcher_free(). */
101
+ path_len = strlen(path_str);
102
+ if(watcher_data->stat_path)
103
+ xfree(watcher_data->stat_path);
104
+ watcher_data->stat_path = xmalloc(path_len + 1);
105
+ memcpy(watcher_data->stat_path, path_str, path_len + 1);
106
+
94
107
  watcher_data->dispatch_callback = Coolio_StatWatcher_dispatch_callback;
95
108
  ev_stat_init(
96
109
  &watcher_data->event_types.ev_stat,
97
110
  Coolio_StatWatcher_libev_callback,
98
- RSTRING_PTR(path),
111
+ watcher_data->stat_path,
99
112
  interval == Qnil ? 0 : NUM2DBL(interval)
100
113
  );
101
114
  watcher_data->event_types.ev_stat.data = (void *)self;
@@ -14,6 +14,7 @@ static VALUE cCoolio_Watcher = Qnil;
14
14
 
15
15
  static VALUE Coolio_Watcher_allocate(VALUE klass);
16
16
  static void Coolio_Watcher_mark(void *data);
17
+ static void Coolio_Watcher_free(void *data);
17
18
 
18
19
  static VALUE Coolio_Watcher_initialize(VALUE self);
19
20
  static VALUE Coolio_Watcher_attach(VALUE self, VALUE loop);
@@ -56,7 +57,7 @@ static const rb_data_type_t Coolio_Watcher_type = {
56
57
  "Coolio::Watcher",
57
58
  {
58
59
  Coolio_Watcher_mark,
59
- RUBY_DEFAULT_FREE,
60
+ Coolio_Watcher_free,
60
61
  },
61
62
  };
62
63
 
@@ -75,6 +76,7 @@ static VALUE Coolio_Watcher_allocate(VALUE klass)
75
76
 
76
77
  watcher_data->loop = Qnil;
77
78
  watcher_data->enabled = 0;
79
+ watcher_data->stat_path = NULL;
78
80
 
79
81
  return watcher;
80
82
  }
@@ -87,6 +89,16 @@ static void Coolio_Watcher_mark(void *data)
87
89
  rb_gc_mark(watcher_data->loop);
88
90
  }
89
91
 
92
+ static void Coolio_Watcher_free(void *data)
93
+ {
94
+ struct Coolio_Watcher *watcher_data = data;
95
+
96
+ if(watcher_data->stat_path)
97
+ xfree(watcher_data->stat_path);
98
+
99
+ xfree(watcher_data);
100
+ }
101
+
90
102
  static VALUE Coolio_Watcher_initialize(VALUE self)
91
103
  {
92
104
  rb_raise(rb_eRuntimeError, "watcher base class should not be initialized directly");
@@ -26,20 +26,15 @@
26
26
 
27
27
  #define Watcher_Detach(watcher_type, watcher) \
28
28
  struct Coolio_Watcher *watcher_data; \
29
- struct Coolio_Loop *loop_data; \
30
29
  \
31
30
  watcher_data = Coolio_Watcher_ptr(watcher); \
32
31
  \
33
32
  if(watcher_data->loop == Qnil) \
34
33
  rb_raise(rb_eRuntimeError, "not attached to a loop"); \
35
34
  \
36
- if (watcher_data->enabled == 0) { \
37
- /* Ignore because watcher was already detached. */ \
38
- return Qnil; \
35
+ if (watcher_data->enabled) { \
36
+ rb_funcall(watcher, rb_intern("disable"), 0); \
39
37
  } \
40
- loop_data = Coolio_Loop_ptr(watcher_data->loop); \
41
- \
42
- ev_##watcher_type##_stop(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type); \
43
38
  rb_call_super(0, 0)
44
39
 
45
40
  #define Watcher_Enable(watcher_type, watcher) \
@@ -66,10 +61,10 @@
66
61
  if(watcher_data->loop == Qnil) \
67
62
  rb_raise(rb_eRuntimeError, "not attached to a loop"); \
68
63
  \
69
- rb_call_super(0, 0); \
70
- \
71
- loop_data = Coolio_Loop_ptr(watcher_data->loop); \
72
- \
73
- ev_##watcher_type##_stop(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type)
64
+ if (watcher_data->enabled) { \
65
+ loop_data = Coolio_Loop_ptr(watcher_data->loop); \
66
+ ev_##watcher_type##_stop(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type); \
67
+ } \
68
+ rb_call_super(0, 0);
74
69
 
75
70
  #endif
data/ext/libev/ev.c CHANGED
@@ -210,7 +210,13 @@
210
210
  #else
211
211
  # include <io.h>
212
212
  # define WIN32_LEAN_AND_MEAN
213
- # define FD_SETSIZE 1024
213
+ /* ruby.h above already pulled in winsock2.h, so fd_set may be dimensioned
214
+ * already. Defining FD_SETSIZE unconditionally here would only move the bound
215
+ * used by EV_WIN_FD_SET, not the array it indexes. Take whatever is in effect
216
+ * and only supply a default when nothing has been decided yet. */
217
+ # ifndef FD_SETSIZE
218
+ # define FD_SETSIZE 1024
219
+ # endif
214
220
  # include <winsock2.h>
215
221
  # include <windows.h>
216
222
  # ifndef EV_SELECT_IS_WINSOCKET
@@ -107,6 +107,16 @@ if (__i == ((fd_set *)(set))->fd_count) {\
107
107
  #define EV_WIN_FD_ZERO(set) (((fd_set *)(set))->fd_count=0)
108
108
  #define EV_WIN_FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set *)(set))
109
109
  #define EV_WIN_FD_COUNT(set) (((fd_set *)(set))->fd_count)
110
+
111
+ /*
112
+ fd_set is dimensioned by whatever FD_SETSIZE was in effect when winsock2.h was
113
+ first pulled in, but EV_WIN_FD_SET and select_modify bound-check against the
114
+ FD_SETSIZE visible here. The two are decided by separate paths, so if the bound
115
+ ever exceeds the declared array we would write past the end of the allocation in
116
+ select_init. Catch that at build time instead.
117
+ */
118
+ typedef char coolio_fd_setsize_matches_fd_set[
119
+ (sizeof (((fd_set *)0)->fd_array) / sizeof (SOCKET) >= (size_t)FD_SETSIZE) ? 1 : -1];
110
120
  /* ######################################## */
111
121
  #else
112
122
  #define EV_WIN_FD_CLR FD_CLR
data/gems.rb CHANGED
@@ -7,3 +7,7 @@ group :maintenance, optional: true do
7
7
  gem "bake-gem"
8
8
  gem "bake-modernize"
9
9
  end
10
+
11
+ group :development, :test do
12
+ gem 'ruby_memcheck', '~> 3.0' if RUBY_PLATFORM.include?('linux')
13
+ end