cool.io 1.0.0 → 1.9.0

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.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yaml +54 -0
  3. data/.gitignore +30 -0
  4. data/.rspec +3 -0
  5. data/{CHANGES → CHANGES.md} +175 -79
  6. data/{README.markdown → README.md} +43 -49
  7. data/Rakefile +44 -69
  8. data/appveyor.yml +18 -0
  9. data/cool.io.gemspec +18 -131
  10. data/examples/callbacked_echo_server.rb +24 -0
  11. data/ext/cool.io/buffer.c +781 -0
  12. data/ext/cool.io/cool.io.h +13 -0
  13. data/ext/cool.io/cool.io_ext.c +1 -0
  14. data/ext/cool.io/ev_wrap.h +4 -2
  15. data/ext/cool.io/extconf.rb +27 -16
  16. data/ext/cool.io/iowatcher.c +17 -8
  17. data/ext/cool.io/loop.c +36 -84
  18. data/ext/cool.io/stat_watcher.c +103 -25
  19. data/ext/cool.io/timer_watcher.c +3 -3
  20. data/ext/cool.io/utils.c +5 -0
  21. data/ext/cool.io/watcher.h +1 -1
  22. data/ext/libev/Changes +152 -3
  23. data/ext/libev/LICENSE +2 -1
  24. data/ext/libev/README +8 -8
  25. data/ext/libev/ev.c +1719 -348
  26. data/ext/libev/ev.h +146 -118
  27. data/ext/libev/ev_epoll.c +71 -20
  28. data/ext/libev/ev_kqueue.c +36 -16
  29. data/ext/libev/ev_poll.c +14 -11
  30. data/ext/libev/ev_port.c +43 -18
  31. data/ext/libev/ev_select.c +82 -25
  32. data/ext/libev/ev_vars.h +28 -21
  33. data/ext/libev/ev_win32.c +19 -10
  34. data/ext/libev/ev_wrap.h +166 -152
  35. data/ext/libev/test_libev_win32.c +1 -1
  36. data/gems.rb +9 -0
  37. data/lib/cool.io/async_watcher.rb +1 -1
  38. data/lib/cool.io/custom_require.rb +9 -0
  39. data/lib/cool.io/dns_resolver.rb +30 -22
  40. data/lib/cool.io/dsl.rb +32 -28
  41. data/lib/cool.io/io.rb +55 -35
  42. data/lib/cool.io/iowatcher.rb +1 -1
  43. data/lib/cool.io/listener.rb +35 -17
  44. data/lib/cool.io/loop.rb +17 -25
  45. data/lib/cool.io/meta.rb +6 -5
  46. data/lib/cool.io/server.rb +5 -4
  47. data/lib/cool.io/socket.rb +41 -30
  48. data/lib/cool.io/timer_watcher.rb +1 -1
  49. data/lib/cool.io/version.rb +7 -0
  50. data/lib/cool.io.rb +11 -10
  51. data/lib/coolio.rb +1 -1
  52. data/libev_ruby_gil.diff +175 -0
  53. data/libev_win_select.diff +130 -0
  54. data/spec/async_watcher_spec.rb +5 -5
  55. data/spec/dns_spec.rb +27 -8
  56. data/spec/iobuffer_spec.rb +147 -0
  57. data/spec/spec_helper.rb +15 -1
  58. data/spec/stat_watcher_spec.rb +81 -0
  59. data/spec/tcp_server_spec.rb +225 -0
  60. data/spec/tcp_socket_spec.rb +185 -0
  61. data/spec/timer_watcher_spec.rb +23 -19
  62. data/spec/udp_socket_spec.rb +58 -0
  63. data/spec/unix_listener_spec.rb +8 -8
  64. data/spec/unix_server_spec.rb +10 -8
  65. metadata +109 -112
  66. data/VERSION +0 -1
  67. data/examples/httpclient.rb +0 -38
  68. data/ext/http11_client/.gitignore +0 -5
  69. data/ext/http11_client/LICENSE +0 -31
  70. data/ext/http11_client/ext_help.h +0 -14
  71. data/ext/http11_client/extconf.rb +0 -6
  72. data/ext/http11_client/http11_client.c +0 -300
  73. data/ext/http11_client/http11_parser.c +0 -403
  74. data/ext/http11_client/http11_parser.h +0 -48
  75. data/ext/http11_client/http11_parser.rl +0 -173
  76. data/lib/cool.io/eventmachine.rb +0 -234
  77. data/lib/cool.io/http_client.rb +0 -419
  78. data/lib/rev.rb +0 -4
  79. data/spec/possible_tests/schedules_other_threads.rb +0 -48
  80. data/spec/possible_tests/test_on_resolve_failed.rb +0 -9
  81. data/spec/possible_tests/test_resolves.rb +0 -27
  82. data/spec/possible_tests/test_write_during_resolve.rb +0 -27
  83. data/spec/possible_tests/works_straight.rb +0 -71
