cool.io 1.9.4 → 1.10.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.
@@ -5,6 +5,9 @@
5
5
  */
6
6
 
7
7
  #include "ruby.h"
8
+ #ifdef _WIN32
9
+ #include "ruby/encoding.h"
10
+ #endif
8
11
  #include "ev_wrap.h"
9
12
 
10
13
  #include "cool.io.h"
@@ -74,12 +77,16 @@ void Init_coolio_stat_watcher()
74
77
  * specified how often in seconds the path should be polled for changes.
75
78
  * Setting interval to zero uses an "automatic" value (typically around 5
76
79
  * seconds) which optimizes performance. Otherwise, values less than
77
- * 0.1 are not particularly meaningful. Where available (at present, on Linux)
78
- * high performance file monitoring interfaces will be used instead of polling.
80
+ * 0.1 are not particularly meaningful. Where available (on Linux and Windows),
81
+ * high performance file monitoring interfaces are used. On Windows, polling
82
+ * remains necessary because notification timing is not guaranteed. An attached
83
+ * Windows watcher may also prevent renaming an ancestor directory until detached.
79
84
  */
80
85
  static VALUE Coolio_StatWatcher_initialize(int argc, VALUE *argv, VALUE self)
81
86
  {
82
- VALUE path, interval;
87
+ VALUE path, interval;
88
+ const char *path_str;
89
+ long path_len;
83
90
  struct Coolio_Watcher *watcher_data;
84
91
 
85
92
  rb_scan_args(argc, argv, "11", &path, &interval);
@@ -89,13 +96,29 @@ static VALUE Coolio_StatWatcher_initialize(int argc, VALUE *argv, VALUE self)
89
96
  path = rb_String(path);
90
97
  rb_iv_set(self, "@path", path);
91
98
 
99
+ #ifdef _WIN32
100
+ /* libev's wide Windows APIs and stat wrapper use UTF-8 paths. */
101
+ path = rb_str_export_to_enc(path, rb_utf8_encoding());
102
+ #endif
103
+ path_str = StringValueCStr(path);
104
+
92
105
  watcher_data = Coolio_Watcher_ptr(self);
93
106
 
107
+ /* libev keeps the path pointer passed to ev_stat_init() for the lifetime of
108
+ * the watcher. RSTRING_PTR(path) points into a Ruby String whose buffer
109
+ * GC.compact may relocate, leaving libev with a dangling pointer. Keep an
110
+ * owned copy instead; it is released in Coolio_Watcher_free(). */
111
+ path_len = strlen(path_str);
112
+ if(watcher_data->stat_path)
113
+ xfree(watcher_data->stat_path);
114
+ watcher_data->stat_path = xmalloc(path_len + 1);
115
+ memcpy(watcher_data->stat_path, path_str, path_len + 1);
116
+
94
117
  watcher_data->dispatch_callback = Coolio_StatWatcher_dispatch_callback;
95
118
  ev_stat_init(
96
119
  &watcher_data->event_types.ev_stat,
97
120
  Coolio_StatWatcher_libev_callback,
98
- RSTRING_PTR(path),
121
+ watcher_data->stat_path,
99
122
  interval == Qnil ? 0 : NUM2DBL(interval)
100
123
  );
101
124
  watcher_data->event_types.ev_stat.data = (void *)self;
@@ -123,7 +146,7 @@ static VALUE Coolio_StatWatcher_attach(VALUE self, VALUE loop)
123
146
  watcher_data = Coolio_Watcher_ptr(self);
124
147
 
125
148
  if(watcher_data->loop != Qnil)
126
- Coolio_StatWatcher_detach(self);
149
+ rb_funcall(self, rb_intern("detach"), 0);
127
150
 
128
151
  watcher_data->loop = loop;
129
152
 
@@ -235,20 +258,22 @@ static VALUE Coolio_StatInfo_build(ev_statdata *statdata_struct)
235
258
  VALUE blksize = Qnil;
236
259
  VALUE blocks = Qnil;
237
260
 
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);
261
+ mtime = rb_funcall(cTime, at_method, 1, LL2NUM((long long)statdata_struct->st_mtime));
262
+ ctime = rb_funcall(cTime, at_method, 1, LL2NUM((long long)statdata_struct->st_ctime));
263
+ atime = rb_funcall(cTime, at_method, 1, LL2NUM((long long)statdata_struct->st_atime));
264
+ dev = LL2NUM((long long)statdata_struct->st_dev);
265
+ ino = ULL2NUM((unsigned long long)statdata_struct->st_ino);
266
+ mode = LL2NUM((long long)statdata_struct->st_mode);
267
+ nlink = LL2NUM((long long)statdata_struct->st_nlink);
268
+ uid = LL2NUM((long long)statdata_struct->st_uid);
269
+ gid = LL2NUM((long long)statdata_struct->st_gid);
270
+ rdev = LL2NUM((long long)statdata_struct->st_rdev);
271
+ size = LL2NUM((long long)statdata_struct->st_size);
272
+ #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
273
+ blksize = LL2NUM((long long)statdata_struct->st_blksize);
274
+ #endif
275
+ #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
276
+ blocks = LL2NUM((long long)statdata_struct->st_blocks);
252
277
  #endif
