cool.io 1.10.1 → 1.11.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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yaml +1 -1
- data/.github/workflows/valgrind.yaml +1 -1
- data/.gitignore +0 -3
- data/CHANGES.md +81 -0
- data/Rakefile +4 -29
- data/cool.io.gemspec +7 -1
- data/examples/dslified_echo_client.rb +1 -0
- data/examples/dslified_echo_server.rb +1 -0
- data/examples/echo_client.rb +3 -3
- data/ext/cool.io/buffer.c +0 -23
- data/ext/cool.io/cool.io.h +0 -16
- data/ext/cool.io/extconf.rb +0 -24
- data/ext/cool.io/iowatcher.c +0 -14
- data/ext/cool.io/stat_watcher.c +14 -8
- data/ext/cool.io/watcher.c +1 -1
- data/ext/libev/ev.c +39 -22
- data/ext/libev/ev_kqueue_stat.c +414 -0
- data/ext/libev/ev_vars.h +4 -0
- data/ext/libev/ev_wrap.h +4 -0
- data/lib/cool.io/version.rb +1 -1
- data/spec/fixtures/kqueue_stat_native.c +333 -0
- data/spec/fixtures/windows_stat_native.c +1 -5
- data/spec/kqueue_stat_native_spec.rb +103 -0
- data/spec/kqueue_stat_watcher_spec.rb +339 -0
- data/spec/stat_watcher_spec.rb +4 -5
- data/spec/timer_watcher_spec.rb +10 -1
- data/spec/windows_stat_native_spec.rb +2 -2
- metadata +14 -19
- data/appveyor.yml +0 -18
- data/examples/google.rb +0 -9
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
/* kqueue (EVFILT_VNODE) notification support for ev_stat. Included by ev.c.
|
|
2
|
+
* Copyright (C) 2026. Distributed under the same terms as ev.c.
|
|
3
|
+
*
|
|
4
|
+
* Each loop has a private kqueue; each registration has an O_EVTONLY fd.
|
|
5
|
+
* Events carry the ev_stat pointer as udata and trigger a stat comparison.
|
|
6
|
+
* The ordinary polling timer remains enabled for path changes that vnode
|
|
7
|
+
* notifications miss. Registration state belongs to the event loop.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include <sys/stat.h>
|
|
11
|
+
#include <sys/event.h>
|
|
12
|
+
|
|
13
|
+
struct ev_kq_stat_slot
|
|
14
|
+
{
|
|
15
|
+
ev_stat *watcher;
|
|
16
|
+
int fd; /* descriptor registered in ctx->fd; -1 while polling only */
|
|
17
|
+
int is_dir; /* fd names a directory used as a creation/replacement hint */
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
struct ev_kq_stat
|
|
21
|
+
{
|
|
22
|
+
int fd; /* the private kqueue collecting vnode events */
|
|
23
|
+
pid_t owner_pid;
|
|
24
|
+
ev_io io; /* loop watcher making ctx->fd's events observable */
|
|
25
|
+
struct ev_kq_stat_slot **slots;
|
|
26
|
+
int slotcnt, slotmax;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/* Process one bounded batch per callback so other watchers can run. */
|
|
30
|
+
#define EV_KQ_EVENT_BUFSIZE 32
|
|
31
|
+
|
|
32
|
+
static void kq_stat_add (EV_P_ ev_stat *w);
|
|
33
|
+
|
|
34
|
+
static unsigned int
|
|
35
|
+
kq_stat_file_flags (void)
|
|
36
|
+
{
|
|
37
|
+
return NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_DELETE;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static unsigned int
|
|
41
|
+
kq_stat_dir_flags (void)
|
|
42
|
+
{
|
|
43
|
+
/* NOTE_WRITE is the hint for directory entry changes; DELETE/RENAME
|
|
44
|
+
* indicate that the hint directory itself may have moved or disappeared. */
|
|
45
|
+
return NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME | NOTE_DELETE;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* ctx->io watches the private kqueue for readability when events are pending. */
|
|
49
|
+
static int
|
|
50
|
+
kq_stat_register (struct ev_kq_stat *ctx, struct ev_kq_stat_slot *slot, int fd, int is_dir, unsigned int nflags)
|
|
51
|
+
{
|
|
52
|
+
struct kevent kev;
|
|
53
|
+
|
|
54
|
+
EV_SET (&kev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, nflags, 0, slot->watcher);
|
|
55
|
+
if (kevent (ctx->fd, &kev, 1, 0, 0, 0) < 0)
|
|
56
|
+
return 0;
|
|
57
|
+
slot->fd = fd;
|
|
58
|
+
slot->is_dir = is_dir;
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Closing the descriptor removes its kernel registration. Events already
|
|
63
|
+
* copied into a callback batch are unaffected. */
|
|
64
|
+
static void
|
|
65
|
+
kq_stat_close_slot (struct ev_kq_stat *ctx, struct ev_kq_stat_slot *slot)
|
|
66
|
+
{
|
|
67
|
+
(void)ctx;
|
|
68
|
+
if (slot->fd >= 0)
|
|
69
|
+
close (slot->fd);
|
|
70
|
+
slot->fd = -1;
|
|
71
|
+
slot->is_dir = 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Reuse a watcher's slot across registration retries; w->wd is its index. */
|
|
75
|
+
static struct ev_kq_stat_slot *
|
|
76
|
+
kq_stat_slot_get (EV_P_ ev_stat *w)
|
|
77
|
+
{
|
|
78
|
+
struct ev_kq_stat *ctx = kq_stat;
|
|
79
|
+
struct ev_kq_stat_slot *slot;
|
|
80
|
+
|
|
81
|
+
if (w->wd >= 0)
|
|
82
|
+
return ctx->slots [w->wd];
|
|
83
|
+
|
|
84
|
+
if (ctx->slotcnt == ctx->slotmax)
|
|
85
|
+
{
|
|
86
|
+
int max = ctx->slotmax ? ctx->slotmax * 2 : 16;
|
|
87
|
+
struct ev_kq_stat_slot **slots
|
|
88
|
+
= (struct ev_kq_stat_slot **)realloc (ctx->slots, max * sizeof (*slots));
|
|
89
|
+
if (!slots)
|
|
90
|
+
return 0;
|
|
91
|
+
ctx->slots = slots;
|
|
92
|
+
ctx->slotmax = max;
|
|
93
|
+
}
|
|
94
|
+
slot = (struct ev_kq_stat_slot *)calloc (1, sizeof (*slot));
|
|
95
|
+
if (!slot)
|
|
96
|
+
return 0;
|
|
97
|
+
slot->watcher = w;
|
|
98
|
+
slot->fd = -1;
|
|
99
|
+
w->wd = ctx->slotcnt;
|
|
100
|
+
ctx->slots [ctx->slotcnt++] = slot;
|
|
101
|
+
return slot;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* The index may be unset after an allocation failure or watcher stop. */
|
|
105
|
+
static struct ev_kq_stat_slot *
|
|
106
|
+
kq_stat_slot_valid (struct ev_kq_stat *ctx, ev_stat *w)
|
|
107
|
+
{
|
|
108
|
+
return w->wd >= 0 && w->wd < ctx->slotcnt && ctx->slots [w->wd]->watcher == w
|
|
109
|
+
? ctx->slots [w->wd] : 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Register notifications without changing the polling timer. O_NOFOLLOW
|
|
113
|
+
* avoids watching a symlink's target; parent directories can provide
|
|
114
|
+
* creation hints when the path cannot be opened. Reopen the path on retries
|
|
115
|
+
* to detect replacement, retaining the current registration if it still
|
|
116
|
+
* refers to the same vnode. */
|
|
117
|
+
static void
|
|
118
|
+
kq_stat_add (EV_P_ ev_stat *w)
|
|
119
|
+
{
|
|
120
|
+
struct ev_kq_stat *ctx = kq_stat;
|
|
121
|
+
struct ev_kq_stat_slot *slot;
|
|
122
|
+
int fd = -1, is_dir = 0;
|
|
123
|
+
unsigned int flags = 0;
|
|
124
|
+
struct stat st;
|
|
125
|
+
|
|
126
|
+
slot = kq_stat_slot_get (EV_A_ w);
|
|
127
|
+
if (!slot)
|
|
128
|
+
return;
|
|
129
|
+
|
|
130
|
+
/* Even a nonblocking FIFO open can wake a waiting writer. Keep known
|
|
131
|
+
* special files on polling; retain the checks below for replacement races. */
|
|
132
|
+
if (lstat (w->path, &st) == 0)
|
|
133
|
+
{
|
|
134
|
+
if (S_ISREG (st.st_mode) || S_ISDIR (st.st_mode))
|
|
135
|
+
fd = open (w->path, O_RDONLY | O_EVTONLY | O_NONBLOCK | O_NOFOLLOW | O_CLOEXEC);
|
|
136
|
+
else if (S_ISLNK (st.st_mode))
|
|
137
|
+
errno = ELOOP;
|
|
138
|
+
else
|
|
139
|
+
{
|
|
140
|
+
kq_stat_close_slot (ctx, slot);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (fd >= 0)
|
|
145
|
+
{
|
|
146
|
+
if (fstat (fd, &st) == 0 && (S_ISREG (st.st_mode) || S_ISDIR (st.st_mode)))
|
|
147
|
+
flags = kq_stat_file_flags ();
|
|
148
|
+
else
|
|
149
|
+
close (fd), fd = -1;
|
|
150
|
+
}
|
|
151
|
+
else if ((errno == ENOENT || errno == ENOTDIR || errno == ELOOP || errno == EACCES)
|
|
152
|
+
&& strlen (w->path) < 4096)
|
|
153
|
+
{
|
|
154
|
+
/* Parent hints only: they select paths to re-stat, so following a
|
|
155
|
+
* symlinked directory here cannot change what the stat reports. */
|
|
156
|
+
char path [4096];
|
|
157
|
+
strcpy (path, w->path);
|
|
158
|
+
do
|
|
159
|
+
{
|
|
160
|
+
char *pend = strrchr (path, '/');
|
|
161
|
+
if (!pend || pend == path)
|
|
162
|
+
break;
|
|
163
|
+
*pend = 0;
|
|
164
|
+
if (stat (path, &st) == 0)
|
|
165
|
+
{
|
|
166
|
+
if (S_ISDIR (st.st_mode))
|
|
167
|
+
fd = open (path, O_RDONLY | O_EVTONLY | O_NONBLOCK | O_CLOEXEC);
|
|
168
|
+
else
|
|
169
|
+
errno = ENOTDIR;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
while (fd < 0 && (errno == ENOENT || errno == ENOTDIR || errno == ELOOP || errno == EACCES));
|
|
173
|
+
|
|
174
|
+
if (fd >= 0)
|
|
175
|
+
{
|
|
176
|
+
if (fstat (fd, &st) == 0 && S_ISDIR (st.st_mode))
|
|
177
|
+
is_dir = 1, flags = kq_stat_dir_flags ();
|
|
178
|
+
else
|
|
179
|
+
close (fd), fd = -1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (fd < 0)
|
|
184
|
+
{
|
|
185
|
+
/* Keep polling when no descriptor can be opened. */
|
|
186
|
+
kq_stat_close_slot (ctx, slot);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (slot->fd >= 0 && slot->is_dir == is_dir)
|
|
191
|
+
{
|
|
192
|
+
struct stat o, n;
|
|
193
|
+
if (fstat (slot->fd, &o) == 0 && fstat (fd, &n) == 0
|
|
194
|
+
&& o.st_dev == n.st_dev && o.st_ino == n.st_ino)
|
|
195
|
+
{
|
|
196
|
+
close (fd); /* same target as before: keep the existing kevent */
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
kq_stat_close_slot (ctx, slot);
|
|
202
|
+
if (!kq_stat_register (ctx, slot, fd, is_dir, flags))
|
|
203
|
+
close (fd);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
static void
|
|
207
|
+
kq_stat_cb (EV_P_ ev_io *w_, int revents)
|
|
208
|
+
{
|
|
209
|
+
struct ev_kq_stat *ctx = kq_stat;
|
|
210
|
+
struct kevent evs [EV_KQ_EVENT_BUFSIZE];
|
|
211
|
+
struct timespec ts;
|
|
212
|
+
int n, i;
|
|
213
|
+
|
|
214
|
+
(void)w_;
|
|
215
|
+
if (revents & EV_ERROR)
|
|
216
|
+
{
|
|
217
|
+
/* fd_kill stops the unreferenced I/O watcher before feeding EV_ERROR. */
|
|
218
|
+
ev_ref (EV_A);
|
|
219
|
+
ev_io_stop (EV_A_ &ctx->io);
|
|
220
|
+
if (ctx->fd >= 0 && ctx->owner_pid == getpid () && fd_valid (ctx->fd))
|
|
221
|
+
close (ctx->fd);
|
|
222
|
+
ctx->fd = -1;
|
|
223
|
+
for (i = 0; i < ctx->slotcnt; ++i)
|
|
224
|
+
kq_stat_close_slot (ctx, ctx->slots [i]);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
ts.tv_sec = 0;
|
|
229
|
+
ts.tv_nsec = 0;
|
|
230
|
+
n = kevent (ctx->fd, 0, 0, evs, EV_KQ_EVENT_BUFSIZE, &ts);
|
|
231
|
+
for (i = 0; i < n; ++i)
|
|
232
|
+
{
|
|
233
|
+
ev_stat *w = (ev_stat *)evs [i].udata;
|
|
234
|
+
struct ev_kq_stat_slot *slot = kq_stat_slot_valid (ctx, w);
|
|
235
|
+
|
|
236
|
+
if (!slot)
|
|
237
|
+
continue;
|
|
238
|
+
|
|
239
|
+
/* Follow the path when the monitored vnode is removed or renamed. */
|
|
240
|
+
if (evs [i].fflags & (NOTE_DELETE | NOTE_RENAME))
|
|
241
|
+
kq_stat_add (EV_A_ w);
|
|
242
|
+
/* A parent hint may mean that a deeper path component now exists. */
|
|
243
|
+
else if (slot->is_dir)
|
|
244
|
+
kq_stat_add (EV_A_ w);
|
|
245
|
+
|
|
246
|
+
stat_timer_cb (EV_A_ &w->timer, 0);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* Retry kqueue creation before walking slots, so persistent failure does
|
|
251
|
+
* not rescan every watcher on each polling tick. */
|
|
252
|
+
static int
|
|
253
|
+
kq_stat_recreate (EV_P)
|
|
254
|
+
{
|
|
255
|
+
struct ev_kq_stat *ctx = kq_stat;
|
|
256
|
+
int i;
|
|
257
|
+
|
|
258
|
+
ctx->fd = kqueue ();
|
|
259
|
+
if (ctx->fd < 0)
|
|
260
|
+
return 0;
|
|
261
|
+
|
|
262
|
+
ctx->owner_pid = getpid ();
|
|
263
|
+
fd_intern (ctx->fd);
|
|
264
|
+
ev_io_set (&ctx->io, ctx->fd, EV_READ);
|
|
265
|
+
ev_io_start (EV_A_ &ctx->io);
|
|
266
|
+
ev_unref (EV_A);
|
|
267
|
+
|
|
268
|
+
for (i = 0; i < ctx->slotcnt; ++i)
|
|
269
|
+
kq_stat_add (EV_A_ ctx->slots [i]->watcher);
|
|
270
|
+
return 1;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
static int
|
|
274
|
+
kq_stat_init (EV_P)
|
|
275
|
+
{
|
|
276
|
+
struct ev_kq_stat *ctx;
|
|
277
|
+
|
|
278
|
+
if (kq_stat_disabled)
|
|
279
|
+
return 0;
|
|
280
|
+
if (kq_stat)
|
|
281
|
+
return kq_stat->fd >= 0 || kq_stat_recreate (EV_A);
|
|
282
|
+
|
|
283
|
+
ctx = (struct ev_kq_stat *)calloc (1, sizeof (*ctx));
|
|
284
|
+
if (!ctx)
|
|
285
|
+
return 0;
|
|
286
|
+
ctx->fd = kqueue ();
|
|
287
|
+
if (ctx->fd < 0)
|
|
288
|
+
{
|
|
289
|
+
free (ctx);
|
|
290
|
+
return 0;
|
|
291
|
+
}
|
|
292
|
+
ctx->owner_pid = getpid ();
|
|
293
|
+
fd_intern (ctx->fd);
|
|
294
|
+
ev_io_init (&ctx->io, kq_stat_cb, ctx->fd, EV_READ);
|
|
295
|
+
ev_set_priority (&ctx->io, EV_MAXPRI);
|
|
296
|
+
ctx->io.data = ctx;
|
|
297
|
+
ev_io_start (EV_A_ &ctx->io);
|
|
298
|
+
ev_unref (EV_A); /* internal watcher must not keep the loop running */
|
|
299
|
+
kq_stat = ctx;
|
|
300
|
+
return 1;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/* The ordinary stat timer remains the backstop when notification setup fails. */
|
|
304
|
+
static void
|
|
305
|
+
kq_stat_start (EV_P_ ev_stat *w)
|
|
306
|
+
{
|
|
307
|
+
if (kq_stat_init (EV_A))
|
|
308
|
+
{
|
|
309
|
+
kq_stat_add (EV_A_ w);
|
|
310
|
+
stat_timer_cb (EV_A_ &w->timer, 0); /* close the stat/registration race */
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
static void
|
|
315
|
+
kq_stat_poll (EV_P_ ev_stat *w, int revents)
|
|
316
|
+
{
|
|
317
|
+
if ((revents & EV_TIMER) && ev_is_active (w) && kq_stat_init (EV_A))
|
|
318
|
+
{
|
|
319
|
+
struct ev_kq_stat_slot *slot = kq_stat_slot_valid (kq_stat, w);
|
|
320
|
+
/* An active watcher can lack a slot after an allocation or initial
|
|
321
|
+
* kqueue failure. Retry it along with failed and parent registrations. */
|
|
322
|
+
if (!slot || slot->fd < 0 || slot->is_dir)
|
|
323
|
+
kq_stat_add (EV_A_ w);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
static void
|
|
328
|
+
kq_stat_refresh (EV_P_ ev_stat *w)
|
|
329
|
+
{
|
|
330
|
+
if (ev_is_active (w) && kq_stat && kq_stat->fd >= 0
|
|
331
|
+
&& (!w->prev.st_nlink || !w->attr.st_nlink
|
|
332
|
+
|| (w->prev.st_mode & S_IFMT) != (w->attr.st_mode & S_IFMT)
|
|
333
|
+
|| w->prev.st_dev != w->attr.st_dev || w->prev.st_ino != w->attr.st_ino))
|
|
334
|
+
{
|
|
335
|
+
kq_stat_add (EV_A_ w);
|
|
336
|
+
ev_stat_stat (EV_A_ w); /* close the stat/registration race */
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
static void
|
|
341
|
+
kq_stat_del (EV_P_ ev_stat *w)
|
|
342
|
+
{
|
|
343
|
+
struct ev_kq_stat *ctx = kq_stat;
|
|
344
|
+
struct ev_kq_stat_slot *slot, *last;
|
|
345
|
+
|
|
346
|
+
if (!ctx || w->wd < 0)
|
|
347
|
+
return;
|
|
348
|
+
slot = ctx->slots [w->wd];
|
|
349
|
+
last = ctx->slots [--ctx->slotcnt];
|
|
350
|
+
kq_stat_close_slot (ctx, slot);
|
|
351
|
+
free (slot);
|
|
352
|
+
ctx->slots [w->wd] = last;
|
|
353
|
+
if (last != slot)
|
|
354
|
+
last->watcher->wd = w->wd;
|
|
355
|
+
w->wd = -1;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* Recreate the private kqueue after fork and re-register existing watchers. */
|
|
359
|
+
static void
|
|
360
|
+
kq_stat_fork (EV_P)
|
|
361
|
+
{
|
|
362
|
+
struct ev_kq_stat *ctx = kq_stat;
|
|
363
|
+
int i;
|
|
364
|
+
|
|
365
|
+
if (!ctx)
|
|
366
|
+
return;
|
|
367
|
+
|
|
368
|
+
if (ev_is_active (&ctx->io))
|
|
369
|
+
{
|
|
370
|
+
ev_ref (EV_A);
|
|
371
|
+
ev_io_stop (EV_A_ &ctx->io);
|
|
372
|
+
}
|
|
373
|
+
/* The child may have reused the parent's non-inherited kqueue fd number. */
|
|
374
|
+
if (ctx->fd >= 0 && ctx->owner_pid == getpid ())
|
|
375
|
+
close (ctx->fd);
|
|
376
|
+
ctx->fd = -1;
|
|
377
|
+
|
|
378
|
+
for (i = 0; i < ctx->slotcnt; ++i)
|
|
379
|
+
{
|
|
380
|
+
struct ev_kq_stat_slot *slot = ctx->slots [i];
|
|
381
|
+
/* drop the inherited descriptors first: the new kqueue holds no
|
|
382
|
+
* registrations, and kq_stat_add would otherwise keep them */
|
|
383
|
+
kq_stat_close_slot (ctx, slot);
|
|
384
|
+
}
|
|
385
|
+
kq_stat_recreate (EV_A);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
static void
|
|
389
|
+
kq_stat_destroy (EV_P)
|
|
390
|
+
{
|
|
391
|
+
struct ev_kq_stat *ctx = kq_stat;
|
|
392
|
+
|
|
393
|
+
if (!ctx)
|
|
394
|
+
return;
|
|
395
|
+
|
|
396
|
+
/* Ruby GC may have freed the watchers already; release only loop-owned data. */
|
|
397
|
+
while (ctx->slotcnt)
|
|
398
|
+
{
|
|
399
|
+
struct ev_kq_stat_slot *slot = ctx->slots [ctx->slotcnt - 1];
|
|
400
|
+
kq_stat_close_slot (ctx, slot);
|
|
401
|
+
free (slot);
|
|
402
|
+
--ctx->slotcnt;
|
|
403
|
+
}
|
|
404
|
+
free (ctx->slots);
|
|
405
|
+
if (ev_is_active (&ctx->io))
|
|
406
|
+
{
|
|
407
|
+
ev_ref (EV_A);
|
|
408
|
+
ev_io_stop (EV_A_ &ctx->io);
|
|
409
|
+
}
|
|
410
|
+
if (ctx->fd >= 0 && ctx->owner_pid == getpid ())
|
|
411
|
+
close (ctx->fd);
|
|
412
|
+
kq_stat = 0;
|
|
413
|
+
free (ctx);
|
|
414
|
+
}
|
data/ext/libev/ev_vars.h
CHANGED
|
@@ -185,6 +185,10 @@ VARx(EV_ATOMIC_T, sig_pending)
|
|
|
185
185
|
VARx(struct ev_win32_fs *, win32_fs)
|
|
186
186
|
VARx(int, win32_fs_disabled)
|
|
187
187
|
#endif
|
|
188
|
+
#if EV_USE_KQUEUE_STAT || EV_GENWRAP
|
|
189
|
+
VARx(struct ev_kq_stat *, kq_stat)
|
|
190
|
+
VARx(int, kq_stat_disabled)
|
|
191
|
+
#endif
|
|
188
192
|
#if EV_USE_SIGNALFD || EV_GENWRAP
|
|
189
193
|
VARx(int, sigfd)
|
|
190
194
|
VARx(ev_io, sigfd_w)
|
data/ext/libev/ev_wrap.h
CHANGED
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
#define kqueue_eventmax ((loop)->kqueue_eventmax)
|
|
51
51
|
#define kqueue_events ((loop)->kqueue_events)
|
|
52
52
|
#define kqueue_fd_pid ((loop)->kqueue_fd_pid)
|
|
53
|
+
#define kq_stat ((loop)->kq_stat)
|
|
54
|
+
#define kq_stat_disabled ((loop)->kq_stat_disabled)
|
|
53
55
|
#define loop_count ((loop)->loop_count)
|
|
54
56
|
#define loop_depth ((loop)->loop_depth)
|
|
55
57
|
#define loop_done ((loop)->loop_done)
|
|
@@ -151,6 +153,8 @@
|
|
|
151
153
|
#undef kqueue_eventmax
|
|
152
154
|
#undef kqueue_events
|
|
153
155
|
#undef kqueue_fd_pid
|
|
156
|
+
#undef kq_stat
|
|
157
|
+
#undef kq_stat_disabled
|
|
154
158
|
#undef loop_count
|
|
155
159
|
#undef loop_depth
|
|
156
160
|
#undef loop_done
|
data/lib/cool.io/version.rb
CHANGED