@@ -8,7 +8,11 @@
8
8
  #define COOLIO_H
9
9
 
10
10
  #include "ruby.h"
11
+ #if defined(HAVE_RUBY_IO_H)
12
+ #include "ruby/io.h"
13
+ #else
11
14
  #include "rubyio.h"
15
+ #endif
12
16
 
13
17
  #ifdef GetReadFile
14
18
  #define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
@@ -32,6 +36,7 @@ struct Coolio_Event
32
36
  struct Coolio_Loop
33
37
  {
34
38
  struct ev_loop *ev_loop;
39
+ struct ev_timer timer; /* for timeouts */
35
40
 
36
41
  int running;
37
42
  int events_received;
@@ -55,4 +60,12 @@ struct Coolio_Watcher
55
60
 
56
61
  void Coolio_Loop_process_event(VALUE watcher, int revents);
57
62
 
63
+ void Init_coolio_buffer();
64
+ void Init_coolio_loop();
65
+ void Init_coolio_watcher();
66
+ void Init_coolio_iowatcher();
67
+ void Init_coolio_timer_watcher();
68
+ void Init_coolio_stat_watcher();
69
+ void Init_coolio_utils();
70
+
58
71
  #endif
@@ -16,6 +16,7 @@ static VALUE mCoolio = Qnil;
16
16
  void Init_cool()
17
17
  {
18
18
  /* Initializers for other modules */
19
+ Init_coolio_buffer();
19
20
  Init_coolio_loop();
20
21
  Init_coolio_watcher();
21
22
  Init_coolio_iowatcher();
@@ -1,7 +1,9 @@
1
1
  #define EV_STANDALONE /* keeps ev from requiring config.h */
2
+
2
3
  #ifdef _WIN32
3
- # define EV_SELECT_IS_WINSOCKET 1 /* configure libev for windows select */
4
- # define FD_SETSIZE 2048 /* wishful thinking, as msvcrt6 [?] seems to only allow 512 fd's and 256 sockets max */
4
+ #define EV_SELECT_IS_WINSOCKET 1 /* configure libev for windows select */
5
+ #define EV_USE_MONOTONIC 0
6
+ #define EV_USE_REALTIME 0
5
7
  #endif
6
8
 
7
9
  #include "../libev/ev.h"
@@ -4,20 +4,25 @@ libs = []
4
4
 
5
5
  $defs << "-DRUBY_VERSION_CODE=#{RUBY_VERSION.gsub(/\D/, '')}"
6
6
 
7
- if have_func('rb_thread_blocking_region')
8
- $defs << '-DHAVE_RB_THREAD_BLOCKING_REGION'
7
+ have_func('rb_io_descriptor')
8
+ have_func('rb_thread_blocking_region')
9
+ have_func('rb_thread_call_without_gvl')
10
+ have_func('rb_thread_alone')
11
+ have_func('rb_str_set_len')
12
+ have_library('rt', 'clock_gettime')
13
+
14
+ have_func("rb_io_descriptor")
15
+ have_library("c", "main")
16
+ if have_macro("HAVE_RB_IO_T", "ruby/io.h")
17
+ have_struct_member("rb_io_t", "fd", "ruby/io.h")
9
18
  end
10
19
 
11
- if have_func('rb_thread_alone')
12
- $defs << '-DHAVE_RB_THREAD_ALONE'
20
+ if have_header('ruby/io.h')
21
+ $defs << '-DHAVE_RUBY_IO_H'
13
22
  end
14
23
 
15
- if have_func('rb_str_set_len')
16
- $defs << '-DHAVE_RB_STR_SET_LEN'
17
- end
18
-
19
- if have_library('rt', 'clock_gettime')
20
- libs << "-lrt"
24
+ if have_header('ruby/thread.h')
25
+ $defs << '-DHAVE_RUBY_THREAD_H'
21
26
  end
22
27
 
23
28
  if have_header('sys/select.h')
@@ -40,9 +45,7 @@ if have_header('port.h')
40
45
  $defs << '-DEV_USE_PORT'
41
46
  end
42
47
 
43
- if have_header('sys/resource.h')
44
- $defs << '-DHAVE_SYS_RESOURCE_H'
45
- end
48
+ have_header('sys/resource.h')
46
49
 
47
50
  # ncpu detection specifics
48
51
  case RUBY_PLATFORM
@@ -54,16 +57,24 @@ else
54
57
  end
55
58
  end
56
59
 
60
+ if RUBY_PLATFORM =~ /solaris/
61
+ # libev/ev.c requires NSIG which is undefined if _XOPEN_SOURCE is defined
62
+ $defs << '-D__EXTENSIONS__'
63
+ end
64
+
57
65
  $LIBS << ' ' << libs.join(' ')
58
66
 
59
67
  dir_config('cool.io_ext')
60
68
  create_makefile('cool.io_ext')
61
69
 
62
70
  # win32 needs to link in "just the right order" for some reason or ioctlsocket will be mapped to an [inverted] ruby specific version. See libev mailing list for (not so helpful discussion--true cause I'm not sure, but this overcomes the symptom)
63
- if RUBY_PLATFORM =~ /mingw|win32/
71
+ if RUBY_PLATFORM =~ /mingw|mswin/
64
72
  makefile_contents = File.read 'Makefile'
65
73
 
66
- makefile_contents.gsub! 'LIBS = $(LIBRUBYARG_SHARED)', 'LIBS = -lws2_32 $(LIBRUBYARG_SHARED)'
74
+ # "Init_cool could not be found" when loading cool.io.so.
75
+ # I'm not sure why this is needed. But this line causes "1114 A dynamic link library (DLL) initialization routine failed." So I commented out this line.
76
+ #makefile_contents.gsub! 'DLDFLAGS = ', 'DLDFLAGS = -export-all '
77
+
78
+ makefile_contents.gsub! /LIBS = (.*) (\S*ws2_32\S*)/i, 'LIBS = \\2 \\1'
67
79
  File.open('Makefile', 'w') { |f| f.write makefile_contents }
68
80
  end
69
-
@@ -5,7 +5,11 @@
5
5
  */
6
6
 
7
7
  #include "ruby.h"
8
+ #if defined(HAVE_RUBY_IO_H)
9
+ #include "ruby/io.h"
10
+ #else
8
11
  #include "rubyio.h"
12
+ #endif
9
13
 
10
14
  #include "ev_wrap.h"
11
15
 
@@ -61,11 +65,6 @@ static VALUE Coolio_IOWatcher_initialize(int argc, VALUE *argv, VALUE self)
61
65
  char *flags_str;
62
66
  int events;
63
67
  struct Coolio_Watcher *watcher_data;
64
- #if HAVE_RB_IO_T
65
- rb_io_t *fptr;
66
- #else
67
- OpenFile *fptr;
68
- #endif
69
68
 
70
69
  rb_scan_args(argc, argv, "11", &io, &flags);
71
70
 
@@ -84,10 +83,20 @@ static VALUE Coolio_IOWatcher_initialize(int argc, VALUE *argv, VALUE self)
84
83
  rb_raise(rb_eArgError, "invalid event type: '%s' (must be 'r', 'w', or 'rw')", flags_str);
85
84
 
86
85
  Data_Get_Struct(self, struct Coolio_Watcher, watcher_data);
87
- GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
86
+ io = rb_convert_type(io, T_FILE, "IO", "to_io");
88
87
 
89
88
  watcher_data->dispatch_callback = Coolio_IOWatcher_dispatch_callback;
89
+ #ifdef HAVE_RB_IO_DESCRIPTOR
90
+ ev_io_init(&watcher_data->event_types.ev_io, Coolio_IOWatcher_libev_callback, rb_io_descriptor(io), events);
91
+ #else
92
+ #if defined(HAVE_RB_IO_T)
93
+ rb_io_t *fptr;
94
+ #else
95
+ OpenFile *fptr;
96
+ #endif
97
+ GetOpenFile(io, fptr);
90
98
  ev_io_init(&watcher_data->event_types.ev_io, Coolio_IOWatcher_libev_callback, FPTR_TO_FD(fptr), events);
99
+ #endif
91
100
  watcher_data->event_types.ev_io.data = (void *)self;
92
101
 
93
102
  return Qnil;
@@ -181,9 +190,9 @@ static void Coolio_IOWatcher_libev_callback(struct ev_loop *ev_loop, struct ev_i
181
190
  static void Coolio_IOWatcher_dispatch_callback(VALUE self, int revents)
182
191
  {
183
192
  if(revents & EV_READ)
184
- rb_funcall(self, rb_intern("on_readable"), 0, 0);
193
+ rb_funcall(self, rb_intern("on_readable"), 0);
185
194
  else if(revents & EV_WRITE)
186
- rb_funcall(self, rb_intern("on_writable"), 0, 0);
195
+ rb_funcall(self, rb_intern("on_writable"), 0);
187
196
  else
188
197
  rb_raise(rb_eRuntimeError, "unknown revents value for ev_io: %d", revents);
189
198
  }
data/ext/cool.io/loop.c CHANGED
@@ -6,19 +6,10 @@
6
6
 
7
7
  #include <assert.h>
8
8
  #include "ruby.h"
9
- #include "rubysig.h"
10
9
  #include "ev_wrap.h"
11
10
 
12
11
  #include "cool.io.h"
13
12
 
14
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
15
- # define Coolio_Loop_may_block_safely() (1)
16
- #elif defined(HAVE_RB_THREAD_ALONE)
17
- # define Coolio_Loop_may_block_safely() (rb_thread_alone())
18
- #else /* just in case Ruby changes: */
19
- # define Coolio_Loop_may_block_safely() (0)
20
- #endif
21
-
22
13
  static VALUE mCoolio = Qnil;
23
14
  static VALUE cCoolio_Loop = Qnil;
24
15
 
@@ -26,12 +17,11 @@ static VALUE Coolio_Loop_allocate(VALUE klass);
26
17
  static void Coolio_Loop_mark(struct Coolio_Loop *loop);
27
18
  static void Coolio_Loop_free(struct Coolio_Loop *loop);
28
19
 
29
- static VALUE Coolio_Loop_initialize(VALUE self);
30
20
  static VALUE Coolio_Loop_ev_loop_new(VALUE self, VALUE flags);
31
- static VALUE Coolio_Loop_run_once(VALUE self);
21
+ static VALUE Coolio_Loop_run_once(int argc, VALUE *argv, VALUE self);
32
22
  static VALUE Coolio_Loop_run_nonblock(VALUE self);
33
23
 
34
- static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data);
24
+ static void Coolio_Loop_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
35
25
  static void Coolio_Loop_dispatch_events(struct Coolio_Loop *loop_data);
36
26
 
37
27
  #define DEFAULT_EVENTBUF_SIZE 32
@@ -51,10 +41,9 @@ void Init_coolio_loop()
51
41
  mCoolio = rb_define_module("Coolio");
52
42
  cCoolio_Loop = rb_define_class_under(mCoolio, "Loop", rb_cObject);
53
43
  rb_define_alloc_func(cCoolio_Loop, Coolio_Loop_allocate);
54
-
55
- rb_define_method(cCoolio_Loop, "initialize", Coolio_Loop_initialize, 0);
44
+
56
45
  rb_define_private_method(cCoolio_Loop, "ev_loop_new", Coolio_Loop_ev_loop_new, 1);
57
- rb_define_method(cCoolio_Loop, "run_once", Coolio_Loop_run_once, 0);
46
+ rb_define_method(cCoolio_Loop, "run_once", Coolio_Loop_run_once, -1);
58
47
  rb_define_method(cCoolio_Loop, "run_nonblock", Coolio_Loop_run_nonblock, 0);
59
48
  }
60
49
 
@@ -63,6 +52,7 @@ static VALUE Coolio_Loop_allocate(VALUE klass)
63
52
  struct Coolio_Loop *loop = (struct Coolio_Loop *)xmalloc(sizeof(struct Coolio_Loop));
64
53
 
65
54
  loop->ev_loop = 0;
55
+ ev_init(&loop->timer, Coolio_Loop_timeout_callback);
66
56
  loop->running = 0;
67
57
  loop->events_received = 0;
68
58
  loop->eventbuf_size = DEFAULT_EVENTBUF_SIZE;
@@ -86,11 +76,6 @@ static void Coolio_Loop_free(struct Coolio_Loop *loop)
86
76
  xfree(loop);
87
77
  }