253
278
 
254
279
  return rb_struct_new(cCoolio_StatInfo,
@@ -102,7 +102,7 @@ static VALUE Coolio_TimerWatcher_attach(VALUE self, VALUE loop)
102
102
  watcher_data = Coolio_Watcher_ptr(self);
103
103
 
104
104
  if(watcher_data->loop != Qnil)
105
- Coolio_TimerWatcher_detach(self);
105
+ rb_funcall(self, rb_intern("detach"), 0);
106
106
 
107
107
  watcher_data->loop = loop;
108
108
 
@@ -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");
@@ -7,23 +7,6 @@
7
7
  #ifndef WATCHER_H
8
8
  #define WATCHER_H
9
9
 
10
- #define Watcher_Attach(watcher_type, detach_func, watcher, loop) \
11
- struct Coolio_Watcher *watcher_data; \
12
- struct Coolio_Loop *loop_data; \
13
- \
14
- if(!rb_obj_is_kind_of(loop, cCoolio_Loop)) \
15
- rb_raise(rb_eArgError, "expected loop to be an instance of Coolio::Loop, not %s", RSTRING_PTR(rb_inspect(loop))); \
16
- \
17
- watcher_data = Coolio_Watcher_ptr(watcher); \
18
- loop_data = Coolio_Loop_ptr(loop); \
19
- \
20
- if(watcher_data->loop != Qnil) \
21
- detach_func(watcher); \
22
- \
23
- watcher_data->loop = loop; \
24
- ev_##watcher_type##_start(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type); \
25
- rb_call_super(1, &loop)
26
-
27
10
  #define Watcher_Detach(watcher_type, watcher) \
28
11
  struct Coolio_Watcher *watcher_data; \
29
12
  \
data/ext/libev/ev.c CHANGED
@@ -210,9 +210,20 @@
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>
222
+ # include <process.h>
223
+ # ifndef EV_WIN32_STAT_CODEPAGE
224
+ /* Interpret narrow paths using the active Windows code page unless overridden. */
225
+ # define EV_WIN32_STAT_CODEPAGE CP_ACP
226
+ # endif
216
227
  # ifndef EV_SELECT_IS_WINSOCKET
217
228
  # define EV_SELECT_IS_WINSOCKET 1
218
229
  # endif
@@ -1849,6 +1860,10 @@ typedef struct
1849
1860
 
1850
1861
  #endif
1851
1862
 
1863
+ #if defined(_WIN32) && EV_STAT_ENABLE && EV_ASYNC_ENABLE
1864
+ static void win32_fs_destroy (EV_P);
1865
+ #endif
1866
+
1852
1867
  #if EV_FEATURE_API
1853
1868
  # define EV_RELEASE_CB if (expect_false (release_cb)) release_cb (EV_A)
1854
1869
  # define EV_ACQUIRE_CB if (expect_false (acquire_cb)) acquire_cb (EV_A)
@@ -2400,8 +2415,8 @@ static ANSIG signals [EV_NSIG - 1];
2400
2415
  #if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE
2401
2416
 
2402
2417
  noinline ecb_cold
2403
- static void
2404
- evpipe_init (EV_P)
2418
+ static int
2419
+ evpipe_init_try (EV_P)
2405
2420
  {
2406
2421
  if (!ev_is_active (&pipe_w))
2407
2422
  {
@@ -2416,8 +2431,8 @@ evpipe_init (EV_P)
2416
2431
  if (fds [1] < 0)
2417
2432
  # endif
2418
2433
  {
2419
- while (pipe (fds))
2420
- ev_syserr ("(libev) error creating signal/async pipe");
2434
+ if (pipe (fds))
2435
+ return 0;
2421
2436
 
2422
2437
  fd_intern (fds [0]);
2423
2438
  }
@@ -2443,6 +2458,16 @@ evpipe_init (EV_P)
2443
2458
  ev_io_start (EV_A_ &pipe_w);
2444
2459
  ev_unref (EV_A); /* watcher should not keep loop alive */
2445
2460
  }
2461
+
2462
+ return 1;
2463
+ }
2464
+
2465
+ noinline ecb_cold
2466
+ static void
2467
+ evpipe_init (EV_P)
2468
+ {
2469
+ while (!evpipe_init_try (EV_A))
2470
+ ev_syserr ("(libev) error creating signal/async pipe");
2446
2471
  }
2447
2472
 
2448
2473
  inline_speed void
@@ -2908,6 +2933,10 @@ loop_init (EV_P_ unsigned int flags) EV_THROW
2908
2933
  pipe_write_wanted = 0;
2909
2934
  evpipe [0] = -1;
2910
2935
  evpipe [1] = -1;
2936
+ #if defined(_WIN32) && EV_STAT_ENABLE && EV_ASYNC_ENABLE
2937
+ win32_fs = 0;
2938
+ win32_fs_disabled = (flags & EVFLAG_NOINOTIFY) != 0;
2939
+ #endif
2911
2940
  #if EV_USE_INOTIFY
2912
2941
  fs_fd = flags & EVFLAG_NOINOTIFY ? -1 : -2;
2913
2942
  #endif
@@ -2976,6 +3005,10 @@ ev_loop_destroy (EV_P)
2976
3005
  }
