cool.io 1.9.5 → 1.10.1

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: 3da0843b8a62fd01aab297ce31f885a5856b4326bef2fb3c90d363f9576fe9a3
4
- data.tar.gz: d073dd104500ccc2c1b62411c8f02c083df004353f9d2798ccf8ae41166effbd
3
+ metadata.gz: 84ec70f3ced722b0919a7e3348a6670e9d474343f8b831d9e942222a1edd0314
4
+ data.tar.gz: b60d4526c1c799119c43db2ae0e0f3debb85cba85086a6c65868477398a88a66
5
5
  SHA512:
6
- metadata.gz: b3f2fab2151abdbd3e0762aa04675cab8b4d196990227bf5d068f12360a62c87e669480f50e70b47b289a9c0f29deeddb28836590422faafe5d0a5ecaf6173fb
7
- data.tar.gz: 233ea14784f4390b6b1f1b8fb131d5b1d20d14d0b78f9f1fd3b39f89bd25d34be505267d23d1b5647be2febcd5216e33a459c0e135ee49737334a06a0626bb47
6
+ metadata.gz: 924f47393f2ae881e1afb914fa16bb67089840e8ef1c3599502919c5fa53263c06e0f6303d263ae376d8bff33da00ad9577b6549e3fc5128ead104a4f383e9d2
7
+ data.tar.gz: 8929ec98ce4161507309610d762c5f22f7fe4b756c361902fb0e37afa69ee2d8adc788383866ec7c9d270b80f27a99e902c2ef5128804e3b9e86c2b46c6e0535
@@ -17,6 +17,7 @@ jobs:
17
17
  os:
18
18
  - ubuntu
19
19
  - macos
20
+ - windows
20
21
 
21
22
  ruby:
22
23
  - "3.2"
@@ -17,9 +17,6 @@ jobs:
17
17
  - ubuntu
18
18
 
19
19
  ruby:
20
- - "3.2"
21
- - "3.3"
22
- - "3.4"
23
20
  - "4.0"
24
21
 
25
22
  experimental: [false]
data/Rakefile CHANGED
@@ -51,6 +51,7 @@ end
51
51
  if RUBY_PLATFORM.include?('linux')
52
52
  require 'ruby_memcheck'
53
53
  require 'ruby_memcheck/rspec/rake_task'
54
+ RubyMemcheck.config(binary_name: 'cool.io_ext')
54
55
  namespace :spec do
55
56
  RubyMemcheck::RSpec::RakeTask.new(valgrind: :compile)
56
57
  end
data/ext/cool.io/buffer.c CHANGED
@@ -15,6 +15,7 @@
15
15
  #include <unistd.h>
16
16
  #endif
17
17
  #include <errno.h>
18
+ #include <limits.h>
18
19
 
19
20
  /* 1 GiB maximum buffer size */
20
21
  #define MAX_BUFFER_SIZE 0x40000000
@@ -303,14 +304,17 @@ Coolio_Buffer_prepend(VALUE self, VALUE data)
303
304
 
304
305
  /**
305
306
  * call-seq:
306
- * Coolio::Buffer#read(length = nil) -> String
307
+ * Coolio::Buffer#read -> String
308
+ * Coolio::Buffer#read(length) -> String
307
309
  *
308
- * Read the specified abount of data from the buffer. If no value
309
- * is given the entire contents of the buffer are returned. Any data
310
- * read from the buffer is cleared.
311
- * The given length must be greater than 0 or an exception would raise.
312
- * If the buffer size is zero then an empty string is returned (regardless
313
- * the given length).
310
+ * Read data from the buffer. Any data read from the buffer is cleared.
311
+ * With no argument, the entire contents of the buffer are returned. With
312
+ * length, at most length bytes are returned.
313
+ * At most LONG_MAX bytes are returned per call; on platforms where a String
314
+ * cannot hold the whole buffer the remainder is kept for later reads.
315
+ * length is converted with to_int, so nil raises TypeError. A length less
316
+ * than 1 raises ArgumentError, even when the buffer is empty.
317
+ * Otherwise, if the buffer is empty, an empty string is returned.
314
318
  */