88
78
 
89
- static VALUE Coolio_Loop_initialize(VALUE self)
90
- {
91
- Coolio_Loop_ev_loop_new(self, INT2NUM(0));
92
- }
93
-
94
79
  /* Wrapper for populating a Coolio_Loop struct with a new event loop */
95
80
  static VALUE Coolio_Loop_ev_loop_new(VALUE self, VALUE flags)
96
81
  {
@@ -174,90 +159,57 @@ void Coolio_Loop_process_event(VALUE watcher, int revents)
174
159
  loop_data->events_received++;
175
160
  }
176
161
 
162
+ /* Called whenever a timeout fires on the event loop */
163
+ static void Coolio_Loop_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
164
+ {
165
+ /* We don't actually need to do anything here, the mere firing of the
166
+ timer is sufficient to interrupt the selector. However, libev still wants a callback */
167
+ }
168
+
177
169
  /**
178
170
  * call-seq:
179
171
  * Coolio::Loop.run_once -> nil
180
172
  *
181
173
  * Run the Coolio::Loop once, blocking until events are received.
182
174
  */
183
- static VALUE Coolio_Loop_run_once(VALUE self)
175
+ static VALUE Coolio_Loop_run_once(int argc, VALUE *argv, VALUE self)
184
176
  {
177
+ VALUE timeout;
185
178
  VALUE nevents;
179
+ struct Coolio_Loop *loop_data;
186
180
 
187
- if (Coolio_Loop_may_block_safely()) {
188
- struct Coolio_Loop *loop_data;
189
-
190
- Data_Get_Struct(self, struct Coolio_Loop, loop_data);
181
+ rb_scan_args(argc, argv, "01", &timeout);
191
182
 
192
- assert(loop_data->ev_loop && !loop_data->events_received);
183
+ if (timeout != Qnil && NUM2DBL(timeout) < 0) {
184
+ rb_raise(rb_eArgError, "time interval must be positive");
185
+ }
193
186
 
194
- Coolio_Loop_ev_loop_oneshot(loop_data);
195
- Coolio_Loop_dispatch_events(loop_data);
187
+ Data_Get_Struct(self, struct Coolio_Loop, loop_data);
196
188
 
197
- nevents = INT2NUM(loop_data->events_received);
198
- loop_data->events_received = 0;
189
+ assert(loop_data->ev_loop && !loop_data->events_received);
199
190
 
191
+ /* Implement the optional timeout (if any) as a ev_timer */
192
+ /* Using the technique written at
193
+ http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_timer_code_relative_and_opti,
194
+ the timer is not stopped/started everytime when timeout is specified, instead,
195
+ the timer is stopped when timeout is not specified. */
196
+ if (timeout != Qnil) {
197
+ /* It seems libev is not a fan of timers being zero, so fudge a little */
198
+ loop_data->timer.repeat = NUM2DBL(timeout) + 0.0001;
199
+ ev_timer_again(loop_data->ev_loop, &loop_data->timer);
200
200
  } else {
201
- nevents = Coolio_Loop_run_nonblock(self);
202
- rb_thread_schedule();
201
+ ev_timer_stop(loop_data->ev_loop, &loop_data->timer);
203
202
  }
204
203
 
205
- return nevents;
206
- }
207
-
208
- /* Ruby 1.9 supports blocking system calls through rb_thread_blocking_region() */
209
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
210
- #define HAVE_EV_LOOP_ONESHOT
211
- static VALUE Coolio_Loop_ev_loop_oneshot_blocking(void *ptr)
212
- {
213
- /* The libev loop has now escaped through the Global VM Lock unscathed! */
214
- struct Coolio_Loop *loop_data = (struct Coolio_Loop *)ptr;
215
-
204
+ /* libev is patched to release the GIL when it makes its system call */
216
205
  RUN_LOOP(loop_data, EVLOOP_ONESHOT);
217
-
218
- return Qnil;
219
- }
220
-
221
- static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data)
222
- {
223
- /* Use Ruby 1.9's rb_thread_blocking_region call to make a blocking system call */
224
- rb_thread_blocking_region(Coolio_Loop_ev_loop_oneshot_blocking, loop_data, RUBY_UBF_IO, 0);
225
- }
226
- #endif
227
-
228
- /* Ruby 1.8 requires us to periodically run the event loop then defer back to
229
- * the green threads scheduler */
230
- #ifndef HAVE_EV_LOOP_ONESHOT
231
- #define BLOCKING_INTERVAL 0.01 /* Block for 10ms at a time */
232
-
233
- /* Stub for scheduler's ev_timer callback */
234
- static void timer_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
235
- {
236
- ev_timer_again (ev_loop, timer);
237
- }
238
206
 