2977
3006
  #endif
2978
3007
 
3008
+ #if defined(_WIN32) && EV_STAT_ENABLE && EV_ASYNC_ENABLE
3009
+ win32_fs_destroy (EV_A);
3010
+ #endif
3011
+
2979
3012
  if (ev_is_active (&pipe_w))
2980
3013
  {
2981
3014
  /*ev_ref (EV_A);*/
@@ -4324,17 +4357,16 @@ ev_child_stop (EV_P_ ev_child *w) EV_THROW
4324
4357
 
4325
4358
  #if EV_STAT_ENABLE
4326
4359
 
4327
- # ifdef _WIN32
4328
- # undef lstat
4329
- # define lstat(a,b) _stati64 (a,b)
4330
- # endif
4331
-
4332
4360
  #define DEF_STAT_INTERVAL 5.0074891
4333
4361
  #define NFS_STAT_INTERVAL 30.1074891 /* for filesystems potentially failing inotify */
4334
4362
  #define MIN_STAT_INTERVAL 0.1074891
4335
4363
 
4336
4364
  noinline static void stat_timer_cb (EV_P_ ev_timer *w_, int revents);
4337
4365
 
4366
+ #if defined(_WIN32) && EV_ASYNC_ENABLE
4367
+ # include "ev_win32_stat.c"
4368
+ #endif
4369
+
4338
4370
  #if EV_USE_INOTIFY
4339
4371
 
4340
4372
  /* the * 2 is to allow for alignment padding, which for some reason is >> 8 */
@@ -4575,7 +4607,23 @@ infy_fork (EV_P)
4575
4607
  #endif
4576
4608
 
4577
4609
  #ifdef _WIN32
4578
- # define EV_LSTAT(p,b) _stati64 (p, b)
4610
+ static int
4611
+ win32_stat (const char *path, ev_statdata *attr)
4612
+ {
4613
+ int result;
4614
+ int length = MultiByteToWideChar (EV_WIN32_STAT_CODEPAGE, 0, path, -1, 0, 0);
4615
+ wchar_t *wide;
4616
+ if (!length)
4617
+ { errno = EINVAL; return -1; }
4618
+ wide = (wchar_t *)malloc (length * sizeof (wchar_t));
4619
+ if (!wide)
4620
+ { errno = ENOMEM; return -1; }
4621
+ MultiByteToWideChar (EV_WIN32_STAT_CODEPAGE, 0, path, -1, wide, length);
4622
+ result = _wstati64 (wide, attr);
4623
+ free (wide);
4624
+ return result;
4625
+ }
4626
+ # define EV_LSTAT(p,b) win32_stat (p, b)
4579
4627
  #else
4580
4628
  # define EV_LSTAT(p,b) lstat (p, b)
4581
4629
  #endif
@@ -4583,7 +4631,7 @@ infy_fork (EV_P)
4583
4631
  void
4584
4632
  ev_stat_stat (EV_P_ ev_stat *w) EV_THROW
4585
4633
  {
4586
- if (lstat (w->path, &w->attr) < 0)
4634
+ if (EV_LSTAT (w->path, &w->attr) < 0)
4587
4635
  w->attr.st_nlink = 0;
4588
4636
  else if (!w->attr.st_nlink)
4589
4637
  w->attr.st_nlink = 1;
@@ -4595,6 +4643,11 @@ stat_timer_cb (EV_P_ ev_timer *w_, int revents)
4595
4643
  {
4596
4644
  ev_stat *w = (ev_stat *)(((char *)w_) - offsetof (ev_stat, timer));
4597
4645
 
4646
+ #if defined(_WIN32) && EV_ASYNC_ENABLE
4647
+ if (revents & EV_TIMER)
4648
+ win32_fs_add (EV_A_ w, 1); /* retry missing parents or failed directory handles */
4649
+ #endif
4650
+
4598
4651
  ev_statdata prev = w->attr;
4599
4652
  ev_stat_stat (EV_A_ w);
4600
4653
 
@@ -4617,6 +4670,12 @@ stat_timer_cb (EV_P_ ev_timer *w_, int revents)
4617
4670
  /* to ensure that prev is always different to attr */
4618
4671
  w->prev = prev;
4619
4672
 
4673
+ #if defined(_WIN32) && EV_ASYNC_ENABLE
4674
+ if (!prev.st_nlink || !w->attr.st_nlink
4675
+ || (prev.st_mode & S_IFMT) != (w->attr.st_mode & S_IFMT))
4676
+ win32_fs_add (EV_A_ w, 0); /* register directory children after creation */
4677
+ #endif
4678
+
4620
4679
  #if EV_USE_INOTIFY
4621
4680
  if (fs_fd >= 0)
4622
4681
  {
@@ -4658,6 +4717,11 @@ ev_stat_start (EV_P_ ev_stat *w) EV_THROW
4658
4717
 
4659
4718
  ev_start (EV_A_ (W)w, 1);
4660
4719
 
4720
+ #if defined(_WIN32) && EV_ASYNC_ENABLE
4721
+ win32_fs_add (EV_A_ w, 0);
4722
+ stat_timer_cb (EV_A_ &w->timer, 0); /* close the stat/registration race */
4723
+ #endif
4724
+
4661
4725
  EV_FREQUENT_CHECK;
4662
4726
  }
4663
4727
 
@@ -4670,6 +4734,10 @@ ev_stat_stop (EV_P_ ev_stat *w) EV_THROW
4670
4734
 
4671
4735
  EV_FREQUENT_CHECK;
4672
4736
 
4737
+ #if defined(_WIN32) && EV_ASYNC_ENABLE
4738
+ win32_fs_del (EV_A_ w);
4739
+ #endif
4740
+
4673
4741
  #if EV_USE_INOTIFY
4674
4742
  infy_del (EV_A_ w);
4675
4743
  #endif
@@ -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/ext/libev/ev_vars.h CHANGED
@@ -181,6 +181,10 @@ VAR (fs_hash, ANFS fs_hash [EV_INOTIFY_HASHSIZE])
181
181
  #endif
182
182
 
183
183
  VARx(EV_ATOMIC_T, sig_pending)
184
+ #if defined(_WIN32) && EV_STAT_ENABLE && EV_ASYNC_ENABLE
185
+ VARx(struct ev_win32_fs *, win32_fs)
186
+ VARx(int, win32_fs_disabled)
187
+ #endif
184
188
  #if EV_USE_SIGNALFD || EV_GENWRAP
185
189
  VARx(int, sigfd)
186
190
  VARx(ev_io, sigfd_w)