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 +4 -4
- data/.github/workflows/test.yaml +1 -0
- data/.github/workflows/valgrind.yaml +44 -0
- data/Rakefile +6 -19
- data/cool.io.gemspec +1 -3
- data/ext/cool.io/buffer.c +50 -39
- data/ext/cool.io/cool.io.h +6 -0
- data/ext/cool.io/extconf.rb +0 -1
- data/ext/cool.io/loop.c +15 -15
- data/ext/cool.io/stat_watcher.c +15 -2
- data/ext/cool.io/watcher.c +13 -1
- data/ext/cool.io/watcher.h +7 -12
- data/ext/libev/ev.c +7 -1
- data/ext/libev/ev_select.c +10 -0
- data/gems.rb +4 -0
- data/lib/cool.io/dns_resolver.rb +52 -8
- data/lib/cool.io/version.rb +2 -2
- data/libev_win_select.diff +25 -9
- data/spec/async_watcher_spec.rb +3 -1
- data/spec/detach_race_condition_spec.rb +87 -20
- data/spec/dns_spec.rb +186 -0
- data/spec/iobuffer_spec.rb +31 -0
- data/spec/stat_watcher_spec.rb +31 -1
- data/spec/tcp_server_spec.rb +28 -4
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3da0843b8a62fd01aab297ce31f885a5856b4326bef2fb3c90d363f9576fe9a3
|
|
4
|
+
data.tar.gz: d073dd104500ccc2c1b62411c8f02c083df004353f9d2798ccf8ae41166effbd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b3f2fab2151abdbd3e0762aa04675cab8b4d196990227bf5d068f12360a62c87e669480f50e70b47b289a9c0f29deeddb28836590422faafe5d0a5ecaf6173fb
|
|
7
|
+
data.tar.gz: 233ea14784f4390b6b1f1b8fb131d5b1d20d14d0b78f9f1fd3b39f89bd25d34be505267d23d1b5647be2febcd5216e33a459c0e135ee49737334a06a0626bb47
|
data/.github/workflows/test.yaml
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
require
|
|
54
|
-
|
|
55
|
-
|
|
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
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
72
|
-
static void buffer_append(struct buffer * buf, char *str,
|
|
73
|
-
static void buffer_read(struct buffer * buf, char *str,
|
|
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,
|
|
76
|
-
static
|
|
77
|
-
static
|
|
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 ->
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
321
|
-
|
|
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
|
-
|
|
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 :
|
|
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
|
|
449
|
+
return SSIZET2NUM(buffer_write_to(buf, rb_io_descriptor(io)));
|
|
439
450
|
#else
|
|
440
|
-
return
|
|
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,
|
|
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,
|
|
590
|
+
buffer_append(struct buffer * buf, char *str, size_t len)
|
|
580
591
|
{
|
|
581
|
-
|
|
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,
|
|
627
|
+
buffer_read(struct buffer * buf, char *str, size_t len)
|
|
617
628
|
{
|
|
618
|
-
|
|
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
|
-
|
|
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,
|
|
702
|
+
buffer_copy(struct buffer * buf, char *str, size_t len)
|
|
692
703
|
{
|
|
693
|
-
|
|
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
|
|
723
|
+
static ssize_t
|
|
713
724
|
buffer_write_to(struct buffer * buf, int fd)
|
|
714
725
|
{
|
|
715
|
-
|
|
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
|
|
762
|
+
static ssize_t
|
|
752
763
|
buffer_read_from(struct buffer * buf, int fd)
|
|
753
764
|
{
|
|
754
|
-
|
|
755
|
-
|
|
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
|
}
|
data/ext/cool.io/cool.io.h
CHANGED
|
@@ -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
|
|
data/ext/cool.io/extconf.rb
CHANGED
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
|
|
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)
|
|
128
|
+
* -> release GVL -> event syscall -> reacquire GVL -> libev callback
|
|
129
|
+
* (GVL = Global VM Lock) ^^^ You are here
|
|
131
130
|
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
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
|
-
*
|
|
154
|
-
*
|
|
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 ->
|
|
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,
|
|
165
|
-
*
|
|
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) {
|
data/ext/cool.io/stat_watcher.c
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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;
|
data/ext/cool.io/watcher.c
CHANGED
|
@@ -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
|
-
|
|
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");
|
data/ext/cool.io/watcher.h
CHANGED
|
@@ -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
|
|
37
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
\
|
|
73
|
-
|
|
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
|
-
|
|
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
|
data/ext/libev/ev_select.c
CHANGED
|
@@ -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
|