239
- /* Run the event loop, calling rb_thread_schedule every 10ms */
240
- static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data)
241
- {
242
- struct ev_timer timer;
243
- struct timeval tv;
244
-
245
- /* Set up an ev_timer to unblock the loop every 10ms */
246
- ev_timer_init(&timer, timer_callback, BLOCKING_INTERVAL, BLOCKING_INTERVAL);
247
- ev_timer_start(loop_data->ev_loop, &timer);
248
-
249
- /* Loop until we receive events */
250
- while(!loop_data->events_received) {
251
- TRAP_BEG;
252
- RUN_LOOP(loop_data, EVLOOP_ONESHOT);
253
- TRAP_END;
254
-
255
- rb_thread_schedule();
256
- }
207
+ Coolio_Loop_dispatch_events(loop_data);
208
+ nevents = INT2NUM(loop_data->events_received);
209
+ loop_data->events_received = 0;
257
210
 
258
- ev_timer_stop(loop_data->ev_loop, &timer);
211
+ return nevents;
259
212
  }
260
- #endif
261
213
 
262
214
  /**
263
215
  * call-seq:
@@ -13,6 +13,7 @@
13
13
  static VALUE mCoolio = Qnil;
14
14
  static VALUE cCoolio_Watcher = Qnil;
15
15
  static VALUE cCoolio_StatWatcher = Qnil;
16
+ static VALUE cCoolio_StatInfo = Qnil;
16
17
  static VALUE cCoolio_Loop = Qnil;
17
18
 
18
19
  static VALUE Coolio_StatWatcher_initialize(int argc, VALUE *argv, VALUE self);
@@ -20,9 +21,11 @@ static VALUE Coolio_StatWatcher_attach(VALUE self, VALUE loop);
20
21
  static VALUE Coolio_StatWatcher_detach(VALUE self);
21
22
  static VALUE Coolio_StatWatcher_enable(VALUE self);
22
23
  static VALUE Coolio_StatWatcher_disable(VALUE self);
23
- static VALUE Coolio_StatWatcher_on_change(VALUE self);
24
+ static VALUE Coolio_StatWatcher_on_change(VALUE self, VALUE previous, VALUE current);
24
25
  static VALUE Coolio_StatWatcher_path(VALUE self);
25
26
 
27
+ static VALUE Coolio_StatInfo_build(ev_statdata *statdata_struct);
28
+
26
29
  static void Coolio_StatWatcher_libev_callback(struct ev_loop *ev_loop, struct ev_stat *stat, int revents);
27
30
  static void Coolio_StatWatcher_dispatch_callback(VALUE self, int revents);
28
31
 
@@ -30,12 +33,27 @@ static void Coolio_StatWatcher_dispatch_callback(VALUE self, int revents);
30
33
  * Coolio::StatWatcher lets you create either one-shot or periodic stats which
31
34
  * run within Coolio's event loop. It's useful for creating timeouts or
32
35
  * events which fire periodically.
33
- */
36
+ **/
34
37
  void Init_coolio_stat_watcher()