315
319
  static VALUE
316
320
  Coolio_Buffer_read(int argc, VALUE * argv, VALUE self)
@@ -322,20 +326,24 @@ Coolio_Buffer_read(int argc, VALUE * argv, VALUE self)
322
326
  TypedData_Get_Struct(self, struct buffer, &Coolio_Buffer_type, buf);
323
327
 
324
328
  if (rb_scan_args(argc, argv, "01", &length_obj) == 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)
329
+ /* Clamp before converting to C: long is only 32 bits on 64-bit Windows,
330
+ * and a request larger than the buffer never needs to fit in C at all.
331
+ * Keep the integer coercion and ArgumentError for non-positive lengths. */
332
+ VALUE requested = rb_to_int(length_obj);
333
+ if(rb_funcall(requested, rb_intern("<"), 1, INT2NUM(1)) == Qtrue)
329
334
  rb_raise(rb_eArgError, "length must be greater than zero");
330
- length = (size_t) requested;
331
- if(length > buf->size)
332
- length = buf->size;
335
+ length = buf->size;
336
+ if(rb_funcall(requested, rb_intern("<"), 1, SIZET2NUM(length)) == Qtrue)
337
+ length = NUM2SIZET(requested);
333
338
  } else
334
339
  length = buf->size;
335
340
 
336
341
  if(buf->size == 0)
337
342
  return rb_str_new2("");
338
343
 
344
+ if(length > (size_t) LONG_MAX)
345
+ length = (size_t) LONG_MAX;
346
+
339
347
  str = rb_str_new(0, (long) length);
340
348
  buffer_read(buf, RSTRING_PTR(str), length);
341
349
 
@@ -384,6 +392,9 @@ Coolio_Buffer_to_str(VALUE self)
384
392
 
385
393
  TypedData_Get_Struct(self, struct buffer, &Coolio_Buffer_type, buf);
386
394
 
395
+ if(buf->size > (size_t) LONG_MAX)
396
+ rb_raise(rb_eRangeError, "buffer is too large to convert to a String");
397
+
387
398
  str = rb_str_new(0, (long) buf->size);
388
399
  buffer_copy(buf, RSTRING_PTR(str), buf->size);
389
400
 
@@ -473,10 +484,13 @@ static void
473
484
  buffer_clear(struct buffer * buf)