35
- {
38
+ {
36
39
  mCoolio = rb_define_module("Coolio");
37
40
  cCoolio_Watcher = rb_define_class_under(mCoolio, "Watcher", rb_cObject);
38
41
  cCoolio_StatWatcher = rb_define_class_under(mCoolio, "StatWatcher", cCoolio_Watcher);
42
+ cCoolio_StatInfo = rb_struct_define("StatInfo",
43
+ "mtime",
44
+ "ctime",
45
+ "atime",
46
+ "dev",
47
+ "ino",
48
+ "mode",
49
+ "nlink",
50
+ "uid",
51
+ "guid",
52
+ "rdev",
53
+ "size",
54
+ "blksize",
55
+ "blocks",
56
+ NULL);
39
57
  cCoolio_Loop = rb_define_class_under(mCoolio, "Loop", rb_cObject);
40
58
 
41
59
  rb_define_method(cCoolio_StatWatcher, "initialize", Coolio_StatWatcher_initialize, -1);
@@ -43,14 +61,14 @@ void Init_coolio_stat_watcher()
43
61
  rb_define_method(cCoolio_StatWatcher, "detach", Coolio_StatWatcher_detach, 0);
44
62
  rb_define_method(cCoolio_StatWatcher, "enable", Coolio_StatWatcher_enable, 0);
45
63
  rb_define_method(cCoolio_StatWatcher, "disable", Coolio_StatWatcher_disable, 0);
46
- rb_define_method(cCoolio_StatWatcher, "on_change", Coolio_StatWatcher_on_change, 0);
64
+ rb_define_method(cCoolio_StatWatcher, "on_change", Coolio_StatWatcher_on_change, 2);
47
65
  rb_define_method(cCoolio_StatWatcher, "path", Coolio_StatWatcher_path, 0);
48
66
  }
49
67
 
50
68
  /**
51
69
  * call-seq:
52
70
  * Coolio::StatWatcher.initialize(path, interval = 0) -> Coolio::StatWatcher
53
- *
71
+ *
54
72
  * Create a new Coolio::StatWatcher for the given path. This will monitor the
55
73
  * given path for changes at the filesystem level. The interval argument
56
74
  * specified how often in seconds the path should be polled for changes.
@@ -75,11 +93,11 @@ static VALUE Coolio_StatWatcher_initialize(int argc, VALUE *argv, VALUE self)
75
93
 
76
94
  watcher_data->dispatch_callback = Coolio_StatWatcher_dispatch_callback;
77
95
  ev_stat_init(
78
- &watcher_data->event_types.ev_stat,
79
- Coolio_StatWatcher_libev_callback,
80
- RSTRING_PTR(path),
96
+ &watcher_data->event_types.ev_stat,
97
+ Coolio_StatWatcher_libev_callback,
98
+ RSTRING_PTR(path),
81
99
  interval == Qnil ? 0 : NUM2DBL(interval)
82
- );
100
+ );
83
101
  watcher_data->event_types.ev_stat.data = (void *)self;
84
102
 
85
103
  return Qnil;
@@ -88,7 +106,7 @@ static VALUE Coolio_StatWatcher_initialize(int argc, VALUE *argv, VALUE self)
88
106
  /**
89
107
  * call-seq:
90
108
  * Coolio::StatWatcher.attach(loop) -> Coolio::StatWatcher
91
- *
109
+ *
92
110
  * Attach the stat watcher to the given Coolio::Loop. If the watcher is already
93
111
  * attached to a loop, detach it from the old one and attach it to the new one.
94
112
  */
@@ -97,9 +115,9 @@ static VALUE Coolio_StatWatcher_attach(VALUE self, VALUE loop)
97
115
  ev_tstamp interval, timeout;
98
116
  struct Coolio_Loop *loop_data;
99
117
  struct Coolio_Watcher *watcher_data;
100
-
101
- if(!rb_obj_is_kind_of(loop, cCoolio_Loop))
102
- rb_raise(rb_eArgError, "expected loop to be an instance of Coolio::Loop");
118
+
119
+ if(!rb_obj_is_kind_of(loop, cCoolio_Loop))
120
+ rb_raise(rb_eArgError, "expected loop to be an instance of Coolio::Loop, not %s", RSTRING_PTR(rb_inspect(loop)));
103
121
 
104
122
  Data_Get_Struct(loop, struct Coolio_Loop, loop_data);
105
123
  Data_Get_Struct(self, struct Coolio_Watcher, watcher_data);
@@ -112,13 +130,13 @@ static VALUE Coolio_StatWatcher_attach(VALUE self, VALUE loop)
112
130
  ev_stat_start(loop_data->ev_loop, &watcher_data->event_types.ev_stat);
113
131
  rb_call_super(1, &loop);
114
132
 
115
- return self;
133
+ return self;
116
134
  }
117
135
 
118
136
  /**
119
137
  * call-seq:
120
138
  * Coolio::StatWatcher.detach -> Coolio::StatWatcher
121
- *
139
+ *
122
140
  * Detach the stat watcher from its current Coolio::Loop.
123
141
  */
124
142
  static VALUE Coolio_StatWatcher_detach(VALUE self)
@@ -131,7 +149,7 @@ static VALUE Coolio_StatWatcher_detach(VALUE self)
131
149
  /**
132
150
  * call-seq:
133
151
  * Coolio::StatWatcher.enable -> Coolio::StatWatcher
134
- *
152
+ *
135
153
  * Re-enable a stat watcher which has been temporarily disabled. See the
136
154
  * disable method for a more thorough explanation.
137
155
  */
@@ -139,15 +157,15 @@ static VALUE Coolio_StatWatcher_enable(VALUE self)
139
157
  {
140
158
  Watcher_Enable(stat, self);
141
159
 
142
- return self;
160
+ return self;
143
161
  }
144
162
 
145
163
  /**
146
164
  * call-seq:
147
165
  * Coolio::StatWatcher.disable -> Coolio::StatWatcher
148
- *
149
- * Temporarily disable a stat watcher which is attached to a loop.
150
- * This is useful if you wish to toggle event monitoring on and off.
166
+ *
167
+ * Temporarily disable a stat watcher which is attached to a loop.
168
+ * This is useful if you wish to toggle event monitoring on and off.
151
169
  */