474
485
  {
475
486
  /* Move everything into the buffer pool */
476
- if (!buf->pool_tail) {
477
- buf->pool_head = buf->pool_tail = buf->head;
478
- } else {
479
- buf->pool_tail->next = buf->head;
487
+ if (buf->head) {
488
+ if (!buf->pool_tail) {
489
+ buf->pool_head = buf->head;
490
+ } else {
491
+ buf->pool_tail->next = buf->head;
492
+ }
493
+ buf->pool_tail = buf->tail;
480
494
  }
481
495
 
482
496
  buf->head = buf->tail = 0;
@@ -1,9 +1,17 @@
1
1
  #define EV_STANDALONE /* keeps ev from requiring config.h */
2
2
 
3
3
  #ifdef _WIN32
4
+ #include "ruby.h"
4
5
  #define EV_SELECT_IS_WINSOCKET 1 /* configure libev for windows select */
5
6
  #define EV_USE_MONOTONIC 0
6
7
  #define EV_USE_REALTIME 0
8
+ #define EV_WIN32_STAT_CODEPAGE CP_UTF8 /* Ruby paths are UTF-8. */
9
+ /* Close the evpipe sockets with closesocket() through Ruby. The CRT close()
10
+ * only calls CloseHandle(), which leaves Winsock state behind. A socket later
11
+ * imported from a WSAPROTOCOL_INFO with the same handle value inherits it, and
12
+ * listen() fails with WSAEISCONN. */
13
+ #define EV_WIN32_HANDLE_TO_FD(handle) rb_w32_wrap_io_handle((HANDLE)(handle), 0)
14
+ #define EV_WIN32_CLOSE_FD(fd) rb_w32_close(fd)
7
15
  #endif
8
16
 
9
17
  #include "../libev/ev.h"
@@ -111,7 +111,24 @@ static VALUE Coolio_IOWatcher_initialize(int argc, VALUE *argv, VALUE self)
111
111
  */
112
112
  static VALUE Coolio_IOWatcher_attach(VALUE self, VALUE loop)
113
113
  {
114
- Watcher_Attach(io, Coolio_IOWatcher_detach, self, loop);
114
+ struct Coolio_Watcher *watcher_data;
115
+ struct Coolio_Loop *loop_data;
116
+
117
+ if(!rb_obj_is_kind_of(loop, cCoolio_Loop))
118
+ rb_raise(rb_eArgError, "expected loop to be an instance of Coolio::Loop, not %s", RSTRING_PTR(rb_inspect(loop)));
119
+
120
+ watcher_data = Coolio_Watcher_ptr(self);
121
+ loop_data = Coolio_Loop_ptr(loop);
122
+
123
+ if(watcher_data->loop != Qnil)
124
+ rb_funcall(self, rb_intern("detach"), 0);
125
+
126
+ /* A new loop must resolve the Windows socket handle for this fd again. */
127
+ ev_io_set(&watcher_data->event_types.ev_io,
128
+ watcher_data->event_types.ev_io.fd, watcher_data->event_types.ev_io.events);
129
+ watcher_data->loop = loop;
130
+ ev_io_start(loop_data->ev_loop, &watcher_data->event_types.ev_io);
131
+ rb_call_super(1, &loop);
115
132
 
116
133
  return self;
117
134
  }
data/ext/cool.io/loop.c CHANGED
@@ -81,10 +81,9 @@ static void Coolio_Loop_free(void *data)
81
81
  {
82
82
  struct Coolio_Loop *loop = data;
83
83
 
84
- if(!loop->ev_loop)
85
- return;
86
-
87
- ev_loop_destroy(loop->ev_loop);
84
+ if(loop->ev_loop) {
85
+ ev_loop_destroy(loop->ev_loop);
86
+ }
88
87
 
89
88
  xfree(loop->eventbuf);
90
89
  xfree(loop);
@@ -100,6 +99,9 @@ static VALUE Coolio_Loop_ev_loop_new(VALUE self, VALUE flags)
100
99
  rb_raise(rb_eRuntimeError, "loop already initialized");
101
100
 
102
101
  loop_data->ev_loop = ev_loop_new(NUM2INT(flags));
102
+ if(!loop_data->ev_loop) {
103
+ rb_raise(rb_eRuntimeError, "unable to initialize the event loop (no usable backend)");
104
+ }
103
105
 
104
106
  return Qnil;
105
107
  }
@@ -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,8 +77,10 @@ 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
  {
@@ -89,9 +94,14 @@ static VALUE Coolio_StatWatcher_initialize(int argc, VALUE *argv, VALUE self)
89
94
  interval = rb_convert_type(interval, T_FLOAT, "Float", "to_f");
90
95
 
91
96
  path = rb_String(path);
92
- path_str = StringValueCStr(path);
93
97
  rb_iv_set(self, "@path", path);
94
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
+
95
105
  watcher_data = Coolio_Watcher_ptr(self);
96
106
 
97
107
  /* libev keeps the path pointer passed to ev_stat_init() for the lifetime of
@@ -136,7 +146,7 @@ static VALUE Coolio_StatWatcher_attach(VALUE self, VALUE loop)
136
146
  watcher_data = Coolio_Watcher_ptr(self);
137
147
 
138
148
  if(watcher_data->loop != Qnil)
139
- Coolio_StatWatcher_detach(self);
149
+ rb_funcall(self, rb_intern("detach"), 0);
140
150
 
141
151
  watcher_data->loop = loop;
142
152
 
@@ -248,20 +258,22 @@ static VALUE Coolio_StatInfo_build(ev_statdata *statdata_struct)
248
258
  VALUE blksize = Qnil;
249
259
  VALUE blocks = Qnil;
250
260
 
251
- mtime = rb_funcall(cTime, at_method, 1, INT2NUM(statdata_struct->st_mtime));
252
- ctime = rb_funcall(cTime, at_method, 1, INT2NUM(statdata_struct->st_ctime));
253
- atime = rb_funcall(cTime, at_method, 1, INT2NUM(statdata_struct->st_atime));
254
- dev = INT2NUM(statdata_struct->st_dev);
255
- ino = INT2NUM(statdata_struct->st_ino);
256
- mode = INT2NUM(statdata_struct->st_mode);
257
- nlink = INT2NUM(statdata_struct->st_nlink);
258
- uid = INT2NUM(statdata_struct->st_uid);
259
- gid = INT2NUM(statdata_struct->st_gid);
260
- rdev = INT2NUM(statdata_struct->st_rdev);
261
- size = INT2NUM(statdata_struct->st_size);
262
- #ifdef HAVE_ST_BLKSIZE
263
- blksize = INT2NUM(statdata_struct->st_blksize);
264
- 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);
265
277
  #endif
266
278
 
267
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
 
@@ -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
@@ -219,6 +219,11 @@
219
219
  # endif
220
220
  # include <winsock2.h>
221
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
222
227
  # ifndef EV_SELECT_IS_WINSOCKET
223
228
  # define EV_SELECT_IS_WINSOCKET 1
224
229
  # endif
@@ -1855,6 +1860,10 @@ typedef struct
1855
1860
 
1856
1861
  #endif
1857
1862
 
1863
+ #if defined(_WIN32) && EV_STAT_ENABLE && EV_ASYNC_ENABLE
1864
+ static void win32_fs_destroy (EV_P);
1865
+ #endif
1866
+
1858
1867
  #if EV_FEATURE_API
1859
1868
  # define EV_RELEASE_CB if (expect_false (release_cb)) release_cb (EV_A)
1860
1869
  # define EV_ACQUIRE_CB if (expect_false (acquire_cb)) acquire_cb (EV_A)
@@ -2406,8 +2415,8 @@ static ANSIG signals [EV_NSIG - 1];
2406
2415
  #if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE
2407
2416
 
2408
2417
  noinline ecb_cold
2409
- static void
2410
- evpipe_init (EV_P)
2418
+ static int
2419
+ evpipe_init_try (EV_P)
2411
2420
  {
2412
2421
  if (!ev_is_active (&pipe_w))
2413
2422
  {
@@ -2422,8 +2431,8 @@ evpipe_init (EV_P)
2422
2431
  if (fds [1] < 0)
2423
2432
  # endif
2424
2433
  {
2425
- while (pipe (fds))
2426
- ev_syserr ("(libev) error creating signal/async pipe");
2434
+ if (pipe (fds))
2435
+ return 0;
2427
2436
 
2428
2437
  fd_intern (fds [0]);
2429
2438
  }
@@ -2449,6 +2458,16 @@ evpipe_init (EV_P)
2449
2458
  ev_io_start (EV_A_ &pipe_w);
2450
2459
  ev_unref (EV_A); /* watcher should not keep loop alive */
2451
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");
2452
2471
  }
2453
2472
 
2454
2473
  inline_speed void
@@ -2914,6 +2933,10 @@ loop_init (EV_P_ unsigned int flags) EV_THROW
2914
2933
  pipe_write_wanted = 0;
2915
2934
  evpipe [0] = -1;
2916
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
2917
2940
  #if EV_USE_INOTIFY
2918
2941
  fs_fd = flags & EVFLAG_NOINOTIFY ? -1 : -2;
2919
2942
  #endif
@@ -2982,6 +3005,10 @@ ev_loop_destroy (EV_P)
2982
3005
  }
2983
3006
  #endif
2984
3007
 
3008
+ #if defined(_WIN32) && EV_STAT_ENABLE && EV_ASYNC_ENABLE
3009
+ win32_fs_destroy (EV_A);
3010
+ #endif
3011
+
2985
3012
  if (ev_is_active (&pipe_w))
2986
3013
  {
2987
3014
  /*ev_ref (EV_A);*/
@@ -4330,17 +4357,16 @@ ev_child_stop (EV_P_ ev_child *w) EV_THROW
4330
4357
 
4331
4358
  #if EV_STAT_ENABLE
4332
4359
 
4333
- # ifdef _WIN32
4334
- # undef lstat
4335
- # define lstat(a,b) _stati64 (a,b)
4336
- # endif
4337
-
4338
4360
  #define DEF_STAT_INTERVAL 5.0074891
4339
4361
  #define NFS_STAT_INTERVAL 30.1074891 /* for filesystems potentially failing inotify */
4340
4362
  #define MIN_STAT_INTERVAL 0.1074891
4341
4363
 
4342
4364
  noinline static void stat_timer_cb (EV_P_ ev_timer *w_, int revents);
4343
4365
 
4366
+ #if defined(_WIN32) && EV_ASYNC_ENABLE
4367
+ # include "ev_win32_stat.c"
4368
+ #endif
4369
+
4344
4370
  #if EV_USE_INOTIFY
4345
4371
 
4346
4372
  /* the * 2 is to allow for alignment padding, which for some reason is >> 8 */
@@ -4581,7 +4607,23 @@ infy_fork (EV_P)
4581
4607
  #endif
4582
4608
 
4583
4609
  #ifdef _WIN32
4584
- # 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)
4585
4627
  #else
4586
4628
  # define EV_LSTAT(p,b) lstat (p, b)
4587
4629
  #endif
@@ -4589,7 +4631,7 @@ infy_fork (EV_P)
4589
4631
  void
4590
4632
  ev_stat_stat (EV_P_ ev_stat *w) EV_THROW
4591
4633
  {
4592
- if (lstat (w->path, &w->attr) < 0)
4634
+ if (EV_LSTAT (w->path, &w->attr) < 0)
4593
4635
  w->attr.st_nlink = 0;
4594
4636
  else if (!w->attr.st_nlink)
4595
4637
  w->attr.st_nlink = 1;
@@ -4601,6 +4643,11 @@ stat_timer_cb (EV_P_ ev_timer *w_, int revents)
4601
4643
  {
4602
4644
  ev_stat *w = (ev_stat *)(((char *)w_) - offsetof (ev_stat, timer));
4603
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
+
4604
4651
  ev_statdata prev = w->attr;
4605
4652
  ev_stat_stat (EV_A_ w);
4606
4653
 
@@ -4623,6 +4670,12 @@ stat_timer_cb (EV_P_ ev_timer *w_, int revents)
4623
4670
  /* to ensure that prev is always different to attr */
4624
4671
  w->prev = prev;
4625
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
+
4626
4679
  #if EV_USE_INOTIFY
4627
4680
  if (fs_fd >= 0)
4628
4681
  {
@@ -4664,6 +4717,11 @@ ev_stat_start (EV_P_ ev_stat *w) EV_THROW
4664
4717
 
4665
4718
  ev_start (EV_A_ (W)w, 1);
4666
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
+
4667
4725
  EV_FREQUENT_CHECK;
4668
4726
  }
4669
4727
 
@@ -4676,6 +4734,10 @@ ev_stat_stop (EV_P_ ev_stat *w) EV_THROW
4676
4734
 
4677
4735
  EV_FREQUENT_CHECK;
4678
4736
 
4737
+ #if defined(_WIN32) && EV_ASYNC_ENABLE
4738
+ win32_fs_del (EV_A_ w);
4739
+ #endif
4740
+
4679
4741
  #if EV_USE_INOTIFY
4680
4742
  infy_del (EV_A_ w);
4681
4743
  #endif
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)