152
170
  static VALUE Coolio_StatWatcher_disable(VALUE self)
153
171
  {
@@ -159,10 +177,10 @@ static VALUE Coolio_StatWatcher_disable(VALUE self)
159
177
  /**
160
178
  * call-seq:
161
179
  * Coolio::StatWatcher#on_change -> nil
162
- *
180
+ *
163
181
  * Called whenever the status of the given path changes
164
182
  */
165
- static VALUE Coolio_StatWatcher_on_change(VALUE self)
183
+ static VALUE Coolio_StatWatcher_on_change(VALUE self, VALUE previous, VALUE current)
166
184
  {
167
185
  return Qnil;
168
186
  }
@@ -170,7 +188,7 @@ static VALUE Coolio_StatWatcher_on_change(VALUE self)
170
188
  /**
171
189
  * call-seq:
172
190
  * Coolio::StatWatcher#path -> String
173
- *
191
+ *
174
192
  * Retrieve the path associated with this StatWatcher
175
193
  */
176
194
  static VALUE Coolio_StatWatcher_path(VALUE self)
@@ -186,6 +204,66 @@ static void Coolio_StatWatcher_libev_callback(struct ev_loop *ev_loop, struct ev
186
204
 
187
205
  /* Coolio::Loop dispatch callback */
188
206
  static void Coolio_StatWatcher_dispatch_callback(VALUE self, int revents)
189
- {
190
- rb_funcall(self, rb_intern("on_change"), 0, 0);
207
+ {
208
+ struct Coolio_Watcher *watcher_data;
209
+ Data_Get_Struct(self, struct Coolio_Watcher, watcher_data);
210
+
211
+ VALUE previous_statdata = Coolio_StatInfo_build(&watcher_data->event_types.ev_stat.prev);
212
+ VALUE current_statdata = Coolio_StatInfo_build(&watcher_data->event_types.ev_stat.attr);
213
+ rb_funcall(self, rb_intern("on_change"), 2, previous_statdata, current_statdata);
214
+ }
215
+
216
+ /**
217
+ * Convience method to build StatInfo structs given an ev_statdata
218
+ * */
219
+ static VALUE Coolio_StatInfo_build(ev_statdata *statdata_struct)
220
+ {
221
+ VALUE at_method = rb_intern("at");
222
+ VALUE cTime = rb_const_get(rb_cObject, rb_intern("Time"));
223
+
224
+ VALUE mtime = Qnil;
225
+ VALUE ctime = Qnil;
226
+ VALUE atime = Qnil;
227
+ VALUE dev = Qnil;
228
+ VALUE ino = Qnil;
229
+ VALUE mode = Qnil;
230
+ VALUE nlink = Qnil;
231
+ VALUE uid = Qnil;
232
+ VALUE gid = Qnil;
233
+ VALUE rdev = Qnil;
234
+ VALUE size = Qnil;
235
+ VALUE blksize = Qnil;
236
+ VALUE blocks = Qnil;
237
+
238
+ mtime = rb_funcall(cTime, at_method, 1, INT2NUM(statdata_struct->st_mtime));
239
+ ctime = rb_funcall(cTime, at_method, 1, INT2NUM(statdata_struct->st_ctime));
240
+ atime = rb_funcall(cTime, at_method, 1, INT2NUM(statdata_struct->st_atime));
241
+ dev = INT2NUM(statdata_struct->st_dev);
242
+ ino = INT2NUM(statdata_struct->st_ino);
243
+ mode = INT2NUM(statdata_struct->st_mode);
244
+ nlink = INT2NUM(statdata_struct->st_nlink);
245
+ uid = INT2NUM(statdata_struct->st_uid);
246
+ gid = INT2NUM(statdata_struct->st_gid);
247
+ rdev = INT2NUM(statdata_struct->st_rdev);
248
+ size = INT2NUM(statdata_struct->st_size);
249
+ #ifdef HAVE_ST_BLKSIZE
250
+ blksize = INT2NUM(statdata_struct->st_blksize);
251
+ blocks = INT2NUM(statdata_struct->st_blocks);
252
+ #endif
253
+
254
+ return rb_struct_new(cCoolio_StatInfo,
255
+ mtime,
256
+ ctime,
257
+ atime,
258
+ dev,
259
+ ino,
260
+ mode,
261
+ nlink,
262
+ uid,
263
+ gid,
264
+ rdev,
265
+ size,
266
+ blksize,
267
+ blocks,
268
+ NULL);
191
269
  }
@@ -95,8 +95,8 @@ static VALUE Coolio_TimerWatcher_attach(VALUE self, VALUE loop)
95
95
  struct Coolio_Loop *loop_data;
96
96
  struct Coolio_Watcher *watcher_data;
97
97
 
98
- if(!rb_obj_is_kind_of(loop, cCoolio_Loop))
99
- rb_raise(rb_eArgError, "expected loop to be an instance of Coolio::Loop");
98
+ if(!rb_obj_is_kind_of(loop, cCoolio_Loop))
99
+ rb_raise(rb_eArgError, "expected loop to be an instance of Coolio::Loop, not %s", RSTRING_PTR(rb_inspect(loop)));
100
100
 
101
101
  Data_Get_Struct(loop, struct Coolio_Loop, loop_data);
102
102
  Data_Get_Struct(self, struct Coolio_Watcher, watcher_data);
@@ -213,7 +213,7 @@ static void Coolio_TimerWatcher_libev_callback(struct ev_loop *ev_loop, struct e
213
213
  static void Coolio_TimerWatcher_dispatch_callback(VALUE self, int revents)
214
214
  {
215
215
  if(revents & EV_TIMEOUT)
216
- rb_funcall(self, rb_intern("on_timer"), 0, 0);
216
+ rb_funcall(self, rb_intern("on_timer"), 0);
217
217
  else
218
218
  rb_raise(rb_eRuntimeError, "unknown revents value for ev_timer: %d", revents);
219
219
  }
data/ext/cool.io/utils.c CHANGED
@@ -15,6 +15,11 @@
15
15
  #include <sys/sysctl.h>
16
16
  #endif
17
17
 
18
+ #ifdef HAVE_SYSCTLBYNAME
19
+ #include <sys/sysctl.h>
20
+ #include <sys/types.h>
21
+ #endif
22
+
18
23
  static VALUE mCoolio = Qnil;
19
24
  static VALUE cCoolio_Utils = Qnil;
20
25
 
@@ -12,7 +12,7 @@
12
12
  struct Coolio_Loop *loop_data; \
13
13
  \
14
14
  if(!rb_obj_is_kind_of(loop, cCoolio_Loop)) \
15
- rb_raise(rb_eArgError, "expected loop to be an instance of Coolio::Loop"); \
15
+ rb_raise(rb_eArgError, "expected loop to be an instance of Coolio::Loop, not %s", RSTRING_PTR(rb_inspect(loop))); \
16
16
  \
17
17
  Data_Get_Struct(watcher, struct Coolio_Watcher, watcher_data); \
18
18
  Data_Get_Struct(loop, struct Coolio_Loop, loop_data); \